歡迎您光臨本站 註冊首頁

在 Linux上如何運行 Windows應用程序

←手機掃碼閱讀     火星人 @ 2014-03-24 , reply:0

  Wine 是一個令人神往而且目標遠大的開放源代碼項目,它嘗試去解決在 Linux 上運行 Windows 可執行文件的複雜問題。儘管 Wine 不是一個新項目,但是,人們對 Linux 桌面的期望以及對 Linux 應用程序的需求日益增加,使得它現在仍具有重要意義。本文對 Wine 進行了介紹,並提出了幾種獲得 Wine 內部操作經驗的途徑。

  Wine 項目起始於 1993 年,它的根源可以追溯到 90 年代早期出現的用於 UNIX 的 DOS 和 Windows 模擬器。Wine 項目最初是將 16 位的應用程序移植到 Linux,而幾年之後,已可以在 Linux 上運行 Microsoft Word 和 Excel。現在它有一百多萬行代碼。

  人們一直認為,在桌面上採用 Linux 的主要障礙是應用程序不足。商用桌面應用程序供應商還不能確定他們是否應該投入時間和精力將他們的 Windows 應用程序移植到 Linux,他們基本上是在等待 Linux 大規模應用於桌面。另一方面,Linux 需要應用程序才能大規模應用於桌面。這是一個經典的先有雞還是先有蛋的問題,而 Wine 通過在 Linux 上運行現有的 Windows 應用程序而解決了這一問題。

Wine 能做什麼

  Wine 項目實際是一個二合一的項目。它們提供了一個名字叫做 Winelib 的開發工具包,用於將應用程序從 Windows 移植到 Linux(和 Unix);它們還提供了一個程序載入器,讓 Windows 二進位文件可以在 Unix 和類 Unix 系統中運行。本文討論的主要是後者;在 參考資料中有關於 Winelib 的更多資料的鏈接。

  Wine 程序載入器讓運行於 x86 上的 Linux 和其他類 Unix 操作系統可以載入並運行 Windows x86 可執行文件 -- 不過那只是它要解決的問題的一部分。因為 Windows 可執行文件總是會鏈接到其他庫,而這些庫是 Windows 操作系統的一部分(如本文稍後圖 1 中的 Dependency Walker 截圖中可以看到),Wine 還最大可能限度地實現了那些 Windows 內部構件,即 Linux 上通常所指的 Win32 API5。

  雖然 Windows 和 Linux 有很大的不同,但是就基本的層次而言,與任何現代操作系統一樣,還是有很多類似之處的 -- 比較明顯的包括,對文件和目錄的支持,對同時運行多個程序的支持,類似的用戶界面以及對多媒體的支持。

不是模擬器

  據 WineHQ的說法,「WINE 代表 Wine Is Not an Emulator(即,Wine 不是一個模擬器)。更確切地說,Wine 是 X 和 UNIX 之上對 Windows API 的一個開放源代碼實現。您可以認為它是一個 Window 兼容層。Wine 不需要 Microsoft Windows,因為它是由 100% 非 Microsoft 代碼構成的另一個實現。但是它可以使用本機系統 DLL,只要這些 DLL 可用。而且它可以讓您在 Linux 或者其他類 UNIX 操作系統之上運行大部分 Windows 軟體。」

  顯示了 callDLL.exe 的依賴,這個可執行文件可以由本文提供的源代碼編譯得到。對一般的用戶而言,依賴的複雜度可能是驚人的,但對任何一個系統程序員來說都不是這樣,他們充分了解,哪怕是運行一個最簡單的程序,操作系統也必須要做很多事情。

  考慮可執行文件的第一個依賴 -- 對 USER32.DLL 的依賴。在 Windows 中,一個 DLL 就是一個動態鏈接庫(dynamically linked library),類似於 Linux 中的一個共享對象(一個 .so 文件)。USER32.DLL 文件通常會由操作系統提供,位於 C:\WINDOWS\system32 或者 C:\WINNT\system32 目錄下。這個文件中包含了 Windows API 中用於用戶界面的函數實現。

  我們的可執行文件調用 USER32.DLL 中的一些函數,USER32.DLL 然後去調用 NTDLL.DLL 中的其他函數,如此繼續。這些函數大部分已經由 Microsoft 文檔化 -- 但是還有很多沒有被文檔化。文檔的缺乏對 Wine 來說是一個極大的障礙,本文稍後將更詳細地討論這一問題。

  現在讓我們來看我們的可執行文件的第二個依賴 -- 對 SIMPLEDLL.DLL 的依賴。這個 DLL 是在編譯本文所附的源代碼時創建的。這個 DLL 中實現了一些特別簡單的函數;它作為常見於安裝 CD 上的各種二進位文件的例子被包括進來。

  如我們的例子所示,一般的 Windows 可執行文件有兩種類型的依賴:一種是對操作系統提供的二進位文件的依賴,另一種是對作為應用程序一部分的二進位文件的依賴。

  還需要特別注意的是,DLL 以難於管理而聞名,即使是在產生它們的 Windows 操作系統中也是如此(參見 參考資料)。值得一提的是,Wine 團隊成功地創建了一個可以在 Linux 上運行很多商用 Windows 應用程序的框架

  當前,開放源代碼的 Wine 項目有一個健壯的平台來運行 Windows 二進位文件以及對 Win32 API 的部分實現。這個項目仍然處於最初的測試階段(alpha),有很多部分還沒有完成。儘管 Wine 提供了一些工具來幫助進行配置、安裝以及運行應用程序,但是它們大部分都是面向程序員的,要讓非技術用戶也可以使用這些工具,還有很多事情需要去做。過去的 Corel 以及現在的 CodeWeavers 為此提供了很多幫助。

