导出定义的语法是:

entryname [ = internal_name | other_module.exported_name ] [ @ ordinal [ NONAME ] ] [ [ PRIVATE ] | [ DATA ] ]

entryname 是要导出的函数或变量名称。 这是必填字段。 如果导出的名称与 DLL 中的名称不同,则使用 internal_name 指定 DLL 中导出的名称。 例如,如果 DLL 导出函数 func1 ,并且你希望调用方将其用作 func2 ,则应指定:

EXPORTS
   func2=func1

如果你导出的名称来自其他模块,则使用 other_module.exported_name 在 DLL 中指定导出名称。 例如,如果 DLL 导出函数 other_module.func1,并且你希望调用方将其用作 func2,则应指定:

EXPORTS
   func2=other_module.func1

如果你导出的名称来自另一个按序号导出的模块,则使用 other_module.ordinal 在 DLL 中指定导出的序号。# 例如,如果你的 DLL 从序号为 42 的其他模块导出一个函数,并且你希望调用者将其用作 func2,你可以指定:

EXPORTS
   func2=other_module.#42

由于 MSVC 编译器对 C++ 函数使用名称修饰,因此你必须使用修饰名称 internal_name 或在源代码中使用 extern "C" 定义导出的函数。 编译器还将修饰使用 __stdcall 调用约定的 C 函数,它带有下划线 (_) 前缀和由 at 符号 (@) 后跟自变量列表中的字节数(采用十进制)所组成的后缀。

若要查找由编译器产生的修饰名,请使用 DUMPBIN 工具或链接器 /MAP 选项。 修饰名特定于编译器。 如果要将修饰名导出到 .DEF 文件中,则链接到 DLL 的可执行文件也必须通过使用同一版本的编译器生成。 这可确保调用方中的修饰名与 .DEF 文件中的导出名相匹配。

可以使用“@序数”指定序号而不是函数名将进入 DLL 的导出表。 许多 Windows DLL 将导出序号以支持旧版代码。 通常使用采用 16 位 Windows 编码的序号,因为这有助于最大程度地减小 DLL 的大小。 除非 DLL 的客户端需要按序号导出函数以支持旧版,否则我们不建议你执行此操作。 由于 .LIB 文件将包含序号与函数之间的映射,因此你可以像通常在使用 DLL 的项目中那样使用函数名。

通过使用可选 NONAME 关键字,你可以只按序号导出,并减小结果 DLL 中导出表的大小。 但是,如果要在 DLL 上使用 GetProcAddress,则必须知道序号,因为该名称无效。

可选关键字 PRIVATE 防止 entryname 包含在 LINK 生成的导入库中。 它不会影响同样是由 LINK 生成的映像中的导出。

可选 DATA 关键字指定导出的是数据,而不是代码。 此示例显示如何导出名为 exported_global 的数据变量:

EXPORTS
   exported_global DATA

可通过采用建议的顺序列出的四种方式导出定义:

  • 源代码中的 __declspec(dllexport) 关键字

  • .DEF 文件中的 EXPORTS 语句

  • LINK 命令中的 /EXPORT 规范

  • 源代码中的 comment 指令,形式为 #pragma comment(linker, "/export: definition ")。 以下示例显示了函数声明之前的 #pragma 注释指令,其中 PlainFuncName 是未修饰的名称,_PlainFuncName@4 是函数的修饰名称:

    #pragma comment(linker, "/export:PlainFuncName=_PlainFuncName@4")
    BOOL CALLBACK PlainFuncName( Things * lpParams)
    

    如果你需要导出未修饰的函数名称,并且根据生成配置(例如,在 32 位或 64 位构建中)具有不同的导出,#pragma 指令很有用。

    所有这四种方法可以用在同一个程序中。 LINK 在生成包含导出的程序时还创建导入库,除非生成中使用了 .EXP 文件。

    下面是 EXPORTS 节的一个示例:

    EXPORTS
       DllCanUnloadNow      @1          PRIVATE
       DllWindowName = WindowName       DATA
       DllGetClassObject    @4 NONAME   PRIVATE
       DllRegisterServer    @7
       DllUnregisterServer
    

    使用 .DEF 文件从 DLL 中导出变量时,不必在变量上指定 __declspec(dllexport)。 但是,在任何使用 DLL 的文件中,必须仍在数据的声明上使用 __declspec(dllimport)

    模块定义语句的规则

  •