我试图以LoadLibrary
方式加载DLL。这是我的DLL的.h
文件:
#ifdef CALLBACKTESTDLL_EXPORTS
#define CALLBACKTESTDLL_API __declspec(dllexport)
#else
#define CALLBACKTESTDLL_API __declspec(dllimport)
#endif
typedef int(CALLBACK *p)(char*);
extern "C" __declspec(dllexport) int __stdcall StrToInt(char* InputString);
extern "C" __declspec(dllexport) char* __stdcall NumCompare(p FuncP, char* InputString, int b);
它有两个功能。 StrToInt
将输入char*
转换为int
。另一个是NumCompare
,其中StrToInt
被调用,其返回值与另一个输入int
进行比较,然后NumCompare
重新比较比较结果。这两个函数将导出到名为callbacktestDLL.dll
的<。}}。
现在我想以LoadLibrary
方式调用此DLL。 (pragma comment(lib,"callbacktestDLL")
方式已经成功测试,所以我想以另一种方式测试)。
为了调用DLL函数,这就是我在CallDLL.cpp
中所做的:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
typedef int (*P_to_Func1)(char*); //pointer for StrToInt
typedef char* (*P_to_Func2)(P_to_Func1, char*, int); //pointer for NumCompare
int main()
{
//load DLL:
HINSTANCE LDLL = LoadLibrary("callbacktestDLL.dll");
if (LDLL == NULL)
{
printf("DLL loading failed");
FreeLibrary(LDLL);
}
else
{
P_to_Func1 p1 = (P_to_Func1)GetProcAddress(LDLL, "_StrToInt@4");
if (p1 = NULL)
{
printf("StrToInt loading failed");
};
P_to_Func2 p2 = (P_to_Func2)GetProcAddress(LDLL, "_NumCompare@12");
if (p2 = NULL)
{
printf("NumCompare loading failed");
};
//Call StrToInt:
std::cout << p1("1234") << std::endl;
//call NumCompare:
p2(p1, "1234", 20);
//release:
FreeLibrary(LDLL);
};
return 0;
}
当我生成CallDLL
项目时,没有错误并且已成功生成。但是,当我运行项目时,会跳出一个对话框并说CallDLL.exe has stopped
和cmd
窗口显示&#34;按任意键继续&#34;。有人能帮助我理解并解决这个问题吗?
答案 0 :(得分:1)
调用约定非常重要,因为GetProcAddress
适用于导出表,所以没有类型信息,编译器也无法识别您的错误。
Visual C ++的默认调用约定(__cdecl
)和__stdcall
之间的区别在于参数传递顺序以及谁负责恢复堆栈指针。 __cdecl
首先将最后一个参数压入堆栈,并让调用者从堆栈中删除参数。这些选择使“varargs”起作用。对于__stdcall
,首先推送第一个参数,并且调用的函数在返回时删除参数。这种选择效率更高。
当你有一个不匹配时,二进制数据被插入错误的参数,然后堆栈指针调整错误(在你的情况下,它被调整两次,但根本没有调整也是可能的)。堆栈指针的这种双重调整会在从p1("1234")
的调用返回时使程序崩溃。如果您首先调用了p2(p1, "1234", 20)
,那么将20
误解为函数指针会使程序崩溃......如果它奇迹般地存活了(决定不调用回调函数),那么当p2
堆栈不匹配时{1}}返回会导致崩溃。
密切关注调用约定,你可以避免这种痛苦。