我从第三方供应商那里获得了一个在Visual Basic中创建的DLL( Sensor DLL.dll )。这个DLL包含与传感器通信的函数,我需要从我正在编写的Visual C ++程序中调用这些函数。供应商不会提供头文件,我不知道Visual Basic。如果我有一个头文件,这将是一个15分钟的项目...相反,我仍然在一周后苦苦挣扎。请帮助!
我被告知DLL中的一个函数( Get_Data )具有以下形式:
Public Function Get_Data(ByVal Handle As String) As String
我尝试了几种方法来调用此Get_Data函数但没有成功:
方法1) DllImport属性
#using <mscorlib.dll>
using namespace System::Runtime::InteropServices;
namespace Sensor
{
[DllImport(“Sensor DLL.dll”,EntryPoint =“Get_Data”,CharSet = System :: Runtime :: InteropServices :: CharSet :: Unicode)] BSTR Get_Data(BSTR Handle); }
//then I call the function
Sensor::Get_Data(Handle);
这种方法似乎是我得到的最接近的方法。它编译,但运行时出现以下错误:
发生了'System.EntryPointNotFoundException'类型的未处理异常
其他信息:无法在DLL“Sensor DLL.dll”中找到名为“Get_Data”的入口点。
除了BSTR之外,我已尝试过各种数据类型组合/排列,包括BSTR *,wchar_t,int等。有可能我错过了一个,但每种数据类型都返回相同的错误。
方法2) dllimport存储类属性
__declspec(dllimport) BSTR Get_Data(BSTR Handle);
//then I call the function
Get_Data(Handle);
这个方法让我感到困惑,因为我没有指定要从中导入的DLL。我已将DLL复制到项目文件夹,我已手动将其添加到项目中,所以希望这意味着可以找到它。编译链接器时返回以下错误:
错误LNK2028:未解析的令牌(0A00034F)函数“int __cdecl main(void)”中引用的“wchar_t * __cdecl Get_Data(wchar_t *)”(?Get_Data @@ $$ FYAPA_WPA_W @ Z)(?main @@ $$ HYAHXZ)< / b>
错误LNK2019:未解析的外部符号“wchar_t * __cdecl Get_Data(wchar_t *)”(?Get_Data @@ $$ FYAPA_WPA_W @ Z)在函数“int __cdecl main(void)”中引用(?主@@ $$ HYAHXZ)
我怀疑这可能意味着我应该使用wchar_t或wchar_t *而不是BSTR,但更改为任一数据类型都会导致相同的错误。
方法3) GetProcAddress
typedef BSTR (*Get_Data_Ptr)(BSTR Handle);
HINSTANCE LoadMe;
LoadMe = LoadLibraryA("Sensor DLL.dll");
if (!LoadMe)
std::cout << "\nDLL failed to load!\n";
Get_Data_Ptr LibMainGet_Data;
LibMainGet_Data = (Get_Data_Ptr)GetProcAddress(LoadMe,"Get_Data");
//then I call the function
LibMainGet_Data(Handle);
这将编译,但在运行时会出现以下错误:
发生了'System.AccessViolationException'类型的未处理异常
附加信息:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。
当我在调试模式下将鼠标悬停在此代码的各个部分上时,似乎与第一种方法一样,它也无法在DLL中找到“Get_Data”入口点。
当你没有自己创建DLL并且没有.idl文件等时,是否有人使用C ++从VB DLL调用函数?有没有人有这样一个你可以分享的工作实例? 谢谢!
答案 0 :(得分:1)
VB6 DLL通常是COM服务器。事实上,你确实有一个.h文件,它有一个嵌入的类型库。使用Project + Properties,Common Properties,Framework和References启动它。添加新参考按钮,浏览选项卡,选择DLL。
接下来,查看+对象浏览器。您应该在列表中看到生成的Interop库。打开节点,看看有什么。您编写正常的托管代码(如gcnew)来创建COM对象并调用接口方法。您确实需要一些关于可用方法的最低限度文档来猜测它们应该如何被调用。
答案 1 :(得分:1)
我相信缺少的部分是召唤惯例。 C ++有自己的函数调用约定不同于VB6(我假设VB6,因为你没有明确声明VB.NET)。 VB6使用STDCALL约定,而C ++(取决于供应商)使用称为__cdecl的不同调用约定,这就是为什么在方法#2的编译器错误行中看到__cdecl的原因。它假定您的外部函数默认使用该调用约定。调用约定是一组描述函数如何相互调用的规则;特别是关于如何使用寄存器,传递什么订单参数,如何确定按值/按引用等等。
我建议坚持使用方法#3,因为方法#1适用于非标准C ++的托管C ++,而方法#2对我来说并不熟悉,看起来有些含糊不清。你想要尝试的是声明函数指针typedef使用STDCALL。
typedef BSTR (__stdcall *Get_Data_Ptr)(BSTR Handle);
答案 2 :(得分:1)
在OLE / COM查看器中,为了在dll / exe / ...中查看COM类型库,您必须使用“File-&gt; View TypeLib”而不是“File-&gt; Bind”打开它发送文件“
答案 3 :(得分:0)
听起来DLL实际上并没有导出名为Get_Data
的函数。打开命令提示符并使用dumpbin获取DLL的导出列表,例如:
dumpbin /exports "Sensor DLL.dll"
(dumpbin.exe位于Visual Studio安装文件夹中的VC \ bin中,通常类似于C:\Program Files\Microsoft Visual Studio 10.0
)。
然后,将Get_Data
替换为实际入口点,看看你是否有更好的运气。
答案 4 :(得分:0)
Visual Basic程序通常需要运行时才能执行。
如果您有一个COM对象(在VB中实现),请使用COM API通过C ++与它进行通信。您必须先注册COM。这是一个解释热点的线程:http://forums.devx.com/archive/index.php/t-87059.html
如果您使用.NET语言,请使用Hans Passant的方法,该方法将为您创建一个interop dll。这要容易得多。
即使具有正确名称的方法3也可能失败,因为如果它是COM对象,则需要一些名为Appartment的环境来使用COM对象。您可以致电CoInitialize
“输入”此公寓。这在TLS(线程本地存储)中创建了一些神奇的东西来执行COM魔术。我希望这可以解释为什么如果您拥有的DLL正在成为COM组件,那么您的尝试将毫无意义,很可能根据ATL,我们可以看到命名。
编辑: 我忘了说如果它是一个带有OLE/COM Viewer的COM,你也可以很容易地看到dll里面的内容(通常如果你有一个编译器你会有这样的工具)。