Chinaunix首页 | 论坛 | 博客
  • 博客访问: 162547
  • 博文数量: 26
  • 博客积分: 1225
  • 博客等级: 中尉
  • 技术积分: 395
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-06 21:11
文章分类

全部博文(26)

文章存档

2018年(2)

2014年(1)

2011年(3)

2010年(2)

2009年(1)

2008年(17)

我的朋友

分类: C/C++

2008-12-25 23:22:29

/*   Define   _CRTIMP   */  
   
  #ifndef   _CRTIMP  
  #ifdef   CRTDLL  
  #define   _CRTIMP   __declspec(dllexport)  
  #else     /*   CRTDLL   */  
  #ifdef   _DLL  
  #define   _CRTIMP   __declspec(dllimport)  
  #else     /*   _DLL   */  
  #define   _CRTIMP  
  #endif     /*   _DLL   */  
  #endif     /*   CRTDLL   */  
  #endif     /*   _CRTIMP   */  


关于DLL的函数:  
   
  动态链接库中定义有两种函数:导出函数(export   function)和内部函数(internal   function)。  
  导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。  
   
  输出函数的方法有以下几种:  
   
  1、传统的方法  
   
  在模块定义文件的EXPORT部分指定要输入的函数或者变量。语法格式如下:  
  entryname[=internalname]   [@ordinal[NONAME]]   [DATA]   [PRIVATE]  
   
  其中:  
   
  entryname是输出的函数或者数据被引用的名称;  
   
  internalname同entryname;  
   
  @ordinal表示在输出表中的顺序号(index);  
   
  NONAME仅仅在按顺序号输出时被使用(不使用entryname);  
   
  DATA表示输出的是数据项,使用DLL输出数据的程序必须声明该数据项为_declspec(dllimport)。  
   
  上述各项中,只有entryname项是必须的,其他可以省略。  
   
  对于“C”函数来说,entryname可以等同于函数名;但是对“C++”函数(成员函数、非成员函数)  
  来说,entryname是修饰名。可以从.map映像文件中得到要输出函数的修饰名,或者使用  
  DUMPBIN   /SYMBOLS得到,然后把它们写在.def文件的输出模块。DUMPBIN是VC提供的一个工具。  
   
  如果要输出一个“C++”类,则把要输出的数据和成员的修饰名都写入.def模块定义文件。  
   
  2、在命令行输出  
   
  对链接程序LINK指定/EXPORT命令行参数,输出有关函数。  
   
  3、使用MFC提供的修饰符号_declspec(dllexport)  
   
  在要输出的函数、类、数据的声明前加上_declspec(dllexport)的修饰符,表示输出。__declspec  
  (dllexport)在C调用约定、C编译情况下可以去掉输出函数名的下划线前缀。extern   "C"使得在C++中  
  使用C编译方式成为可能。在“C++”下定义“C”函数,需要加extern   “C”关键词。用extern   "C"来  
  指明该函数使用C编译方式。输出的“C”函数可以从“C”代码里调用。  
   
  例如,在一个C++文件中,有如下函数:  
  extern   "C"   {void   __declspec(dllexport)   __cdecl   Test(int   var);}  
  其输出函数名为:Test    
   
  MFC提供了一些宏,就有这样的作用。  
   
  AFX_CLASS_IMPORT:__declspec(dllexport)  
   
  AFX_API_IMPORT:__declspec(dllexport)  
   
  AFX_DATA_IMPORT:__declspec(dllexport)  
   
  AFX_CLASS_EXPORT:__declspec(dllexport)  
   
  AFX_API_EXPORT:__declspec(dllexport)  
   
  AFX_DATA_EXPORT:__declspec(dllexport)  
   
  AFX_EXT_CLASS:   #ifdef   _AFXEXT    
  AFX_CLASS_EXPORT  
  #else  
  AFX_CLASS_IMPORT  
   
  AFX_EXT_API:#ifdef   _AFXEXT  
  AFX_API_EXPORT  
  #else  
  AFX_API_IMPORT  
   
  AFX_EXT_DATA:#ifdef   _AFXEXT  
  AFX_DATA_EXPORT  
  #else  
  AFX_DATA_IMPORT  
   
  像AFX_EXT_CLASS这样的宏,如果用于DLL应用程序的实现中,则表示输出(因为_AFX_EXT被定义,通  
  常是在编译器的标识参数中指定该选项/D_AFX_EXT);如果用于使用DLL的应用程序中,则表示输入  
  (_AFX_EXT没有定义)。  
   
  要输出整个的类,对类使用_declspec(_dllexpot);要输出类的成员函数,则对该函数使用  
  _declspec(_dllexport)。如:  
   
  class   AFX_EXT_CLASS   CTextDoc   :   public   CDocument  
  {  
  …  
  }  
   
  extern   "C"   AFX_EXT_API   void   WINAPI   InitMYDLL();  
   
  这几种方法中,最好采用第三种,方便好用;其次是第一种,如果按顺序号输出,调用效率会高些;  
  最次是第二种。    
   
  六、模块定义文件(.DEF)  
   
  模块定义文件(.DEF)是一个或多个用于描述DLL属性的模块语句组成的文本文件,每个DEF文件至少必  
  须包含以下模块定义语句:  
   
  *   第一个语句必须是LIBRARY语句,指出DLL的名字;  
  *   EXPORTS语句列出被导出函数的名字;将要输出的函数修饰名罗列在EXPORTS之下,这个名字必须与  
  定义函数的名字完全一致,如此就得到一个没有任何修饰的函数名了。  
  *   可以使用DESCRIPTION语句描述DLL的用途(此句可选);  
  *   ";"对一行进行注释(可选)。  
   
  七、DLL程序和调用其输出函数的程序的关系  
   
  1、dll与进程、线程之间的关系  
   
  DLL模块被映射到调用它的进程的虚拟地址空间。  
  DLL使用的内存从调用进程的虚拟地址空间分配,只能被该进程的线程所访问。  
  DLL的句柄可以被调用进程使用;调用进程的句柄可以被DLL使用。  
  DLL使用调用进程的栈。  
   
  2、关于共享数据段  
   
  DLL定义的全局变量可以被调用进程访问;DLL可以访问调用进程的全局数据。使用同一DLL的每一个  
  进程都有自己的DLL全局变量实例。如果多个线程并发访问同一变量,则需要使用同步机制;对一个  
  DLL的变量,如果希望每个使用DLL的线程都有自己的值,则应该使用线程局部存储(TLS,Thread    
  Local   Strorage)。  
   
  在程序里加入预编译指令,或在开发环境的项目设置里也可以达到设置数据段属性的目的.必须给  
  这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

 

  C++   Language   Reference        
   
  dllexport,   dllimportSee   Also  
  __declspec   |   C++   Keywords  
  Microsoft   Specific  
   
  The   dllexport   and   dllimport   storage-class   attributes   are   Microsoft-specific   extensions   to   the   C   and   C++   languages.   They   enable   you   to   export   and   import   functions,   data,   and   objects   to   and   from   a   DLL.  
   
  __declspec(   dllimport   )   declarator  
  __declspec(   dllexport   )   declarator  
  These   attributes   explicitly   define   the   DLL's   interface   to   its   client,   which   can   be   the   executable   file   or   another   DLL.   Declaring   functions   as   dllexport   eliminates   the   need   for   a   module-definition   (.DEF)   file,   at   least   with   respect   to   the   specification   of   exported   functions.   Note   that   dllexport   replaces   the   __export   keyword.  
   
  If   a   class   is   marked   declspec(dllexport),   any   specializations   of   class   templates   in   the   class   hierarchy   are   implicitly   marked   as   declspec(dllexport).    
   
  The   declaration   of   dllexport   and   dllimport   must   use   extended   attribute   syntax   and   the   __declspec   keyword.  
   
  Example  
  //   Example   of   the   dllimport   and   dllexport   class   attributes  
  __declspec(   dllimport   )   int   i;  
  __declspec(   dllexport   )   void   func();  
  Alternatively,   to   make   your   code   more   readable,   you   can   use   macro   definitions:  
   
  #define   DllImport       __declspec(   dllimport   )  
  #define   DllExport       __declspec(   dllexport   )  
   
  DllExport   void   func();  
  DllExport   int   i   =   10;  
  DllImport   int   j;  
  DllExport   int   n;  
  For   additional   information,   see:    
   
  Definitions   and   Declarations    
  Defining   Inline   C++   Functions   with   dllexport   and   dllimport    
  General   Rules   and   Limitations    
  Using   dllimport   and   dllexport   in   C++   Classes    
  END   Microsoft   Specific  
   
  See   Also  
  __declspec   |   C++   Keywords

 


  The   following   example   defines   an   exportable   class.   All   its   member   functions   and   static   data   are   exported:  
   
  #define   DllExport       __declspec(   dllexport   )  
   
  class   DllExport   C  
  {  
        int   i;  
        virtual   int   func(   void   )  
        {   return   1;   }  
  };  
  Note   that   explicit   use   of   the   dllimport   and   dllexport   attributes   on   members   of   an   exportable   class   is   prohibited.  
   
  dllexport   Classes  
  When   you   declare   a   class   dllexport,   all   its   member   functions   and   static   data   members   are   exported.   You   must   provide   the   definitions   of   all   such   members   in   the   same   program.   Otherwise,   a   linker   error   is   generated.   The   one   exception   to   this   rule   applies   to   pure   virtual   functions,   for   which   you   need   not   provide   explicit   definitions.   However,   because   a   destructor   for   an   abstract   class   is   always   called   by   the   destructor   for   the   base   class,   pure   virtual   destructors   must   always   provide   a   definition.   Note   that   these   rules   are   the   same   for   nonexportable   classes.  
   
  If   you   export   data   of   class   type   or   functions   that   return   classes,   be   sure   to   export   the   class.  
   
  dllimport   Classes  
  When   you   declare   a   class   dllimport,   all   its   member   functions   and   static   data   members   are   imported.   Unlike   the   behavior   of   dllimport   and   dllexport   on   nonclass   types,   static   data   members   cannot   specify   a   definition   in   the   same   program   in   which   a   dllimport   class   is   defined.  
   
  Inheritance   and   Exportable   Classes  
  All   base   classes   of   an   exportable   class   must   be   exportable.   If   not,   a   compiler   warning   is   generated.   Moreover,   all   accessible   members   that   are   also   classes   must   be   exportable.   This   rule   permits   a   dllexport   class   to   inherit   from   a   dllimport   class,   and   a   dllimport   class   to   inherit   from   a   dllexport   class   (though   the   latter   is   not   recommended).   As   a   rule,   everything   that   is   accessible   to   the   DLL's   client   (according   to   C++   access   rules)   should   be   part   of   the   exportable   interface.   This   includes   private   data   members   referenced   in   inline   functions.  
   
  Selective   Member   Import/Export  
  Because   member   functions   and   static   data   within   a   class   implicitly   have   external   linkage,   you   can   declare   them   with   the   dllimport   or   dllexport   attribute,   unless   the   entire   class   is   exported.   If   the   entire   class   is   imported   or   exported,   the   explicit   declaration   of   member   functions   and   data   as   dllimport   or   dllexport   is   prohibited.   If   you   declare   a   static   data   member   within   a   class   definition   as   dllexport,   a   definition   must   occur   somewhere   within   the   same   program   (as   with   nonclass   external   linkage).  
   
  Similarly,   you   can   declare   member   functions   with   the   dllimport   or   dllexport   attributes.   In   this   case,   you   must   provide   a   dllexport   definition   somewhere   within   the   same   program.  
   
  It   is   worthwhile   to   note   several   important   points   regarding   selective   member   import   and   export:    
   
  Selective   member   import/export   is   best   used   for   providing   a   version   of   the   exported   class   interface   that   is   more   restrictive;   that   is,   one   for   which   you   can   design   a   DLL   that   exposes   fewer   public   and   private   features   than   the   language   would   otherwise   allow.   It   is   also   useful   for   fine-tuning   the   exportable   interface:   when   you   know   that   the   client,   by   definition,   is   unable   to   access   some   private   data,   you   need   not   export   the   entire   class.    
  If   you   export   one   virtual   function   in   a   class,   you   must   export   all   of   them,   or   at   least   provide   versions   that   the   client   can   use   directly.    
  If   you   have   a   class   in   which   you   are   using   selective   member   import/export   with   virtual   functions,   the   functions   must   be   in   the   exportable   interface   or   defined   inline   (visible   to   the   client).    
  If   you   define   a   member   as   dllexport   but   do   not   include   it   in   the   class   definition,   a   compiler   error   is   generated.   You   must   define   the   member   in   the   class   header.    
  Although   the   definition   of   class   members   as   dllimport   or   dllexport   is   permitted,   you   cannot   override   the   interface   specified   in   the   class   definition.    
  If   you   define   a   member   function   in   a   place   other   than   the   body   of   the   class   definition   in   which   you   declared   it,   a   warning   is   generated   if   the   function   is   defined   as   dllexport   or   dllimport   (if   this   definition   differs   from   that   specified   in   the   class   declaration).    
  END   Microsoft   Specific  
   
  See   Also  
  dllexport,   dllimport  
   
   
   
  --------------------------------------------------------------------------------  
   
  Send   feedback   on   this   topic   to   Microsoft  
   
  ©   Microsoft   Corporation.   All   rights   reserved.  

 


