什么是.manifest 文件
[現(xiàn)象]
對這個問題的研究是起源于這么一個現(xiàn)象:當(dāng)你用VC++2005(或者其它.NET)寫程序后,在自己的計算機上能毫無問題地運行,但是當(dāng)把此exe文件拷貝到別人電腦上時,便不能運行了,大致的錯誤提示如下:應(yīng)用程序配置不正確,請重新安裝程序……或者是MSVCR80D.dll沒有找到什么的(我記得不是很清楚,不過大致是這樣的)
[分析]
看到這樣的提示,當(dāng)然不會傻到重裝咯。第一反應(yīng)應(yīng)該是什么配置有問題、或者是缺少了什么依賴的庫文件;于是我就根據(jù)以前Windows缺少庫文件的經(jīng)驗,把所有庫文件(××.DLL)統(tǒng)統(tǒng)一股腦地復(fù)制到當(dāng)前文件夾下來,滿心歡喜以為可以運行了,以運行……@#¥@#%¥……還是掛了。
[探索]
于是開始網(wǎng)上搜索,我Google,我擺渡;漸漸我發(fā)現(xiàn),這一切都和一個叫做***.manifest 類型的文件發(fā)生關(guān)系,那么到底什么是.manifest 文件呢?他有什么用,以前為什么沒有?
后來,經(jīng)過艱苦努力,終于得知,原來這一切都是Windows 的AssemblyManifest搞的鬼。這個東東的作用就是為了解決 以前windows上的“Dll 地獄”問題才產(chǎn)生的新的DLL管理解決方案。大家知道,Dll是動態(tài)加載共享庫,同一個Dll可能被多個程序所使用,而所謂“Dll地獄”就是當(dāng)不通程序依賴的Dll相同,但版本不同時,由于系統(tǒng)不能分辨到底哪個是哪個,所以加載錯了Dll版本,然后就掛了。于是蓋茨就吸取了教訓(xùn),搞了一個程序集清單的東東,每個程序都要有一個清單,這個清單存再和自己應(yīng)用程序同名的.manifest文件中,里面列出其所需要的所有依賴,這兒所列出的依賴可不是簡單地靠文件明來區(qū)分的,而是根據(jù)一種叫做“強文件名”的東西區(qū)分的,那么什么是強文件明呢?我們來看一下這個.manifest文件便知道了。
<?xml version='1.0' encoding='UTF-8'standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1'manifestVersion='1.0'>
<dependency>
<dependentAssembly>
<assemblyIdentity type='win32'name='Microsoft.VC80.CRT' version='8.0.50608.0'processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'/>
</dependentAssembly>
</dependency>
</assembly>
我們發(fā)現(xiàn)原來這是一個XML格式的文件,其中<dependency>這一部分指明了其依賴于一個名字叫做Microsoft.VC80.CRT的庫。但是我們發(fā)現(xiàn),<assemblyIdentity>屬性里面還有其它的東東,分別是
type系統(tǒng)類型,version版本號,processorArchitecture平臺環(huán)境,publicKeyToken公匙(一般用來標(biāo)示一個公司)……把他們加在一起便成了“強文件名”了,有了這種“強文件名”,我們就可以根據(jù)其區(qū)分不同的版本、不同的平臺……總之,有了這種強文件名,系統(tǒng)中可以有多個不同版本的相同的庫共存而不會發(fā)生沖突。
[深入]
恩,那么現(xiàn)在,我們就來具體了解一下這一套機制。
首先是強弱文件名的問題。正如上面提到的那樣,為了區(qū)分不同版本或不同廠商生成的相同的程序集,必須用一個AssemblyManifest程序清單來列出我這個程序集的強文件名--慢著,到這里你可能會問:剛才不是說AssemblyManifest程序清單是列出其所依賴的程序集的強文件名呢,怎么這里變成了當(dāng)前文件的強文件明了呢?其實,AssemblyManifest程序清單有兩部分功能,上面這個實例之所以標(biāo)注了其所依賴的文件的強文件名是因為其是客戶端的AssemblyManifest,在服務(wù)端有另外一個Manifest 來標(biāo)注。
<?xml version="1.0" encoding="UTF-8"standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"manifestVersion="1.0">
<noInheritable></noInheritable>
<assemblyIdentity type="win32"name="Microsoft.VC80.CRT" version="8.0.50727.42"processorArchitecture="x86"publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
<file name="msvcr80.dll"hash="2a0d797a8c5eac76e54e98db9682e0938c614b45"hashalg="SHA1"><asmv2:hashxmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:TransformAlgorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethodAlgorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>phRUExlAeZ8BwmlD8VlO5udAnRE=</dsig:DigestValue></asmv2:hash></file>
<file name="msvcp80.dll"hash="cc4ca55fb6aa6b7bb8577ab4b649ab77e42f8f91"hashalg="SHA1"><asmv2:hashxmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:TransformAlgorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethodAlgorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>7AY1JqoUvK3u/6bYWbOagGgAFbc=</dsig:DigestValue></asmv2:hash></file>
<file name="msvcm80.dll"hash="55e8e87bbde00d1d96cc119ccd94e0c02c9a2768"hashalg="SHA1"><asmv2:hashxmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:TransformAlgorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethodAlgorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>hWq8zazTsMeKVxWFBa6bnv4hEOw=</dsig:DigestValue></asmv2:hash></file>
</assembly>
這個便是從WINDOWSWinSxSManifests目錄下取出來的一個manifest文件,再這個文件夾下有一陀子這種XML格式的manifest文件,其是服務(wù)端的程序清單。WinSxs是windowsXP以上版本提供的[blue]非托管并行緩存(side-by-sidecatche)[/blue]里面安裝了各種版本的經(jīng)過強文件名簽名的系統(tǒng)庫,而上面這個文件<assemblyIdentity>正是標(biāo)注了系統(tǒng)中Microsoft.VC80.CRT的一個版本的強文件名簽名,如果其和客戶端。.manifest清單里面<dependentAssembly>所列出的依賴項對上的話,就會被加載。剛才說的side-by-side是指各種不同的版本并行運行。
上面這個服務(wù)端manifest文件中<file>標(biāo)簽具體指明了當(dāng)前強文件名簽名的到底是哪一個文件,其中還有這個文件的Hash簽名,以確保文件的完整性。
好了,有了這一套機制,就可以非常非常安全地進(jìn)行庫文件關(guān)聯(lián)了,但是、但是貌似還有一個一直困擾我們的問題:這套機制安全是安全了,但是卻失去了以前良好的前后版本兼容性,即如果你的系統(tǒng)庫發(fā)生了升級,那么服務(wù)端的版本號發(fā)生了變化,那豈不是所有服務(wù)端程序都不能使用了嗎?其實,windows還使用一個policy的策略文件來確認(rèn)映射關(guān)系。
<?xml version="1.0" encoding="UTF-8"standalone="yes"?>
<!-- Copyright ? 1981-2001 Microsoft Corporation-->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"manifestVersion="1.0">
<assemblyIdentity type="win32-policy"name="policy.8.0.Microsoft.VC80.CRT" version="8.0.50727.42"processorArchitecture="x86"publicKeyToken="1fc8b3b9a1e18e3b"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"name="Microsoft.VC80.CRT" processorArchitecture="x86"publicKeyToken="1fc8b3b9a1e18e3b"/>
<bindingRedirectoldVersion="8.0.41204.256-8.0.50608.0"newVersion="8.0.50727.42"/>
</dependentAssembly>
</dependency>
</assembly>
這便是在WINDOWSWinSxSPolicies目錄下的一個Policy文件,其中<bindingRedirect>標(biāo)簽便指定了所有8.0.41204.256-8.0.50608.0變本的客戶需求映射到8.0.50727.42這個我現(xiàn)在系統(tǒng)中安裝的比較新的版本的庫。當(dāng)然我們也能對別的字段進(jìn)行映射,這樣便能很好解決系統(tǒng)升級帶來的問題。
[應(yīng)用]
經(jīng)過以上的講解,大家對整個依賴查找過程都有了一個整體的認(rèn)識,那么在實際中問題就好解決了。
讓我們回到實際問題中,我之前說了,把一個程序編譯連接成可執(zhí)行程序后,在別人的電腦上發(fā)現(xiàn)找不到其所依賴的庫了,那么怎么辦呢?聰明的你自然想到把其所依賴的庫相應(yīng)的版本拷貝到目標(biāo)計算機上面,可是……當(dāng)你在拼命尋找那個可執(zhí)行文件的assemblymanifests文件的時候,卻突然發(fā)現(xiàn)找不到了,在執(zhí)行目錄下面明明只有一個exe文件嘛。是不是沒有生成呢?顯然不會,原來是資源連接器把那個assemblymanifests文件連接到了可執(zhí)行文件里面了;不信,你可以用你的vc++打開一個可執(zhí)行文件看看,在其資源項里面就有一個叫做RT_MANIFEST的項目。這個里面就是二進(jìn)制標(biāo)示的manifests文件。那么根據(jù)這里面提供的要求,將相應(yīng)版本的依賴文件(一般就是CRT運行庫)拷貝到系統(tǒng)目錄WindowsWinSxS,記住一般會是連帶著一個特殊命名的目錄一起拷貝到那個文件夾下,比如CRT的運行庫就是WinSxSx86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50608.0_x-ww_b7acac55有這樣一個目錄,其標(biāo)注了此庫的版本號以及簽名等信息,以防止多個版本重名時不能復(fù)制到同一WinSxS目錄下。
這樣就搞定了么?如果是以前,那么一切都解決了,系統(tǒng)會在這個目錄下面找到這個運行庫,可是現(xiàn)在單單這樣可不行,系統(tǒng)可是要找到這個運行庫的assemblymanifests文件,并且對比強文件名之后才能加載,所以所以千萬別忘了把相應(yīng)的manifests文件拷貝到WinSxSManifests目錄下面。
當(dāng)然,這樣在目標(biāo)的系統(tǒng)文件夾下面打動干戈,自然有些過于暴動了,還好,Windows還為我們提供了一種私有查找方式。這種方式會在前面的位置找不到合適庫的時候在本地文件夾下面找。所以你只要把之前的庫以及那個manifests文件一起拷貝到你的應(yīng)用程序的路徑下面,就可以使用啦。
根據(jù)MSDN的說明,在本地查找并加載遵循一下規(guī)則:
在應(yīng)用程序本地文件夾中查找名為<assemblyName>.manifest的清單文件。在此示例中,加載程序試圖在 appl.exe 所在的文件夾中查找Microsoft.VC80.CRT.manifest。如果找到該清單,加載程序?qū)膽?yīng)用程序文件夾中加載 CRT DLL。如果未找到CRT DLL,加載將失敗。
嘗試在 appl.exe 本地文件夾中打開文件夾<assemblyName>,如果存在此文件夾,則從中加載清單文件<assemblyName>.manifest。如果找到該清單,加載程序?qū)?lt;assemblyName> 文件夾中加載 CRT DLL。如果未找到CRT DLL,加載將失敗。
最后,我想補充的一點是,在你的VC++安裝目錄下面的“Microsoft Visual Studio8VCredist”目錄下,有著所有的提供發(fā)布的已經(jīng)配備相應(yīng).manifest的庫文件。所以你想要發(fā)布一個程序最簡單最安全的做法(不用擔(dān)心用戶電腦是否包含你所需要的庫)就是把這個目錄下面的相應(yīng)的庫的文件夾和你的可執(zhí)行文件放在一起發(fā)布。
比如在X86平臺下如果你的可執(zhí)行文件用到了CRT庫(廢話么),那么就拷貝Microsoft Visual Studio8VCredistx86Microsoft.VC80.CRT這個文件夾到你的程序所在的目錄,一起發(fā)布,就萬事大吉啦!
愛華網(wǎng)