Wine 如何工作

  既然我們已經理解了 Wine 的基本原理,讓我們更詳細地來研究 Wine 能夠做什麼。本文中,我們討論的是 Wine 在純 Linux 上的安裝,沒有任何 Windows 分區。

  Windows 可執行文件:

  Wine 完全支持 Windows 可執行文件( .exe 和 DLL)的二進位載入。

  DLL:

  Wine 有幾百個 Windows DLL 的內部實現 -- 不過,其中沒有多少是完全的實現。例如,包含有用戶界面相關函數的 userd32.dll 在開放源代碼的 Wine 中實現了 92%。

  COM:

  這是一種幾乎被所有的大型 Windows 應用程序所使用的 Windows 技術,它支持諸如將一個 Excel 電子數據表嵌入到 Word 文檔中等功能。這一技術得到了 Wine 的很好的支持。

  註冊表:

  這是另一個幾乎任何一個 Windows 應用程序都會使用的關鍵技術,Wine 實現了大約 90% 的 Windows 註冊表管理 API。

  核心功能:

  核心繫統功能也得到了特別好的支持。如前面提到的,儘管 Linux 和 Windows 之間存在區別,但是基本的層次上還有很多類似之處,因此與進程、線程、文件和消息隊列相關的核心繫統 API 得到了近乎完美的支持。

  音頻和視頻:

  Wine 支持 Windows 音頻和視頻文件的運行(還可以使用 Windows 媒體播放器)。

  列印:

  也得到了支持,可以從一個在 Wine 中運行的 Windows 應用程序進行列印。

  ODBC:

  Wine 支持那些需要通過 ODBC 訪問資料庫的 Windows 應用程序。

  調試:

  Wine 有一個非常健壯和強大的內置調試器,除了支持標準的調試功能外,它還為調試運行於 Linux 上的 Windows 二進位程序進行了定製。它是 Wine 為其開發者提供的最重要工具之一。Wine 還有一個設計完備的追蹤和記錄日誌的模塊,可以幫助調試。我們之所以強調這一點,原因在於,儘管框架是健壯的,但是,當在 Wine 中安裝和運行 Windows 應用程序時有很多不確定因素,在使用開放源代碼的 Wine 時遲早會停下來進行調試。

Wine 不能做什麼

  不幸的是,很多用戶發現 Wine 難於使用。誠然,在 Linux 上使用類似於 MS Office 這樣的應用程序可能是困難的;在這裡我們來看一些原因,為什麼確實是這樣。在開始之前,我們應該指出,CodeWeavers 提供的商用 Wine 已經解決了大部分此類問題(參閱 參考資料以獲得鏈接)。假以時日,這些問題將可能在開放源代碼的 Wine 中同樣得到解決。

  使用命令行與大部分 Linux 中的應用程序一樣,Wine 必須在命令行中安裝。用戶必須回到命令行中來在 Wine 下運行應用程序。例如,要運行Internet Explorer,用戶通常需要在 shell 中輸入 wine IEXPLORE.EXE 。儘管經驗豐富的用戶喜歡這樣做,但初學者會發現這比較困難,而且不太可能懂得起別名等快捷方式。