__stdcall,__fastcall,__cdecl用于确定以下三方面函数调用信息:

1。将函数参数推送到堆栈上的顺序。
2。是由调用方函数还是由被调用函数在调用结束时从堆栈中移除参数。
3。编译器用来标识各个函数的名称修饰约定。

 


__cdecl 细节
对于 C,__cdecl 命名约定使用以下划线 ( _ ) 开头的函数名;不执行任何大小写转换。除非声明为 extern "C",否则 C++ 函数将使用不同的名称修饰方案。有关更多信息,请参阅修饰名。

__fastcall 细节
某些 __fastcall 函数参数在寄存器 x86 Specific —> ECX 和 EDX END x86 Specific 中传递,其余参数则被从右到左推送到堆栈上。被调用例程在返回之前从堆栈中弹出这些参数。/Gr 通常减少执行时间。

注意 在对用内联程序集语言编写的任意函数使用 __fastcall 调用约定时,一定要小心。您对寄存器的使用可能与编译器对它们的使用发生冲突。
对于 C,__fastcall 命名约定使用以“at”符 (@) 开头的函数名,后跟函数参数大小(以字节为单位)。不执行任何大小写转换。编译器使用下列命名约定模板:

@function_name@number
注意 Microsoft 不保证不同编译器版本之间的 __fastcall 调用约定的实现相同。例如,16 位编译器与 32 位编译器的实现就不同。
当使用 __fastcall 命名约定时,请使用标准包含文件。否则将获取无法解析的外部引用。

