在 Linux 上發布 WinForm 應用程式

不管是 WinForm, WPF 都是針對 Windows 作業系統所開放,除非使用特殊方式,不然是不能在 Windows 以外的地方發布(打包)。
「Windows 程式就是要在 Windows 上發布阿,誰會在 Linux 上發布 Windows 應用程式啊?」
其實是有的,其中一種情況就是在 CI/CD 流程中會用到,程式寫完後將原始碼 commit 到版本控制中,透過 webhook 自動觸發持續整合工具(例如 Jenkins),依照 Dockerfile 的內容拉取 dotnet SDK docker image 建立環境,在裡面將程式編譯、發布為執行檔案,再上傳到伺服器中,使用者開啟程式時因為 ClickOnce 自動部署技術,在程式一開始執行前自動檢查指定網站上是否有新版本可以更新,然後自動更新。在開發者只要 commit ,在使用者沒有特別操作的情況下應用程式就完成了更新,行雲流水,非常夢幻。

但現實中,在 Linux 發布 WinForm 應用程式會出現下列錯誤:
    
#13 [build 6/7] RUN dotnet publish -c Release -o out
#13 sha256:c7a2b22c53f02596f8e7f1ac89f674dcruyutb2c3ruyute165beefcffe3fd2ea
#13 1.243 Microsoft (R) Build Engine version 17.2.0+41abc5629 for .NET
#13 1.243 Copyright (C) Microsoft Corporation. All rights reserved.
#13 1.243
#13 2.765   Determining projects to restore...
#13 2.916 /usr/share/dotnet/sdk/6.0.302/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(90,5): error NETSDK1100: Windows is required to build Windows desktop applications. [/app/Paint/Paint.csproj]
#13 ERROR: executor failed running [/bin/sh -c dotnet publish -c Release -o out]: exit code: 1
------
 > [build 6/7] RUN dotnet publish -c Release -o out:
------
executor failed running [/bin/sh -c dotnet publish -c Release -o out]: exit code: 1
    

研究後發現 windows 只能在 windows 上發布,要不就是把檔案上傳到 windows 主機,透過腳本發布後再上傳,不然就是做一個 windows 的 docker image。

解決方式

好在後來微軟似乎也發現這個問題(?)在 Github 上的這個 issure 中可以發現在 dotnet sdk 6.0.4xx 以上調整設定後就可以在 linux 打包了!(於 2022 年 8 月 9 日發布)

只要將專案下 Project.csproj 檔案中加入 <enablewindowstargeting>true</enablewindowstargeting> 即可
註: Project 需要替換為專案名稱,例如筆者的專案名稱叫做 RuyutWinFormsAppExample ,檔名就是 RuyutWinFormsAppExample.csproj
    
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net6.0-windows</TargetFramework>
        <UseWindowsForms>true</UseWindowsForms>
        <ImplicitUsings>enable</ImplicitUsings>
        <EnableWindowsTargeting>true</EnableWindowsTargeting>
    </PropertyGroup>
</Project>
    

建立 Dockerfile
    
FROM mcr.microsoft.com/dotnet/sdk:6.0.400-1-bullseye-slim-amd64 AS build
WORKDIR /app
COPY . ./
RUN dotnet publish -c Release -o out
    

打包:
註: ruyut_winforms_app 為 docker image 名稱,RuyutWinFormsAppExample 為專案名稱
    
docker build -t ruyut_winforms_app -f .\RuyutWinFormsAppExample\Dockerfile .
    

執行 docker 容器
    
docker run -t -d --name ruyut_winforms_app ruyut_winforms_app
    

將打包完的檔案取出來:
註:D:\docker\winform 為檔案複製到本機的目的地
    
docker cp ruyut_winforms_app:/app/out D:\docker\winform
    

開啟資料夾後會發現檔案已經被複製出來了

嗯...exe檔案在哪裡?筆者嘗試了很多次,也調整過 OutputType ,但筆者目前沒有辦法輸出 exe 檔案。
那要怎麼執行?還好有「跨平台程式檔」,就是那個 dll,可以使用下面的指令執行:
註: 需要替換檔名
    
dotnet RuyutWinFormsAppExample.dll
    

補充: 後來筆者使用下面這行替換 DockerFile 內的 publish
    
RUN dotnet publish -c Release -r win-x64 --self-contained false -o out
    

可以正常輸出 exe 檔案

但在執行 exe 檔案時會多出一個指令視窗,關閉指令視窗程式就會被關閉

如果知道解決方式的請替筆者解惑,感激不敬!

參考資料:
Support suppressing transitive framework reference downloads, and other changes #25358
Error NETSDK1100: Windows is required to build Windows desktop applications

留言