配置

  CDROM 和驅動器映射等工作必須手工完成。這對初學者來說也是個問題。

取消對隱藏文件的隱藏

  如果掛載 CDROM 時沒有使用專門的 unhide 選項,那麼安裝 CD 上的一些文件可能會無法找到,從而導致安裝失敗。

調整配置

  Wine 安裝很少能直接使用(對它來說也就是從 tarfile 解開)。通常需要進行全面的調整;例如,一個程序的安裝可能與您在配置文件中設置的 Windows 版本不相容;而修改那個版本又可能會影響一些已安裝的應用程序。對很多 DLL 來說也是如此。為此,Wine 提供了一個巧妙的修復,讓您可以設置特定於應用程序的屬性。不過,一般的用戶可能不會像我們一樣滿意。

額外的安裝步驟

  大部分應用程序的安裝過程都不是標準的。例如,當我們安裝 Internet Explorer 6 時,必須跳過一些步驟,比如 DCOM98 的定位與安裝。對用戶來說,這類事情可能是最大的障礙。

  注意,這些並不是病症,而是舉例說明了是哪些種類的事情導致對一般用戶來說 Wine 看起來複雜而且困難。

更多 Wine 所不能做的

  看完 Wine 的可用性問題后,現在讓我們來總結在嘗試使用 Wine 時一些常見的技術問題。

  缺少 DLL,這可能是最常見的問題:很多安裝由於缺少 DLL 而失敗。人們應該熟練使用調試器來決定下一步如何去做。(稍後將深入討論調試器)。

DLL 版本問題

  一些安裝程序在開始之前會檢查現有的系統 DLL。Wine 解決這一問題的方法是,創建假 DLL 以滿足安裝程序的需要。不過,有一些安裝程序會更進一步並深入檢查 DLL 以獲得它們的版本。這對假的 DLL 來說要求太高了,會導致安裝失敗。

DLL 載入次序

  Wine 有對很多 Windows DLL 的實現,而且如果可用,它還可以使用原始的 Windows DLL。如果兩種 DLL 都可用,好像顯然應該選擇總是使用 Windows 自己的 DLL,但實際上 Windows DLL 有時會包含不能被滿足的依賴。要確定是更應該使用 Wine 的 DLL 還是應該使用本機 DLL,惟一的方法是,基於各個應用程序反覆進行試驗。

DLL 中的函數

  當一個 Wine DLL 沒有實現 Windows 中相應的 DLL 的全部功能時,應用程序可能會遭遇函數調用失敗。由於 DLL 是動態載入的,可能沒有辦法事先知道會發生這樣的事情。這是一個複雜的問題,有一些可做的工作,但最終實際來說它只是取決於應用程序的代碼如何編寫。

  有一些因素會減輕這些問題。一方面,您將會一個一個地遇到這些問題,而不是一次遇到全部問題,這樣處理起來要容易些。另外,您遇到的那些問題可能其他人曾遇到過並已經解決(而且解決方案已經公布出來)。Wine 用戶組非常活躍,會提供許多幫助,每周一次的 Wine 時事通訊(參閱 參考資料)是極好的信息資源。

Wine 中還沒有實現的 Win32 API

  在 Wine 中,很多 Win32 API5 的函數是殘缺不全的。最常見的原因是,相當多的 Win32 API 並沒有被文檔化。這就意味著一個特別的應用程序可能會調用某個函數,而完全沒有關於此函數的可用資料。例如,我們在運行一個簡單的 RPC 程序時發現了 RtlAnsiCharToUnicodeChar 這個函數。在 MSDN 上的搜索結果顯示沒有關於這個函數的資料,而且沒有關於所有 RtlXXXX 類別函數的資料。因此,如果它們在 Wine 中的實現對一些應用程序來說至關重要,那麼人們可能只有去猜測它們的行為了。

商用的 Wine

  CodeWeavers 為 Wine 做了很多工作。多年來他們為 Wine 項目貢獻了很多代碼,他們出售商用版本的 Wine,其改進的用戶界面解決了我們在本文中提出的很多問題。

  例如,CodeWeavers 的二進位安裝文件會在用戶的開始菜單中添加一個 Crossover 條目;安裝后,絕大多數 Crossover 相關的任務可以通過開始菜單條目來完成。在開放源代碼的 Wine 中,所有這些任務 -- 安裝、程序執行以及其他任務 -- 都必須在命令行中執行。此外,CodeWeavers Crossover 將會嘗試去為新安裝的軟體包配置一個合理的默認值,如果需要的話會在安裝完成後自動重新引導,並以其他形式減輕用戶的負擔。

  CodeWeavers 使用開放源代碼的 Wine 作為他們的 Crossover 產品的基礎,所以,除非遇到上面我們討論過的可用性問題,否則,在其中一個產品中能運行的應用程序,在另一個產品中同樣也能運行。要深入了解 CodeWeavers 和 Crossover,以及要獲得可以在 Wine 上運行的應用程序列表,請參閱在 參考資料中列出的鏈接。

