當前位置:首頁 » 編程軟體 » 多個dll編譯成一個

多個dll編譯成一個

發布時間: 2022-07-23 15:12:44

㈠ vbnet編譯後有很多的dll文件如何才能編譯成只有一個exe文件

exe程序文件與dll文件是兩個獨立的文件,功能不同。exe是調用dll文件的代碼的。想通過編譯把n多dll文件與exe文件合為一個exe文件,目前沒見過。可試試捆綁來實現。

㈡ 有沒有將多個dll合並成為一個dll的工具或方法

在做.Net底層編碼過程中,為了功能獨立,有可能會生成多個DLL,引用時非常不便。這方面微軟提供了一個ILMerge工具
原版DOS工具
,可以將多個DLL合並成一個。下載完成後需要安裝一下,然後通過DOS命令進入。具體用法如下:D:\ProgramFiles\Microsoft\ILMergeilmerge/log:log
.txt/targetplatform:v4/out:merge
.dllAnalysisLibrary.dllUSBClassLibrary.dll
說明:此操作的目的是:將當前目錄下的AnalysisLibrary.dll和USBClassLibrary.dll類庫,按照.NetFramework 4.0形式(V4)合並成merge.dll。
其中:/log:log
.txt命令是將生成過程中的日誌,寫入到log.txt文件中。
.dll:是輸出文件。

㈢ 如何將多個類製作成一個dll,但只導出其中一個類

