DLL声明

有关DLL的问题现在资料很多,但是很多人写DLL时经常出现调用程序无法找到相关的导出函数的问题,这里主要的原因是DLL在声明时出的问题。
  在这里主要有两个问题,一个是调用约定的问题,一个是函数名修饰的问题,而这两个问题又是相互影响的。
  一:声明为:extern "C" int __declspec(dllexport)add(int x, int y);
  这种声明是强制用C语言方式进行修饰,且用C的默认约定,即__cdecl方式。这种方式编译产生的DLL中有一个导出函数:add,不加任何修饰。
  二:声明为:extern "C" int __declspec(dllexport) __stdcall add(int x, int y);
  这种声明是强制用C语言方式进行修饰,且用stdcall约定,这种方式编译产生的DLL中有一个导出函数:_add@8,即前面有“_”,后面加了参数长。
  三:声明为:int __declspec(dllexport) __stdcall add(int x, int y);
  这种声明不强制用C语言方式进行修饰,但是用stdcall约定,这种方式编译产生的DLL中有一个导出函数:?add@ @YGHHH @Z。这个名字很怪,后面的不好理解。
  四:声明为:int __declspec(dllexport) __cdecl add(int x, int y);
  这种声明是不强制用C语言修饰,且用cdecl约定,这种方式编译产生的DLL中有一个导出函数:?add@ @YAHHH @Z,注意看,和第三种方有一点不同。
  
  实验一:显式调用方式调用DLL中的add函数。
  #include 
  #include  
  typedef int(_stdcall *lpAddFun)(int, int); //宏定义函数指针类型
  int main(int argc, char *argv[])
  {
  HINSTANCE hDll; //DLL句柄
  lpAddFun addFun; //函数指针
  hDll = LoadLibrary("1.dll");
  if (hDll != NULL)
  {
  addFun = (lpAddFun)GetProcAddress(hDll, "add");
  if (addFun != NULL)
  {
  int result = addFun(2, 3);
  printf("%d", result);
  }
  else
  printf("No Function");
  }
  else
  printf("NO DLL");
  FreeLibrary(hDll);
  return 0;
  }
  方式一:调用成功。另外三种方式全部出错
  实验二:隐式调用DLL中的add函数
  #include  
  #include  
  #pragma comment(lib,"1.lib")
  extern "C" int __declspec(dllimport) add(int x, int y);//声明方式随着DLL中的声明方式改变
  int main(int argc, char *argv[])
  {
  int result = add(2, 3);
  printf("%d", result);
  return 0;
  }
  方式一:调用成功。另外发现一个奇怪现象:在调用程序中
  声明函数时extern "C" int __declspec(dllimport) add(int x, int y);
  写作:extern "C" int __declspec(dllecprot) add(int x, int y);同样成功,将__declspec(…)去掉也同样成功。换句话说,在调用DLL的程序中,导入是没有必要加的。
  方式二:调用成功。同样出现上面导入标识可以不加的现象。
  方式三:调用成功,同样也出现上面导入标识可以不加的现象。
  方式四:调用成功,同样也出现上面导入标识可以不加的现象。
  总结:对于DLL导出函数声明的四种写法,在动态调用时,
  声明成这样:extern "C" int __declspec(dllimport) add(int x, int y);是最好的,其它声明方式调用都没有成功。但是众所周知,windows默认的调用约定是stdcall方式,如果想别的语言能用DLL的话,最好是将调用约定写成stdcall方式,但是这种方式又不能动态调用。
  在隐式调用时,四种声明方式都是可以的,只要调用者的声明方式和DLL声明时的方式一致即可。另外,在调用程序中对于导入的声明是可以去掉的,大量书籍中关于导入、导出的问题都是利用宏来处理的,如:在头文件中写作:
  #ifdef DLL_FILE
  extern "C" int __declspec(dllexport) add(int x, int y);
  #else
  extern "C" int __declspec(dlleximport) add(int x, int y);
  这样这个头文件既可以用在DLL工程中,又可以用在调用程序中,但是经过实验发现,这个根本就没有必要,在调用者程序中不管是写作__declspce(dllexport)还是写作__declspec(dllimport)或者不写都能成功调用。
  关于DEF文件
  在DLL工程中引用DEF文件,内容如下:
  LIBRARY 1
  EXPORTS
  add @ 1
  通过depends查看导出函数全是add,但是隐式方式调用时,还是要求调用者的声明方式和DLL中声明方式相同。
  对于动态调用实验结果:
  方式一:成功。方式二:不成功,但是将函数指针改为typedef int(_stdcall *lpAddFun)(int, int);成功,即调用者要声明约定方式与DLL中声明的调用约定方式相同,否则报错。
  方式三:同方式二,同样要将函数指针改为typedef int(_stdcall *lpAddFun)(int, int);才成功完成调用。
  方式四:成功。
  总结:通过DEF文件来导出函数,调用者同样也要声明相同的调用约定,即_stdcall或是_cdecl必须要相同,其中_cdecl是C语言默认方式。  
  原贴:
  http://topic.csdn.net/u/20090824/13/BE972397-BEC9-4F7F-910C-83E0D91BCBAD.html

http://www.htchi.com/viewthread.php?tid=272191&extra=page%3D1

你可能感兴趣的:(DLL声明)