动态调用DLL函数有时正常,有时报Access violation的异常
typedef int (add *)(int a,int b);
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
add(1,2);
}
按这个代码执行,add函数有时OK,有时报Access violation的异常。看到提示,第一反应就是内存异常了,但是这是什么引起了内存异常呢?
于是想着用一个变量来接收add的返回值看看。
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
int sum=add(1,2);
}
结果,add函数执行了之后,还是有时OK,有时报Access violation的异常。这会是什么原因?于是谷歌了下,有人提到,可能要加__cdecall。于是我果断修改代码。新代码如下:
typedef int __cdecall (add *)(int a,int b);
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
add(1,2);
}
结果,add函数执行了之后,还是有时OK,有时报Access violation的异常。纠结了!为什么 !!这时,想起在C中调用DLL函数时,需要用__stdcall,那是不是这里也要?立即改之!
typedef int __stdcall (add *)(int a,int b);
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
add(1,2);
}
运行后,一切OK。问题至此已经解决。可这是为什么呢?于是回过头来看了看__stdcall和__cdecall的说法。
_stdcall:Win32 API的调用协议,由被调用的函数清理堆栈,所有参数自右至左入栈,生成的代码中函数名有一个_做前缀和一个@和参数的总字节数(十进制)作后缀。它不支持可变参数,但它产生的代码比_cdecl短,因为没有每次调用后的清理堆栈的代码。
_cdecl:C\C++的缺省调用协议,由调用者清理堆栈,这就是C\C++中可以使用可变参数的函数的原因,所有参数自右至作入栈,生成的代码中函数名有一个_做前缀.
至此算是明白过来了,一般dll中的函数都采用extern "C" __stdcall的方式引出函数接口的,在调用DLL中的函数时,如果没有加__stdcall和__cdecall是缺省调用了__cdecall,而__cdecall是要由调用者清理堆栈的,而在代码中并没有清理堆栈的操作,只是调用了函数,所以调
用函数的地址可能会跑飞。不跑飞就OK,而一旦跑飞就出现Access violation的异常。而_stdcall是由被调用的函数清理堆栈,所以调用函数的地址不会跑飞,自然也就OK了。
具体__stdcall/__cdecal/__fastcall的区别可以参见http://blog.csdn.net/limenglandon/article/details/8553201。
typedef int (add *)(int a,int b);
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
add(1,2);
}
按这个代码执行,add函数有时OK,有时报Access violation的异常。看到提示,第一反应就是内存异常了,但是这是什么引起了内存异常呢?
于是想着用一个变量来接收add的返回值看看。
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
int sum=add(1,2);
}
结果,add函数执行了之后,还是有时OK,有时报Access violation的异常。这会是什么原因?于是谷歌了下,有人提到,可能要加__cdecall。于是我果断修改代码。新代码如下:
typedef int __cdecall (add *)(int a,int b);
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
add(1,2);
}
结果,add函数执行了之后,还是有时OK,有时报Access violation的异常。纠结了!为什么 !!这时,想起在C中调用DLL函数时,需要用__stdcall,那是不是这里也要?立即改之!
typedef int __stdcall (add *)(int a,int b);
void test()
{
hInst=LoadLibraryA("aimdtl.dll");
(FARPROC &)add=GetProcAddress(hInst,"add");
add(1,2);
}
运行后,一切OK。问题至此已经解决。可这是为什么呢?于是回过头来看了看__stdcall和__cdecall的说法。
_stdcall:Win32 API的调用协议,由被调用的函数清理堆栈,所有参数自右至左入栈,生成的代码中函数名有一个_做前缀和一个@和参数的总字节数(十进制)作后缀。它不支持可变参数,但它产生的代码比_cdecl短,因为没有每次调用后的清理堆栈的代码。
_cdecl:C\C++的缺省调用协议,由调用者清理堆栈,这就是C\C++中可以使用可变参数的函数的原因,所有参数自右至作入栈,生成的代码中函数名有一个_做前缀.
至此算是明白过来了,一般dll中的函数都采用extern "C" __stdcall的方式引出函数接口的,在调用DLL中的函数时,如果没有加__stdcall和__cdecall是缺省调用了__cdecall,而__cdecall是要由调用者清理堆栈的,而在代码中并没有清理堆栈的操作,只是调用了函数,所以调
用函数的地址可能会跑飞。不跑飞就OK,而一旦跑飞就出现Access violation的异常。而_stdcall是由被调用的函数清理堆栈,所以调用函数的地址不会跑飞,自然也就OK了。
具体__stdcall/__cdecal/__fastcall的区别可以参见http://blog.csdn.net/limenglandon/article/details/8553201。