應用程序安裝分析

  由於 Wine 支持 Windows 可執行文件的運行,您會想當然地認為可以使用程序的安裝程序從頭安裝,這是正常的。不幸的是,幾乎不會那樣。對 Windows 安裝過程的理解將有助於解釋原因。下面非常簡單地描述了 Windows 安裝程序通常要做的事情的(不必是這個次序):

  將文件拷貝到一些目錄。

  註冊 DLL,並將其他應用程序相關的信息添加到註冊表中。

  在安裝過程中,有時會檢查 DLL 的版本(如前面所提到的)。

  修改 INI 和一些其他配置文件。

  因而,Wine 會遇到兩種類型的問題,必須按順序解決:

  安裝過程中的問題。

  執行過程中的問題。

  在調試 Wine 安裝的過程中,如果您同時有一個可用的 Windows 系統的話會非常有幫助。那樣,您可以對 Windows 安裝使用追蹤器以確切斷定哪些文件被拷貝,哪些註冊表條目被添加或更新,哪個 INI 文件被修改,等等。記錄安裝步驟的順序並與失敗的 Wine 安裝相比較,是故障診斷的好嚮導。

在 Linux 上安裝 Wine

  如果您正在使用 Red Hat 或者 SUSE,最簡單的方法是從 CD 安裝 Wine。不過,如果那些 CD 比較老,您可能需要通過源文件安裝,因為 Wine 項目經常更新。如果通過源文件安裝,您會發現 Wine 用戶指南(參閱 參考資料以獲得鏈接)是一份價值無法估量的資料。簡化的安裝過程如下:

  解開源文件后,切換到 tools 目錄下以用戶身份運行 ./tools/wineinstall。

  在 tools 目錄下運行 winecheck 腳本來檢查安裝。您可能不會獲得 100% 的成功,但只要沒有關鍵問題就行。

  Wine 的所有配置都保存在 ~/.wine/config 文件中。這個文件很容易理解:它描述了您希望將 Linux 文件系統的哪部分看作是 Windows C 驅動器,以及 DLL 的載入次序等其他的細節。

  您應該可以快速進行了。例如,要安裝 WinZip 8.1,您可以下載安裝程序並在命令行中運行 wine winzip81.exe 。

  快速瀏覽一下可以了解很多內容:您可以看到 WinZip 在運行,它的文件瀏覽器組件顯示出熟悉的 Windows 驅動器 C、軟盤驅動器 A、一個 CD-ROM M 以及另外的 Z 驅動器。您可以猜到,所有這些都映射在我們上面提到的 ~/.wine/config 文件中。下面是文件中與 所示驅動器有關的片斷:

清單 1. Wine 的配置文件
CODE:
[Drive A]
"Path" = "/mnt/floppy"
"Type" = "floppy"
"Label" = "FLOPPY1"
"Device" = "auto"
[Drive C]
"Path" = "/home/aditya/aug14/3/c"
"Type" = "hd"
"Label" = "fake_windows"
"Filesystem" = "win2k"
"Codepage" = "0"
[Drive Z]
"Path"="/home/aditya/downloads/wine/"
"Type" = "hd"
"Label" = "wine src"
"Filesystem" = "win95"
[Drive M]
"Path" = "/mnt/cdrom"
"Type" = "cdrom"
"Label" = "CD-ROM1"
"Filesystem" = "win95"
"Device" = "auto"

  從這裡您可以看到, M 驅動器實際上是 /mnt/cdrom;C 驅動器是 /home/aditya/aug14/3/c;等等。