自從Windows的開始階段動態鏈接庫(DLL)就是Windows平台的一個組成部分。動態鏈接庫允許在一個獨立的模塊中封裝一系列的功能函數然後以一個顯式的C函數列表提供外部使用者使用。在上個世紀80年代,當Windows DLLs面世時,對於廣大開發者而言只有C語言是切實可行的開發手段。所以, Windows DLLs很自然地以C函數和數據的形式向外部暴露功能。從本質來說,一個DLL可以由任何語言實現,但是為了使DLL用於其它的語言和環境之下,一個DLL介面必須後退到最低要求的母體——C語言。使用C介面並不自動意味一個開發者應該應該放棄面向對象的開發方式。甚至C介面也能用於真正的面向對象編程,盡管它有可能被認為是一種單調乏味的實現方式。很顯然世界上使用人數排第二的編程語言是C++,但它卻不得不被DLL所誘惑。然而,和C語言相反,在調用者和被調用者之間的二進制介面被很好的定義並被廣泛接受,但是在C++的世界裡卻沒有可識別的應用程序二進制介面。實際上,由一個C++編譯器產生的二進制代碼並不能被其它C++編譯器兼容。再者,在同一個編譯器但不同版本的二進制代碼也是互不兼容的。所有這些導致從一個DLL中一個C++類簡直就是一個冒險。這篇文章就是演示幾種從一個DLL模塊中導出C++類的方法。源碼演示了導出虛構的Xyz對象的不同技巧。Xyz對象非常簡單,只有一個函數:Foo。下面是Xyz對象的圖解:Xyzint Foo(int)Xyz對象在一個DLL里實現,這個DLL能作為一個分布式系統供范圍很廣的客戶端使用。一個用戶能以下面三種方式調用Xyz的功能:使用一個規則的C++類使用一個抽象的C++介面源碼(譯註:文章附帶的源碼)包含兩個工程:XyzLibrary– 一個DLL工程XyzExecutable– 一個Win32 使用"XyzLibrary.dll"的控制台程序XyzLibrary工程使用下列方便的宏導出它的代碼:#if defined(XYZLIBRARY_EXPORT) // inside DLL# define XYZAPI __declspec(dllexport)#else // outside DLL# define XYZAPI __declspec(dllimport)#endif // XYZLIBRARY_EXPORTXYZLIBRARY_EXPORT標識符僅僅在XyzLibrary工程定義,因此在XYZAPI宏在DLL生成時被擴展為__declspec(dllexport)而在客戶程序生成時被擴展為__declspec(dllimport)。C語言方式 經典的C語言方式進行面向對象編程的一種方式就是使用晦澀的指針,比如句柄。一個用戶能夠使用一個函數創建一個對象。實際上這個函數返回的是這個對象的一個句柄。接著用戶能夠調用這個對象相關的各種操作函數只要這個函數能夠接受這個句柄作為它的一個參數。一個很好的例子就是在Win32窗口相關的API中句柄的習慣是使用一個HWND句柄來代表一個窗口。虛構的Xyz對象通過下面這樣一種方式導出一個C介面:typedef tagXYZHANDLE {} * XYZHANDLE;// 創建一個Xyz對象實例的函數XYZAPI XYZHANDLE APIENTRY GetXyz(VOID);// 調用Xyz.Foo函數XYZAPI INT APIENTRY XyzFoo(XYZHANDLE handle, INT n);// 釋放Xyz實例和佔用的資源XYZAPI VOID APIENTRY XyzRelease(XYZHANDLE handle);// APIENTRY is defined as __stdcall in WinDef.h header.下面是一個客戶端調用的C代碼:#include "XyzLibrary.h"/* 創建Xyz實例*/XYZHANDLE hXyz = GetXyz();if(hXyz) /* 調用 Xyz.Foo函數*/ XyzFoo(hXyz, 42); /*析構 Xyz實例並釋放已取得的資源. */ XyzRelease(hXyz); hXyz = NULL;使用這種方式,一個DLL必須提供顯式的對象構建和刪除函數。 對於所有的導出函數記住它們調用協定是重要的。對於很多初學者來說忘記添加調用協定是非常普遍的錯誤。只要客戶端的調用協定和DLL的調用協定匹配,一切都能運行。但是,一旦客戶端改變了它的調用協定,開發者將會產生一個難以察覺的直到運行時才發生的錯誤。XyzLibrary工程使用一個APIENTRY宏,這個宏在"WinDef.h"這個頭文件里被定義為__stdcall。異常安全性 在DLL范圍內不允許發生C++異常。在一段時間內,C語言不識別C++的異常,並且不能正確處理它們。假如一個對象的方法需要報告一個錯誤,這時一個返回碼需要用到。l 一個DLL能被最廣泛的合適的開發者所使用。幾乎每一種現代編程語言都支持純C函數的互用性。l 一個DLL的C運行時庫和它的客戶端是互相獨立的。因為資源的獲取和釋放完全發生在DLL模塊的內部,所以一個客戶端不受一個DLL的C運行時庫選擇的影響。l 獲取正確對象的合適的方法的責任落在DLL的使用者的肩上。比如在下面的代碼片斷,編譯器不能捕捉到其中發生的錯誤:l 獲取正確對象的合適的方法的責任落在DLL的使用者的肩上。比如在下面的代碼片斷,編譯器不能捕捉到其中發生的錯誤:/* void* GetSomeOtherObject(void)是別的地方定義的一個函數 */XYZHANDLE h = GetSomeOtherObject();/* 啊! 錯誤: 在錯誤的對象實例上調用Xyz.Foo函數*/XyzFoo(h, 42); l 顯式要求創建和摧毀一個對象的實例。其中特別煩人的是對象實例的刪除。客戶端必須極仔細地在一個函數的退出點調用XyzRelease函數。假如開發者忘記調用XyzRelease函數,那時資源就會泄露,因為編譯器不能跟蹤一個對象實例的生命周期。那些支持析構函數或垃圾收集器的語言通過在C介面上作一層封裝有助於降低這個問題發生的概率。l 假如一個對象的函數返回或接受其它對象作為參數,那時DLL作者也就不得不為這些對象提供一個正確的C介面。假如退回到最大限度的復用,也就是C語言,那麼只有以位元組創建的類型(如int, double, char*等等)可以作為返回類型和函數參數 C++天然的方式:導出一個類在Windows平台上幾乎每一個現代的編譯器都支持從一個DLL中導出一個類。導出一個類和導出一個C函數非常相似。用那種方法一個開發者被要求做就是在類名之前使用__declspec(dllexport/dllimport)關鍵字來指定假如整個類都需要被導出,或者在指定的函數聲明前指定假如只是特定的類函數需要被導出。這兒有一個代碼片斷:// 整個CXyz類被導出,包括它的函數和成員class XYZAPI CXyzpublic: int Foo(int n);// 只有 CXyz::Foo函數被導出class CXyzpublic: XYZAPI int Foo(int n); 在導出整個類或者它們的方法沒有必要顯式指定一個調用協定。根據預設,C++編譯器使用__thiscall作為類成員函數的調用協定。然而,由於不同的編譯器具有不同的命名修飾法則,導出的C++類只能用於同一類型的同一版本的編譯器。這兒有一個MS Visual C++編譯器的命名修飾法則的應用實例: 注意這里修飾名是怎樣不同於C++原來的名字。下面是屏幕截圖顯示的是通過使用Dependency Walker 工具對同一個DLL的修飾名進行破譯得到的: 只有MS Visual C++編譯器能使用這個DLL.DLL和客戶端代碼只有在同一版本的MS Visual C++編譯器才能確保在調用者和被調用者修飾名匹配。這兒有一個客戶端代碼使用Xyz對象的例子:#include "XyzLibrary.h"// 客戶端使用Xyz對象作為一個規則C++類.CXyz xyz;xyz.Foo(42);正如你所看到的,導出的C++類的用法和其它任何C++類的用法幾乎是一樣的。沒什麼特別的。重要事項:使用一個導出C++類的DLL和使用一個靜態庫沒有什麼不同。所有應用於有C++代碼編譯出來的靜態庫的規則完全適用於導出C++類的DLL。所見即所得一個細心的讀者必然已經注意到Dependency Walker工具顯示了額外的導出成員,那就是CXyz& CXyz::operator =(const CXyz&)賦值操作符。在工作你所看到的正是C++的收入(譯註:我估計這是原文作者幽默的說法,意思是你沒有定義一個=賦值操作符,而編譯器幫你自動定義一個,不是收入是什麼?)。根據C++標准,每一個類有四個指定的成員函數:默認構造函數拷貝構造函數賦值操作符 (operator =)假如類的作者沒有聲明同時沒有提供這些成員的實現,那麼C++編譯器會聲明它們,並產生一個隱式的默認的實現。在CXyz類,編譯器斷定它的默認構造函數,拷貝構造函數和析構函數都毫無意義,經過優化後把它們排除掉了。而賦值運算符在優化之後還存活並從DLL中導出。重要事項:使用__declspec(dllexport)來指定類導出來告訴編譯器來嘗試導出任何和類相關的東西。它包括所有類的數據成員,所有類的成員函數(或者顯式聲明,或者由編譯器隱式生成),所有類的基類和所有它們的成員。考慮:class Baseclass Data// MS Visual C++ 中國piler 會發出C4275 warning ,因為沒有導出基類class __declspec(dllexport) Derived : public Baseprivate: Data m_data; // C4251 warning,因為沒有導出數據成員. 在上面的代碼片斷,編譯器會警告你沒有導出基類和類的數據成員。所以,為了成功導出一個類,一個開發者被要求導出所有相關基類和所有類的已定義的數據成員。這個滾雪球般的導出要求是一個重大缺點。這也是為什麼,比如,導出派生自STL模板類或者使用STL模板類對象作為數據成員是非常困難和令人生厭的。比如一個STL容器比如std::map實例可能要求導出數十個額外的內部類。異常安全性一個導出的C++類可能會在沒有任何錯誤發生的情況下拋出異常。因為一個DLL和它的客戶端使用同一版本的同一類型的編譯器的事實,C++異常將在超出DLL的范圍進行捕捉和拋出好像DLL沒有分界線一樣。記住,使用一個帶有導出C++代碼和使用帶有相同代碼的靜態庫是完全一樣的。優點l 一個導出的C++類和其它任何C++類的用法是一樣的l 客戶端能毫不費力地捕捉在DLL發生的異常l 當在一個DLL模塊內有一些小的代碼改動時,其它模塊也不用重新生成。這對於有著許多復雜難懂代碼的大工程是非常有用的。l 在一個大工程中按照業務邏輯分成不同的DLL實現可以被認為真正的模塊劃分的第一步。總的來說,它是使工程達到模塊化值得去做的事缺點l 從一個DLL中導出C++類在它的對象和使用者需要保持緊密的聯系。DLL應該被視作一個帶有考慮到代碼依賴的靜態庫。l 客戶端代碼和DLL都必須和同一版本的CRT(譯註:C運行時庫)動態連接在一起。為了能夠在模塊之間修正CRT資源的紀錄,這一步是必需的。假如一個客戶端和DLL連接到不同版本的CRT,或者靜態連接到CRT,那麼在一個CRT實例申請的資源有可能在另一個CRT實例中釋放。它將損壞CRT實例的內在狀態並企圖操作外部資源,並很可能導致運行失敗。l 客戶端代碼和DLL必須在異常處理和產生達成一致,同時在編譯器的異常設置也必須一致l 導出C++類要求同時導出這個類的所有相關的東西,包括:所有它的基類、所有類定義的用到的數據成員等等。C++成熟的方法:使用抽象介面一個C++抽象介面(比如一個擁有純虛函數和沒有數據成員的C++類)設法做到兩全其美:對對象而言獨立於編譯器的規則的介面以及方便的面向對象方式的函數調用。為達到這些要求去做的就是提供一個介面聲明的頭文件,同時實現一個能返回最新創建的對象實例的工廠函數。只有這個工廠函數需要使用__declspec(dllexport/dllimport)指定。介面不需要任何額外的指定。// Xyz object的抽象介面// 不要求作額外的指定struct IXyz virtual int Foo(int n) = 0; virtual void Release() = 0;// 創建Xyz對象實例的工廠函數extern "C" XYZAPI IXyz* APIENTRY GetXyz();在上面的代碼片斷中,工廠函數GetXyz被聲明為extern XYZAPI。這樣做是為了防止函數名被修飾(譯註:如上面提到的導出一個C++類,其成員函數名導出後會被修飾)。這樣,這個函數在外部表現為一個規則的C函數,並且很容易被和C兼容的編譯器所識別。這就是當使用一個抽象介面時客戶端代碼看起來和下面一樣:#include "XyzLibrary.h"IXyz* pXyz = ::GetXyz();if(pXyz) pXyz->Foo(42); pXyz->Release(); pXyz = NULL;C++不用為介面提供一個特定的標記以便其它編程語言使用(比如C#或Java)。但這並不意味C++不能聲明和實現介面。設計一個C++的介面的一般方法是去聲明一個沒有任何數據成員的抽象類。這樣,派生類可以繼承這個介面並實現這個介面,但這個實現對客戶端是不可見的。介面的客戶端不用知道和關注介面是如何實現的。它只需知道函數是可用的和它們做什麼。內部機制在這種方法背後的思想是非常簡單的。一個由純虛函數組成的成員很少的類只不過是一個虛函數表——一個函數指針數組。在DLL范圍內這個函數指針數組被它的作者填充任何他認為必需的東西。這樣這個指針數組在DLL外部使用就是調用介面的實際上的實現。下面是IXyz介面的用法說明圖表。 上面的圖表演示了IXyz介面被DLL和EXE模塊二者都用到。在DLL模塊內部,XyzImpl類派生自IXyz介面並實現它的方法。在EXE的函數調用引用DLL模塊經過一個虛表的實際實現。這種DLL為什麼能和其它的編譯器一起運行簡短的解釋是:因為COM技術和其它的編譯器一起運行。現在作一個詳細解釋,實際上,在模塊之間使用一個成員很少的虛基類作為介面准確來說是COM對外暴露了一個COM介面。如我們所知的虛表的概念,能很精確地添加COM標準的標記。這不是一個巧合。C++語言,作為一個至少跨越了十年的主流開發語言,已經廣泛地應用在COM編程。因為C++天生地支持面向對象的特性。微軟將它作為產業COM開發的重量級的工具是毫不奇怪的。作為COM技術的所有者,微軟已經確保COM的二進制標准和它們擁有的在Visual C++編譯器實現的C++對象模型能以最小的成本實現匹配。難怪其它的編譯器廠商都和微軟採用相同的方式實現虛表的布局。畢竟,每個人都想支持COM技術,並做到和微軟已存在的解決方法兼容。假設某個C++編譯器不能有效支持COM,那麼它註定會被Windows市場所拋棄。這就是為什麼時至今日,通過一個抽象介面從一個DLL導出一個C++類能和Windows平台上過得去的編譯器能可靠地運行在一起。使用一個智能指針為了確保正確的資源釋放,一個虛介面提供了一個額外的函數來清除對象實例。手動調用這個函數令人厭煩並容易導致錯誤發生。我們都知道這個錯誤在C世界裡這是一個很普遍的錯誤,因為在那兒開發者不得不記得釋放顯式函數調用獲取的資源。這就是為什麼典型的C++代碼藉助於智能指針使用RAII(資源獲取即初始化)的習慣。XyzExecutable工程提供了一個例子,使用了AutoClosePtr模板。AutoClosePtr模板是一個最簡單的智能指針,這個智能指針調用了一個類消滅一個實例的主觀方法來代替delete操作符。這兒有一段演示帶有IXyz介面的一個智能指針的用法的代碼片斷:#include "XyzLibrary.h"#include "AutoClosePtr.h"typedef AutoClosePtr IXyzPtr;IXyzPtr ptrXyz(::GetXyz());if(ptrXyz) ptrXyz->Foo(42);// 不需要調用ptrXyz->Release(). 智能指針將在析構函數里自動調用這個函數不管怎樣,使用智能指針將確保Xyz對象能正當地適當資源。因為一個錯誤或者一個內部異常的發生,函數會過早地退出,但是C++語言保證所有局部對象的析構函數能在函數退出之前被調用。異常安全性和COM介面一樣不再允許因為任何內部異常的發生而導致資源泄露,抽象類介面不會讓任何內部異常突破DLL范圍。函數調用將會使用一個返回碼來明確指示發生的錯誤。對於特定的編譯器,C++異常的處理都是特定的,不能夠分享。所以,在這個意義上,一個抽象類介面表現得十足像一個C函數。優點:l 一個導出的C++類能夠通過一個抽象介面,被用於任何C++編譯器l 一個DLL的C運行庫和DLL的客戶端是互相獨立的。因為資源的初始化和釋放都完全發生在DLL內部,所以客戶端不受DLL的C運行庫選擇的影響。l 真正的模塊分離能高度完美實現。結果模塊可以重新設計和重新生成而不受工程的剩餘模塊的影響。l 如果需要,一個DLL模塊能很方便地轉化為真正的COM模塊。缺點:l 一個顯式的函數調用需要創建一個新的對象實例並刪除它。盡管一個智能指針能免去開發者之後的調用l 一個抽象介面函數不能返回或者接受一個規則的C++對象作為一個參數。它只能以內置類型(如int、double、char*等)或者另一個虛介面作為參數類型。它和COM介面有著相同的限制。STL模板類是怎樣做的C++標准模板庫的容器(如vector,list或map)和其它模板並沒有設計為DLL模塊(以抽象類介面方式)。有關DLL的C++標準是沒有的因為DLL是一種平台特定技術。C++標准不需要出現在沒有用到C++語言的其它平台上。當前,微軟的Visual C++編譯器能夠導出和導入開發者顯式以__declspec(dllexport/dllimport)關鍵字標識的STL類實例。編譯器會發出幾個令人討厭的警告,但是還能運行。然而,你必須記住,導出STL模板實例和導出規則C++類是完全一樣的,有著一樣的限制。所以,在那方面STL是沒什麼特別的。總結這篇文章討論了幾種從一個DLL模塊中導出一個C++對象的不同方法。對每種方法的優點和缺點的詳細論述也已給出。下面是得出的幾個結論:l 以一個完全的C函數導出一個對象有著最廣泛的開發環境和開發語言的兼容性。然而,為了使用現代編程範式一個DLL使用者被要求使用過時的C技巧對C介面作一層額外的封裝。l 導出一個規則的C++類和以C++代碼提供一個單獨的靜態庫沒什麼區別。用法非常簡單和熟悉,然而DLL和客戶端有著非常緊密的連接。DLL和它的客戶端必須使用相同版本和相同類型的編譯器。l 定義一個無數據成員的抽象類並在DLL內部實現是導出C++對象的最好方法。到目前為止,這種方法在DLL和它的客戶端提供了一個清晰的,明確界定的面向對象介面。這樣一種DLL能在Windows平台上被任何現代C++編譯器所使用。介面和智能指針一起結合使用的用法幾乎和一個導出的C++類的用法一樣方便