__stdcall 细节
__stdcall 函数的参数被从右到左推送到堆栈上,被调用函数在返回之前从堆栈中弹出这些参数。

对于 C,__stdcall 命名约定使用以下划线 ( _ ) 开头的函数名,后跟“at”符 (@) 和函数参数大小(以字节为单位)。不执行任何大小写转换。编译器使用下列命名约定模板:


x86 Specific—>

此选项对 C++ 方法和函数的名称修饰无效。除非声明为 extern "C",否则 C++ 方法和函数将使用不同的名称修饰方案。有关更多信息,请参阅修饰名。

 

C 和 C++ 程序中的函数在内部通过其修饰名加以识别。修饰名是在编译函数定义或函数原型期间由编译器创建的字符串。

当指定 LINK 或其他工具的函数名时,有时需要修饰名。对于需要修饰名的情况,请参考正在使用的工具的文档,获取详细信息。


使用修饰名
修饰名 | C++ 修饰名的格式和 C 修饰名的格式
大多数情况下,无需知道函数的修饰名。LINK 和其他工具通常可以处理未修饰形式的名称。

但是,某些情况要求指定修饰形式的名称。必须指定重载的 C++ 函数和特殊成员函数(如构造函数和析构函数)的修饰名,以使 LINK 和其他工具可以与该名称匹配。在引用 C 或 C++ 函数名的程序集源文件中也必须使用修饰名。

