標籤:
最近做在Windows XP X64,VS2005環境下做32位程式編譯為64位程式的工作,遇到了一些64位編程中可能遇到的問題:如內聯彙編(解決方案改為C/C++代碼),long類型的變化,最關鍵的遇到了64位進程需要調用32位dll的問題。由於有一些32位dll沒有原始碼,無法重新編譯為64位dll,所以只能想辦法解決64位進程調用32位dll問題,這個問題讓我很是撓頭了幾天。
相關資料:
微軟公司的官方網站針對這個問題描述如下:
在64位的windows系統中,一個64位進程不能載入一個32位dll,同理一個32位進程也不能載入一個64位dll。但是,64位windows支援64位和32位進程(包括本機或跨機)間處理序間通訊(RPC)。在64位windows中,一個進程外32位COM伺服器能夠與64位用戶端進行通訊,同樣一個進程外64位COM伺服器也能與32位用戶端進行通訊。因此,如果你有一個32位COM無法識別的DLL,你可以將它封裝到一個進程外COM伺服器中並在一個64位進程中用COM配置調用DLL。(最後一句我也看不太懂!!哈哈哈)
驗證:
工作流程:
1.建立一個進程外COM伺服器(EXE)。
2.將32位dll的介面函數封裝為COM伺服器的相關介面。
3.註冊COM伺服器*.exe /regserver (登出 *.exe /unregserver)。
4.64位進程調用32位COM伺服器介面,成功。從而曲線實現了64位進程調用32位dll。
具體步驟:
我首先建立了一個簡單的dll工程,只輸出一個函數int c = add(int a,int b); 產生lib和dll
然後建立一個進程外COM(EXE類型),內部連結dll,添加方法Method: Add(long *c)
{ *c = add(1,2);}編譯產生。
然後註冊COM,*.exe /regserver
最建立一個64位WIN32工程驗證64位環境下方法調用是否正確,經驗證正確!!!
結論:以上方法可以解決64位進程調用32位dll的問題
32位進程調用64位dll應該也可以通過這種方法解決,原因64位windows系統下安裝了32位和64位兩套COM系統
程式64位化帶來的問題和思考
1. 指標和long的轉換
這是最基本的處理部分,由於32位系統下地址是32位,所以很多代碼裡都會存在這樣的轉換:
void* pData;
LONG lData;
lData = (LONG)pData;
現在地址是64位了,所以原來的這種轉換就會導致地址高4Byte丟棄的問題.
這種轉換向來被認為不安全,但還是大量出現,實際程式員代碼的時候只要用一個指標保留就可以,完全沒有必要用一個long保留,同樣的問題也會出現在函數指標的保留.
思考:
所謂的存在即合理的思維在作怪吧,很多程式員認為這麼做程式啟動並執行好好的,所以不管是否有風險還照樣這麼寫,實際上改成安全寫法代碼多不了多少.
當然不排除很多程式員對16位、32位、64位完全沒有概念的.
還有一點可能是微軟的windows訊息給人以誤導,訊息的WPARAM和LPARAM中常會夾帶資料指標,然後強制類型轉換,但是看一下它們的定義,其中隔了一層,雖然只是文法上的小小手腳,但是絕對是有先見之明, 而很多人只會依樣畫葫蘆而未明白期間的巧妙.
2. PE Import和Export Table的變化
程式中用到了PE hack,由於之前有人發現vista 32下原來所需要替換的一個import table中的函數找不到了,結果竟然去修改了export table, 姑且不談修改export table的危險吧,結果vista 64的時候發現PE格式有了小小的變更.
MS將Import table中的地址轉成了64bit,但是export table卻還是32bit,估計其認為代碼不可能大於4GB吧,這樣就會發現更改export table變得很難,因為不同的dll會被load在不同的位址區段,其間差距會大於4GB,所以只有將自己的dll強制定義在和目標dll相同的4GB範圍內.
回頭來說這種更改是極其危險的,因為對於export table OS會盡量保持全域唯一,所以一旦你的dll退出未能正確恢複原有的值,會造成其他所有使用到這個dll的程式crash.
最終我發現需要hack的那個函數放在了delayload import table中,簡單的修改就解決了問題.
如果這個問題最終找不到而還是採用修改export table的方法,那麼就會很慘,測試部門已經發現了經常性的系統crash.
思考:
這裡出現了彎路,這個彎路項目的時間壓迫難辭其咎,但是程式員未能自己去分析問題是更大的問題,vista在很多方面都保留了很好的相容性,去分析下windows的目錄就會發現基本和xp區別不大,所以很多基礎dll的功能也沒有變化(這一點讓我感覺到vista並未如其發表的那樣70%代碼重寫),在分析dll的時候只要稍微仔細點就會找到問題很簡單.
另一個問題就是MS在定義PE格式的時候的確有很高的前瞻性,很好的保持了松耦合的能力,其實PE格式在2001年後就基本沒有什麼大的更改,那時大家還基本沒有64位的概念,這一點是需要學習的,但是同樣對於export table沒有擴充到64bit我還是保留意見,難道真的認為4GB不會超過?當年蓋茨大叔不是宣稱640KB就夠了嗎^.^
3. DelayLoad的問題
DelayLoad這個特性在VC6開始出現,一般大家不會去接觸,包括我自己,要不是這次機會也不會去看.
推薦一篇文章
http://www.microsoft.com/msj/0200/hood/hood0200.aspx
有詳細的闡述,有興趣可以自己看一下,其實其原理很簡單,主要是在使用到某個dll的介面的時候再去load這個dll,這樣可以節約空間,因為有些dll中的介面可能在程式周期中永遠不會用到, 而且如果某個dll沒有,如果沒有用到,那麼整個程式也能繼續執行.
這真的是一個巧妙的設定, 雖然在第一次呼叫的時候會有小小的效能損失,並且代碼也會稍微大一點,但是可能會帶來更大的空間節省.
但是隨即帶來的思考, 為什麼這麼好的一個東西會那麼生疏呢?個人考慮有以下原因:
1. 這種技術顯然對於很多人來說過於底層了,有多少人在意編譯出來的程式是怎麼樣存放,又是如何被載入啟動並執行呢?
2. DelayLoad帶來的好處對很多人不可見,只要我的程式編譯通過,並且可以正確執行就可以了,節省了那麼些空間或者擴充相容有必要考慮嗎?
3. 現在的硬體太好了,何必要去做這種最佳化呢?記憶體消耗多了,沒什麼嘛去買1GB記憶體插上不久解決了?
4. 編譯系統不夠智能,可能Visual Studio做的更加智能些,自動去分析然後產生delay load會更好吧?
64位進程調用32位dll的解決方案 / 程式64位化帶來的問題和思考