程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> DLL頭文件的格式和應用

DLL頭文件的格式和應用

編輯:關於VC++

1、DLL的起源

動態鏈接庫(DLL)是從C語言函數庫和Pascal庫單元的概念發展而 來的。所有的C語言標准庫函數都存放在某一函數庫中。在鏈接應用程序的過程中,鏈接器從 庫文件中拷貝程序調用的函數代碼,並把這些函數代碼添加到可執行文件中。這種方法同只 把函數儲存在已編譯的OBJ文件中相比更有利於代碼的重用。

但隨著Windows這樣的多 任務環境的出現,函數庫的方法顯得過於累贅。如果為了完成屏幕輸出、消息處理、內存管 理、對話框等操作,每個程序都不得不擁有自己的函數,那麼Windows程序將變得非常龐大。 Windows的發展要求允許同時運行的幾個程序共享一組函數的單一拷貝。動態鏈接庫就是在這 種情況下出現的。動態鏈接庫不用重復編譯或鏈接,一旦裝入內存,DLL函數可以被系統中的 任何正在運行的應用程序軟件所使用,而不必再將DLL函數的另一拷貝裝入內存。

2、 DLL中函數的聲明

根據微軟DLL的編寫和調用規范,在DLL中,聲明和定義導出函數時 ,需要在函數前使用__declspec(dllexport)關鍵字,以表明該函數是DLL的導出函數;在DLL 的隱式調用方式中,應用程序在調用導出函數時,必須使用__declspec(dllimport)關鍵字先 聲明導入的函數。這種導入和導出函數的聲明方法也符合C/C++的函數的先聲明再調用的調用 規范。

3、DLL導出函數的鏈接類別及引用方式

導出函數在編譯、鏈接過程中 ,可以采用C鏈接和C++鏈接兩種方式,當采用C鏈接時,編譯器不更改導出函數的名稱,與之 相反,當采用C++鏈接時,編譯器則更改導出函數的名稱。

導出函數可以使用C語言編 寫,也可以使用C++語言編寫。對於采用C語言編寫的執行文件而言,如果調用采用C++語言編 寫的導出函數,應當強制指定使用C鏈接而不是C++鏈接生成導出函數庫;而對於采用C++語言 編寫的執行文件而言,如果調用采用C語言編寫的導出函數,應當強制指定使用C鏈接生成導 出函數庫。根據編譯器規范,指定、聲明函數使用C鏈接,則應當在函數聲明前使用關鍵字 extern "C"。

通常情況下,為了確保不同的語言編寫的可執行模塊都能夠 正確地訪問到導出函數,習慣上都采用extern "C"來指定導出函數采用C鏈接方式 。

4、DLL頭文件格式

在實際的編程中,通常都是把導出函數的聲明統一放在 一個頭文件中,而其定義則根據需要分布在不同的CPP文件中,這樣的實現方式比較方便對文 件及其功能的管理和維護。因此,DLL頭文件的格式如下:

#ifndef _DLLMODULENAME_H
#define _DLLMODULENAME_H
......
/*
*   if using C++ Compiler to compile the file, adopting C linkage mode
*/
#ifdef __cplusplus
extern "C" {
#endif
// macro define __declspec(dllexport)
#define DLLMODULENAME_LIB_API __declspec(dllexport)
// define export functions
DLLMODULENAME_LIB_API returntype FuncName (parameters);
// ... more declarations as needs
#undef DLLMODULENAME_LIB_API
#ifdef __cplusplus
}
#endif
#endif

根據微軟DLL隱式調用的規范,在使用導出函數前,應當首先聲明該導 出函數。在實際編程中,大多采用在一個頭文件中,統一聲明程序運行中調用到的DLL導出函 數,然後在所有調用DLL導出函數的文件中,包含該頭文件的方式。因此導出函數的引入頭文 件格式如下:

#ifndef _IMPORTFUNC_H
#define _IMPORTFUNC_H
#ifdef __cplusplus
extern "C" {
#endif
// macro define __declspec(dllimport)
#define DLLMODULENAME_LIB_API __declspec(dllimport)
// define export functions
DLLMODULENAME_LIB_API returntype FuncName (parameters);
// ... more declarations as needs
#undef DLLMODULENAME_LIB_API
#ifdef __cplusplus
}
#endif
#endif

從上述闡述可以看出,對於DLL導出函數而言,在DLL頭文件中聲明了一 次,而在隱式調用時,又聲明了一次,為消除這種重復聲明和減少文件數量,實際應用中通 常將兩個頭文件合並成一個DLL頭文件,同時定義一個宏,用於控制函數處於導出聲明或調用 導入聲明狀態。對於DLL定義文件,在包含DLL頭文件之前,首先定義一個控制宏,用於聲明 所有的函數為導出函數;而在隱式調用中,在包含DLL頭文件時不需要定義控制宏,用於聲明 所有的函數為導入函數。因此最終的DLL頭文件格式如下:#ifndef _DLLMODULENAME_H
#define _DLLMODULENAME_H
#include <>
#include ""
/*
*  if using C++ Compiler to compile the file, adopting C linkage mode
*/
#ifdef __cplusplus
extern "C" {
#endif
// according to the control macro, deciding whether export or import functions
#ifdef _DLLMODULENAME_
#define DLLMODULENAME_LIB_API __declspec(dllexport)
#else
#define DLLMODULENAME_LIB_API __declspec(dllimport)
#endif
// functions declarations
DLLMODULENAME_LIB_API returntype FuncName (parameters);
// ... more declarations as needs
#undef DLLMODULENAME_LIB_API
#ifdef __cplusplus
}
#endif
#endif

5、DLL頭文件的使用

DLL導出函數 的鏈接、導入、導出指示符在函數第一次聲明時確定,在以後的函數聲明和定義時,函數都 接受第一次函數的鏈接、導入、導出聲明,不必再次對函數作鏈接、導入、導出聲明,因此 DLL導出函數的定義文件中,可以使用如下的編碼格式:

/*
*  ensure compiler to compile correctly, through including
* the precompiled headers, or else resulting in C1010 error
*/
#include "stdafx.h"
#define _DLLMODULENAME_
#include "dllmodulename.h"
returntype FuncName (parameters)
{
  // function body
}
// other functions definitions

而在調用文件中,只需要包含頭文件即可, 即使用#include "dllmodulename.h"語句實現對DLL導出函數的導入聲明。

參 考文獻

Joseph M. Newcomer. The Ultimate (DLL) Header File, www.codeproject.com

《Windows核心編程》第四版,機械工業出版社

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved