分类: 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 *);