警告 如果更改函数名、类、调用约定、返回类型或任何参数,修饰名不再有效。必须获取函数名的新版本并在所有指定了修饰名的位置使用。

 

查看修饰名请参见
修饰名
编译包含函数定义或函数原型的源文件之后,可以获取函数名的修饰形式。若要在程序中检查修饰名,请执行下列操作之一:

1.使用列表
2.使用 DUMPBIN 工具

可以使用 undname.exe 将修饰名转换为未修饰形式。例如,

C:\>undname
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation 1981-2000. All rights reserved.Undecoration
of :- ""
is :- "private: void __thiscall a::func1(int)"


__cdecl----参数从右向左入栈,调用者清除栈  
__stdcall----参数从右向左入栈,被调用者清除栈

  __stdcall   是按PASCAL的约定调用函数,参数按从右向左的顺序入栈,在函数返回前清除堆栈中的参数。  
  __cdecl   是按C的约定调用函数,参数按从右到做的顺序入栈,在函数调用返回以后清除堆栈中的参数。

---------------------------------------------------------------------------------------------------------------------
  调用约定    
  调用约定(Calling   convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:    
       
  _cdecl    
   
  按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。    
  如函数void   test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是。    
  这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。    
       
  _stdcall    
 
  按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int   func(int   a,   double   b)的修饰名是。对于“C++”函数,则有所不同。      
  所有的Win32   API函数都遵循该约定。
---------------------------------------------------------------------------------------------------------------------
函数func(int   a,int   b)  
  补充一点:  
  __cdecl函数被编译成:_func  
  __stdcall函数被编译成:   (8   为参数的字节数)  

是函数调用的约定,现在VC支持四种:  
  __cdecl  
  __stdcall  
  __fastcall  
  thiscall   (not   a   keyword)  
  _cdecl      
          C/C++的缺省调用协定,由调用者清理堆栈,这就是C/C++中可以使    
          用可变参数的函数的原因,所有参数自右至左入栈,生成的代码中    
          函数名有一个_(下划线)作前缀。    
  _stdcall    
          Win32   API的调用协定,由被调用的函数清理堆栈,所有参数自右至    
          左入栈,生成的代码中函数名有一个_(下划线)作前缀一个@和参数总    
          字节数(十进制)作後缀。它不支持可变参数,但它产生的代码比    
          _cdecl的短,因为没有每次调用後的清理堆栈的代码。    


The   __stdcall   calling   convention   is   used   to   call   Win32   API   functions.   The   callee   cleans   the   stack,   so   the   compiler   makes   vararg   functions   __cdecl.   Functions   that   use   this   calling   convention   require   a   function   prototype.   The   following   list   shows   the   implementation   of   this   calling   convention.  
   
  Element   Implementation    
  Argument-passing   order   Right   to   left.    
  Argument-passing   convention   By   value,   unless   a   pointer   or   reference   type   is   passed.    
  Stack-maintenance   responsibility   Called   function   pops   its   own   arguments   from   the   stack.    
  Name-decoration   convention   An   underscore   (_)   is   prefixed   to   the   name.   The   name   is   followed   by   the   at   sign   (@)   followed   by   the   number   of   bytes   (in   decimal)   in   the   argument   list.   Therefore,   the   function   declared   as   int   func(   int   a,   double   b   )   is   decorated   as   follows:       
  Case-translation   convention   None    
   
   
  The   /Gz   compiler   option   specifies   __stdcall   for   all   functions   not   explicitly   declared   with   a   different   calling   convention.    
   
  Functions   declared   using   the   __stdcall   modifier   return   values   the   same   way   as   functions   declared   using   __cdecl.    
   
  END   Microsoft   Specific  
   
  Example  
  In   the   following   example,   use   of   __stdcall   results   in   all   WINAPI   function   types   being   handled   as   a   standard   call:  
   
  //   Example   of   the   __stdcall   keyword  
  #define   WINAPI   __stdcall  

This   is   the   default   calling   convention   for   C   and   C++   programs.   Because   the   stack   is   cleaned   up   by   the   caller,   it   can   do   vararg   functions.   The   __cdecl   calling   convention   creates   larger   executables   than   __stdcall,   because   it   requires   each   function   call   to   include   stack   cleanup   code.   The   following   list   shows   the   implementation   of   this   calling   convention.  
   
  Element   Implementation    
  Argument-passing   order   Right   to   left    
  Stack-maintenance   responsibility   Calling   function   pops   the   arguments   from   the   stack    
  Name-decoration   convention   Underscore   character   (_)   is   prefixed   to   names    
  Case-translation   convention   No   case   translation   performed    
   
   
  Note       For   related   information,   see   Decorated   Names.    
   
  Place   the   __cdecl   modifier   before   a   variable   or   a   function   name.   Because   the   C   naming   and   calling   conventions   are   the   default,   the   only   time   you   need   to   use   __cdecl   is   when   you   have   specified   the   /Gz   (stdcall)   or   /Gr   (fastcall)   compiler   option.   The   /Gd   compiler   option   forces   the   __cdecl   calling   convention.  
   
  Example  
  In   the   following   example,   the   compiler   is   instructed   to   use   C   naming   and   calling   conventions   for   the   system   function:  
   
  //   Example   of   the   __cdecl   keyword  
  _CRTIMP   int   __cdecl   system(const   char   *);  

阅读(1531) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~