__dllexport vs *.def
*.idl,COM接口的定义文件,MIDL.exe可以为根据idl生成*.h和*_i.c文件。
*.h, 头文件
*_i.c文件,定义目标COM对象的CLSID以及IID的GUID值
*.tlb,type library,COM组件的二进制描述,供其它非C/C++语言使用COM组件的接口。当然c#生成的COM也可以得到tlb文件供C++使用。
*.def, 定义dll的输出函数,extern "C",允许客户程序按照序号调用函数而不是函数名。
在 32 位编译器版本中,可以使用 __declspec(dllexport) 关键字从 DLL 导出数据、函数、类或类成员函数。
__declspec(dllexport) 在link时会将导出指令添加到obj文件中,因此不需要使用 .def 文件。当然,即使用了__declspec(dllexport)依然可
以使用*.def文件,因为不同编译器对于类的成员函数的name mangling规则不同,可以定义.def文件通过序号调用。
*.lib,
lib文件有两种:一、是DLL文件的导入库lib文件;二、是静态链接库(Static Library)库文件lib文件。
前者只是包含动态链接库DLL中导出函数的重定位信息,而真正的动态链接库库文件是DLL文件;后者就是一个库文件。
前者在隐式调用dll时用到,__declspec(dllexport)关键字可以使编译器生成*lib文件。
extern “C”
使用extern “C”关键字实际上相当于一个编译器的开关,它可以将c++语言的函数编译为c语言的函数名称。即保持编译后的函数符号名等于源代码
中的函数名称
__declspec(dllexport)
为每个dll写def显得很繁杂,目前def使用已经比较少了,更多的是使用__declspec(dllexport)在源代码中定义dll的输出函数。
若要输出类的所有成员:数据or函数,__declspec(dllexport)放在类名左边声明:
class __declspec(dllexport) Class1{}
如果类没有数据成员,__declspec(dllexport)放在class关键字前声明就会被编译器忽略,就没有lib生成:
__declspec(dllexport) class Class1{}
其实def的功能相当于extern “C” __declspec(dllexport)
The optional keyword PRIVATE prevents entryname from being placed in the import library generated by LINK. It has no
effect on the export in the image also generated by LINK.用了PRIVATE就不能隐式调用这个方法了,因为lib里没有这个entryname。
__declspec(dllexport) vs. *.def(zz)
使用 .DEF 文件的优缺点
在 .def 文件中导出函数使您得以控制导出序号。当将附加的导出函数添加到 DLL 时,可以给它们分配更高的序号值(高于任何其他导出函数)。
当您进行此操作时,使用隐式链接的应用程序不必与包含新函数的新导入库重新链接。这非常重要,例如,在设计将由许多应用程序使用的第三方
DLL 时。可以通过添加附加功能不断地增强 DLL,同时确保现有应用程序继续正常使用新的 DLL。MFC DLL 是使用 .def 文件生成的。
使用 .def 文件的另一个优点是:可以使用 NONAME 属性导出函数,该属性仅将序号放到 DLL 的导出表中。对具有大量导出函数的 DLL,使用
NONAME 属性可以减小 DLL 文件的大小。有关编写模块定义语句的信息,请参见模块定义语句的规则。有关序号导出的更多信息,请参见按序号而不
是按名称从 DLL 导出函数。
使用 .def 文件的主要缺点是:在 C++ 文件中导出函数时,必须将修饰名放到 .def 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数
,以避免编译器进行名称修饰。
如果需要将修饰名放到 .def 文件中,则可以通过使用 DUMPBIN 工具或 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特
定的。如果将 Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样
调用应用程序中的修饰名才能与 DLL 的 .def 文件中的导出名相匹配。
使用 __declspec(dllexport) 的优缺点
使用 __declspec(dllexport) 非常方便,因为不必考虑维护 .def 文件和获取导出函数的修饰名。例如,如果您设计的 DLL 供自己控制的应用程
序使用,则此方法很适用。如果通过新的导出函数重新生成 DLL,还必须重新生成应用程序,因为如果使用不同版本的编译器进行重新编译,则导出
的 C++ 函数的修饰名可能会发生变化。
