這篇東西太專業了,引用下。
[現象]
對這個問題的研究是起源于這麼一個現象:當你用VC++2005(或者其它.NET)寫程式後,在自己的計算機上能毫無問題地運作,但是當把此exe檔案拷貝到别人電腦上時,便不能運作了,大緻的錯誤提示如下:應用程式配置不正确,請重新安裝程式……或者是MSVCR80D.dll 沒有找到什麼的(我記得不是很清楚,不過大緻是這樣的)
[分析]
看到這樣的提示,當然不會傻到重裝咯。第一反應應該是什麼配置有問題、或者是缺少了什麼依賴的庫檔案;于是我就根據以前Windows缺少庫檔案的經驗,把所有庫檔案(××.DLL)統統一股腦地複制到目前檔案夾下來,滿心歡喜以為可以運作了,以運作……@#¥@#%¥……還是挂了。
[探索]
于是開始網上搜尋,我Google,我擺渡;漸漸我發現,這一切都和一個叫做***.manifest 類型的檔案發生關系,那麼到底什麼是 .manifest 檔案呢?他有什麼用,以前為什麼沒有?
後來,經過艱苦努力,終于得知,原來這一切都是Windows 的Assembly Manifest搞的鬼。這個東東的作用就是為了解決 以前windows上的“Dll 地獄” 問題才産生的新的DLL管了解決方案。大家知道,Dll是動态加載共享庫,同一個Dll可能被多個程式所使用,而所謂“Dll 地獄”就是當不通程式依賴的Dll相同,但版本不同時,由于系統不能分辨到底哪個是哪個,是以加載錯了Dll版本,然後就挂了。于是蓋茨就吸取了教訓,搞了一個程式集清單的東東,每個程式都要有一個清單,這個清單存再和自己應用程式同名的.manifest檔案中,裡面列出其所需要的所有依賴,這兒所列出的依賴可不是簡單地靠檔案明來區分的,而是根據一種叫做“強檔案名”的東西區分的,那麼什麼是強檔案明呢?我們來看一下這個.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> |
我們發現原來這是一個XML格式的檔案,其中<dependency>這一部分指明了其依賴于一個名字叫做Microsoft.VC80.CRT的庫。但是我們發現,<assemblyIdentity>屬性裡面還有其它的東東,分别是
type系統類型,version版本号,processorArchitecture平台環境,publicKeyToken公匙(一般用來标示一個公司)……把他們加在一起便成了“強檔案名”了,有了這種“強檔案名”,我們就可以根據其區分不同的版本、不同的平台……總之,有了這種強檔案名,系統中可以有多個不同版本的相同的庫共存而不會發生沖突。
[深入]
恩,那麼現在,我們就來具體了解一下這一套機制。
首先是強弱檔案名的問題。正如上面提到的那樣,為了區分不同版本或不同廠商生成的相同的程式集,必須用一個Assembly Manifest程式清單來列出我這個程式集的強檔案名--慢着,到這裡你可能會問:剛才不是說Assembly Manifest程式清單是列出其所依賴的程式集的強檔案名呢,怎麼這裡變成了目前檔案的強檔案明了呢?其實,Assembly Manifest程式清單有兩部分功能,上面這個執行個體之是以标注了其所依賴的檔案的強檔案名是因為其是用戶端的Assembly Manifest,在服務端有另外一個Manifest 來标注。
<?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:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="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:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="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:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>hWq8zazTsMeKVxWFBa6bnv4hEOw=</dsig:DigestValue></asmv2:hash></file> |
這個便是從WINDOWS\WinSxS\Manifests目錄下取出來的一個manifest檔案,再這個檔案夾下有一陀子這種XML格式的manifest檔案,其是服務端的程式清單。WinSxs是windows XP以上版本提供的[blue]非托管并行緩存(side-by-side catche)[/blue]裡面安裝了各種版本的經過強檔案名簽名的系統庫,而上面這個檔案<assemblyIdentity>正是标注了系統中Microsoft.VC80.CRT的一個版本的強檔案名簽名,如果其和用戶端。.manifest 清單裡面<dependentAssembly>所列出的依賴項對上的話,就會被加載。剛才說的side-by-side 是指各種不同的版本并行運作。
上面這個服務端manifest檔案中<file>标簽具體指明了目前強檔案名簽名的到底是哪一個檔案,其中還有這個檔案的Hash簽名,以確定檔案的完整性。
好了,有了這一套機制,就可以非常非常安全地進行庫檔案關聯了,但是、但是貌似還有一個一直困擾我們的問題:這套機制安全是安全了,但是卻失去了以前良好的前後版本相容性,即如果你的系統庫發生了更新,那麼服務端的版本号發生了變化,那豈不是所有服務端程式都不能使用了嗎?其實,windows還使用一個policy的政策檔案來确認映射關系。
<!-- Copyright ? 1981-2001 Microsoft Corporation --> <assemblyIdentity type="win32-policy" name="policy.8.0.Microsoft.VC80.CRT" version="8.0.50727.42" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/> <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/> <bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0" newVersion="8.0.50727.42"/> |
這便是在WINDOWS\WinSxS\Policies目錄下的一個Policy檔案,其中<bindingRedirect>标簽便指定了所有8.0.41204.256-8.0.50608.0變本的客戶需求映射到8.0.50727.42這個我現在系統中安裝的比較新的版本的庫。當然我們也能對别的字段進行映射,這樣便能很好解決系統更新帶來的問題。
在應用程式本地檔案夾中查找名為 <assemblyName>.manifest 的清單檔案。在此示例中,加載程式試圖在 appl.exe 所在的檔案夾中查找 Microsoft.VC80.CRT.manifest。如果找到該清單,加載程式将從應用程式檔案夾中加載 CRT DLL。如果未找到 CRT DLL,加載将失敗。 嘗試在 appl.exe 本地檔案夾中打開檔案夾 <assemblyName>,如果存在此檔案夾,則從中加載清單檔案 <assemblyName>.manifest。如果找到該清單,加載程式将從 <assemblyName> 檔案夾中加載 CRT DLL。如果未找到 CRT DLL,加載将失敗。 |
最後,我想補充的一點是,在你的VC++安裝目錄下面的“Microsoft Visual Studio 8\VC\redist”目錄下,有着所有的提供釋出的已經配備相應.manifest的庫檔案。是以你想要釋出一個程式最簡單最安全的做法(不用擔心使用者電腦是否包含你所需要的庫)就是把這個目錄下面的相應的庫的檔案夾和你的可執行檔案放在一起釋出。
比如在X86平台下如果你的可執行檔案用到了CRT庫(廢話麼),那麼就拷貝Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT這個檔案夾到你的程式所在的目錄,一起釋出,就萬事大吉啦!