跨语言调用动态链接库(DLL)

动态链接库(DLL)是一种实现共享库机制的方式,允许多个程序共享同一代码。在Windows平台上,DLL被广泛使用。本文将探讨如何在Visual C++(VC++)和Visual Basic(VB)应用程序中调用C++编写的DLL中的函数和类。

第一部分:在VC++应用程序中调用DLL

在VC++应用程序中调用DLL中的函数和类是相对直接的。首先,需要创建一个DLL项目,并定义想要导出的函数和类。然后,在VC++应用程序中,可以通过包含相应的头文件和使用适当的宏来调用这些函数和类。

以下是一个简单的示例,展示如何在VC++应用程序中调用DLL中的C++函数。

#include "stdafx.h" #define DLL2_EXPORTS #include "DLL2.h" BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } static inline unsigned __int64 GetCycleCount() { unsigned int timehi, timelo; __asm { rdtsc mov timehi, edx; mov timelo, eax; } return ((unsigned __int64)timehi << 32) + (unsigned __int64)timelo; } DLL2_API int __stdcall GetCpuSpeed() { const unsigned __int64 ui64StartCycle = GetCycleCount(); Sleep(1000); return static_cast((GetCycleCount() - ui64StartCycle) / 1000000); }

在上述代码中,定义了一个名为GetCpuSpeed的函数,该函数返回CPU的速度(以MHz为单位)。使用__stdcall调用约定,这是与VB兼容所必需的。

调用DLL中的C++类稍微复杂一些,因为需要确保类的方法可以被外部程序调用。这通常涉及到定义一个接口(或抽象类),然后在DLL中实现这个接口。

第二部分:在VB应用程序中调用DLL

VB应用程序中调用DLL需要一些额外的步骤,因为VB不支持C++的名称修饰(name mangling)。这意味着需要确保导出的函数名称在VB中是可识别的。

以下是一个VB代码示例,展示如何调用DLL中的C++函数。

Private Declare Function GetCpuSpeed Lib "DLL2.dll" () As Integer Private Declare Sub InitCommonControls Lib "comctl32.dll" () Private Sub Form_Initialize() InitCommonControls ChDir App.Path End Sub Private Sub Command1_Click() Dim nSpeed As Integer Dim s As String Screen.MousePointer = vbHourglass nSpeed = GetCpuSpeed() Screen.MousePointer = 0 s = nSpeed Form1.Text1.Text = "GetCpuSpeed() returned " & s End Sub Private Sub Form_Load() Form1.Text1.Text = "" End Sub

在上述代码中,使用Private Declare Function语句来声明DLL中的函数。然后,可以通过调用这个函数来获取CPU速度。

在VB中调用DLL中的C++类通常涉及到创建一个包装类,这个类将DLL中的C++类封装起来,以便VB可以更容易地调用。

第三部分:动态加载DLL

在某些情况下,可能希望在运行时动态加载DLL。这可以通过使用VC++中的LoadLibraryGetProcAddress函数来实现。

以下是一个VC++代码示例,展示如何动态加载DLL并调用其中的函数。

HMODULE hDLL = LoadLibrary("DLL2.dll"); if (hDLL != NULL) { FARPROC GetCpuSpeedFunc = GetProcAddress(hDLL, "GetCpuSpeed"); if (GetCpuSpeedFunc != NULL) { int (*GetCpuSpeed)() = (int (*)())GetCpuSpeedFunc; int speed = GetCpuSpeed(); // 使用speed变量... } FreeLibrary(hDLL); }

在上述代码中,首先使用LoadLibrary函数加载DLL,然后使用GetProcAddress函数获取DLL中函数的地址。最后,调用这个函数并处理返回值。

第四部分:使用模块定义文件

为了解决名称修饰问题,可以使用模块定义文件(.def)来明确指定DLL中要导出的函数。这允许VB应用程序直接调用这些函数,而不需要使用别名。

以下是一个模块定义文件的示例,它定义了DLL中要导出的函数。

; DLL2.def - 定义DLL2.dll的导出 LIBRARY DLL2 DESCRIPTION '一个可以从VB调用的C++ DLL' EXPORTS GetCpuSpeed

在上述文件中,使用EXPORTS语句来指定DLL中要导出的函数。这确保了VB应用程序可以正确地调用这些函数。

关键概念

为了避免名称修饰问题,并在VB中避免使用别名,可以使用模块定义文件(.def)。

当使用.def文件时,不需要使用__declspec(dllexport)__declspec(dllimport)

对于VB调用的函数,请使用__stdcall调用约定。

使用.def文件实现的DLL可以被VC++和VB程序调用(无需使用别名)。

演示

  • EXE2.exe
  • VB2.exe
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485