DLL(Dynamic Link Library),動態鏈接庫。
1.關于__declspec(dllimport)和__declspec(dllexport)
在一個DLL的編寫過程中,如果要使一個變量、一個函數或一個類能夠被外部程序調用,在函數聲明上加上__declspec(dllimport)關鍵字。
例:
引出一個變量:
__declspec(dllexport) int iEx;
引出一個函數:
__declspec(dllexport) int fnEx( int i );
引出一個類:
class __declspec(dllexport) CClassEx
{……};
在外部程序引用DLL時,如果采用隱式調用的方法(關于隱式調用稍后討論),需要包含DLL文件相關的頭文件,這個頭文件用__declspec(dllimport)關鍵字指出了DLL導出的內容(實際上就是把原來的__declspec(dllexport)全部替換為__declspec(dllimport))。
例:
引入一個變量:
__declspec(dllimport) int iEx;
引入一個函數:
__declspec(dllimport) int fnEx( int i );
引入一個類:
class __declspec(dllimport) CClassEx
{……};
為了提高程序可讀性,可以定義如下宏:
#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)
以便使用。
實際上一般定義如下宏:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec ( dllexport )
#else
#define MYDLL_API __declspec ( dllimport )
#endif
這樣,在源程序的實現文件中先定義 MYDLL_EXPORTS ,再包含這個頭文件;在外程序內,可以直接包含這個頭文件。這種方法可以實現一個文件多種用途。
? 2 .隱式調用和顯式調用
隱式調用是最常用的一種調用方法。它需要原 DLL 文件、原頭文件和庫文件組成。在程序內要包含這個頭文件,并加入庫文件一同編譯。在程序啟動時會把所有隱式調用的 DLL 文件加載到內存。
如果想在使用的時候才把 DLL 文件加入到內存的話,可以使用顯式調用。顯式調用只需要原 DLL 文件即可,但是使用時必須對知道這個 DLL 有哪些導出的符號。可以在 VS 的命令行方式下輸入“ depends ”命令來查看 DLL 信息。
? 示例:
? 隱式調用:
#include "..\DllTest\DllTest.h"
……
void CallDll( void )
{
???? wchar_t wszBuf[ 32 ];
???? wsprintf( wszBuf, TEXT( "%d" ), fnDllTest( 9 ) ); //fnDllTest 是DLL文件導出的方法
???? wprintf( wszBuf );
} ?
顯式調用:
void CallDll( void )
{
???? typedef int (*ADDPROC)( int i );
???? ADDPROC Fn;
?
???? HINSTANCE hInst = LoadLibrary( L "DllTest.dll" );
???? if ( NULL == hInst )
???? {
???????? wprintf( L "Failed on load DLL." );
???????? return ;
???? }
???? Fn = ( ADDPROC )GetProcAddress( hInst, "fnDllTest" );
???? // 如果導出時使用的是_stdcall, 那么這里我們也應該使用(_stdcall ADDPROC)
???? if ( !Fn )
???? ???? wprintf( L "Failed on get Process Address." );
???? else
???? {
???????? int i;
???????? i = Fn( 9 );
???????? wchar_t wszBuf[ 32 ];
???????? wsprintf( wszBuf, L "%d" , i );
???????? wprintf( wszBuf );
???? }
???? FreeLibrary(hInst); // 釋放DLL
???? /*
???? ?* ADDRPROC Add = (ADDPROC)GetProcAddress(hInst, "add");
???? ?* 第而個參數也可以使用序號:
???? ?* MAKEINTRESOURCE(1) 代替函數名.
???? ?*/
}
3 .注意事項
如果使用 C++ 編程,那么在程序編譯的時候會為變量名等符號重命為諸如“ ?fnDllTest@QAEAAV 0 ” 之類的形式,為顯式調用帶來了不便。針對這個問題,我們可以為要導出的符號加上“ extern “C” ”修飾符,就可以保持原有名稱。但是導出的類名仍然是重命名后的名稱,要再研究研究。
如果你使用MFC生成MFC DLL那么只要做如下定義就可以了class AFX_EXT_CLASS yourClass。