使用 DEF 文件从 DLL 导出

模块定义或 DEF 文件 (*.def) 文件是文本文件,其中包含一个或多个描述 DLL 的各种特性的模块语句。 如果没有使用 __declspec(dllexport) 关键字来导出 DLL 的函数,则 DLL 需要 DEF 文件。

最小的 DEF 文件必须包含以下模块定义语句:

  • 文件中的第一个语句必须是 LIBRARY 语句。 此语句将 DEF 文件标识为属于 DLL。 LIBRARY 语句后跟 DLL 的名称。 链接器将此名称放置在 DLL 的导入库中。

  • EXPORTS 语句列出 DLL 导出的函数的名称和(可选)序号值。 可以通过在函数名称后加一个 at 符号 (@) 和一个数字,为函数分配序号值。 指定序号值时,它们必须在 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 文件并将它添加到项目。 然后转到“项目”>“属性”>“连接器”>“输入”>“模块定义文件”,并输入 DEF 文件的名称。 为每个配置和平台重复此步骤,或者通过选择“配置 = 所有配置”和“平台 = 所有平台”,对所有配置和平台同时执行此步骤。

如果要导出 C++ 文件中的函数,则必须将修饰名放置在 DEF 文件中,或使用 extern "C" 通过标准 C 链接定义导出函数。 如果需要将修饰名放入 DEF 文件,则可以使用 DUMPBIN 工具或链接器 /MAP 选项来获取这些修饰名。 请注意,编译器生成的修饰名特定于编译器。 如果将 Microsoft C++ 编译器 (MSVC) 生成的修饰名放入 DEF 文件,则链接到 DLL 的应用程序也必须使用相同版本的 MSVC 生成,以便让调用应用程序中的修饰名与 DLL 的 DEF 文件中的导出名称相匹配。

注意

使用 Visual Studio 2015 生成的 DLL 可由使用 Visual Studio 2017 或 Visual Studio 2019 生成的应用程序使用。

如果要生成扩展 DLL 并使用 DEF 文件导出,请将以下代码放置在包含导出类的头文件的开头和结尾:

#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA
// <body of your header file>
#undef AFX_DATA
#define AFX_DATA

这些行可确保从 MFC 扩展 DLL 导出(或导入)在内部使用或添加到类的 MFC 变量。 例如,使用 DECLARE_DYNAMIC 派生类时,宏会进行扩展以向类添加 CRuntimeClass 成员变量。 省略这四行代码可能会导致 DLL 无法正确编译或链接,或是在客户端应用程序链接到 DLL 时导致错误。

生成 DLL 时,链接器使用 DEF 文件创建导出 (.exp) 文件和导入库 (.lib) 文件。 然后,链接器使用导出文件生成 DLL 文件。 隐式链接到 DLL 的可执行文件会在生成时链接到导入库。

请注意,MFC 本身使用 DEF 文件从 MFCx0.dll 导出函数和类。

你希望做什么?

你想进一步了解什么?

另请参阅

从 DLL 导出