C中DLL函数的导出与导入
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.使用DEF 文件从DLL 导出
模块定义(.def) 文件是包含一个或多个描述DLL 各种属性的Module 语句的文本文件。如果不使用__declspec(dllexport)关键字导出DLL 的函数,则DLL 需要 .def 文件。
.def 文件必须至少包含下列模块定义语句:
文件中的第一个语句必须是LIBRARY 语句。此语句将 .def 文件标识为属于DLL。LIBRARY 语句的后面是DLL 的名称。链接器将此名称放到DLL 的导入库中。
EXPORTS 语句列出名称,可能的话还会列出DLL 导出函数的序号值。通过在函数名的后面加上@ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从1 到N,其中N 是DLL 导出函数的个数。如果希望按序号导出函数,请参见按序号而不是按名称从DLL 导出函数以及本主题。
例如,包含实现二进制搜索树的代码的DLL 看上去可能像下面这样:
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
如果使用MFC DLL 向导创建MFC DLL,则向导将为您创建主干 .def 文件并将其自动添加到项目中。添加要导出到此文件的函数名。对于非MFC DLL,必须亲自创建 .def 文件并将其添加到项目中。
如果导出C++ 文件中的函数,必须将修饰名放到 .def 文件中,或者通过使用外部“C”定义具有标准C 链接的导出函数。如果需要将修饰名放到 .def
文件中,则可以通过使用DUMPBIN 工具或/MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到DLL 的应用程序必须也是用相同版本的Visual C++ 生成的,这样调用应用程序中的修饰名才能与DLL 的 .def 文件中的导出名相匹配。
如果生成扩展DLL 并使用 .def 文件导出,则将下列代码放在包含导出类的头文件的开头和结尾:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA
//
#undef AFX_DATA
#define AFX_DATA
这些代码行确保内部使用的MFC 变量或添加到类的变量是从扩展DLL 导出(或导入)的。例如,当使用DECLARE_DYNAMIC派生类时,该宏扩展以将CRuntimeClass成员变量添加到类。省去这四行代码可能会导致不能正确编译或链接DLL,或在客户端应用程序链接到DLL 时导致错误。
当生成DLL 时,链接器使用 .def 文件创建导出(.exp) 文件和导入库(.lib) 文件。然后,链接器使用导出文件生成DLL 文件。隐式链接到DLL 的可执行文件在生成时链接到导入库。
请注意,MFC 本身使用 .def 文件从MFCx0.dll 导出函数和类。
2.使用_declspec(dllexport) 从DLL 导出
Microsoft 在Visual C++ 的16 位编译器版本中引入了_export,使编译器得以自动生成导出名并将它们放到一个 .lib 文件中。然后,此 .lib 文件就可以像静态 .lib 那样用于与DLL 链接。
在更新的编译器版本中,可以使用_declspec(dllexport)关键字从DLL 导出数据、函数、类或类成员函数。_declspec(dllexport)会将导出指令添加到对象文件中,因此您不需要使用 .def 文件。
当试图导出C++ 修饰函数名时,这种便利最明显。由于对名称修饰没有标准规范,因此导出函数的名称在不同的编译器版本中可能有所变化。如果使用_declspec(dllexport),仅当解决任何命名约定更改时才必须重新编译DLL 和依赖 .exe 文件。
许多导出指令(如序号、NONAME 和PRIVATE)只能在 .def 文件中创建,并且必须使用 .def 文件来指定这些属性。不过,在 .def 文件的基础上另外使用_declspec(dllexport)不会导致生成错误。
若要导出函数,_declspec(dllexport)关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:
_declspec(dllexport) void _cdecl Function1(void);
若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边,如下所示:
class _declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };
生成DLL 时,通常创建一个包含正在导出的函数原型和/或类的头文件,并将_declspec(dllexport)添加到头文件中的声明中。若要提高代码的可读性,请为_declspec(dllexport)定义一个宏并对正在导出的每个符号使用该宏:
#define DllExport _declspec( dllexport )
_declspec(dllexport)将函数名存储在DLL 的导出表中。如果希望优化表的大小,请参见按序号而不是按名称从DLL 导出函数。
注意:将DLL 源代码从Win16 移植到Win32 时,请用
_declspec(dllexport)替换_export的每个实例。
作为参考,请在Win32 Winbase.h 头文件中搜索。它包含
_declspec(dllimport)的用法示例。
3.使用AFX_EXT_CLASS 导出和导入
扩展DLL 使用AFX_EXT_CLASS宏导出类;链接到扩展DLL 的可执行文件使用该宏导入类。用于生成扩展DLL 的相同头文件可通过
AFX_EXT_CLASS宏与链接到DLL 的可执行文件一起使用。
在DLL 的头文件中,将AFX_EXT_CLASS关键字添加到类的声明中,如下所示:
class AFX_EXT_CLASS CMyClass : public CDocument
{
//
};
当定义了预处理器符号_AFXDLL和_AFXEXT时,该宏被MFC 定义为__declspec(dllexport)。但当定义了_AFXDLL而未定义_AFXEXT时,该宏被定义为__declspec(dllimport)。定义后,预处理器符号_AFXDLL指示共享MFC 版本正在由目标可执行文件(DLL 或应用程序)使用。当_AFXDLL 和_AFXEXT都定义了时,这指示目标可执行文件是扩展DLL。
由于从扩展DLL 导出时,AFX_EXT_CLASS被定义为
__declspec(dllexport),因此可以导出整个类,而不必将该类的所有符号的修饰名放到 .def 文件中。此方法由MFC 示例DLLHUSK使用。
虽然使用此方法可以避免创建 .def 文件和类的所有修饰名,但由于名称可以按序号导出,因此创建 .def 文件的效率更高。若要使用 .def 文件导出方法,请将下列代码放在头文件的开头和结尾处:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA