在 C++ 中调用 Visual Basic DLL

Posted

技术标签:

【中文标题】在 C++ 中调用 Visual Basic DLL【英文标题】:Calling a Visual Basic DLL in C++ 【发布时间】:2010-09-09 19:37:49 【问题描述】:

我从第三方供应商处获得了一个用 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 函数,但都没有成功:

方法一) 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 句柄);

//then I call the function
Sensor::Get_Data(Handle);

这种方法似乎是我得到的最接近 sloution 的方法。它可以编译,但运行时会出现以下错误:

发生了“System.EntryPointNotFoundException”类型的未处理异常

附加信息:无法在 DLL“Sensor DLL.dll”中找到名为“Get_Data”的入口点。

我尝试了除 BSTR 之外的各种数据类型组合/排列,包括 BSTR*、wchar_t、int 等。可能我错过了一个,但每种数据类型都返回相同的错误。

方法二) dllimport 存储类属性

__declspec(dllimport) BSTR Get_Data(BSTR Handle);

//then I call the function
Get_Data(Handle);

这种方法让我感到困惑,因为我没有指定要从中导入的 DLL。我已将 DLL 复制到项目文件夹并手动将其添加到项目中,因此希望这意味着可以找到它。当我编译链接器返回以下错误:

错误 LNK2028:函数“int __cdecl main(void)”(?main@@$$HYAHXZ)

错误 LNK2019:函数“int __cdecl main(void)”(?main@@$$HYAHXZ) 中引用了无法解析的外部符号“wchar_t * __cdecl Get_Data(wchar_t *)”(?Get_Data@@$$FYAPA_WPA_W@Z)

我怀疑这可能意味着我应该使用 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 调用函数?有没有人可以分享这样的工作示例? 谢谢!

【问题讨论】:

属性在 C++ 中不可用。仅在托管 C++ 中,不一样 您是否有意使用 C++/CLI?或者您只是因为它是调用您熟悉的 COM 对象的唯一方式而使用它? 【参考方案1】:

VB6 DLL 通常是一个 COM 服务器。实际上,您确实拥有一个 .h 文件的等价物,它嵌入了一个类型库。从 Project + Properties、Common Properties、Framework 和 References 开始。添加新引用按钮,浏览选项卡,选择 DLL。

接下来,视图 + 对象浏览器。您应该在列表中看到生成的互操作库。打开节点看看里面有什么。您编写普通托管代码(如 gcnew)来创建 COM 对象并调用接口方法。您确实需要一些关于可用方法的最少文档来猜测应该如何调用它们。

【讨论】:

你是对的,当我这样做时,我可以在对象浏览器中看到 DLL 中的函数列表。它显示 Get_Data(System::String)。 我仍在为创建 COM 对象和调用接口方法的最后一部分而苦苦挣扎。我以前没有这样做过。 我不知道那是什么意思。问一个真实的问题,得到一个真实的答案。这真的是一个“最少文档”问题吗? 很抱歉让您感到痛苦。我仍在阅读 jdehaan 建议的 COM 文章,但这远远超出了我的经验。我在对象浏览器中看到了这些函数,但我不知道我需要做什么才能在我的程序中调用它们。我试着简单地调用 Get_Data(Handle);但它没有被识别。我也尝试先调用 CoInitialize,但这并没有帮助。对于文档,我只知道“Public Function Get_Data(ByVal Handle As String) As String”之前提到的形式,它将返回一个值在 -16777216 和 +16777215 之间的字符串。我现在必须写什么来调用这个函数? 这应该是一个新问题。我无法在评论框中填写答案。【参考方案2】:

我相信缺少的部分是调用约定。 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);

【讨论】:

【参考方案3】:

在 OLE/COM 查看器中,为了查看 dll/exe/... 中的 COM 类型库,您必须使用“File->View TypeLib”而不是“File->Bind to File”打开它"

【讨论】:

对不起。这应该与 John 的 OLE/COM 查看器问题有关(见下文)【参考方案4】:

听起来 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替换为实际的入口点,看看你是否有更好的运气。

【讨论】:

我试过了,dumpbin /exports 返回了以下内容:“文件类型:DLL,摘要 2000 .reloc 2000.rsrc 2000.sdata C000.txt” - 仅此而已【参考方案5】:

Visual Basic 程序通常需要运行时才能执行。

如果您有一个 COM 对象(在 VB 中实现),请使用 COM API 从 C++ 与其通信。您必须先注册 COM。这是一个解释热这样做的线程:http://forums.devx.com/archive/index.php/t-87059.html

如果您使用 .NET 语言,请使用 Hans Passant 的方法以及将为您创建互操作 dll 的参考。这要容易得多。

方法 1:不要这样做,如果您有一个要从 .NET 环境使用的 COM 对象,请引用它。 方法 2:由于缺少正确链接到 DLL 的 .lib 文件(静态动态链接)而出现错误 方法 3:将是一个纯动态解决方案,但您必须知道 DLL 中方法的确切名称。这些可能会根据使用的参数和调用约定而有所不同。这与您在使用方法 1 解决方案时遇到的问题非常相似(我会说实际上是相同的)。该方法的名称不是“Get_Data”而是其他名称。使用dependency viewer 之类的工具,您可以查看导出的名称。

即使名称正确的方法 3 也可能会失败,因为如果它是 COM 对象,您将需要一些名为 Appartment 的环境来使用 COM 对象。你通过调用CoInitialize“进入”这个公寓。这在 TLS(线程本地存储)中创建了一些神奇的东西来执行 COM 魔术。我希望这可以解释为什么如果您拥有的 DLL 恰好是 COM 组件,那么您的尝试将毫无意义,根据我们可以看到的 ATL 命名很可能。

编辑: 我忘了说,如果它是带有OLE/COM Viewer 的 COM,您也可以轻松查看 dll 中的内容(通常,如果您有编译器,您将拥有这样的工具)。

【讨论】:

我不熟悉 COM 对象,但会做一些阅读。感谢您的链接。但是,我无法使用 OLE/COM 查看器打开 DLL,这会给出错误:IMoniker::BindToObject 在从 (....) 创建的文件名字对象上失败。文件扩展名错误。 MK_E_INVALIDEXTENSION($800401E6)。也许它不是 COM?

以上是关于在 C++ 中调用 Visual Basic DLL的主要内容,如果未能解决你的问题,请参考以下文章

visual C++中error C2352是啥意思

visual C++中error C2352是啥意思

visual C++ 创建一个窗口却无法显示窗口的原因是啥?

如何在visual c++中使用OpenGL

C++ 中的多个定义 (Visual Basic 2010)

为啥某些 Microsoft 语言被称为“视觉”? (Visual C#、Visual Basic .NET、Visual C++)