㈣ c#如何合並多個dll文件

ILMerge可以滿足合並dll的需求:
下載連接 http://download.microsoft.com/download/1/3/4/1347C99E-9DFB-4252-8F6D-A3129A069F79/ILMerge.msi

安裝完成以後,可以將安裝目錄加入PATH下,
在命令行用如下命令進行merge

ILMerge /ndebug /target:dll /out:D:\output\KCService.dll /log D:\project\xxx-1.dll D:\project\xxx-2.dll
參數說明:
(1) "/out:D:\output\KCService.dll" 為合並後的DLL目錄及最終的DLL文件
(2) 「D:\project\xxx-1.dll D:\project\xxx-2.dll」 為要合並的源頭DLL文件

如果要發布exe文件則可以將dll文件打入exe文件中
ILMerge /ndebug /target:winexe /out:D:\output\runKCService.exe /log D:\project\kcservice.exe D:\project\xxx-1.dll D:\project\xxx-2.dll

㈤ vs2013怎麼把多個dll文件打包成一個dll,使用的時候不再引用其他dll

項目--屬性--配置屬性--常規
MFC的使用,修改為:在靜態庫中使用MFC
就會把用到的DLL編譯器來。

㈥ 如何編譯一個 dll文件

創建DLL工程
這里,我們為了簡要說明DLL的原理,我們決定使用最簡單的編譯環境VC6.0,如下圖,我們先建立一個新的Win32 Dynamic-Link Library工程,名稱為「MyDLL」,在Visual Studio中,你也可以通過建立Win32控制台程序,然後在「應用程序類型」中選擇「DLL」選項,

