動態調用DLL函數有時正常,有時報Access violation的異常
動態調用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。