一、背景
之前寫過的幾個WPF小工具,每次發布都需要給它打安裝包和升級包,涉及到一些系列繁瑣的手工操作,有了Power Automate Desktop,於是便尋思着能不能做成一個自動化的流來使用。

二、創建流任務
創建名為創建WPF程序安裝包及升級包的流任務。
三、添加主流程
因為整個步驟比較長,為了更好的設計整個流,我們將這次流拆成幾個子流程,然后通過主流程串起來。
3.1 設置WPF項目目錄位置SettingProjectDir
1. 顯示選擇文件夾對話框,彈窗交互選擇當前WPF項目所在的文件夾,我們用名為CurrentProjectDir變量來存儲它,如果是常用位置,我們還可以設置默認值。

3.2 設置WPF項目存檔位置SettingArchiveDir
1. 顯示選擇文件夾對話框,彈窗交互選擇安裝包和升級包的存檔位置,我們用名為CurrentArchiveDir變量來存儲它,如果是常用位置,我們還可以設置默認值。

3.3 設置變量,設置WPF項目起始子項目名稱ProjectStartDir
1. 設置變量,變量名ProjectStartDir,用來存儲WPF項目的起始子項目名稱,方便我們后續找到正確的起始入口。

大家都知道,一般來說我們在一個解決方案中會構建多個子項目,所以我們這里需要知道在CurrentProjectDir中入口子項目是哪個,也就是包括App.xaml的那個項目。
3.4 獲取當前程序版本號GetAppVersion
0. 概覽

1. 設置變量,變量名StartAssemblyFilePath,拼接出ProjectStartDir的AssemblyInfo.cs文件位置。
%CurrentProjectDir%\%ProjectStartDir%\Properties\AssemblyInfo.cs

這個信息中,存儲了程序版本號等基礎信息。
2. 從文件讀取文本,從StartAssemblyFilePath中讀取文本到變量StartAssemblyContents中。

3. 分析文本,從StartAssemblyContents中正則匹配出版本號信息,將結果存儲到MatchAssemblyContents變量。

"[0-9.]+"
但是遺憾的是,這里得到的結果是帶了雙引號的數據,比如: "1.0.0.0"
4. 拆分文本,從MatchAssemblyContents中我們通過拆分文本來提取最終我們要的版本號,通過自定義分隔符的模式,用分隔符"來進行分割,分割結果放在變量SplitAssemblyContents中。

5. 設置變量,從SplitAssemblyContents結果中,提取第二個位置的數據,就是我們要的版本號,將版本號存在變量CurrentAppVersion中。
%SplitAssemblyContents[1]%

這樣我們就可以得到我們要的最終數據1.0.0.0
3.5 獲取當前環境模式GetEnvMode
0. 概覽

1. 設置變量,變量名AppSettingsFilePath,拼接出ProjectStartDir的AppSettings.json文件位置。
%CurrentProjectDir%\%ProjectStartDir%\appsettings.json

這個信息中,存儲了程序本地應用配置信息。
2. 從文件讀取文本,從AppSettingsFilePath中讀取文本到變量AppSettingsContents中。

這里留意AppSettingsContents的內容通常是JSON格式的。
{
"ThirdHostConfig": {
"HostAddress": "http://gateway.xxxxxxx.com",
"CallModel": "GateWay"
}
}
3. 將JSON轉換為自定義對象,將AppSettingsContents內容以JSON對象的形式提取出來,存儲到變量AppSettingsJsonObj。

4. 設置變量,從AppSettingsJsonObj中提取網關地址,存儲為變量CurrentGatewayUrl。
%AppSettingsJsonObj.ThirdHostConfig.HostAddress%

JSON對象的數據,在Power Automate中可以直接用.來逐層獲取值,非常方便。
5. 設置變量,變量名為CurrentEnvMode來存儲,當前環境模式。
設置一個初始值為UnKnown。
UnKnown

6. Switch-Case,根據CurrentGatewayUrl的特征,來識別對應的CurrentEnvMode值。
以其中一個Case為例,我們選擇運算符為包含,把特征值寫在要比較的值中,並且可以設置忽略大小寫。

