1 背景
對於大部分的券商和機構投資者,只能通過有交易所交易系統接入資質的券商提供的櫃台系統來進行現貨交易。相對於期貨市場,現貨市場的櫃台系統千差萬別,接入協議有明文字符串、二進制數據和FIX協議等,接入方式有TCP連接、COM組件和dll動態庫等。要想開發一個覆蓋市面上所有的現貨櫃台的報盤系統,就必須能同時支持這些櫃台的所有接入方式。在開發的過程中遇到的關於動態庫版本兼容問題有以下幾個:
- 同一櫃台系統提供商發布了2套櫃台系統,用於對接這2套系統的開發包是一樣的,只是由於版本不同不能通用
- 不同櫃台系統提供的動態庫包含了不同版本的第三方庫文件(如libeay32.dll等)
- 部分券商對櫃台系統進行了自定義開發,在櫃台提供的開發包中加入了自己開發的動態庫,以增加對接入授權的控制
當系統中存在同名的且不同版本的動態庫時,為了能方便管理和更新程序,只能對這些動態庫進行分類整理。具體做法是在程序執行目錄下對每個類別的櫃台分別建立一個文件夾,用於存放對接該櫃台所需要的所有的動態庫文件。由於程序采用了靜態加載的方式去加載dll,為了能讓可執行文件在啟動時能找到這些動態庫,manifest文件就派上用途了。
2 關於manifest文件
manifest文件是用於組織和描述並行組件或獨立應用程序的xml文件。它主要包含了用於綁定和激活COM類、接口和庫的相關信息,這些信息以往是存儲在系統注冊表中的[1]。在xp及以后的windows系統中,系統在執行EXE可執行文件時會首先讀取Manifest文件,獲得exe文件需要調用的DLL列表(此時獲得的,並不直接是DLL文件的本身的位置,而是DLL的manifest),操作系統再根據DLL的Manifest文件提供的信息去尋找對應的DLL ,這樣就可以區別不同版本的相同文件名的DLL文件[2]。
微軟msdn上的一個配圖比較直觀地描述了這一過程[3]:
3 組件查找順序
在windows系統中,如果應用程序指定了組件依賴關系,則程序啟動時首先在WinSxS文件夾中查找共享組件,若未找到,則在程序安裝目錄下查找私有組件。
在大部分的情況下,組件的查找順序如下所示:
- WinSxS 文件夾
- \\<appdir>\<assemblyname>.DLL
- \\<appdir>\<assemblyname>.manifest
- \\<appdir>\<assemblyname>\<assemblyname>.DLL
- \\<appdir>\<assemblyname>\<assemblyname>.manifest
更詳細的描述可以查看msdn中的Assembly Searching Sequence
4 實現
場景一 程序需加載兩個不同版本的sample.dll
解決辦法:
- 分別建立兩個文件夾(dlla,dllb),將兩個不同版本的dll分別放入這兩個文件夾
- 文件夾下分別新建一個manifest文件,文件名同文件夾名,例dlla.manifest、dllb.manifest
- 編輯manifest文件,內容如下(以dlla為例):
1 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 2 <assemblyIdentity name="dlla" processorArchitecture="x86" version="1.0.0.0" type="win32" /> <-- 這邊的name必須為manifest文件所在的文件夾的名字 3 <file name = "sample.dll"/> 4 </assembly>
- 在需要用到動態庫提供的函數的cpp中增加以下預處理指令(以dlla為例):
1 // 指定加載dll的位置; 2 #pragma comment(linker,"/manifestdependency:\"type='win32' "\ 3 "name='dlla' "\ 4 "version='1.0.0.0' "\ 5 "processorArchitecture='x86' "\ 6 "language='*' "\ 7 "\"")
重新編譯后,程序便能正確找到指定的動態庫了。
場景二 程序需加載兩個不同版本的sample.dll,且不同版本的sample.dll分別又依賴不同的第三方開發庫
解決辦法:在場景一的解決辦法的基礎之上,在manifest文件中增加依賴的第三方庫的信息,例:
1 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
2
3 <assemblyIdentity name="dlla" processorArchitecture="x86" version="1.0.0.0" type="win32" />
4
5 <file name = "sample.dll"/>
6 <file name = "libeay32.dll"/>
7 <file name = "ssleay32.dll"/>
8
9 </assembly>
場景三 程序需加載兩個不同版本的sample.dll,但是調用這2個動態庫的代碼存在某種引用關系。
解決辦法:在這種場景下,由於預編譯宏會互相覆蓋,導致動態庫不能正確加載,只能通過LoadLibrary的方式進行動態加載。事實上,通過動態加載可以加載任意路徑的動態庫,只是在使用方式上沒有靜態加載方便。
4 總結
利用manifest文件同時加載多版本的同名的動態庫的方式是windows系統特有的功能,在平時c++的開發中也很少接觸到。這邊僅作記錄供以后碰到相同問題的時候作為參考。
1. https://msdn.microsoft.com/en-us/library/aa375365(v=vs.85).aspx
2. http://blog.csdn.net/suxinpingtao51/article/details/42870937
3. https://msdn.microsoft.com/en-us/library/ff951640(v=vs.85).aspx
本文為原創內容,若有錯誤的地方煩請指正
本文地址:http://www.cnblogs.com/morebread/p/4953497.html