64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考
最近做在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會更好吧?
轉載于:https://www.cnblogs.com/MaxWoods/p/3363622.html
總結
以上是生活随笔為你收集整理的64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 判断list集合不为空
- 下一篇: C# 多线程 与 委托