點擊確定,選擇「一個空的DLL工程」,確定,完成即可。

一個簡單的dll
在第一步我們建立的工程中建立一個源碼文件」dllmain.cpp「,在「dllmain.cpp」中,鍵入如下代碼

[cpp] view plain
#include <Windows.h>
#include <stdio.h>

BOOL APIENTRY DllMain(HMODULE hMole, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("DLL_PROCESS_ATTACH\n");
break;
case DLL_THREAD_ATTACH:
printf("DLL_THREAD_ATTACH\n");
break;
case DLL_THREAD_DETACH:
printf("DLL_THREAD_DETACH\n");
break;
case DLL_PROCESS_DETACH:
printf("DLL_PROCESS_DETACH\n");
break;
}
return TRUE;
}
之後,我們直接編譯,即可以在Debug文件夾下,找到我們生成的dll文件,「MyDLL.dll」,注意,代碼裡面的printf語句,並不是必須的,只是我們用於測試程序時使用。而DllMain函數,是dll的進入/退出函數。

實際上,讓線程調用DLL的方式有兩種,分別是隱式鏈接和顯式鏈接,其目的均是將DLL的文件映像映射進線程的進程的地址空間。我們這里只大概提一下,不做深入研究,如果感興趣,可以去看《Window高級編程指南》的第12章內容。
隱式鏈接調用
隱士地鏈接是將DLL的文件影響映射到進程的地址空間中最常用的方法。當鏈接一個應用程序時,必須制定要鏈接的一組LIB文件。每個LIB文件中包含了DLL文件允許應用程序(或另一個DLL)調用的函數的列表。當鏈接器看到應用程序調用了某個DLL的LIB文件中給出的函數時,它就在生成的EXE文件映像中加入了信息,指出了包含函數的DLL文件的名稱。當操作系統載入EXE文件時,系統查看EXE文件映像的內容來看要裝入哪些DLL,而後試圖將需要的DLL文件映像映射到進程的地址空間中。當尋找DLL時,系統在系列位置查找文件映像。