Windows 應用程序移植到 Linux 的一個例子

  技術上講,使 Windows 可執行文件在 Wine 中運行並不是移植,但是經常被認為是移植。這也是測試一個應用程序是否適合使用 Winelib 進行移植的好辦法。要獲得關於使用 Wine Winelib 庫編譯來移植源代碼的資料,請參閱 參考資料中關於 Winelib 用戶指南的鏈接。

  我們已經整理了一個非常簡單的應用程序,來說明 Windows 程序如何在 Wine 中運行。程序創建了一個窗口;從一個 DLL 中載入了兩個函數,有一些對話框。我們將繼續使用非常簡單的例子;作為實際應用中的例子,我們非常鼓勵您用 MS Office 等大型應用程序進行同樣的嘗試。

  同時,這個例子可以通過讓您體驗代碼的運行來入門。

運行代碼

  示例 DLL 的源代碼在 DLLCode 目錄中,使用 DLL 的源代碼在 callDll 目錄中。要運行只需要將兩者(DLL 文件和可執行文件)存放於同一目錄下,並運行 callDll.exe 。winetests 文件夾中的 README.TXT 文件描述了如何在 Linux 上 Wine 中執行那個二進位程序。

  如果您願意,可以隨意打開項目文件並進行修改。

  DLLCode 項目正確編譯后,您可以在 Debug 文件夾中看到 DLLSample.dll 文件。這個文件是 callDll 項目所需要的。為了您的方便,DLLSample.dll 已經存放於 callDll 中正確的位置。舉例說明的 callDll 中產生的可執行文件的依賴;您可以使用 Dependency Walker 來查看其他程序的依賴(參閱 參考資料)。

比較

  下面是對運行於 Windows XP 上的和運行於 Red Hat 的 GNOME 中的消息框(Message Box)視覺上的對比:

  對應於此的 C 代碼(callDll.cpp 中第 60 行):
CODE:
MessageBox(NULL, "Wine test ending...", "", MB_OK);

  這提出了一些有趣的觀察:

  最明顯的觀察之一是,雙方都支持消息框的使用;這是將 Windows 調用有可能映射為 Linux 中相應部分的因素之一。如果您在 Wine 源代碼中搜索 MessageBox,您可以找到說明文件,文件中詳細說明了哪些函數已經被實現,哪些被去除掉。

  大小、字體、顏色和標題欄以及其他可視元素繼承自底層操作系統設置的默認值。

  從本文中所包括的追蹤文件中,您可以看到這個函數是在 user32.dll 中實現的。如果您在文件中搜索字元串「Wine test ending」,您將更深入地理解到,當您在做一些與在消息框中顯示字元串同樣簡單的事情時,幕後發生了多少事情。

  既然我們已經看過了一個 Win32 函數,讓我們來看一下,當 simpleDLL.dll 中實現的我們自己的函數之一被調用時,幕後發生了什麼。我們將展示追蹤記錄中的片斷來說明何時 simpleDll.dll 被載入,哪些函數被導出,在哪一點 getTitle() 函數被調用(按 README 文件中的步驟生成完全的追蹤記錄):

清單 2. 解析出我們的 DLL 的路徑並檢查 DLL 是否存在
CODE:
0009:trace:module:MODULE_GetLoadOrder looking for
C:\documents\article-sample\simpleDLL.dll

清單 3. 這一行展示我們的 DLL 所導出的函數
CODE:
Module name is simpleDLL.dll, 3 functions, 3 names
Ord RVA Addr Name
1 0000100a 0x1000100a add
2 0000100f 0x1000100f getSize
3 00001014 0x10001014 getTitle

清單 4. 這裡展示了 getTitle 被調用的位置
CODE:
0009:CALL simpleDLL.getTitle((0x40580000,00000002,00000040):
returning 405b7210
) ret=004010d4
0009:RET simpleDLL.getTitle() retval=1002901c ret=004010d4

  分析一個更大的應用程序或多或少與此相似,不過您當然會得到多得多的細節,會遇到沒有被實現的函數,等等。故障診斷通常大概是提供缺少的 DLL 和調整配置;偶爾,您將需要去實現或修復一個函數。

結束語

  如果您正在尋找將現有的 Windows 應用程序轉移到 Linux 的方法,開放源代碼的 Wine 和來自 CodeWeavers 的商用產品都是極好的選擇。應用程序可以運行於 Wine 之上,或者使用 Winelib 工具包進行移植。

  Wine 還為那些希望介入開放源代碼軟體的人們提供了一個獨一無二的機會。從特別困難的到適合初學者的,有大量的各種複雜程度的項目--而且 Wine 社區非常活躍並提供非常多的支持。

[火星人 ] 在 Linux上如何運行 Windows應用程序已經有596次圍觀

http://coctec.com/docs/linux/show-post-134758.html