Microsoft特殊處
dllimport和dllexport存儲類修飾符是C語言的Microsoft特殊處擴充。這些修飾顯式定義了DLL的客戶界面(可執行的文件或另外的DLL)。說明為dllexport的函數消除了一個模塊定義(.DLL)文件的需要。你可以為數據和對象使用dllimport和dllexport修飾符。
dllimport和dllexport存儲類修飾符必須與擴充的屬性語法關鍵字__declspec一起使用,下面是這樣的例子:
#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)
DllExport void func();
Dllexport int i = 10;
DllExport int j;
DllExport int n;
有關擴充的存儲類修飾符的語法的指定信息,參見第3章“說明和類型”中的“擴充的存儲類型屬性”。
Microsoft特殊處結束
定義和說明
Microsoft特殊處
DLL界面指的是系統中某個程序中輸出的所有已知項(函數和數據);也就是所有說明為dllimport或dllexport的所有項。包括在DLL界面中的所有說明必須指定為dllimport或dllexport屬性。但該定義只能指定dllexport屬性。例如,如下函數定義生成一個編譯器錯誤:
#define DLLImport __declspec(dllimport)
#define DLLExport __declspec(dllexport)
DLLImport int func()/*錯誤:在定義中禁止dllimport*/
{
return 1;
}
下面代碼也產生一個錯誤:
#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)
DllImport int i=10; /*錯誤:這是一個定義*/
但如下是正確的語法:
#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)
DllExport int i=10; /*正確:這是一個輸出定義*/
dllexport的使用隱含一個定義,而dllimport隱含一個說明。你必須對dllexport使用extern關鍵字強制為一個說明;否則,隱含是一個定義。
#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)
extern DllImport int k; /*這是正確的並隱含一個說明*/
Dllimport int j;
Microsoft特殊處結束
用dllexport和dllimport定義聯編函數
Microsoft特殊處
你可以用dllexport屬性定義一個聯編函數,在這種情況下,該函數總是被實例化和被輸出,無論程序中的任何模塊引用該函數。該函數假定是被另一程序輸入。
你也可以用dllimport屬性說明一個函數為聯編函數,在這種情況下,該函數可以被伸展(從屬於/Ob(聯編)編譯器選項規格)但不能被實例化。在特殊情況中,如果一個聯編輸入的函數的地址被占用,該函數的地址保留在返回的DLL中。這個行為和占用一個非聯編輸入的函數的地址相同。在聯編函數中的靜態局部數據和字符串在DLL和象在單個程序中似的客戶(也就是,一個沒有DLL界面的可執行文件)之間維護相同的標識符。
在進行提供輸入的聯編函數的練習時要小心,例如,如果你修改DLL,不要假設該客戶使用該DLL的改變的版本。為了保證你加載適當的DLL版本,重新建立該DLL的客戶。
Microsoft特殊處結束
dllimport/dllexport的規則和限制
Microsoft特殊處
* 如果你說明一個函數沒有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);/*警告:dllexport更優先*/
* 你不能用一個以dllimport屬性說明的數據對象的地址初始化一個靜態數據指針。
例如,如下代碼產生一個錯誤:#define DllImport __declspec(dllimport)#define DllExport __declspec(dllexport) DllImport int i ; . . . int *pi=&i; /* 錯誤 */ void func2() { static int *pi=&i; /* 錯誤 */ }
* 用一個dllimport說明的函數的地址初始化一個靜態函數指針,設置該指針為該DLL輸入形實替換程序(一個轉換控制到該函數的代碼塊)而不是該函數的地址。如下賦值不產生錯誤消息:
#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)
DllImport void func1(void)
. . . static void (*pf)(void)=&func1;/* 沒有錯誤 */
void func2()
{
static void (*pf)(void)=&func1;/* 沒有錯誤 */
}
* 因為在一個對象的說明中包括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; /* 正確 */
static void(*pf)(void) = &func1;
/* 正確 */
void func2()
{
static int *pi=i; /* 正確 */
static void (*pf)(void) = &func1; /* 正確 */
}