1.包含EXE映像文件的目錄
2.進程的當前目錄
3.Windows系統的目錄
4.Windows目錄
5.列在PATH環境變數中的目錄

這種方法,一般都是在程序鏈接時控制,反映在鏈接器的配置上,網上大多數講的各種庫的配置,比如OPENGL或者OPENCV等,都是用的這種方法

顯式鏈接調用
這里我們只提到兩種函數,一種是載入函數
[cpp] view plain
HINSTANCE LoadLibrary(LPCTSTR lpszLibFile);

HINSTANCE LoadLibraryEx(LPCSTR lpszLibFile,HANDLE hFile,DWORD dwFlags);
返回值HINSTANCE值指出了文件映像映射的虛擬內存地址。如果DLL不能被映進程的地址空間,函數就返回NULL。你可以使用類似於

[cpp] view plain
LoadLibrary("MyDLL")
或者

[cpp] view plain
LoadLibrary("MyDLL.dll")
的方式進行調用,不帶後綴和帶後綴在搜索策略上有區別,這里不再詳解。

顯式釋放DLL

在顯式載入DLL後,在任意時刻可以調用FreeLibrary函數來顯式地從進程的地址空間中解除該文件的映像。
[cpp] view plain
BOOL FreeLibrary(HINSTANCE hinstDll);
這里,在同一個進程中調用同一個DLL時,實際上還牽涉到一個計數的問題。這里也不在詳解。
線程可以調用GetMoleHandle函數:

[cpp] view plain
GetMoleHandle(LPCTSTR lpszMoleName);
來判斷一個DLL是否被映射進進程的地址空間。例如,下面的代碼判斷MyDLL.dll是否已被映射到進程的地址空間,如果沒有,則裝入它:

[cpp] view plain
HINSTANCE hinstDll;
hinstDll = GetMoleHandle("MyDLL");
if (hinstDll == NULL){
hinstDll = LoadLibrary("MyDLL");
}
實際上,還有一些函數,比如 GetMoleFileName用來獲取DLL的全路徑名稱,FreeLibraryAndExitThread來減少DLL的使用計數並退出線程。具體內容還是參見《Window高級編程指南》的第12章內容,此文中不適合講太多的內容以至於讀者不能一下子接受。

DLL的進入與退出函數

說到這里,實際上只是講了幾個常用的函數,這一個小節才是重點。
在上面,我們看到的MyDLL的例子中,有一個DllMain函數,這就是所謂的進入/退出函數。系統在不同的時候調用此函數。這些調用主要提供信息,常常被DLL用來執行進程級或線程級的初始化和清理工作。如果你的DLL不需要這些通知,就不必再你的DLL源代碼中實現此函數,例如,如果你創建的DLL只含有資源,就不必實現該函數。但如果有,則必須像我們上面的格式。
DllMain函數中的ul_reason_for_call參數指出了為什麼調用該函數。該參數有4個可能值: DLL_PROCESS_ATTACH、DLL_THREAD_ATTACH、DLL_THREAD_DETACH、DLL_PROCESS_DETACH。
其中,DLL_PROCESS_ATTACH是在一個DLL首次被映射到進程的地址空間時,系統調用它的DllMain函數,傳遞的ul_reason_for_call參數為DLL_PROCESS_ATTACH。這只有在首次映射時發生。如果一個線程後來為已經映射進來的DLL調用LoadLibrary或LoadLibraryEx,操作系統只會增加DLL的計數,它不會再用DLL_PROCESS_ATTACH調用DLL的DllMain函數。
而DLL_PROCESS_DETACH是在DLL被從進程的地址空間解除映射時,系統調用它的DllMain函數,傳遞的ul_reason_for_call值為DLL_PROCESS_DETACH。我們需要注意的是,當用DLL_PROCESS_ATTACH調用DLL的DllMain函數時,如果返回FALSE,說明初始化不成功,系統仍會用DLL_PROCESS_DETACH調用DLL的DllMain。因此,必須確保沒有清理那些沒有成功初始化的東西。
DLL_THREAD_ATTACH:當進程中創建一個線程時,系統察看當前映射到進程的地址空間中的所有DLL文件映像,並用值DLL_THREAD_ATTACH調用所有的這些DLL的DllMain函數。該通知告訴所有的DLL去執行線程級的初始化。注意,當映射一個新的DLL時,進程中已有的幾個線程在運行,系統不會為已經運行的線程用值DLL_THREAD_ATTACH調用DLL的DllMain函數。
而DLL_THREAD_DETACH,如果線程調用ExitThread來終結(如果讓線程函數返回而不是調用ExitThread,系統會自動調用ExitThread),系統察看當前映射到進程空間的所有DLL文件映像,並用值DLL_THREAD_DETACH來調用所有的DLL的DllMain函數。該通知告訴所有的DLL去執行線程級的清理工作。
這里,我們需要注意的是,如果線程的終結是因為系統中的一個線程調用了TerminateThread,系統就不會再使用DLL_THREAD_DETACH來調用DLL和DllMain函數。這與TerminateProcess一樣,不再萬不得已時,不要使用。
下面,我們貼出《Window高級編程指南》中的兩個圖來說明上述四種參數的調用情況。