最終,通過多個Case的組合我們得到一個完整的提取CurrentEnvMode值的流程。
| 運算符 | 要比較的值 | 結果值 |
|---|---|---|
| 包含 | xxxx-dev |
DevC1 |
| 包含 | gwkbs |
DevC1 |
| 包含 | gxxdev |
Dev |
| 包含 | gwdev |
Dev |
| 包含 | gxxfat |
Fat |
| 包含 | gwfat |
Fat |
| 包含 | gxxuat |
Uat |
| 包含 | gwfat |
Uat |
| 包含 | gatewayxx |
Pro |

3.5 創建WPF安裝包CreatePackage
0. 概覽

1. 設置變量,變量名VisualStudioEnvDir,設定當前系統安裝的Visual Studio版本對應的IDE目錄位置
C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\Common7\IDE

2. 設置變量,變量名InstallerProjectPath, 設定CurrentProjectDir中Visual Studio Installer Project的相對位置。
Setup1\Setup1.vdproj

3. 運行PowerShell腳本,來執行調用devenv.com命令,進行對Visual Studio Installer Project編譯,得到安裝包,這一步我們命名為BuildInstallProject。
cd '%VisualStudioEnvDir%'
.\devenv.com '%CurrentProjectDir%\%InstallerProjectPath%' /rebuild "Release|Any CPU"

- 我們先切換到
VisualStudioEnvDir這個目錄,因為我們需要到當前系統安裝的Visual Studio版本對應的IDE目錄,去調用devenv.exe; - 這里使用
.\devenv.com,而不是.\devenv.exe; - 隨后跟的是指定的
.vdproj文件的完整路徑,也就是說指定是對這個Visual Studio Installer Project進行處理; /rebuild是devenv的一個參數,代表先清理后編譯生成指定的項目或者解決方案,如果不需要清理,使用/build命令也是可以的;"Release|Any CPU"代表以Release模式進行生成,並且針對的設備平台是Any CPU;
如果順利的話,最終根據這個命令可以得到.msi的安裝包。

上述命令,建議先在終端中驗證一下,單獨在終端中執行效果如下圖:


如果遇到下面這個錯誤,繼續看下面的解決辦法。
ERROR: An error occurred while validating. HRESULT = '8000000A'

官方給出了一個快捷的解決辦法,只要是Visual Studio 2017+,都可以使用這個方法。

找到當前系統安裝的Visual Studio版本對應的DisableOutOfProcBuild目錄。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\Common7\IDE\CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild

在地址欄,輸入cmd,以快速進入這個目錄的當前終端上下文,然后執行如下命令即可。
DisableOutOfProcBuild.exe

這個執行之后,實際上是它為我們創建或者修改了已存在的一個注冊表項,以便解決前面的那個報錯。
4. 設置變量,變量名InstallerProductName, 設定安裝包產品名稱。

5. 設置變量,變量名InstallerPackagePath, 提取最終的.msi安裝包路徑。
%CurrentProjectDir%\Setup1\Release\%InstallerProductName%.msi

6. 設置變量,變量名InstallerVersionName,拼接出按版本號命名的文件名。
%CurrentEnvMode%-%InstallerProductName%-v%CurrentAppVersion%

最終得到的是比如是:Dev-xxxxx-v1.0.0.0
7. 重命名文件,將InstallerPackagePath文件重命名為InstallerVersionName,得到按版本號命名的新安裝包,這個動作我們叫RenamePackageFile

8. 設置變量,變量名InstallerPackageFilePath, 拼接出按版本號命名的新文件名。
%CurrentProjectDir%\Setup1\Release\%InstallerVersionName%.msi

參考
- 使用devenv在命令行中編譯項目
- How to build visual studio installer project (.vdproj) from jenkins to generate .exe and .msi files?
- An error occurred while validating. HRESULT = '8000000A'
- Visual Studio Installer 部署
- .Net5 WPF快速入門系列教程
Power Automate Desktop/RPA 愛好者交流群

