分享

DLL 导入和导出函数 | Microsoft Docs

 兰亭文艺 2022-07-06 发布于加拿大
  • Microsoft 专用

有关本主题的最完整且最新的信息可在 dllexport、dllimport 中找到。

dllimportdllexport 存储类修饰符是 C 语言的 Microsoft 专用扩展。 这些修饰符显式定义了 DLL 与其客户端(可执行文件或另一个 DLL)的接口。 如果将函数声明为 dllexport,则不再需要模块定义 (.DEF) 文件。 还可以对数据和对象使用 dllimportdllexport 修饰符。

dllimportdllexport 存储类修饰符必须与扩展的特性语法关键字 __declspec 一起使用,如下面的示例所示:

#define DllImport __declspec( dllimport ) #define DllExport __declspec( dllexport ) DllExport void func(); DllExport int i = 10; DllExport int j; DllExport int n;

有关扩展的存储类修饰符的语法的特定信息,请参阅扩展的存储类特性

---------

定义和声明 (C)

DLL 接口引用已知由系统中的某程序导出的所有项(函数和数据);即所有被声明为 dllimport 或 dllexport 的项。 DLL 接口中包含的所有声明都必须指定 dllimport 或 dllexport 特性。 但是,定义仅可指定 dllexport 特性。 例如,以下函数定义产生了一个编译器错误:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )

DllImport int func()    /* Error; dllimport prohibited in definition. */
{
   return 1;
}

以下代码也会产生错误:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )

DllImport int i = 10;      /* Error; this is a definition. */

但是,这是正确的语法:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )

DllExport int i = 10;      /* Okay: this is an export definition. */

使用 dllexport 意味着定义,而使用 dllimport 则意味着声明。 必须使用带 extern 的 dllexport 关键字来强制进行声明;否则,会进行隐式定义。

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )

extern DllImport int k;   /* These are correct and imply */
Dllimport int j;          /* a declaration. */

----------

定义带有 dllexport 和 dllimport 的内联 C 函数

可以定义为将函数与 dllexport 特性内联。 在这种情况下,将始终实例化并导出该函数,无论程序中是否有模块引用该函数。 假定该函数由另一个程序导入。

还可以定义为内联使用 dllimport 特性声明的函数。 在这种情况下,函数可以展开(遵从 /Ob(内联)编译器选项规范),但决不实例化。 具体而言,如果采用内联导入函数的地址,则返回驻留在 DLL 中的函数地址。 此行为与采用非内联导入函数的地址相同。

内联函数中的静态本地数据和字符串在 DLL 和客户端之间保持的标识与它们在单一程序(即,没有 DLL 接口的可执行文件)中保持的一样。

在提供导入的内联函数时谨慎操作。 例如,如果更新 DLL,请不要假定该客户端将使用更改后的 DLL 版本。 若要确保加载 DLL 的适当版本,请重新生成 DLL 的客户端。

-----------

dllimport/dllexport 的规则和限制

如果你没有使用 dllimport 或 dllexport 特性声明函数,则此函数被视为不是 DLL 接口的一部分。 因此,函数的定义必须存在于该模块或同一程序的另一个模块中。 若要使函数成为 DLL 接口的一部分,您必须将其他模块中函数的定义声明为 dllexport。 否则,在构建客户端时,将生成链接器错误。

如果程序中的单个模块包含对同一函数的 dllimport 和 dllexport 声明,则 dllexport 特性优先于 dllimport 特性。 但是,会生成编译器警告。 例如:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )
   DllImport void func1( void );

DllExport void func1( void );   /* Warning; dllexport takes precedence. */

无法利用使用 dllimport 特性声明的数据对象的地址来初始化静态数据指针。 例如,下面的代码将生成错误:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )
DllImport int i;


   int *pi = &i;                           /* Error */
   void func2()
   {
      static int *pi = &i;                   /* Error */
   }

如果利用使用 dllimport 声明的函数的地址来初始化静态函数指针,会将指针设置为 DLL 导入 thunk(将控制权转移给函数的代码存根)的地址,而不是函数的地址。 此赋值不生成错误消息:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )
   DllImport void func1( void


   static void ( *pf )( void ) = &func1;   /* No Error */
   void func2()
   {
      static void ( *pf )( void ) = &func1;  /* No Error */
   }

由于包含对象声明中的 dllexport 特性的程序必须为该对象提供定义,因此您可以利用 dllexport 函数的地址初始化全局或局部静态函数指针。 同样,您可以利用 dllexport 数据对象的地址初始化全局或局部静态数据指针。 例如:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )
   DllImport void func1( void );
   DllImport int i;
   DllExport void func1( void );

   DllExport int i;


   int *pi = &i;                            /* Okay */

   static void ( *pf )( void ) = &func1;    /* Okay */
   void func2()
   {
      static int *pi = i;                     /* Okay */
      static void ( *pf )( void ) = &func1;   /* Okay */
   }

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多