DLL庫的編寫(匯出、匯入)與使用
DLL庫的編寫(匯出、匯入)與使用
相關說明:
(1) 編寫dll時,為什麼用 extern “C”:
因為C和C++的重新命名規則是不一樣的。這種重新命名稱為“Name-Mangling”。據說,C++標準並沒有規定Name-Mangling的方案,所以不同編譯器使用的是不同的,例如:Borland C++跟Microsoft C++就不同,而且可能不同版本的編譯器他們的Name-Mangling規則也是不同的。這樣的話,不同編譯器編譯出來的目標檔案.obj 是不通用的,因為同一個函式,使用不同的Name-Mangling在obj檔案中就會有不同的名字。如果DLL裡的函式重新命名規則跟DLL的使用者採用的重新命名規則不一致,那就會找不到這個函式。
C標準規定了C語言Name-Mangling的規範。這樣就使得,任何一個支援C語言的編譯器,它編譯出來的obj檔案可以共享,連結成可執行檔案。這是一種標準,如果DLL跟其使用者都採用這種約定,那麼就可以解決函式重新命名規則不一致導致的錯誤。
影響符號名的除了C++和C的區別、編譯器的區別之外,還要考慮呼叫約定導致的Name Mangling。如extern “c” _stdcall的呼叫方式就會在原來函式名上加上表示引數的符號,而extern “c” _cdecl則不會附加額外的符號。
dll中的函式在被呼叫時是以函式名或函式編號的方式被索引的。
所以綜上。若採用某編譯器的C++的Name-Mangling方式產生的dll檔案可能不通用。因為它們的函式名重新命名方式不同。為了使得dll可以通用些,很多時候都要使用C的Name-Mangling方式,即是對每一個匯出函式宣告為extern “C”,而且採用_stdcall呼叫約定,接著還需要對匯出函式進行重新命名,以便匯出不加修飾的函式名。
注意到extern “C”的作用是為了解決函式符號名的問題,這對於動態連結庫的製造者和動態連結庫的使用者都需要遵守的規則。但extern "C"只解決了C和C++語言之間呼叫的問題(extern “C”是告訴編譯器,讓它按C的方式編譯),它只能用於匯出全域性函式這種情況而不能匯出一個類的成員函式。
(2)_declspec(dllexport)和_declspec(dllimport)的作用:
_declspec還有另外的用途,這裡只討論跟dll相關的使用。正如括號裡的關鍵字一樣,匯出和匯入。
因為dll中必須說明函式要用於匯出,所以_declspec(dllexport)很有必要。但是可以換一種方式,可以使用def檔案來說明哪些函式用於匯出,同時def檔案裡邊還有函式的編號。而使用_declspec(dllimport)卻不是必須的,但是建議這麼做。因為如果不用_declspec(dllimport)來說明該函式是從dll匯入的,那麼編譯器就不知道這個函式到底在哪裡,生成的exe裡會有一個call XX的指令,這個XX是一個常數地址,XX地址處是一個jmp dword ptr[XXXX]的指令,跳轉到該函式的函式體處【可理解為先到一個函式表裡找到具體的函式體地址,然後跳轉到該地址執行】,顯然這樣就無緣無故多了一次中間的跳轉。如果使用了_declspec(dllimport)來說明,那麼就直接產生call dword ptr[XXX],這樣就不會有多餘的跳轉了。
(3)_sdtcall: 這是一種函式的呼叫方式。預設情況下VC使用的是_cdecl的函式呼叫方式,如果產生的dll只會給C/C++程式使用,那麼就沒必要定義為_stdcall呼叫方式,如果要給Win32彙編使用(或者其他的_stdcall呼叫方式的程式),那麼就應該使用_stdcall。這個可能不是很重要,因為可以自己在呼叫函式的時候可以設定函式呼叫的規則。像VC就可以設定函式的呼叫方式,所以可以方便的使用win32彙編產生的dll。不過_stdcall這呼叫約定會Name-Mangling,所以我覺得用VC預設的呼叫約定簡便些。但是,如果既要_stdcall呼叫約定,又要函式名不給修飾,那可以使用*.def檔案,或者在程式碼裡#pragma的方式給函式提供別名(這種方式需要知道修飾後的函式名是什麼)。
舉例:
·extern “C”_declspec(dllexport) bool _stdcall cswuyg();
·extern “C”_declspec(dllimport) bool _stdcall cswuyg();
·#pragma comment(linker,"/export:cswuyg=_cswuyg@0")
(4).def檔案的規則為:
①LIBRARY語句說明.def檔案相應的DLL;
②EXPORTS語句後列出要匯出函式的名稱。可以在.def檔案中的匯出函式名後加@n,表示要匯出函式的序號為n(在進行函式呼叫時,這個序號將發揮其作用);
③.def 檔案中的註釋由每個註釋行開始處的分號 (;) 指定,且註釋不能與語句共享一行。
*.def檔案只負責修改函式名稱,不負責呼叫約定。
舉例def檔案格式:
LIBRARY XX(dll名稱這個並不是必須的,但必須確保跟生成的dll名稱一樣)
EXPORTS
[函式別名]=[函式名](要匯出的函式) @ [函式序號]
如果把*.def檔案加入到工程之後,連結的時候還需把它加進去。那麼可以這樣做:
手動的在link新增:
1)工程的properties→Configuration Properties→Linker→Command Line→在 “Additional options”里加上:/def:[完整檔名].def
2)工程的properties→Configuration Properties→Linker→Input→Module Definition File 里加上[完整檔名].def
附:*.exe找dll的順序是1)程式的當前目錄;2)windows目錄下的系統目錄是c:/windows/system32;3)Windows目錄;4)PATH環境變數中列出的目錄
相關文章
- Windows環境下,動態連結庫(DLL)的“匯入”與“匯出”概念Windows
- AWR跨庫匯出與匯入
- Mysql 資料庫匯入與匯出MySql資料庫
- Oracle資料庫匯入匯出。imp匯入命令和exp匯出命令Oracle資料庫
- Angular Excel 匯入與匯出AngularExcel
- [Docker核心之容器、資料庫檔案的匯入匯出、容器映象的匯入匯出]Docker資料庫
- Mongodb資料的匯出與匯入MongoDB
- SQL資料庫的匯入和匯出SQL資料庫
- OracleDatabase——資料庫表空間dmp匯出與匯入OracleDatabase資料庫
- ASP.NET 開源匯入匯出庫Magicodes.IE 完成Csv匯入匯出ASP.NET
- sqoop部署及匯入與匯出OOP
- excel的匯入與匯出---通用版Excel
- mysqldump匯出匯入所有庫、某些庫、某些表的例子MySql
- 伺服器批次匯入與匯出伺服器
- 資料庫 MySQL 資料匯入匯出資料庫MySql
- Mongodb的備份恢復與匯出匯入MongoDB
- 如何使用 JavaScript 匯入和匯出 ExcelJavaScriptExcel
- Activity 流程模型匯入匯出-activity流程模型匯入匯出模型
- Mysql匯入&匯出MySql
- doris匯入匯出
- esayExcel匯入匯出Excel
- js匯入匯出總結與實踐JS
- SpringBoot+Vue之表格的CRUD與匯入匯出Spring BootVue
- 使用Dbeaver 進行資料的匯入和匯出
- 開源匯入匯出庫Magicodes.IE 多sheet匯入教程
- vue excel匯入匯出VueExcel
- navlicat 匯入匯出SQLSQL
- MongoDB--Mongodb 中資料匯出與匯入MongoDB
- .NET 開源匯入匯出庫 Magicodes.IE 2.5釋出
- 如何使用JavaScript匯入和匯出Excel檔案JavaScriptExcel
- java使使用者EasyExcel匯入匯出excelJavaExcel
- 使用csv批量匯入、匯出資料的需求處理
- 比 poi匯入匯出更好用的 EasyExcel使用小結Excel
- SVN庫匯出與還原
- [phpword][laravel-admin]匯入文件轉html,並使用富文字編輯後匯出PHPLaravelHTML
- 匯入和匯出AWR的資料
- EasyPoi, Excel資料的匯入匯出Excel
- .NET Core開源匯入匯出庫 Magicodes.IE 2.3釋出
- sqoop資料匯入匯出OOP