动态链接库(DLL)是一种实现共享库机制的方式,允许多个程序共享同一代码。在Windows平台上,DLL被广泛使用。本文将探讨如何在Visual C++(VC++)和Visual Basic(VB)应用程序中调用C++编写的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不支持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。这可以通过使用VC++中的LoadLibrary
和GetProcAddress
函数来实现。
以下是一个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程序调用(无需使用别名)。