版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、1. 什么是lib文件,lib和dll的關(guān)系如何 (1)lib是編譯時需要的,dll是運(yùn)行時需要的。 如果要完成源代碼的編譯,有l(wèi)ib就夠了。 如果也使動態(tài)連接的程序運(yùn)行起來,有dll就夠了。 在開發(fā)和調(diào)試階段,當(dāng)然最好都有。 (2)一般的動態(tài)庫程序有l(wèi)ib文件和dll文件。lib文件是必須在編譯期就連接到應(yīng)用程序中的,而dll文件是運(yùn)行期才會被調(diào)用的。如果有dll文件,那么對應(yīng)的lib文件一般是一些索引信息,具體的實現(xiàn)在dll文件中。如果只有l(wèi)ib文件,那么這個lib文件是靜態(tài)編譯出來的,索引和實現(xiàn)都在其中。靜態(tài)編譯的lib文件有好處:給用戶安裝時就不需要再掛動態(tài)庫了。但也有缺點,就是導(dǎo)致應(yīng)
2、用程序比較大,而且失去了動態(tài)庫的靈活性,在版本升級時,同時要發(fā)布新的應(yīng)用程序才行。 (3)在動態(tài)庫的情況下,有兩個文件,一個是引入庫(.LIB)文件,一個是DLL文件,引入庫文件包含被DLL導(dǎo)出的函數(shù)的名稱和位置,DLL包含實際的函數(shù)和數(shù)據(jù),應(yīng)用程序使用LIB文件鏈接到所需要使用的DLL文件,DLL庫中的函數(shù)和數(shù)據(jù)并不復(fù)制到可執(zhí)行文件中,因此在應(yīng)用程序的可執(zhí)行文件中,存放的不是被調(diào)用的函數(shù)代碼,而是DLL中所要調(diào)用的函數(shù)的內(nèi)存地址,這樣當(dāng)一個或多個應(yīng)用程序運(yùn)行時再把程序代碼和被調(diào)用的函數(shù)代碼鏈接起來,從而節(jié)省了內(nèi)存資源。從上面的說明可以看出,DLL和.LIB文件必須隨應(yīng)用程序一起發(fā)行,否則應(yīng)用
3、程序?qū)a(chǎn)生錯誤。2、嚴(yán)重警告:(1) 用 extern "C" _declspec(dllexport) 只可以導(dǎo)出全局函數(shù),不能導(dǎo)出類的成員函數(shù)(2) 使用extern "C" _declspec(dllexport)輸出的函數(shù)可以被c語言調(diào)用,否則則不可(3) 注意標(biāo)準(zhǔn)調(diào)用約定的問題,輸出與調(diào)用的函數(shù)約定應(yīng)該一致,如當(dāng)dll模塊的函數(shù)輸出采用標(biāo)準(zhǔn)調(diào)用約定_stdcall,則調(diào)用程序的導(dǎo)入函數(shù)說明也要用標(biāo)準(zhǔn)約定(4)用extern "C" _declspec(dllexport) 和 EXPOTRT導(dǎo)出的函數(shù)不改變函數(shù)名,可以給c
4、+或c編寫的exe調(diào)用.假如沒有extern "C",導(dǎo)出的函數(shù)名將會改變,只能給c+編寫的exe調(diào)用 (5)在動態(tài)加載動態(tài)鏈接庫函數(shù)時注意GetProcAddress(hInst,"add")中第二個參數(shù)是否為動態(tài)鏈接庫導(dǎo)出的函數(shù)名,因為在生成動態(tài)庫時可能會改變動態(tài)庫導(dǎo)出函數(shù)的函數(shù)名,加上修飾符(6)dll初始化全局變量時,全局變量要放在共享數(shù)據(jù)斷,并且初始化每一個變量,在StartHook函數(shù)里初始化其值,記得一進(jìn)函數(shù)就初始化(7)調(diào)試時,編譯器會自動查找其目錄下(不含debug和release目錄)的dll文件,所以dll文件應(yīng)該放在主文件目錄下,
5、但生成的應(yīng)用程序則只會在同一個目錄下找dll(不需要lib文件),所以單純的運(yùn)行exe,不通過編譯器,那就要把dll文件放在與exe相同的目錄下(8)用#pragma comment(lib,"dllTest.lib")導(dǎo)入lib文件,不需要在設(shè)置里修改(9) dll里的指針變量不要用newDLL 調(diào)用方式DLL(動態(tài)連接庫),可以分為動態(tài)調(diào)用于靜態(tài)調(diào)用。下面我分別舉一個例子說說。1)動態(tài)調(diào)用:首先:在VC+6.0中創(chuàng)建 Win32 Dynamic-link library工程創(chuàng)建一個動態(tài)連接庫工程: 在頭文件TestDll.h中寫下代碼 extern "C&qu
6、ot; int _declspec(dllexport) add(int numa, int numb);/聲明導(dǎo)出函數(shù) 在源文件TestDll.cpp中實現(xiàn)改函數(shù): int _declspec(dllexport) add(int numa, int numb) return numa + numb; 然后,創(chuàng)建一個測試程序,TestDemo,創(chuàng)建一個.cpp文件,然后放下代碼:HINSTANCE hinstance;typedef int (*lpAdd)(int a, int b);lpAdd lpadd;int main() hinstance = LoadLibrary("
7、E:vcDLLTestDllDebugTestDll.dll"); lpadd = (lpAdd)GetProcAddress(hinstance, "add"); cout << "2 + 3 = " << lpadd(2, 3) << endl; FreeLibrary(hinstance); return 0;下面我們來逐一分析。首先,語句typedef int ( * lpAddFun)(int,int)定義了一個與add函數(shù)接受參數(shù)類型和返回值均相同的函數(shù)指針類型。隨后,在main函數(shù)中定義了lpA
8、ddFun的實例addFun; 其次,在函數(shù)main中定義了一個DLL HINSTANCE句柄實例hDll,通過Win32 Api函數(shù)LoadLibrary動態(tài)加載了DLL模塊并將DLL模塊句柄賦給了hDll; 再次,在函數(shù)main中通過Win32 Api函數(shù)GetProcAddress得到了所加載DLL模塊中函數(shù)add的地址并賦給了addFun。經(jīng)由函數(shù)指針addFun進(jìn)行了對DLL中add函數(shù)的調(diào)用; 最后,應(yīng)用工程使用完DLL后,在函數(shù)main中通過Win32 Api函數(shù)FreeLibrary釋放了已經(jīng)加載的DLL模塊。 通過這個簡單的例子,我們獲知DLL定義和調(diào)用的一般概念: (1)D
9、LL中需以某種特定的方式聲明導(dǎo)出函數(shù)(或變量、類); (2)應(yīng)用工程需以某種特定的方式調(diào)用DLL的導(dǎo)出函數(shù)(或變量、類)。2)靜態(tài)連接:代碼如下:#include <iostream>using namespace std;#pragma comment(lib,"Testlib.lib") /.lib文件中僅僅是關(guān)于其對應(yīng)DLL文件中函數(shù)的重定位信息extern "C" _declspec(dllimport) add(int x,int y);/聲明導(dǎo)入函數(shù) int main() int result = add(2,3); cout &
10、lt;<result <endl; return 0;注意:(1)、在編寫dll文件時,必須以某種方式聲明導(dǎo)出函數(shù)(dll文件內(nèi)有導(dǎo)出函數(shù)和內(nèi)部函數(shù)兩種,內(nèi)部函數(shù)供dll內(nèi)部調(diào)用),可以用自定義模塊文件(.def文件)來聲明導(dǎo)出函數(shù),也可以使用_declspec(dllexport)前綴來聲明導(dǎo)出函數(shù)。PS:.def文件除了聲明導(dǎo)出函數(shù)之外還可以消除函數(shù)修飾符的影響,并產(chǎn)生動態(tài)鏈接庫導(dǎo)入庫(.lib文件)(2)、在顯示調(diào)用dll時,只需要.dll文件即可,而且只能通過函數(shù)指針來調(diào)用dll中的導(dǎo)出函數(shù)。在隱式調(diào)用dll時,要將相應(yīng)的.lib文件加入到工程中,并且在調(diào)用dll的導(dǎo)出函數(shù)
11、之前,必須對使用的函數(shù)進(jìn)行聲明(或者包含進(jìn)相應(yīng)的dll文件的頭文件也可)。(3)、.def文件只在生成dll的過程中起作用,在應(yīng)用程序調(diào)用dll時,不起作用。· DLL的調(diào)用方法1. 動態(tài)鏈接庫(Dynamic Link Library),簡稱DLL。DLL 是一個包含可由多個程序同時使用的代碼和數(shù)據(jù)的庫。它允許程序共享執(zhí)行特殊任務(wù)所必需的代碼和其他資源,一般來說,DLL是一種磁盤文件,以.dll、.DRV、.FON、.SYS和許多以.EXE為擴(kuò)展名的系統(tǒng)文件都可以是DLL。它由全局?jǐn)?shù)據(jù)、服務(wù)函數(shù)和資源組成,在運(yùn)行時被系統(tǒng)加載到調(diào)用進(jìn)程的虛擬空間中,成為調(diào)用進(jìn)程的一部分。DLL的調(diào)用
12、可以分為兩種:一種是隱式調(diào)用,一種是顯示調(diào)用 這里簡單分享DLL的兩種調(diào)用方法。隱式的調(diào)用這種調(diào)用方式需要把產(chǎn)生動態(tài)連接庫時產(chǎn)生的.LIB文件加入到應(yīng)用程序的工程中,在使用DLL中的函數(shù)時,只須聲明一下后就可以直接通過函數(shù)名調(diào)用DLL的輸出函數(shù),調(diào)用方法和程序內(nèi)部其他的函數(shù)是一樣的。隱式調(diào)用不需要調(diào)用LoadLibrary()和FreeLibrary()。程序員在建立一個DLL文件時,鏈接程序會自動生成一個與之對應(yīng)的LIB導(dǎo)入文件。該文件包含了每一個DLL導(dǎo)出函數(shù)的符號名和可選的標(biāo)識號,但是并不含有實際的代碼。LIB文件作為DLL的替代文件被編譯到應(yīng)用程序項目中。當(dāng)程序員通過隱式調(diào)用方式編譯生
13、成應(yīng)用程序時,應(yīng)用程序中的調(diào)用函數(shù)與LIB文件中導(dǎo)出符號相匹配,這些符號或標(biāo)識號被寫入到生成的EXE文件中。LIB文件中也包含了對應(yīng)的DLL文件名(但不是完全的路徑名),鏈接程序也將其存儲在EXE文件內(nèi)部。當(dāng)應(yīng)用程序運(yùn)行過程中需要加載DLL文件時,Windows根據(jù)這些信息發(fā)現(xiàn)并加載DLL,然后通過符號名或標(biāo)識號實現(xiàn)對DLL函數(shù)的動態(tài)鏈接。所有被應(yīng)用程序調(diào)用的DLL文件都會在應(yīng)用程序EXE文件加載時被加載在到內(nèi)存中。 顯式調(diào)用這種調(diào)用方式是指在應(yīng)用程序中用LoadLibrary或MFC提供的AfxLoadLibrary顯式的將自己所做的動態(tài)連接庫調(diào)進(jìn)來,并指定DLL的路徑作為參數(shù)。LoadLi
14、bary返回HINSTANCE參數(shù),應(yīng)用程序在調(diào)用GetProcAddress函數(shù)時使用這一參數(shù)。當(dāng)完成對動態(tài)鏈接庫的導(dǎo)入以后,再使用GetProcAddress()獲取想要引入的函數(shù),該函數(shù)將符號名或標(biāo)識號轉(zhuǎn)換為DLL內(nèi)部的地址,之后就可以象使用本應(yīng)用程序自定義的函數(shù)一樣來調(diào)用此引入函數(shù)了。在應(yīng)用程序退出之前,應(yīng)該用FreeLibrary或MFC提供的AfxFreeLibrary釋放動態(tài)連接庫。DLL的優(yōu)點簡單的說,dll有以下幾個優(yōu)點:1) 節(jié)省內(nèi)存。同一個軟件模塊,若是以源代碼的形式重用,則會被編譯到不同的可執(zhí)行程序中,同時運(yùn)行這些exe時這些模塊的二進(jìn)制碼會被重復(fù)加載到內(nèi)存中。如 果使
15、用dll,則只在內(nèi)存中加載一次,所有使用該dll的進(jìn)程會共享此塊內(nèi)存(當(dāng)然,像dll中的全局變量這種東西是會被每個進(jìn)程復(fù)制一份的)。2) 不需編譯的軟件系統(tǒng)升級,若一個軟件系統(tǒng)使用了dll,則該dll被改變(函數(shù)名不變)時,系統(tǒng)升級只需要更換此dll即可,不需要重新編譯整個系統(tǒng)。事實上,很多軟件都是以這種方式升級的。例如我們經(jīng)常玩的星際、魔獸等游戲也是這樣進(jìn)行版本升級的。3) Dll庫可以供多種編程語言使用,例如用c編寫的dll可以在vb中調(diào)用。這一點上DLL還做得很不夠,因此在dll的基礎(chǔ)上發(fā)明了COM技術(shù),更好的解決了一系列問題。顯示/隱式加載dll例子:首先生成dll文件項目屬性,配置屬
16、性,常規(guī),配置類型,動態(tài)庫(dll)dll.hint add(int left, int right);dll.cpp#include"dll.h"int add(int left, int right)return left+right;然后會在相應(yīng)工程目錄debug目錄下找到dll和lib文件-調(diào)用dll把dll.h XXX.dll XXX.lib文件拷貝到新工程目錄下加載dll.h到工程里,下面開始使用dllimport.cpp#include"dll.h"/顯示加載無需lib文件HINSTANCE hinstance = LoadLibrary(
17、"XXX.dll");typedef int (*_add) (int left, int right);_add addofdll = (_add)GetProAddress(hinstance, "add");/隱式加載需要lib一起只用#prama comment(lib, "XXX.lib");下面可以正常使用add或者addofdll兩個函數(shù)了-當(dāng)函數(shù)功能比較小的時候,其實可以不用dll,直接用靜態(tài)連接庫lib,雖然它可以使得最后的exe文件變大項目屬性,配置屬性,常規(guī),配置類型,靜態(tài)庫(lib)定義都和一樣,使用的時候直接
18、根據(jù)XXX.h文件找到你想使用的函數(shù),然后#prama comment(lib, "XXX.lib");后便可使用。動態(tài)庫的創(chuàng)建:1 在VC里創(chuàng)建一個Win32項目,比如項目名稱叫MyTestDLL,選擇DLL和空項目,確定,一個空的DLL工程便創(chuàng)建好;2 添加頭文件和源文件以及def文件,之所以使用def文件,是因為對于C+里面的函數(shù),編譯器會生成原函數(shù)名不同的名字,使用def文件可以直接避免這個問題,也不需要定義_declspec(dllexport)和_declspec(dllimport)這些修飾符。定義要在DLL里包含的函數(shù),比如:int Add(int a, i
19、nt b)return a + b;在def文件里,添加要導(dǎo)出的函數(shù)名,如下格式:然后編譯工程,就會生成MyTestDLL.dll和MyTestDLL.lib這兩個文件。對于動態(tài)庫的顯示調(diào)用,在編譯時,直接使用一個dll文件,然后在程序中LoadLibrary即可。對于隱式調(diào)用,在編譯時,需要同時有頭文件、dll文件和lib文件,并且也需要和調(diào)用靜態(tài)庫一樣,使用預(yù)處理指令#pragma comment(lib, "libname")靜態(tài)庫的創(chuàng)建很簡單,在調(diào)用工程中,編譯時,只需要頭文件和lib文件,也需要上面的預(yù)處理指令,注意,“l(fā)ibname”可以帶也可以不帶“.lib”
20、后綴。對于動態(tài)庫,調(diào)用工程不管編譯時采用的是隱式還是顯示,運(yùn)行時都需要dll文件;對于靜態(tài)庫,調(diào)用工程則不需要在運(yùn)行期引用靜態(tài)庫文件。動態(tài)庫和靜態(tài)庫和運(yùn)行時庫和引入庫的區(qū)別 1。運(yùn)行時庫:Unix中一個典型的運(yùn)行時庫例子就是libc,它包含標(biāo)準(zhǔn)的C函數(shù),如,print(),exit()等等,用戶能創(chuàng)建他們自己的運(yùn)行庫(在Windows中是DLL),而具體的細(xì)節(jié)依賴編譯器和操作系統(tǒng)的。2。靜態(tài)庫:函數(shù)和數(shù)據(jù)被編譯進(jìn)一個二進(jìn)制文件(通常擴(kuò)展名為.lib),靜態(tài)庫實際上是在鏈接時被鏈接到EXE的,庫本身不需要與可執(zhí)行文件一起發(fā)行。3。動態(tài)庫:用VC+創(chuàng)建的動態(tài)庫包含兩個文件,一個lib文件和一個dl
21、l文件,這個lib文件就是引入庫,不是靜態(tài)庫,引入庫有時也叫輸入庫或?qū)霂?。注:windows操作系統(tǒng)下動態(tài)庫和運(yùn)行時庫的擴(kuò)展名都是.dll,COM組件的擴(kuò)展名也是.dll,動態(tài)庫的引入庫和靜態(tài)庫的擴(kuò)展名都是.lib。windows下調(diào)用動態(tài)庫的方法:1。隱式加載:即在程序中包含lib文件和.h文件,隱式鏈接有時稱為靜態(tài)加載或加載時動態(tài)鏈接。例如:#include "somedll.h"#pragma comment( lib, "somedll.lib")然后就可以直接調(diào)用此dll中的函數(shù),注意運(yùn)行時仍然需要somedll.dll。2。顯示加載:使用l
22、oadlibrary,GetProcAddress,FreeLibrary,不需要.h文件和.lib文件,但是要知道函數(shù)的原型。顯式鏈接有時稱為動態(tài)加載或運(yùn)行時動態(tài)鏈接。3。區(qū)別:如果在進(jìn)程啟動時未找到 DLL,操作系統(tǒng)將終止使用隱式鏈接的進(jìn)程。同樣是在此情況下,使用顯式鏈接的進(jìn)程則不會被終止,并可以嘗試從錯誤中恢復(fù)。有關(guān)Win32 DLL,Unix共享庫及普通庫的詳細(xì)庫結(jié)構(gòu)信息請參考鏈接器與加載器一書。 MSDN:1。確定要使用的鏈接方法:有兩種類型的鏈接:隱式鏈接和顯式鏈接。隱式鏈接應(yīng)用程序的代碼調(diào)用導(dǎo)出 DLL 函數(shù)時發(fā)生隱式鏈接。當(dāng)調(diào)用可執(zhí)行文件的源代碼被編譯或被匯編時,DLL 函數(shù)調(diào)
23、用在對象代碼中生成一個外部函數(shù)引用。若要解析此外部引用,應(yīng)用程序必須與 DLL 的創(chuàng)建者所提供的導(dǎo)入庫(.LIB 文件)鏈接。導(dǎo)入庫僅包含加載 DLL 的代碼和實現(xiàn) DLL 函數(shù)調(diào)用的代碼。在導(dǎo)入庫中找到外部函數(shù)后,會通知鏈接器此函數(shù)的代碼在 DLL 中。要解析對 DLL 的外部引用,鏈接器只需向可執(zhí)行文件中添加信息,通知系統(tǒng)在進(jìn)程啟動時應(yīng)在何處查找 DLL 代碼。系統(tǒng)啟動包含動態(tài)鏈接引用的程序時,它使用程序的可執(zhí)行文件中的信息定位所需的 DLL。如果系統(tǒng)無法定位 DLL,它將終止進(jìn)程并顯示一個對話框來報告錯誤。否則,系統(tǒng)將 DLL 模塊映射到進(jìn)程的地址空間中。如果任何 DLL 具有(用于初始
24、化代碼和終止代碼的)入口點函數(shù),操作系統(tǒng)將調(diào)用此函數(shù)。在傳遞到入口點函數(shù)的參數(shù)中,有一個指定用以指示 DLL 正在附帶到進(jìn)程的代碼。如果入口點函數(shù)沒有返回 TRUE,系統(tǒng)將終止進(jìn)程并報告錯誤。最后,系統(tǒng)修改進(jìn)程的可執(zhí)行代碼以提供 DLL 函數(shù)的起始地址。與程序代碼的其余部分一樣,DLL 代碼在進(jìn)程啟動時映射到進(jìn)程的地址空間中,且僅當(dāng)需要時才加載到內(nèi)存中。因此,由 .def 文件用來在 Windows 的早期版本中控制加載的 PRELOAD 和 LOADONCALL 代碼屬性不再具有任何意義。顯式鏈接大部分應(yīng)用程序使用隱式鏈接,因為這是最易于使用的鏈接方法。但是有時也需要顯式鏈接。下面是一些使用
25、顯式鏈接的常見原因:直到運(yùn)行時,應(yīng)用程序才知道需要加載的 DLL 的名稱。例如,應(yīng)用程序可能需要從配置文件獲取 DLL 的名稱和導(dǎo)出函數(shù)名。如果在進(jìn)程啟動時未找到 DLL,操作系統(tǒng)將終止使用隱式鏈接的進(jìn)程。同樣是在此情況下,使用顯式鏈接的進(jìn)程則不會被終止,并可以嘗試從錯誤中恢復(fù)。例如,進(jìn)程可通知用戶所發(fā)生的錯誤,并讓用戶指定 DLL 的其他路徑。如果使用隱式鏈接的進(jìn)程所鏈接到的 DLL 中有任何 DLL 具有失敗的 DllMain 函數(shù),該進(jìn)程也會被終止。同樣是在此情況下,使用顯式鏈接的進(jìn)程則不會被終止。因為 Windows 在應(yīng)用程序加載時加載所有的 DLL,故隱式鏈接到許多 DLL 的應(yīng)用
26、程序啟動起來會比較慢。為提高啟動性能,應(yīng)用程序可隱式鏈接到那些加載后立即需要的 DLL,并等到在需要時顯式鏈接到其他 DLL。顯式鏈接下不需將應(yīng)用程序與導(dǎo)入庫鏈接。如果 DLL 中的更改導(dǎo)致導(dǎo)出序號更改,使用顯式鏈接的應(yīng)用程序不需重新鏈接(假設(shè)它們是用函數(shù)名而不是序號值調(diào)用 GetProcAddress),而使用隱式鏈接的應(yīng)用程序必須重新鏈接到新的導(dǎo)入庫。下面是需要注意的顯式鏈接的兩個缺點:如果 DLL 具有 DllMain 入口點函數(shù),則操作系統(tǒng)在調(diào)用 LoadLibrary 的線程上下文中調(diào)用此函數(shù)。如果由于以前調(diào)用了 LoadLibrary 但沒有相應(yīng)地調(diào)用 FreeLibrary 函數(shù)
27、而導(dǎo)致 DLL 已經(jīng)附加到進(jìn)程,則不會調(diào)用此入口點函數(shù)。如果 DLL 使用 DllMain 函數(shù)為進(jìn)程的每個線程執(zhí)行初始化,顯式鏈接會造成問題,因為調(diào)用 LoadLibrary(或 AfxLoadLibrary)時存在的線程將不會初始化。 如果 DLL 將靜態(tài)作用域數(shù)據(jù)聲明為 _declspec(thread),則在顯式鏈接時 DLL 會導(dǎo)致保護(hù)錯誤。用 LoadLibrary 加載 DLL 后,每當(dāng)代碼引用此數(shù)據(jù)時 DLL 就會導(dǎo)致保護(hù)錯誤。(靜態(tài)作用域數(shù)據(jù)既包括全局靜態(tài)項,也包括局部靜態(tài)項。)因此,創(chuàng)建 DLL 時應(yīng)避免使用線程本地存儲區(qū),或者應(yīng)(在用戶嘗試動態(tài)加載時)告訴 DLL 用戶潛
28、在的缺陷。2。隱式鏈接:為隱式鏈接到 DLL,可執(zhí)行文件必須從 DLL 的提供程序獲取下列各項:包含導(dǎo)出函數(shù)和/或 C+ 類的聲明的頭文件(.h 文件)。類、函數(shù)和數(shù)據(jù)均應(yīng)具有 _declspec(dllimport),有關(guān)更多信息,請參見 dllexport, dllimport。要鏈接的導(dǎo)入庫(.LIB files)。(生成 DLL 時鏈接器創(chuàng)建導(dǎo)入庫。)實際的 DLL(.dll 文件)。使用 DLL 的可執(zhí)行文件必須包括頭文件,此頭文件包含每個源文件中的導(dǎo)出函數(shù)(或 C+ 類),而這些源文件包含對導(dǎo)出函數(shù)的調(diào)用。從編碼的角度講,導(dǎo)出函數(shù)的函數(shù)調(diào)用與任何其他函數(shù)調(diào)用一樣。若要生成調(diào)用可執(zhí)行
29、文件,必須與導(dǎo)入庫鏈接。如果使用的是外部生成文件,請指定導(dǎo)入庫的文件名,此導(dǎo)入庫中列出了要鏈接到的其他對象 (.obj) 文件或庫。操作系統(tǒng)在加載調(diào)用可執(zhí)行文件時,必須能夠定位 DLL 文件。3。顯式鏈接:在顯式鏈接下,應(yīng)用程序必須進(jìn)行函數(shù)調(diào)用以在運(yùn)行時顯式加載 DLL。為顯式鏈接到 DLL,應(yīng)用程序必須:調(diào)用 LoadLibrary(或相似的函數(shù))以加載 DLL 和獲取模塊句柄。調(diào)用 GetProcAddress,以獲取指向應(yīng)用程序要調(diào)用的每個導(dǎo)出函數(shù)的函數(shù)指針。由于應(yīng)用程序是通過指針調(diào)用 DLL 的函數(shù),編譯器不生成外部引用,故無需與導(dǎo)入庫鏈接。使用完 DLL 后調(diào)用 FreeLibrar
30、y。typedef UINT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT);.HINSTANCE hDLL; / Handle to DLLLPFNDLLFUNC1 lpfnDllFunc1; / Function pointerDWORD dwParam1;UINT uParam2, uReturnVal;hDLL = LoadLibrary("MyDLL");if (hDLL != NULL)lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL,"DLLFunc1");if (!
31、lpfnDllFunc1)/ handle the errorFreeLibrary(hDLL); return SOME_ERROR_CODE;else/ call the functionuReturnVal = lpfnDllFunc1(dwParam1, uParam2);4。將可執(zhí)行文件鏈接到 DLL 可執(zhí)行文件以下列兩種方式之一鏈接到(或加載)DLL:隱式鏈接 顯式鏈接 隱式鏈接有時稱為靜態(tài)加載或加載時動態(tài)鏈接。顯式鏈接有時稱為動態(tài)加載或運(yùn)行時動態(tài)鏈接。在隱式鏈接下,使用 DLL 的可執(zhí)行文件鏈接到該 DLL 的創(chuàng)建者所提供的導(dǎo)入庫(.lib 文件)。使用 DLL 的可執(zhí)行文件加載
32、時,操作系統(tǒng)加載此 DLL。客戶端可執(zhí)行文件調(diào)用 DLL 的導(dǎo)出函數(shù),就好像這些函數(shù)包含在可執(zhí)行文件內(nèi)一樣。在顯式鏈接下,使用 DLL 的可執(zhí)行文件必須進(jìn)行函數(shù)調(diào)用以顯式加載和卸載該 DLL,并訪問該 DLL 的導(dǎo)出函數(shù)。客戶端可執(zhí)行文件必須通過函數(shù)指針調(diào)用導(dǎo)出函數(shù)??蓤?zhí)行文件對兩種鏈接方法可以使用同一個 DLL。另外,由于一個可執(zhí)行文件可隱式鏈接到某個 DLL,而另一個可顯式附加到此 DLL,故這些機(jī)制不是互斥的。vc調(diào)用dll 調(diào)用DLL,首先需要將DLL文件映像到用戶進(jìn)程的地址空間中,然后才能進(jìn)行函數(shù)調(diào)用,這個函數(shù)和進(jìn)程內(nèi)部一般函數(shù)的調(diào)用方法相同。Windows提供了兩種將DLL映像到進(jìn)
33、程地址空間的方法:1. 隱式的加載時鏈接這種方法需要DLL工程經(jīng)編譯產(chǎn)生的LIB文件,此文件中包含了DLL允許應(yīng)用程序調(diào)用的所有函數(shù)的列表,當(dāng)鏈接器發(fā)現(xiàn)應(yīng)用程序調(diào)用了LIB文件列出的某個函數(shù),就會在應(yīng)用程序的可執(zhí)行文件的文件映像中加入一些信息,這些信息指出了包含這個函數(shù)的DLL文件的名字。當(dāng)這個應(yīng)用程序運(yùn)行時,也就是它的可執(zhí)行文件被操作系統(tǒng)產(chǎn)生映像文件時,系統(tǒng)會查看這個映像文件中關(guān)于DLL的信息,然后將這個DLL文件映像到進(jìn)程的地址空間。系統(tǒng)通過DLL文件的名稱,試圖加載這個文件到進(jìn)程地址空間時,它尋找DLL 文件的路徑按照先后順序如下:·程序運(yùn)行時的目錄,即可執(zhí)行文件所在的目錄;&
34、#183;當(dāng)前程序工作目錄·系統(tǒng)目錄:對于Windows95/98來說,可以調(diào)用GetSystemDirectory函數(shù)來得到,對于WindowsNT/2000來說,指的是32位Windows的系統(tǒng)目錄,也可以調(diào)用GetSystemDirectory函數(shù)來得到,得到的值為SYSTEM32。·Windows目錄·列在PATH環(huán)境變量中的所有目錄VC中加載DLL的LIB文件的方法有以下三種:LIB文件直接加入到工程文件列表中在VC中打開File View一頁,選中工程名,單擊鼠標(biāo)右鍵,然后選中“Add Files to Project”菜單,在彈出的文件對話框中選中要
35、加入DLL的LIB文件即可。設(shè)置工程的 Project Settings來加載DLL的LIB文件打開工程的 Project Settings菜單,選中Link,然后在Object/library modules下的文本框中輸入DLL的LIB文件。通過程序代碼的方式加入預(yù)編譯指令#pragma comment (lib,”*.lib”),這種方法優(yōu)點是可以利用條件預(yù)編譯指令鏈接不同版本的LIB文件。因為,在Debug方式下,產(chǎn)生的LIB文件是Debug版本,如Regd.lib;在Release方式下,產(chǎn)生的LIB文件是Release版本,如Regr.lib。當(dāng)應(yīng)用程序?qū)LL的LIB文件加載后,
36、還需要把DLL對應(yīng)的頭文件(*.h)包含到其中,在這個頭文件中給出了DLL中定義的函數(shù)原型,然后聲明。2 顯式的運(yùn)行時鏈接 ,(我用的是此方法)隱式鏈接雖然實現(xiàn)較簡單,但除了必須的*.dll文件外還需要DLL的*.h文件和*.lib文件,在那些只提供*.dll文件的場合就無法使用,而只能采用顯式鏈接的方式。這種方式通過調(diào)用API函數(shù)來完成對DLL的加載與卸載,其能更加有效地使用內(nèi)存,在編寫大型應(yīng)用程序時往往采用此方式。這種方法編程具體實現(xiàn)步驟如下:使用Windows API函數(shù)Load Library或者M(jìn)FC提供的AfxLoadLibrary將DLL模塊映像到進(jìn)程的內(nèi)存空間,對DLL模塊進(jìn)行
37、動態(tài)加載。使用GetProcAddress函數(shù)得到要調(diào)用DLL中的函數(shù)的指針。不用DLL時,用Free Library函數(shù)或者AfxFreeLibrary函數(shù)從進(jìn)程的地址空間顯式卸載DLL。extern "c"與.def文件的作用首先,我們需要知道C和C+編譯器對函數(shù)名字的處理方式是不一樣的;其次,就是同為C編譯器的兩個不同產(chǎn)品,在編譯時對函數(shù)名字的處理方式也是有區(qū)別的,比如microsoft vc+與dev c+。所以,extern "C"與.def文件正是為了解決這兩種情況而引入的處理方法。第一、extern "C"的作用 比如一
38、個C源程序A.c要使用C+編寫的庫函數(shù),在A.c中#include "B.h",其中B.h中有要使用的函數(shù)的原形聲明func。當(dāng)編譯鏈接源程序時,卻發(fā)現(xiàn)了“鏈接錯誤,未決的外部符號.”的錯誤,這是什么原因呢?原因就是,C編譯器編譯A.c時,將func編譯為func,當(dāng)鏈接時鏈接器去C+庫中尋找func,但是C+的編譯器在編譯庫時將func編譯成_funcyyyrrr,自然鏈接器就找不著相應(yīng)的函數(shù)的信息了,所以就會報錯!有什么辦法可以處理這種情況呢?可以在編寫C+庫的時候,為每一個函數(shù)(或?qū)С龊瘮?shù))加上extern "C",它的含義是告知C+編譯器在編譯這
39、些函數(shù)的時候,以C編譯器的方式處理函數(shù)名。這樣生成的庫中的函數(shù)名字就是func了,當(dāng)C程序調(diào)用庫函數(shù),編譯鏈接時,鏈接器就能找到期望的信息,則鏈接成功。第二、.def文件的作用(僅與VC+編程相關(guān)) 前面提到,不同廠商開發(fā)的兩個C編譯器也會有一些差異,最突出的就是microsoft的C編譯器,它對函數(shù)名字的處理很特別(究竟是什么樣子,可以使用Dumpbin工具查看dll的導(dǎo)出函數(shù)),所以要在使用他方編寫的庫時,程序鏈接能成功,有兩種方法:1使用庫編寫者使用的C編譯器(這里指VC+),顯然這種方法不合理;2庫的編寫者在使用VC+編寫庫時使用.def文件。 .def文件的作用即是,告知編譯器不要以
40、microsoft編譯器的方式處理函數(shù)名,而以指定的某方式編譯導(dǎo)出函數(shù)(比如有函數(shù)func,讓編譯器處理后函數(shù)名仍為func)。這樣,就可以避免由于microsoft VC+編譯器的獨特處理方式而引起的鏈接錯誤。用.def文件描述dll的輸出函數(shù),比如 EXPORTS DllFun1 1 NONAME DllFun2 2 NONAME DllFun3 3 NONAME DllFun4 4 NONAME . 后面是dll輸出函數(shù)的順序號,順序號要求在1到max(函數(shù)個數(shù))之間。 用.def聲明了輸出函數(shù)以后,不要在頭文件里再聲明AFX_EXT_API或者是dllexport之類的聲明,而用ext
41、ern "C"聲明了輸出函數(shù)以后,要引用原來輸出函數(shù)的頭文件。如果不是用extern "C"聲明的(包括用.def聲明的),那么頭文件里無需用AFX_EXT_API或者是dllexport之類的聲明。.def文件的作用在VC+中,生成DLL可以不使用.def文件。只需要在VC+的函數(shù)定義前要加_declspec(dllexport)修飾就可以了。但是使用_declspec(dllexport)和使用.def文件是有區(qū)別的。如果DLL是提供給VC+用戶使用的,你只需要把編譯DLL時產(chǎn)生的.lib提供給用戶,它可以很輕松地調(diào)用你的DLL。但是如果你的DLL是
42、供其他程序如VB、delphi,以及.NET用戶使用的,那么會產(chǎn)生一個小麻煩。因為VC+對于_declspec(dllexport)聲明的函數(shù)會進(jìn)行名稱轉(zhuǎn)換,如下面的函數(shù): _declspec(dllexport) int _stdcall IsWinNT() 會轉(zhuǎn)換為IsWinNT0,這樣你在VB中必須這樣聲明: Declare Function IsWinNT Lib "my.dll" Alias "IsWinNT0" () As Long 的后面的數(shù)由于參數(shù)類型不同而可能不同。這顯然不太方便。所以如果要想避免這種轉(zhuǎn)換,就要使用.def文件方式。 E
43、XPORTS后面的數(shù)可以不給,系統(tǒng)會自動分配一個數(shù)。對于VB、PB、Delphi用戶,通常使用按名稱進(jìn)行調(diào)用的方式,這個數(shù)關(guān)系不大,但是對于使用.lib鏈接的VC程序來說,不是按名稱進(jìn)行調(diào)用,而是按照這個數(shù)進(jìn)行調(diào)用的,所以最好給出。例子:我們用VC6.0制作一個dll,不使用.def文件,在頭文件中這樣寫#ifndef LIB_H#define LIB_Hextern "C" int _declspec(dllexport)add(int x,int y);#endif 如果是.def文件,可以這樣LIBRARY "xxx_dll"EXPORTSadd
44、PRIVAT動態(tài)鏈接庫(Dynamic Link Library)學(xué)習(xí)筆記 我對動態(tài)鏈接和動態(tài)鏈接庫的概念并不陌,但一直以來就停留在概念的層面上,沒有更深入的了解。今天抽空看了一下有關(guān)動態(tài)鏈接和動態(tài)鏈接庫的文章,有了一些新的認(rèn)識,當(dāng)然不能忘了寫在這里。那么現(xiàn)在就開始.什么是動態(tài)鏈接和動態(tài)鏈接庫 動態(tài)鏈接(Dynamic Linking)是相對于靜態(tài)鏈接(Static Linking)而言的。程序設(shè)計中,為了能做到代碼和模塊的重用,程序設(shè)計者常常將常用的功能函數(shù)做成庫,當(dāng)程序需要實現(xiàn)某種功能時,就直接調(diào)用庫文件中的函數(shù),從而實現(xiàn)了代碼的重用。早期的程序設(shè)計中,可重用的函數(shù)模塊以編譯好的二進(jìn)制代碼
45、形式放于靜態(tài)庫文件中,在MS的操作系統(tǒng)中是Lib為后綴的文件。程序編寫時,如果用戶程序調(diào)用到了靜態(tài)庫文件中的函數(shù),則在程序編譯時,編譯器會自動將相關(guān)函數(shù)的二進(jìn)制代碼從靜態(tài)庫文件中復(fù)制到用戶目標(biāo)程序,與目標(biāo)程序一起編譯成可執(zhí)行文件。這樣做的確在編碼階段實現(xiàn)了代碼的重用,減輕了程序設(shè)計者的負(fù)擔(dān),但并未在執(zhí)行期實現(xiàn)重用。如一個程序a.exe使用了靜態(tài)庫中的 f() 函數(shù),那么當(dāng)a.exe有多個實例運(yùn)行時,內(nèi)存中實際上存在了多份f()的拷貝,造成了內(nèi)存的浪費(fèi)。 隨著技術(shù)的進(jìn)步,出現(xiàn)了新的鏈接方式,即動態(tài)鏈接,從根本上解決了靜態(tài)鏈接方式帶來的問題。動態(tài)鏈接的處理方式與靜態(tài)鏈接很相似,同樣是將可重用代碼放
46、在一個單獨的庫文件中(在MS的操作系統(tǒng)中是以dll為后綴的文件,Linux下也有動態(tài)鏈接庫,被稱為Shared Object的so文件),所不同的是編譯器在編譯調(diào)用了動態(tài)鏈接庫的程序時并不將庫文件中的函數(shù)執(zhí)行體復(fù)制到可執(zhí)行文件中,而是只在可執(zhí)行文件中保留一個函數(shù)調(diào)用的標(biāo)記。當(dāng)程序運(yùn)行時,才由操作系統(tǒng)將動態(tài)鏈接庫文件一并加載入內(nèi)存,并映射到程序的地址空間中,這樣就保證了程序能夠正常調(diào)用到庫文件中的函數(shù)。同時操作系統(tǒng)保證當(dāng)程序有多個實例運(yùn)行時,動態(tài)鏈接庫也只有一份拷貝在內(nèi)存中,也就是說動態(tài)鏈接庫是在運(yùn)行期共享的。 使用動態(tài)鏈接方式帶來了幾大好處:首先是動態(tài)鏈接庫和用戶程序可以分開編寫,這里的分開即
47、可以指時間和空間的分開,也可以指開發(fā)語言的分開,這樣就降低了程序的耦合度;其次由于動態(tài)鏈接獨特的編譯方式和運(yùn)行方式,使得目標(biāo)程序本身體積比靜態(tài)鏈接時小,同時運(yùn)行期又是共享動態(tài)鏈庫,所以節(jié)省了磁盤存儲空間和運(yùn)行內(nèi)存空間;最后一個是增加了程序的靈活性,可以實現(xiàn)諸如插件機(jī)制等功能。用過winamp的人都知道,它的很多功能都是以插件的形式提供的,這些插件就是一些動態(tài)鏈接庫,主程序事先規(guī)定好了調(diào)用接口,只要是按照規(guī)定的調(diào)用接口寫的插件,都能被winamp調(diào)用。 WIndow 95、98、NT系列等系統(tǒng)都提供了動態(tài)鏈接庫的功能,并且這些操作系統(tǒng)的系統(tǒng)調(diào)用大多都是通過動態(tài)鏈接庫實現(xiàn)的,最常見的NT系列OS中
48、的KENEL32.dll,USER32.dll,GDI32.dll等動態(tài)鏈接庫文件就包含了大量的系統(tǒng)調(diào)用。在windows家族中,NT內(nèi)核的操作系統(tǒng)在動態(tài)鏈接庫機(jī)制上較之前的95、98系統(tǒng)要更安全。95、98系統(tǒng)在程序調(diào)用動態(tài)鏈接庫時,將動態(tài)鏈接庫加載到2G-3G之間的被稱為進(jìn)程共享空間的虛擬地址空間,并且所有進(jìn)程關(guān)于這1G的虛擬地址空間的頁表都是相同的,也就是說對于所有的進(jìn)程,這片共享區(qū)的頁表都指向同一組物理頁,這樣一來,加載入內(nèi)存的的動態(tài)鏈接庫對所有正在運(yùn)行的進(jìn)程都是可見的。如果一個動態(tài)鏈接庫被其中一個進(jìn)程更改,或其自身崩潰,將影響到所有調(diào)用它的進(jìn)程,如果該動態(tài)鏈接庫是系統(tǒng)的動態(tài)鏈接庫,那
49、么將導(dǎo)致系統(tǒng)的崩潰。在Windows NT系統(tǒng)中,動態(tài)鏈接庫被映射到進(jìn)程的用戶地址空間中,并用Copy On Write機(jī)制保證動態(tài)鏈接庫的共享安全,Copy On Write可以理解為寫時拷貝。一般情況下,多個運(yùn)行的進(jìn)程還是按原來的模式共享同一個動態(tài)鏈接庫,直到有進(jìn)程需要向動態(tài)鏈接庫的某個頁面寫數(shù)據(jù)時,系統(tǒng)將該頁做一個拷貝,并將新復(fù)制頁面的屬性置為可讀可寫,最后修改進(jìn)程的頁表使之指向新拷貝的物理頁。這樣無論該進(jìn)程怎么修改此頁的數(shù)據(jù),也不會影響到其他調(diào)用了此動態(tài)鏈接庫的進(jìn)程了。Windows下動態(tài)鏈接庫的編寫 因為本人對linux沒有太多研究,所以這里只介紹windwos環(huán)境下動態(tài)鏈接庫的編寫
50、。 在VC中新建一個空的Win32動態(tài)鏈接庫工程(Win32 Domanic Library),然后添加一個C+ Sourse File到工程,我這里的文件名取DllTest.cpp。然后在文件中添加如下內(nèi)容: /DllTest.cpp _declspec(dllexport) int add(int a,int b) return a+b; _declspec(dllexport) int subtract(int a,int b) return a-b; 接下來編譯鏈接,就會在debug目錄下生成一個調(diào)試版本的動態(tài)鏈接庫,該鏈接庫包含了add和subtract兩個可供外部調(diào)用的函數(shù)。我們注
51、意到,在源文件中多了一個沒有見過的語句 _declspec(dllexport) ,這個語句的作用就是向編譯器指出我需要在生成的動態(tài)鏈接庫中導(dǎo)出的函數(shù),沒有導(dǎo)出的函數(shù)是不能被其他程序調(diào)用的。要知道一個動態(tài)鏈接庫導(dǎo)出了什么函數(shù),可以在命令提示行用命令"dumpbin -exports DllTest.dll"來查看(也可以用VC工具包中的depends使用程序來查看)。以下是用dumpbin命令查看DllTest.dll而生成的信息:Dump of file DllTest.dll File Type: DLL Section contains the following e
52、xports for DllTest.dll 0 characteristics 4420BEA4 time date stamp Wed Mar 22 11:04:04 2006 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 0000100A ?addYAHHHZ 2 1 00001005 ?subtractYAHHHZ Summary 7000 .data 1000 .idata 3000 .rdata 2000 .reloc 2A000 .text
53、 可以看到,我們編寫的動態(tài)鏈接庫導(dǎo)出了兩個函數(shù),分別名為?addYAHHHZ 和 ?subtractYAHHHZ,為什么名字不是add和subtract呢?這是因為C+為了支持函數(shù)的重載,會在編譯時將函數(shù)的參數(shù)類型信息以及返回值類型信息加入到函數(shù)名中,這樣代碼中名字一樣的重載函數(shù),在經(jīng)過編譯后就互相區(qū)分開了,調(diào)用時函數(shù)名也經(jīng)過同樣的處理,就能找到對應(yīng)的函數(shù)了。編譯器對函數(shù)的重命名規(guī)則是與調(diào)用方式相關(guān)的,在這里采用的是C+的默認(rèn)調(diào)用方式。以此對應(yīng)的還有stdcall方式、cdecl方式、fastcall方式和thiscall方式,不同調(diào)用方式的重命名規(guī)則不一樣。 需要特別說一下的是stdcall
54、方式和cdecl方式: stdcall方式(標(biāo)準(zhǔn)調(diào)用方式)也即pascal調(diào)用方式,它的重命名規(guī)則是函數(shù)名自動加前導(dǎo)的下劃線,后面緊跟一個符號,其后緊跟著參數(shù)所占字節(jié)數(shù),之所以要跟參數(shù)字節(jié)數(shù),是因為stdcall采用被調(diào)函數(shù)平衡堆棧方式,用函數(shù)名最后的數(shù)字告訴編譯器需要為函數(shù)平衡的字節(jié)數(shù)。例如,如果我們的DllTest.dll采用stdcall方式編譯的話,導(dǎo)出的函數(shù)名將會是 _add8 和 _subtract8 ,而函數(shù)編譯后的匯編代碼最后一句一定是 ret8。 cdecl方式即C語言調(diào)用方式,它的重命名規(guī)則僅僅是在函數(shù)名前加下劃線(奇怪的是我用vc6編譯的c語言函數(shù),名字沒有任何改變),因
55、為C語言采用的是調(diào)用函數(shù)平衡堆棧的方式,所以不需要在函數(shù)名中加入?yún)?shù)所占的字節(jié)數(shù),這樣的堆棧平衡方式也使C語言可以編寫出參數(shù)不固定的函數(shù);同時C語言不支持函數(shù)重載,因此不需要在函數(shù)名中加入?yún)?shù)類型信息和返回值類型信息。 更多關(guān)于調(diào)用方式的介紹請看我收藏的文章C語言函數(shù)調(diào)用約定 。 動態(tài)鏈接庫已經(jīng)生成了,接下來就是調(diào)用的工作了。調(diào)用動態(tài)鏈接庫有兩種方式:隱式調(diào)用和顯式調(diào)用,下面我們分別來看兩種調(diào)用方式的具體過程:動態(tài)鏈接庫的隱式調(diào)用 新建一個空的Win32 Console Application,命名為DllCaller,向工程中添加名為DllCaller.cpp 的C+ Sourse File
56、,在文件中寫入如下代碼:#include <iostream>using namespace std;/extern int add(int a,int b);_declspec(dllimport) int add(int a,int b);int main() cout<<"3+5="<<add(3,5)<<endl; return 1; 編譯,沒有錯誤,鏈接,有兩個錯誤:找不到外部引用符號。要怎樣才能讓我們的程序找到動態(tài)連接庫中的函數(shù)呢?這里是關(guān)鍵的一步。到剛才的DllTest工程目錄下,從debug文件夾中拷貝生成的DllTest.dll文件和DllTest.lib文件到DllCaller工程目錄。然后依次在vc中選擇菜單:Project ->Settings->Liink, 在Object/library Modules中加入一項文件名:DllTest.lib,這里的DllTest.lib并不是靜態(tài)庫文件,而是DllTest.dll的導(dǎo)入庫文件,它包含了DllTest.dll動態(tài)鏈接庫導(dǎo)出的函數(shù)信息,只有在工程鏈接設(shè)置里添加了該文件,才能夠使調(diào)用了該動態(tài)鏈接庫的工程正確鏈接。完成以上步驟后,我們再編譯鏈接
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度鋼材品牌授權(quán)及合作推廣合同3篇
- 二零二五版戶外燈具打膠制作合同范本3篇
- 二零二五版建筑材料租賃與資金支付合同3篇
- 二零二五版消防管道材料買賣合同范本3篇
- 二零二五版空壓機(jī)租賃與租賃期滿設(shè)備回收合同3篇
- 二零二五版文化旅游項目開發(fā)合作購銷合同文化融合3篇
- 二零二五版股票期權(quán)授予及解約條款合同書3篇
- 二零二五年度電腦系統(tǒng)集成與售后全面保修合同3篇
- 2025年廠房維修保養(yǎng)與安全責(zé)任合同3篇
- 2025版冷凍食品儲藏租賃合同范本3篇
- 霧化吸入療法合理用藥專家共識(2024版)解讀
- 寒假作業(yè)(試題)2024-2025學(xué)年五年級上冊數(shù)學(xué) 人教版(十二)
- 銀行信息安全保密培訓(xùn)
- 市政道路工程交通疏解施工方案
- 2024年部編版初中七年級上冊歷史:部分練習(xí)題含答案
- 拆遷評估機(jī)構(gòu)選定方案
- 床旁超聲監(jiān)測胃殘余量
- 上海市松江區(qū)市級名校2025屆數(shù)學(xué)高一上期末達(dá)標(biāo)檢測試題含解析
- 綜合實踐活動教案三上
- 《新能源汽車電氣設(shè)備構(gòu)造與維修》項目三 新能源汽車照明與信號系統(tǒng)檢修
- 2024年新課標(biāo)《義務(wù)教育數(shù)學(xué)課程標(biāo)準(zhǔn)》測試題(附含答案)
評論
0/150
提交評論