解決VS2005/VS2008下在子目錄下部署DLL的私有程序集部署問題

採用VC++2005非託管C++代碼開發,主程序名爲MyApp.exe,MyPulgin.dll。MyApp.exe部署所在目錄爲主目錄,MyPulgin.dll部署在主目錄下的Plugin子目錄下,如下所示:
 $Home     ---應用程序主目錄
  MyApp.exe
  Plugin   ---插件部署目錄(主目錄下的一個子目錄)
   MyPlugin.dll
程序發佈要求採用XCopy方式,即將這個目錄結構複製到目標機器就可以運行。

因爲MyApp.exe和MyPlugin.dll依賴VS2005 CRT DLL庫。所以這些庫也需要隨應用程序一起發佈。VS2005 CRT DLL 的發佈可以採用2種部署方式,即採用私有程序集方式和共享程序集方式部署。

將VS2005 CRT DLL部署爲共享程序集的方式,即採用微軟發佈的重分發程序vcredist_x86.exe (x86是其版本,還有x64 和 IA64)將VS2005 CRT DLL安裝到$Windows/WinSXS目錄下。

將VS2005 CRT DLL部署爲私有程序集的方式,即將VS2005 CRT DLL部署到應用程序私有目錄下,這種方式無需訪問系統目錄,部署簡單。微軟官方發佈了將 Visual C++ CRT DLL 部署爲私有程序集的方法及步驟(http://msdn.microsoft.com/zh-cn/library/ms235291(v=VS.80).aspx)。內容如下:
1.在開發計算機上創建一個文件夾結構,讓它與將在目標計算機上使用的文件夾結構一致。對於此示例,創建一個 /bin 文件夾並將 MyApp.exe 複製到其中。然後,創建一個 /bin/Plugin 文件夾並將 MyPulgin.dll 複製到其中。
2.在開發計算機上,將 Microsoft.VC80.CRT 和 Microsoft.VC80.MFC 從 %PROGDIR%/Microsoft Visual Studio 8/VC/Redist/x86 複製到 /bin 和 /bin/Plugin 中。
 3.將 /bin 文件夾複製到目標計算機。在支持基於清單的綁定的目標計算機 (Windows XP Home Edition、Windows XP Professional、Windows Server 2003) 上,沒有必要做進一步的準備。在不支持類似綁定的計算機(Windows 98、Windows 98 Second Edition、Windows Millennium Edition 和 Windows 2000)上,路徑中必須有 Microsoft.VC80.CRT 和 Microsoft.VC80.MFC。

微軟提供的方法即在主目錄和包含DLL的子目錄下皆部署VS2005 CRT DLL。但這種方法存在以下問題。
其一,依賴庫重複部署,造成安裝包額外的空間需求
其二,DLL和EXE都是使用各自的CRT DLL,在進程中會造成多個運行時庫實例。如果在DLL(Exe)中分配內存,而在Exe(DLL)中釋放內存,則會造成系統崩潰。因爲DLL和Exe使用的是不同的運行時庫實例提供的內存管理器,在DLL中申請的內存是由Dll所使用的CRT DLL的內存管理器分配的,在Exe中釋放這塊內存,則會由Exe所使用的CRT DLL的內存管理器回收,結果回收的內存不是自己分配的內存,從而造成系統崩潰。

因此微軟提供的方案並不適合DLL部署在子目錄的需求。解題的思路是使子目錄下的DLL和主目錄中的Exe依賴於同一套VS2005 CRT DLL,使進程中僅存在一個運行時庫實例。

考慮到vc6.0生成的應用程序依賴庫只需要部署到主目錄中,加載子目錄中的DLL時能在已加載庫中找到其依賴庫。因此只要將vs2005生成的應用程序加載子目錄的DLL時也能在已加載庫中命中其依賴庫,問題就得到解決。

vs2005與vc6.0在生成應用程序的一個不同之處在於vs2005生成的應用程序(程序集Exe或DLL)內嵌了一個manifest清單文件,該文件記錄了其依賴的程序集。用vs2005打開DLL(Exe),導出RT_MANIFEST資源,得到一個xml文件,內容如下:
<?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.DebugCRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

該文件表示DLL依賴一個Microsoft.VC80.DebugCRT的程序集,而該程序集名稱並不是DLL的文件名。進程在加載DLL時,檢查有內嵌manifest,則按照manifest文件中的依賴項信息探測依賴項,具體探測方式參見微軟官方說明:http://msdn.microsoft.com/zh-cn/library/aa663631.aspx#EIAA
若DLL沒有內嵌manifest,則進程加載DLL時,就會按照DLL的弱依賴信息(動態庫文件名---弱名稱,可以通過View Dependency工具查看程序集的依賴項)加載動態庫,若進程已經加載則不會再加載,從而保證Exe和Dll只加載一個運行時庫實例。

讓vs2005生成的程序集不產生內嵌manifest文件的方法是按照如下方法修改其工程屬性:
配置屬性/連接器/清單文件選項卡中,設置生成清單文件選項爲否。/MANIFEST:NO

總結:
1)在主目錄下部署Visual C++ CRT DLL。
2)去掉子目錄下部署的程序集(DLL)的內嵌manifest。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章