当前位置:松语文学 > 其他类型 >VC++_6.0程序设计从入门到精通最新章节 > VC++_6.0程序设计从入门到精通TXT下载
错误举报

第 54 章

  种方法创建DLL 文件的方法。

  1.使用关键字_declspec(dllexport)

  使用导出函数关键字_declspec(dllexport)创建MyDll.dll,该动态链接库中有两个函数,分别

  用来实现得到两个数的最大和最小值。在MyDll.h 和MyDLL.cpp 文件中分别输入如下原代码:

  //MyDLL.h

  extern "C" _declspec(dllexport) int Max(int a, int b);

  extern "C" _declspec(dllexport) int Min(int a, int b);

  //MyDll.cpp

  #include

  #include"MyDll.h"

  int Max(int a, int b)

  {

  if(a>=b)

  return a;

  else

  return b;

  }

  int Min(int a, int b)

  {

  if(a>=b)

  return b;

  else

  return a;

  }

  该动态链接库编译成功后,打开“MyDll”工程中的“debug”目录,可以看到MyDll.dll、

  MyDll.lib 两个文件。LIB 文件中包含DLL 文件名和DLL 文件中的函数名等,该文件只是对

  应DLL 文件的“映像文件”,比DLL 文件中LIB 文件的长度小,在进行隐式链接DLL 时要

  用到它。在MyDll.h 中有关键字"extern C",它可以使其他编程语言访问所编写的DLL 中的

  函数。

  2.用DEF 文件创建工程

  为了用DEF 文件创建DLL,请先删除上个例子创建的工程中的MyDll.h 文件,保留

  MyDll.cpp 并在该文件中删除#include MyDll.h 语句,同时加入一个文本文件,命名为

  MyDll.def,再添加如下代码:

  LIBRARY MyDll

  枫叶文学网www.fywxw.com

  第10 章 动态链接库

  ·261·

  EXPORTS

  Max

  Min

  其中LIBRARY 语句说明该DEF 文件属于相应的DLL,可以在EXPORTS 语句下列出要

  导出的函数名称。如果在DEF 文件中的导出函数后加@n,如Max@1Min@2,表示要导

  出的函数顺序号,在进行显式连时可以用到它。该DLL 编译成功后,打开工程中的Debug

  目录,同样也会看到MyDll.dll 和MyDll.lib 文件。

  10.4.2 MFC AppWizard[dll]方式生成常规/扩展DLL

  在MFC AppWizard[dll]下生成DLL 文件有3 种方式,在创建DLL 时,要根据实际情况

  选择创建DLL 的方式。一种是常规DLL 静态链接到MFC,另一种是常规DLL 动态链接到

  MFC。前者使用的是MFC 的静态链接库,生成的DLL 文件长度大,一般不使用这种方式;

  后者使用MFC 的动态链接库,生成的DLL 文件长度小。动态链接到MFC 的规则DLL 所有

  输出的函数应该以如下语句开始:

  AFX_MANAGE_STATE(AfxGetStaticModuleState()) //此语句用来正确地切换MFC 模块状态

  最后一种是MFC 扩展DLL,这种DLL 特点是用来建立MFC 的派生类,DLL 只被用

  MFC 类库所编写的应用程序所调用。前面已经介绍过,Extension DLLs 和Regular DLLs 不

  一样,它没有一个从CWinApp 继承而来的类的对象,编译器默认了一个DLL 入口函数

  DLLMain()作为对DLL 的初始化,可以在此函数中实现初始化,代码如下:

  BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll,DWORD reason,LPVOID flmpload)

  {

  switch(reason)

  {

  ?????//初始化代码;

  }

  return true;

  }

  参数hinstDll 表示存放DLL 的句柄,参数reason 指明调用函数的原因。对于隐式链接是

  一个非零值,对于显式链接值是零。

  在MFC 下建立DLL 文件,会自动生成def 文件框架,其他与建立传统的Non-MFC DLL

  没有什么区别,只要在相应的头文件写入关键字_declspec(dllexport)函数类型和函数名等,或

  在生成的def 文件中EXPORTS 下输入函数名就可以了。需要注意的是在向其他开发人员分

  发MFC 扩展DLL 时,不要忘记提供描述DLL 中类的头文件以及相应的LIB 文件和DLL 本

  身,此后开发人员就能充分利用开发的扩展DLL 了。

  10.4.3 导出函数调用约定

  关于动态链接库输出函数的约定有调用约定和名字修饰约定两种。调用约定决定着函数

  参数传送时入栈和出栈的顺序,以及编译器用来识别函数名字的修饰约定。名字修饰约定随

  枫叶文学网www.fywxw.com

  Visual C++ 6.0 程序设计从入门到精通

  ·262·

  调用约定和编译种类(C 或C++)的不同而变化。为了让不同的编程语言共享动态链接库带

  来的方便,函数输出时必须使用正确的调用约定,并且最好不带有任何由编译器生成的名字

  修饰。下面详细介绍实现这些需求的方法。

  1.调用约定

  Visual C++ 6.0 支持的函数调用约定有多种,在这里主要介绍_stdcall 调用约定、C 调用约

  定和_fastcall 调用约定。

  _stdcall 调用约定相当于16 位动态库中经常使用的PASCAL 调用约定。在32 位的Visual

  C++ 6.0 中PASCAL 调用约定不再被支持(实际上它已被定义为_stdcall。除了_pascal 外,

  _fortran 和_syscall 也不被支持),取而代之的是_stdcall 调用约定。两者实质上是一致的,即

  函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的

  是函数名的修饰部分(具体参见下一节介绍)。

  C 调用约定(即用_cdecl 关键字说明)和_stdcall 调用约定有所不同,虽然在参数传送方

  面是一样的,但C 调用约定对于传送参数的内存栈却是由调用者来维护的(也正因为如此,

  实现可变参数的函数只能使用该调用约定),另外,在函数名修饰约定方面也有所不同。

  _fastcall 调用约定的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用

  ECX 和EDX 传送前两个双字或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的

  函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。

  关键字_stdcall、_cdecl 和_fastcall 可以直接加在要输出的函数前,也可以在编译环境中选

  择“Setting...|C/C++|Code Generation”菜单命令,在如图10-3 所示的对话框中设置编译环境。

  当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。

  它们对应的命令行参数分别为/Gz、/Gd 和/Gr。默认状态为/Gd,即_cdecl。

  图10-3 编译环境设置

  如果要完全模仿PASCAL 调用约定首先必须使用_stdcall 调用约定,至于函数名修饰约

  定,可以通过其他方法模仿。另外需要注意的是Windows.h 支持WINAPI 宏,该宏可以将输

  出函数翻译成适当的调用约定,在WIN32 中,它被定义为_stdcall。

  2.函数名修饰约定

  函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。对于C 编译,

  枫叶文学网www.fywxw.com

  第10 章 动态链接库

  ·263·

  _stdcall 调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的

  字节数,格式为_functionncom@number。_cdecl 调用约定仅在输出函数名前加上一个下划线

  前缀,格式为_functionncom。_fastcall 调用约定在输出函数名前加上一个“@”符号,后面

  也是一个“@”符号和其参数的字节数,格式为@functionncom@number。它们均不改变输出

  函数名中字符的大小写,这和PASCAL 调用约定不同,PASCAL 约定输出的函数名无任何修

  饰且全部大写。本例将给出一种完全模仿PASCAL 调用约定的方法,在DEF 文件的EXPORTS

  段通过别名来实现。例如:

  int __stdcall MyFunc (int a,double b);

  void __stdcall InitCode (void);

  在DEF 文件中:

  EXPORTS

  MYFUNC=_MyFunc@12

  INITCODE=_InitCode@0

  Visual C++编译输出的函数名修饰较为复杂,编译时stdcall 调用约定规则如下。

  ? 以“?”标识函数名的开始,后跟函数名。

  ? 函数名后面以“@@YG”标识参数表的开始,后跟参数表。

  ? 参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型。

  ? 参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。

  其格式为“?functionncom@@YG*****@Z”或“?functionncom@@YG*XZ”,例如:

  int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”,void Test2()-----

  “?Test2@@YGXXZ”。

  cdecl 调用约定规则同上面的stdcall 调用约定类似,只是参数表的开始标识由上面的

  “@@YG”变为“@@YA”。

  fastcall 调用约定规则同上面的stdcall 调用约定,只是参数表的开始标识由上面的

  “@@YG”变为“@@YI”。

  3.得到没有修饰的函数名

  Visual C++ 输出函数时使用_declspec(dllexport) , 而不再用_export 修饰字。

  _declspec(dllexport)在C 调用约定、C 编译情况下可以去掉输出函数名的下划线前缀。extern

  "C"使得在C++中使用C 编译方式成为可能,在一个C++文件中,用extern "C"来指明该函数

  使用C 编译方式。例如,在一个C++文件中,有如下函数:

  extern "C" {void __declspec(dllexport) __cdecl Test(int var);}

  其输出函数名为Test。

  为了方便,可以使用下列预处理语句:

  #if defined(__cplusplus)

  extern "C"

  {

  #endif

  //函数原型说明

  #if defined(__cplusplus)

  枫叶文学网www.fywxw.com

  Visual C++ 6.0 程序设计从入门到精通

  ·264·

  }

  #endif

  经过上面的特殊处理,不管在C 中,还是在C++中都可以得到一个无任何修饰的函数名。

  如果不用_declspec(dllexport)修饰字输出函数,而用DEF 文件来输出函数。将要输出的

  函数修饰名罗列在EXPORTS 之下,这个名字必须与定义函数的名字完全一致,如此就得到

  一个没有任何修饰的函数名了。

  10.4.4 模块定义文件(DEF 文件)

  模块定义文件(DEF)是一个或多个用于描述DLL 属xìng的模块语句组成的文本文件,每

  个DEF 文件至少必须包含以下模块定义语句。

  ? 第一个语句必须是LIBRARY 语句,指出DLL 的名字;

  ? EXPORTS 语句列出被导出函数的名字;将要输出的函数修饰名罗列在EXPORTS

  松语文学免费小说阅读_www.16sy.com