C++中調用dll的兩種方式

        Windows下調用C++動態庫,即dll文件有兩種方式:隱式調用和顯式調用。如下以VS2013爲編譯環境說明。假若有一個項目TestDLL,產生的可執行文件名爲TestDLL.exe,在這個工程中調用mydll.dll,mydll.dll對應的.lib文件名爲mydll.lib。
        一 隱式調用
        這種調用方式須要編譯動態庫時用到的頭文件、靜態庫文件(.lib),動態庫文件(.dll)。
        這裏的.lib文件只包含一些索引,即.dll文件中導出的變量或函數的符號名和可選的標識號,不包含具體的代碼實現,具體的代碼實現都在.dll文件中, 應用程序中對函數或變量的調用與.lib文件中的符號相匹配,這些符號或標識號被寫入最終的可執行文件中,可執行文件運行時,就按照這些符號或標識號到.dll中尋找定義。.lib文件中也包含了對應的.dll文件名,但不是徹底路徑,連接程序會把這個.dll文件名寫到可執行文件中,當應用程序運行過程當中須要加載.dll文件時, windows根據這些信息發現並加載.dll, 而後經過符號名或標識符實現對dll函數的動態連接。全部被應用程序調用的.dll文件都會在應用程序加載時被加載到內存中。
        若是要完成源代碼的編譯,只須要.lib文件就能夠了;若是要使可執行文件運行起來,只要有.dll文件就能夠了,在編譯和測試階段,這兩個文件最好都有。
        隱式調用步驟以下:
        1 將編譯mydll.dll時用到的頭文件拷貝到TestDLL的項目目錄下,保證TestDLL項目在編譯時能夠找到這些頭文件。
        在VS2013中,右鍵點擊項目->Properties->Configuration Properties->VC++ Directories->Include Directories,而後將頭文件的路徑添加到項目配置中。
        2 將mydll.lib的文件路徑添加到TestDLL項目的依賴庫路徑下,保證TestDLL項目在編譯時可以找到這個mydll.lib。
        在VS2013中,右鍵點擊項目->Properties->Configuration Properties->VC++ Directories->Library Directories,而後將mydll.lib的文件路徑添加到項目配置中。
        3 將mydll.lib文件添加到TestDLL項目的附加依賴中,告訴程序要使用mydll.lib。
        在VS2013中,右鍵點擊項目->Properties->Configuration Properties->Linker->Input->Additional Dependencies, 將mydll.lib添加進去。
        4 將要用到的mydll.dll文件拷貝到可執行文件所在的目錄,即TestDLL.exe所在的目錄,或系統環境變量配置的目錄中(系統環境變量配置可到網上查找),保證TestDLL.exe運行時可以找到mydll.dll。
        在TestDLL項目中,須要用到mydll.dll的地方,包含相應的頭文件之後,就可使用mydll.dll中定義的變量或函數了。
        二 顯示調用
       
這種方式不須要頭文件,也不須要相應的.lib文件,只要.dll文件就能夠了。
        這種方式在應用程序編譯前並不知道會調用哪些.dll文件,在運行過程當中才肯定,運行過程當中,應用程序調用系統函數,在須要的時候加載.dll文件,而後獲取指定變量的址或指定函數的入口地址後,就可使用變量或進行函數調用了。 
        顯示式調用步驟以下:   
        
1 調用系統函數LoadLibraryEx加載mydll.dll。示例代碼片斷:windows

string dllPath = "D:\\Project\\lib\\mydll.dll";                 //mydll.dll的路徑
TCHAR dynLibPath[MAX_PATH_LEN];                                 //若是使用的是ANSI字符集,這一步不須要
swprintf(dynLibPath, MAX_PATH_LEN, L"%S", dllPath.c_str());     //若是使用的是ANSI字符集,這一步不須要
//若是使用的是ANSI字符集,LoadLibraryEx的第一個參數,直接使用dllPath.c_str(),即ANSI字符串便可。
HMODULE dllHandler = LoadLibraryEx(dynLibPath, NULL, DONT_RESOLVE_DLL_REFERENCES); 
if(dllHandler)
{
    cout << "Load mydll.dll successfully!" << endl;
}
else
{
    cout << "Load mydll.dll failed!" << endl;
}

        2 調用系統函數GetProcAddress獲取變量或函數的地址。示例代碼片斷:函數

FARPROC pFun = NULL;
pFun = GetProcAddress(dllHandler, "TestFun");    //TestFun是mydll.dll中導出的函數
pFun();                    //調用TestFun
FARPROC pVar = NULL;
pVar = GetProcAddress(dllHandler, "TestVar");    //TestVar是mydll.dll中導出的整型變量
int num = *(int*)pVar;     //將pVar轉換成整形
cout<<num<<endl;           //打印TestVar的值

        注意: 有時候,dll中肯定有相應的函數,調用GetProcAddress卻返回NULL,例如: mydll.dll中肯定導出了函數「 AnotherTestFun",在Dependency Walker下看到相應的函數名爲?AnotherTestFun@3HA,調用GetProcAddress(dllHandler, "AnotherTestFun")始終返回NULL;
        而調用GetProcAddress(dllHandler, "?AnotherTestFun@3HA")卻能夠返回有效地址值。
        像這種狀況,多是由於在定義AnotherTestFun函數中,函數聲明前忘了加 extern "C"。
        若是沒有加extern "C",編譯器在編譯AnotherTestFun這個函數時,會按照C++的規則翻譯這個函數名,便可能把函數名字改掉,例如改爲上面的"?AnotherTestFun@3HA",具體改爲什麼樣的名字視編譯器而定,這樣一來,以TestDLL項目中再以"AnotherTestFun"爲參數獲取該函數的地址,確定會失敗。
        若是加上了extern "C",則是告訴編譯器在編譯函數時按照C語言的規則去翻譯相應的函數名,即「請保持函數名稱,不要生成中間函數名"。測試

相關文章
相關標籤/搜索