好的,介紹了以上的情況,下面,我們來繼續實踐,這次,建立一個新的空的win32控制台工程TestDLL,不再多說,代碼如下:

[cpp] view plain
#include <iostream>
#include <Windows.h>
using namespace std;

DWORD WINAPI someFunction(LPVOID lpParam)
{
cout << "enter someFunction!" << endl;
Sleep(1000);
cout << "This is someFunction!" << endl;
Sleep(1000);
cout << "exit someFunction!" << endl;
return 0;
}

int main()
{
HINSTANCE hinstance = LoadLibrary("MyDLL");
if(hinstance!=NULL)
{
cout << "Load successfully!" << endl;
}else {
cout << "Load failed" << endl;
}
HANDLE hThread;
DWORD dwThreadId;

cout << "createThread before " << endl;
hThread = CreateThread(NULL,0,someFunction,NULL,0,&dwThreadId);
cout << "createThread after " << endl;
cout << endl;

Sleep(3000);

cout << "waitForSingleObject before " << endl;
WaitForSingleObject(hThread,INFINITE);
cout << "WaitForSingleObject after " << endl;
cout << endl;

FreeLibrary(hinstance);
return 0;
}

代碼很好理解,但是前提是,你必須對線程有一定的概念。另外,注意,我們上面編譯的獲得的「MyDLL.dll"必須拷貝到能夠讓我們這個工程找到的地方,也就是上面我們提到的搜索路徑中的一個地方。
這里,我們先貼結果,當然,這只是在我機器上其中某次運行結果。

