“Windows 下的 GUI 開發(fā),真的太難了!”(windows開發(fā)教程)
近日,開發(fā)者 Samuel Tulach 在個人博客上感嘆道,“在 Windows 上編寫 GUI 應用程序真是太難了!”在文中, Samuel Tulach 討論了使用 C 編寫 GUI 應用的各種困難。他詳細列舉了幾種流行的庫,但這些庫通常會面臨諸如依賴項管理、應用程序大小以及樣式定制等問題,并沒有完全契合的解決方案。
萬萬沒想到,這個小吐槽引起了不少開發(fā)者的共鳴,甚至登上了 HN 熱榜。接下來,我們將通過本文看看難點究竟在哪里?
過去幾天里,來自捷克的程序員 Samuel Tulach,一直在尋找一個能夠讓他用 C 編寫帶有 GUI 程序的庫。他給出的要求非常簡單:
- 只需要支持 Windows;
- 允許商業(yè)用途;
- 易于設計,包括支持暗黑模式;
- 結(jié)果應該是一個單一的 .exe 文件,沒有或只有極少的依賴項,大小不超過 40MB;
- 編寫程序的 GUI 部分所花費的時間不應該比實際功能花費的時間多。
所以,Samuel Tulach 開始挑選一些稱手的工具、框架,結(jié)果發(fā)現(xiàn)并沒有想象中那么簡單。
WinUI 3
首先,Samuel Tulach 從專為 Windows 應用程序開發(fā)而設計的現(xiàn)代化的用戶界面(UI)框架——WinUI 3 入手。
乍一看,它是個不錯的選擇。它允許你使用現(xiàn)代的 Windows 組件,還可以自定義樣式顏色。設計方面,你可以使用非常容易掌握的 XAML,或者直接使用 Visual Studio 設計器。
WinUI 3 控件庫
問題:使用 WinUI 3 框架開發(fā)的應用程序在發(fā)布時不能以無包裝(unpacked)的形式進行分發(fā)。這意味著無法將應用程序打包為一個單獨的可執(zhí)行文件(.exe)及其所需的所有依賴項,而是需要依賴于特定的分發(fā)方式,如打包為 AppX 包或者通過 Microsoft Store 進行分發(fā)。
對于 Samuel Tulach 而言,大多數(shù)情況下,當他嘗試將應用程序遷移到虛擬機或不同計算機時,由于缺少一些不明依賴項而無法正常啟動。更糟糕的是,你需要提供一堆處理 WinUI 功能的 .dll 文件,沒有辦法提供一個可移植的 .exe 文件。使用打包形式通常沒有問題,但它們是以 AppX 包的形式安裝的,這本身就會帶來很多問題(尤其是在需要訪問所有 Win32 API 的情況下)。
Win32 / MFC / 封裝 Win32 的小型庫
緊接著,Samuel Tulach 將目光轉(zhuǎn)移到了 Win32 身上。Win32 是一種應用程序編程接口(API),它是微軟 Windows 操作系統(tǒng)的核心組成部分。它提供了開發(fā)者與操作系統(tǒng)交互的一組標準接口和功能,用于創(chuàng)建和管理 Windows 應用程序的各個方面,包括窗口、消息處理、文件操作、網(wǎng)絡通信等。
Samuel Tulach 表示,其需要高度的可移植性,因此使用操作系統(tǒng)的原生渲染是有道理的。這樣的程序可以是一個單一的 .exe 文件(因為我們對 MFC 進行了靜態(tài)鏈接),而且體積超?。ㄖ挥袔浊?span id="qsh1b7padf" class="candidate-entity-word" data-gid="6760316307995162421">字節(jié))。
同時,Samuel Tulach 稱,還可以使用別人已經(jīng)編寫好的更簡約的庫,這意味著從概念到可運行應用程序的快速實現(xiàn)將變得非常容易。
基本 Win32 形式
問題:對原生的 Win32 控件進行樣式化處理是一項非常困難的任務。為了改變這些空間的外觀和風格,Samuel Tulach 需要為每一個控件編寫一個專門的自定義繪制函數(shù),這個過程不僅需要詳細了解 Win32 API 的繪制機制,還需要大量的時間和精力投入。
對此,Samuel Tulach 吐槽稱,“有這個時間,我都可以直接去養(yǎng)家糊口了?!?/span>
此外,Windows 文件資源管理器(File Explorer)使用的一些原生 Win32 控件具有一個被稱為 “隱藏” 的暗色模式。用戶可以激活這個模式,但它只會對部分控件生效,并且在視覺效果上并不十分理想或完善。
QT
Qt 在 C 圖形用戶界面開發(fā)中被認為是一個非常優(yōu)秀的選擇。盡管它相對復雜,但它提供了 Qt 樣式表(Qt Style Sheets),這使得開發(fā)者可以輕松地設計和定制界面的外觀和樣式。Qt 樣式表使用一種類似于 CSS 的語言,讓開發(fā)者可以用熟悉的方式來定義控件的外觀、布局和交互效果。
OBS studio 使用 Qt 和自定義樣式表
然而,問題在于,當使用動態(tài)鏈接方式時,運行 Qt 應用程序需要大量不同的 .dll 文件,這些文件總共的大小超過了 40MB。為了減少文件大小,可以選擇將 Qt 靜態(tài)鏈接到應用程序中,這樣未使用的部分會被移除,從而減小應用程序的體積。
不過,Qt 使用的是 LGPL 許可證。根據(jù) LGPL 的條款,如果選擇靜態(tài)鏈接 Qt,那么必須遵守 LGPL 的要求之一:要么開源應用程序的源代碼,要么提供用于重新編譯的目標文件。另外,Qt 也提供了商業(yè)許可證選項,可以以幾千美元的價格購買,從而免除遵守 LGPL 條款的義務,只是這樣成本又太高了。
wxWidgets
wxWidgets(https://www.wxWidgets.org/)是一個相對容易學習的庫,還可以使用 wxFormBuilder。它所遵循的許可證要比 Qt 更寬松,可以靜態(tài)鏈接成一個 3MB 的可執(zhí)行文件。
wxWidgets 啟用了實驗性的 Windows 暗模式選項
問題:wxWidgets 是一個在 Windows 上使用原生的 Win32 組件的庫。然而,與直接使用 Win32 或 MFC 相比,wxWidgets 并沒有提供輕松覆蓋繪制函數(shù)的選項,因此在樣式定制方面甚至比直接使用 Win32 或 MFC 更為困難。
盡管 wxWidgets 支持應用 Windows 文件資源管理器的暗模式控件,但實際效果并不理想。
hikogui
hikogui(https://github.com/hikogui/hikogui)是一個相對較新的保留模式 GUI 庫,它使用 Vulkan 作為后端技術(shù)。這意味著它利用 Vulkan 的強大功能來進行圖形渲染和顯示。hikogui 內(nèi)置了暗模式的支持,開發(fā)者可以很容易地自行定制和樣式化界面,以適應應用程序的需求和設計風格。
問題:要成功編譯 hikogui,可能需要具備計算機科學博士學位,特別是在編譯器開發(fā)方面的專業(yè)知識。在體驗過程中,Samuel Tulach 嘗試了超過 30 分鐘去編譯示例,包括嘗試不同的代碼分支和發(fā)布標簽,但最終只得到了一個立即在某個 Vulkan 庫內(nèi)發(fā)生訪問沖突的可執(zhí)行文件,因此他決定放棄嘗試。
不過,盡管 Samuel Tulach 不太喜歡大量使用 STL(標準模板庫),甚至認為有時候并不是必需的,但他認為 hikogui 看起來確實有前景。
Sciter
Sciter 實際上是一個不錯的 Electron 替代品,允許開發(fā)者使用 HTML/CSS 編寫桌面應用程序的 GUI。
SVG 圖標上抗鋸齒效果不佳的示例
問題:你可能認為應用程序大小或稱為一個問題,但實際上,包含所有必需的 .dll 文件的最終應用程序大小大約只有 25MB,這對 Samuel Tulach 來說完全可以接受。
Samuel Tulach 表示,如果 Sciter 能夠完全開源,那將是更好的選擇,因為這樣開發(fā)者可以將靜態(tài)鏈接版本用于商業(yè)用途(與 Qt 的問題相同)。同時,Sciter 的獨立許可證價格相比 Qt 要低不少(目前為 310 美元),Samuel Tulach 也愿意為此支付費用。
然而,現(xiàn)在不選用這個框架的主要原因在于,如上圖所示(看看標題欄圖標),渲染效果不太好。在體驗過程中,Samuel Tulach 透露其遇到了各種字體和圖像的抗鋸齒問題(啟用了高分辨率選項,即使在預編譯的 scapp.exe 中也存在這個問題)。而且,無論你做什么,窗口都會有一個相當厚(2-3 像素)的灰色邊框,完全無法自定義或修改。
WinForms / WPF
如果你在一些論壇上詢問關(guān)于在 Windows 上使用 C 編寫 GUI 庫的問題,大概率會收到一些“這不是一個好主意”等類似的反饋,甚至論壇上的專家們會直接建議你采用其他技術(shù)堆棧來編寫應用程序的前端部分,然后將用 C 編寫的功能作為組件或模塊加載進來。
這種做法可以讓你更輕松地定制和風格化界面,并顯著加快開發(fā)速度。從技術(shù)角度來看,確實可以使用像 WinForms 或 WPF 這樣的工具生成一個體積較小的單獨可執(zhí)行文件(.exe)。
詳細來看,有兩種方式可以實現(xiàn):
- 將動態(tài)鏈接庫(.dll 文件)打包到應用程序的資源中,然后在運行時從應用程序的資源中提取這些 .dll 文件到一個臨時文件夾中。接下來,使用 P/Invoke 技術(shù)在 C#/.NET 應用程序中調(diào)用這些從 .dll 編譯而來的庫文件。這種做法允許將 C 編寫的功能模塊作為 .dll 文件嵌入到 C# 或 .NET 應用程序中,并在運行時動態(tài)加載和調(diào)用它們。
- 使用 C /CLI。
問題:.NET Framework 在 Windows 10 及更新版本中是預裝的,因此從技術(shù)上來說,我們?nèi)匀豢梢詽M足無依賴項的標準。然而,如果我們選擇將 C 編寫的功能模塊打包為 .dll 文件,我們?nèi)匀恍枰谶\行時將這些 .dll 文件從打包的資源中提取到一個臨時位置,并編寫額外的 P/Invoke 代碼來調(diào)用這些模塊。
此外,如果使用 C /CLI 編譯,生成的代碼將轉(zhuǎn)換為 .NET IL 代碼。換句話說,生成的應用程序可以在調(diào)試工具(如 dnSpy)中打開,你可以看到 C 代碼被翻譯為等效的 C# 代碼。這種情況下,生成的應用程序并不是純粹的原生代碼應用程序,而是包含了.NET Framework 的中間代碼。
而正如文章伊始所提及的,Samuel Tulach 想要原生的代碼,所以基于 WinForms 或 WPF 得到的代碼并不他真正想要的。
解決方案?
以上是 Samuel Tulach 考慮過的幾個方案。經(jīng)過長時間嘗試各種不同的庫,甚至一度編寫自己的 MFC 樣式之后,Samuel Tulach 發(fā)現(xiàn)對于簡單的應用程序來說,沒有什么比 Dear ImGui 更合適的了。
Dear ImGui 是一個用于 C 的輕量級界面開發(fā)框架,它的主要目標是提供高效、靈活和易于使用的界面開發(fā)工具。Dear ImGui 是一個基于文本的界面開發(fā)框架,它使用簡單的文本命令來創(chuàng)建和更新用戶界面。
不過,Dear ImGui 在設計復雜的用戶界面時也有一些缺點,而且它不是保留模式的用戶界面,而是即時模式的用戶界面,因此想要使用,必須運行像 DirectX 這樣的 GPU 渲染器來渲染每秒 60 幀或更多幀的用戶界面。
正如上圖所示,Samuel Tulach 已經(jīng)寫了一個示例,如何使用內(nèi)置的多視口功能來制作簡單的圖形用戶界面應用程序。
編譯后的程序大小只有500KB,非常小巧。這個程序不需要安裝任何額外的依賴項,即使是將 MFC(Microsoft Foundation Classes)靜態(tài)鏈接到程序中,也不需要安裝 VC (Visual C )的運行時庫。這使得部署和使用這個程序變得非常簡便,用戶可以直接執(zhí)行這個程序而無需擔心缺少運行時組件或依賴項。
對于 Samuel Tulach 這段經(jīng)歷,不少開發(fā)者感同身受。來自 HN 的網(wǎng)友 pshirshov 表示:
我也有過類似痛苦的經(jīng)歷。我需要一個真正跨平臺(Windows、Linux/Wayland、Mac、iOS、Android)的 GUI 工具包,它要有豐富的控件庫和合理的主題。事實上,唯一不錯的選擇是 QT,而使用 C 很難提高效率,因為它仍然缺乏基本功能,如類型解構(gòu)和帶窮舉檢查的 ADT/GADT。QT 與其他語言的綁定還不夠成熟。
除了 QT 之外,還有漏洞百出的 Avalonia。對 Linux 的支持非常糟糕。
此外還有 Kotlin/Compose Multiplatform。它缺乏良好的文檔和先進的控件,而且仍然充滿 Bug。
我無法找到任何適用于 JS/TS 的優(yōu)秀控件庫,即使是付費的控件庫(如 Telerik 和其他控件庫)也非?;靵y。因此,Electron、Capacitor、React Native——它們都認為即使是一個非?;镜膽贸绦蛞残枰度氪罅烤?。
Flutter 給人的感覺極不成熟,尤其是全局單例模式的普及使得在使用 Flutter 時,即使是管理小型代碼庫也變得非常困難。
另一用戶稱:
在 Mac 上我遇到同樣的困擾,我在 Mac 上 SwiftUI 的體驗是,它仍需要大量工作。文檔很差。如果你以直截了當?shù)姆绞阶鍪?,性能可能會很差。支持舊版本的操作系統(tǒng)相當痛苦等等。
至此,你是否體驗過 GUI 開發(fā)?有哪些工具推薦?
來源:
https://news.ycombinator.com/item?id=40839208
https://tulach.cc/writing-gui-apps-for-windows-is-painful/