一 dllexport, dllimport
Microsoft Specific
- The dllexport and dllimport storage-class attributes are Microsoft-specific extensions to the C and C++ languages. You can use them to export and import functions, data, and objects to or from a DLL.
- 该 DLLEXPORT 和 dllimport 的存储类属性是 Microsoft 特定扩展到 C 和 C ++ 语言。您可以使用它们在 DLL 中导出和导入函数,数据和对象。
Syntax(句法)
__declspec( dllimport ) declarator
__declspec( dllexport ) declarator
Remarks(备注)
-
These attributes explicitly define the DLL’s interface to its client, which can be the executable file or another DLL. Declaring functions as dllexport eliminates the need for a module-definition (.def) file, at least with respect to the specification of exported functions. The dllexport attribute replaces the __export keyword.
-
这些属性明确定义了 DLL 与其客户端的接口,该客户端可以是可执行文件或另一个 DLL。将函数声明为 dllexport,至少就导出函数的规范而言,消除了对模块定义(.def)文件的需求。该 DLLEXPORT 属性替代 __export 关键字。
-
If a class is marked declspec(dllexport), any specializations of class templates in the class hierarchy are implicitly marked as declspec(dllexport). This means that class templates are explicitly instantiated and the class’s members must be defined.
-
如果将一个类标记为 declspec(dllexport),则该类层次结构中类模板的任何特殊化都将隐式标记为 declspec(dllexport)。这意味着将显式实例化类模板,并且必须定义类的成员。
-
dllexport of a function exposes the function with its decorated name. For C++ functions, this includes name mangling. For C functions or functions that are declared as extern “C”, this includes platform-specific decoration that’s based on the calling convention. For information on name decoration in C/C++ code, see Decorated Names. No name decoration is applied to exported C functions or C++ extern “C” functions using the __cdecl calling convention.
-
函数的dllexport会使用修饰后的名称公开该函数。对于 C++ 函数,这包括名称修饰。对于 C 函数或声明为 extern “C” 的函数,这包括基于调用约定的特定于平台的装饰。有关 C/C++ 代码中名称修饰的信息,请参见修饰名称。使用__cdecl调用约定,不会将名称修饰应用于导出的C函数或C++ extern “C” 函数。
-
To export an undecorated name, you can link by using a Module Definition (.def) file that defines the undecorated name in an EXPORTS section. For more information, see EXPORTS. Another way to export an undecorated name is to use a #pragma comment(linker, “/export:alias=decorated_name”) directive in the source code.
-
若要导出未修饰的名称,可以使用模块定义(.def)文件进行链接,该文件在 EXPORTS 节中定义了未修饰的名称。有关更多信息,请参见EXPORTS。导出未修饰名称的另一种方法是在源代码中使用指令 #pragma comment(linker, “/export:alias=decorated_name”)。
-
When you declare dllexport or dllimport, you must use extended attribute syntax and the __declspec keyword.
-
声明dllexport或dllimport时,必须使用扩展属性语法和__declspec关键字。
Example
// Example of the dllimport and dllexport class attributes
__declspec( dllimport ) int i;
__declspec( dllexport ) void func();
- Alternatively, to make your code more readable, you can use macro definitions:
- 另外,为了使代码更具可读性,可以使用宏定义:
#define DllImport __declspec( dllimport )
#define DllExport __declspec( dllexport )
DllExport void func();
DllExport int i = 10;
DllImport int j;
DllExport int n;
二 相关信息
Definitions and Declarations (定义和声明 C++)
- The DLL interface refers to all items (functions and data) that are known to be exported by some program in the system; that is, all items that are declared as dllimport or dllexport. All declarations included in the DLL interface must specify either the dllimport or dllexport attribute. However, the definition must specify only the dllexport attribute. For example, the following function definition generates a compiler error:
- DLL 接口指的是已知由系统中的某些程序导出的所有项(功能和数据)。也就是说,所有声明为 dllimport 或 dllexport 的项。DLL接口中包含的所有声明必须指定 dllimport 或 dllexport 属性。但是,定义必须仅指定dllexport属性。例如,以下函数定义会生成编译器错误:
__declspec( dllimport ) int func() { // Error; dllimport
// prohibited on definition.
return 1;
}
- This code also generates an error:
__declspec( dllimport ) int i = 10; // Error; this is a definition.
- However, this is correct syntax:
__declspec( dllexport ) int i = 10; // Okay--export definition
- The use of dllexport implies a definition, while dllimport implies a declaration. You must use the extern keyword with dllexport to force a declaration; otherwise, a definition is implied. Thus, the following examples are correct:
- dllexport 的使用意味着定义,而 dllimport 的含义是声明。你必须在 dllexport 中使用 extern 关键字来强制进行声明;否则,隐含定义。因此,以下示例是正确的:
#define DllImport __declspec( dllimport )
#define DllExport __declspec( dllexport )
extern DllExport int k; // These are both correct and imply a
DllImport int j; // declaration.
- The following examples clarify the preceding:
- 以下示例阐明了上述内容:
static __declspec( dllimport ) int l; // Error; not declared extern.
void func() {
static __declspec( dllimport ) int s; // Error; not declared
// extern.
__declspec( dllimport ) int m; // Okay; this is a
// declaration.
__declspec( dllexport ) int n; // Error; implies external
// definition in local scope.
extern __declspec( dllimport ) int i; // Okay; this is a
// declaration.
extern __declspec( dllexport ) int k; // Okay; extern implies
// declaration.
__declspec( dllexport ) int x = 5; // Error; implies external
// definition in local scope.
}
Defining Inline C++ Functions with dllexport and dllimport(使用 dllexport 和 dllimport 定义内联 C++ 函数)
-
You can define as inline a function with the dllexport attribute. In this case, the function is always instantiated and exported, whether or not any module in the program references the function. The function is presumed to be imported by another program.
-
你可以使用 dllexport 属性将函数定义为内联函数。在这种情况下,无论程序中的任何模块是否引用了该函数,该函数始终都会实例化并导出。该功能假定是由另一个程序导入的。
-
You can also define as inline a function declared with the dllimport attribute. In this case, the function can be expanded (subject to /Ob specifications), but never instantiated. In particular, if the address of an inline imported function is taken, the address of the function residing in the DLL is returned. This behavior is the same as taking the address of a non-inline imported function.
-
你还可以将使用 dllimport 属性声明的函数定义为内联函数。在这种情况下,该功能可以扩展(服从 /Ob 规范),但不能实例化。特别是,如果采用内联导入函数的地址,则返回驻留在 DLL 中 的函数的地址。此行为与获取非内联导入函数的地址相同。
-
These rules apply to inline functions whose definitions appear within a class definition. In addition, static local data and strings in inline functions maintain the same identities between the DLL and client as they would in a single program (that is, an executable file without a DLL interface).
-
这些规则适用于其定义出现在类定义中的内联函数。此外,内联函数中的静态局部数据和字符串在 DLL 与 客户端之间保持与在单个程序(即不具有 DLL 接口的可执行文件)中的标识相同。
-
Exercise care when providing imported inline functions. For example, if you update the DLL, don’t assume that the client will use the changed version of the DLL. To ensure that you are loading the proper version of the DLL, rebuild the DLL’s client as well.
-
提供导入的内联函数时请注意。例如,如果你更新 DLL,请不要假定客户端将使用更改后的 DLL 版本。为确保您正在加载 DLL 的正确版本,请重新构建 DLL 的客户端。
General Rules and Limitations(一般规则和限制)
- If you declare a function or object without the dllimport or dllexport attribute, the function or object is not considered part of the DLL interface. Therefore, the definition of the function or object must be present in that module or in another module of the same program. To make the function or object part of the DLL interface, you must declare the definition of the function or object in the other module as dllexport. Otherwise, a linker error is generated.
- 如果声明的函数或对象没有 dllimport 或 dllexport 属性,则该函数或对象不被视为 DLL 接口的一部分。因此,函数或对象的定义必须存在于该模块或同一程序的另一个模块中。要使函数或对象成为 DLL 接口的一部分,必须在另一个模块中将函数或对象的定义声明为 dllexport。否则,将生成链接器错误。
- If you declare a function or object with the dllexport attribute, its definition must appear in some module of the same program. Otherwise, a linker error is generated.
- 如果使用 dllexport 属性声明函数或对象,则其定义必须出现在同一程序的某些模块中。否则,将生成链接器错误。
- If a single module in your program contains both dllimport and dllexport declarations for the same function or object, the dllexport attribute takes precedence over the dllimport attribute. However, a compiler warning is generated. For example:
- 如果程序中的单个模块包含相同函数或对象的 dllimport 和 dllexpor t声明,则 dllexport 属性优先于 dllimport 属性。但是,会生成一个编译器警告。例如:
__declspec( dllimport ) int i;
__declspec( dllexport ) int i; // Warning; inconsistent;
// dllexport takes precedence.
- In C++, you can initialize a globally declared or static local data pointer or with the address of a data object declared with the dllimport attribute, which generates an error in C. In addition, you can initialize a static local function pointer with the address of a function declared with the dllimport attribute. In C, such an assignment sets the pointer to the address of the DLL import thunk (a code stub that transfers control to the function) rather than the address of the function. In C++, it sets the pointer to the address of the function. For example:
- 在 C++ 中,你可以初始化全局声明的或静态的局部数据指针,或者使用 dllimport 属性声明的数据对象地址,这会在 C 中产生错误。此外,你可以用dllimport属性声明的函数地址初始化静态的局部函数指针。在 C 中,这样的赋值将指针设置为 DLL 导入 thunk 地址(将控制权转移到函数的代码存根),而不是函数地址。在 C++ 中,它将指针设置为函数地址。例如:
__declspec( dllimport ) void func1( void );
__declspec( dllimport ) int i;
int *pi = &i; // Error in C
static void ( *pf )( void ) = &func1; // Address of thunk in C,
// function in C++
void func2()
{
static int *pi = &i; // Error in C
static void ( *pf )( void ) = &func1; // Address of thunk in C,
// function in C++
}
- However, because a program that includes the dllexport attribute in the declaration of an object must provide the definition for that object somewhere in the program, you can initialize a global or local static function pointer with the address of a dllexport function. Similarly, you can initialize a global or local static data pointer with the address of a dllexport data object. For example, the following code does not generate errors in C or C++:
- 但是,由于在对象的声明中包含 dllexport 属性的程序必须在程序中的某个位置提供该对象的定义,因此可以使用一个 dllexport 函数地址初始化全局或局部静态函数指针。同样,你可以使用 dllexport 数据对象地址初始化全局或本地静态数据指针。例如,以下代码不会在 C 或 C++ 中生成错误:
__declspec( dllexport ) void func1( void );
__declspec( 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
}
- If you apply dllexport to a regular class that has a base class that is not marked as dllexport, the compiler will generate C4275.
- 如果将 dllexport 应用于具有未标记为 dllexport 的基类的常规类,则编译器将生成C4275。
- The compiler generates the same warning if the base class is a specialization of a class template. To work around this, mark the base-class with dllexport. The problem with a specialization of a class template is where to place the __declspec(dllexport); you are not allowed to mark the class template. Instead, explicitly instantiate the class template and mark this explicit instantiation with dllexport. For example:
- 如果基类是类模板的特化,则编译器会生成相同的警告。要解决此问题,请用 dllexport 标记基类。类模板特化的问题是在哪里放置 __declspec(dllexport);你无权标记类模板。而是显式实例化类模板,并使用 dllexport 标记此显式实例。例如:
template class __declspec(dllexport) B<int>;
class __declspec(dllexport) D : public B<int> {
// ...
- This workaround fails if the template argument is the deriving class. For example:
- 如果模板参数是派生类,则此解决方法将失败。例如:
class __declspec(dllexport) D : public B<D> {
// ...
- Because this is common pattern with templates, the compiler changed the semantics of dllexport when it is applied to a class that has one or more base-classes and when one or more of the base classes is a specialization of a class template. In this case, the compiler implicitly applies dllexport to the specializations of class templates. You can do the following and not get a warning:
- 因为这是模板的常见模式,所以当将 dllexport 应用于具有一个或多个基类的类时,并且当一个或多个基类是类模板特化时,编译器会更改 dllexport 的语义。在这种情况下,编译器将 dllexport 隐式应用于类模板特化。你可以执行以下操作,不会收到警告:
class __declspec(dllexport) D : public B<D> {
// ...
Using dllimport and dllexport in C++ Classes(在C++类中使用dllimport和dllexport)
- You can declare C++ classes with the dllimport or dllexport attribute. These forms imply that the entire class is imported or exported. Classes exported this way are called exportable classes.
- 你可以使用 dllimport 或 dllexport 属性声明 C ++ 类。这些形式表示整个类都已导入或导出。以这种方式导出的类称为可导出类。
- The following example defines an exportable class. All its member functions and static data are exported:
- 以下示例定义了一个可导出类。它的所有成员函数和静态数据都将导出:
#define DllExport __declspec( dllexport )
class DllExport C {
int i;
virtual int func( void ) { return 1; }
};
- Note that explicit use of the dllimport and dllexport attributes on members of an exportable class is prohibited.
- 请注意,禁止在可导出类的成员上显式使用 dllimport 和 dllexport 属性。
dllexport Classes
-
When you declare a class dllexport, all its member functions and static data members are exported. You must provide the definitions of all such members in the same program. Otherwise, a linker error is generated. The one exception to this rule applies to pure virtual functions, for which you need not provide explicit definitions. However, because a destructor for an abstract class is always called by the destructor for the base class, pure virtual destructors must always provide a definition. Note that these rules are the same for nonexportable classes.
-
声明类 dllexport 时,将导出其所有成员函数和静态数据成员。你必须在同一程序中提供所有此类成员的定义。否则,将生成链接器错误。此规则的一个例外适用于纯虚函数,无需为其提供明确的定义。但是,由于抽象类的析构函数始终由基类的析构函数调用,因此纯虚拟析构函数必须始终提供定义。请注意,这些规则对于不可导出的类是相同的。
-
If you export data of class type or functions that return classes, be sure to export the class.
-
如果导出类类型的数据或返回类的函数,请确保导出该类。
dllimport Classes
- When you declare a class dllimport, all its member functions and static data members are imported. Unlike the behavior of dllimport and dllexport on nonclass types, static data members cannot specify a definition in the same program in which a dllimport class is defined.
- 声明类 dllimport 时,将导入其所有成员函数和静态数据成员。与 dllimport 和 dllexport 对非类类型的行为不同,静态数据成员不能 在定义 dllimport 类的同一程序中指定定义。
Inheritance and Exportable Classes(继承和可导出类)
- All base classes of an exportable class must be exportable. If not, a compiler warning is generated. Moreover, all accessible members that are also classes must be exportable. This rule permits a dllexport class to inherit from a dllimport class, and a dllimport class to inherit from a dllexport class (though the latter is not recommended). As a rule, everything that is accessible to the DLL’s client (according to C++ access rules) should be part of the exportable interface. This includes private data members referenced in inline functions.
- 可导出类的所有基类必须是可导出的。如果不是,则生成编译器警告。此外,所有也是类的可访问成员必须是可导出的。此规则允许 dllexport 类从 dllimport 类继承,以及 dllimport 类从 dllexport 类继承(尽管不建议使用后者)。通常,DLL客户端可以访问的所有内容(根据 C ++访问规则)都应该是可导出接口的一部分。这包括内联函数中引用的私有数据成员。
Selective Member Import/Export(选择性成员导入/导出)
-
Because member functions and static data within a class implicitly have external linkage, you can declare them with the dllimport or dllexport attribute, unless the entire class is exported. If the entire class is imported or exported, the explicit declaration of member functions and data as dllimport or dllexport is prohibited. If you declare a static data member within a class definition as dllexport, a definition must occur somewhere within the same program (as with nonclass external linkage).
-
由于类中的成员函数和静态数据隐式具有外部链接,因此,除非导出整个类,否则可以使用 dllimport 或 dllexport 属性声明它们。如果导入或导出整个类,则禁止将成员函数和数据显式声明为 dllimport 或 dllexport。如果在类定义中将静态数据成员声明为 dllexport,则定义必须出现在同一程序内的某个位置(与非类外部链接一样)。
-
Similarly, you can declare member functions with the dllimport or dllexport attributes. In this case, you must provide a dllexport definition somewhere within the same program.
-
同样,你可以使用 dllimport 或 dllexport 属性声明成员函数。在这种情况下,必须在同一程序内的某个位置提供 dllexport 定义。
-
It is worthwhile to note several important points regarding selective member import and export:
-
值得注意的是有关选择性成员 import 和 export 的几个要点:
- Selective member import/export is best used for providing a version of the exported class interface that is more restrictive; that is, one for which you can design a DLL that exposes fewer public and private features than the language would otherwise allow. It is also useful for fine-tuning the exportable interface: when you know that the client, by definition, is unable to access some private data, you need not export the entire class.
- 选择性成员导入/导出最适合用于提供导出类接口的版本,其更为严格。也就是说,你可以为其设计一个 DLL,该 DLL 公开的公共和私有功能少于该语言所允许的数目。这对于微调可导出的接口也很有用:当你知道客户端(根据定义)无法访问某些私有数据时,就无需导出整个类。
- If you export one virtual function in a class, you must export all of them, or at least provide versions that the client can use directly.
- 如果你在类中导出一个虚函数,则必须导出所有虚函数,或至少提供客户端可以直接使用的版本。
- If you have a class in which you are using selective member import/export with virtual functions, the functions must be in the exportable interface or defined inline (visible to the client).
- 如果你使用的类中使用虚函数的选择性成员导入/导出,则这些函数必须在可导出接口中或定义为内联(对客户端可见)。
- If you define a member as dllexport but do not include it in the class definition, a compiler error is generated. You must define the member in the class header.
- 如果将成员定义为 dllexport 但未将其包括在类定义中,则会生成编译器错误。你必须在类头文件中定义成员。
- Although the definition of class members as dllimport or dllexport is permitted, you cannot override the interface specified in the class definition.
- 尽管允许将类成员定义为 dllimport 或 dllexport,但是你不能覆盖在类定义中指定的接口。
- If you define a member function in a place other than the body of the class definition in which you declared it, a warning is generated if the function is defined as dllexport or dllimport (if this definition differs from that specified in the class declaration).
- 如果在声明成员函数的类定义主体之外的其他位置定义成员函数,则将函数定义为 dllexport 或 dllimport(如果此定义与类声明中指定的不同),会生成警告。