有了上面我們介紹的知識,這個就不是很難理解,主進程在調用LoadLibrary時,用DLL_PROCESS_ATTACH調用了DllMain函數,而線程創建時,用DLL_THREAD_ATTACH調用了DllMain函數,而由於主線程和子線程並行的原因,可能輸出的時候會有打斷。但是,這樣反而能讓我們更清楚的理解程序。

㈦ vc中如何將dll件和exe編譯成一個exe文件

在項目屬性中設置就可以,以VS2005為例,如下圖選擇第二項,就會把MFC的DLL編譯到EXE文件中,文件長度增加大約400多K

㈧ 一個應用程序里有很多DLL是怎麼編譯出來的

DLL就是功能獨立出來的軟體包,主要就是為了實現程序功能模塊的分割,一個DLL可以同時在系統中被多個EXE進程映射,而時間上在內存中的拷貝只有一份。
DLL和EXE一樣都是PE文件。

比如你的控制台程序(EXE的)要引用第三方的軟體包,你就 可以將他們提供的lib文件加入到你的程序中,使用「#pragram comment(lib,"xxx.lib") 」——如果對方提供有.h文件,也要將其在包含在cpp的前面,或者延遲載入「LoadLibrary / GetProcAddress / FreeLibray 」,

自己也可以做個dll,導出函數、類、變數,等

任何一個程序都默認包含了系統的幾個dll庫文件。

㈨ 如何將兩個(多個)DLL合並成一個DLL

使用ILMerge工具,在DOS下用命令行合並

ILMerge工具和合並方法詳見:http://download.csdn.net/detail/h_h_h_123/5883277

熱點內容
上編程序 發布:2025-02-06 10:49:08 瀏覽:795
會議源碼 發布:2025-02-06 10:47:09 瀏覽:92
phpeclipse斷點 發布:2025-02-06 10:47:02 瀏覽:360
腳本之家知網收錄嗎 發布:2025-02-06 10:39:08 瀏覽:770
昵稱與密碼的意思是什麼 發布:2025-02-06 10:27:38 瀏覽:671
伺服器沒密碼怎麼辦 發布:2025-02-06 10:27:36 瀏覽:206
寶塔面板阿里雲伺服器偽靜態 發布:2025-02-06 10:24:50 瀏覽:342
android輸入框限制 發布:2025-02-06 10:24:47 瀏覽:398
阿里雲新購伺服器備案 發布:2025-02-06 10:22:05 瀏覽:982
棋牌數據怎麼轉移伺服器 發布:2025-02-06 10:19:53 瀏覽:314