Visual Studio 內建「安裝專案」雖老舊但零依賴、無需學習 WiX/NSIS 腳本,適合中小型桌面應用快速交付。本文以「安裝時輸入 SQL Server 連接參數 → 自定義動作修改 WinUI.exe.config」為例,完整演示從建立專案到生成 MSI 的全流程。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="ConnectionString" connectionString="" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
關鍵:connectionString 預留空值,安裝時由自定義動作填入。
SetupProject。| 屬性 | 值 | 說明 |
|---|---|---|
| BannerText | 資料庫連接設定 | 對話框標題 |
| BodyText | 請輸入 SQL Server 連接資訊 | 提示文字 |
| Edit1Label | 伺服器 IP/名稱 | 第 1 欄標籤 |
| Edit1Property | SQLSERVER_NAME | 第 1 欄屬性名(供 CustomActionData 取用) |
| Edit1Visible | True | 顯示 |
| Edit2Label | 資料庫名稱 | 第 2 欄標籤 |
| Edit2Property | DATABASE_NAME | 第 2 欄屬性名 |
| Edit3Label | 登入帳號 | 第 3 欄標籤 |
| Edit3Property | USERNAME | 第 3 欄屬性名 |
| Edit4Label | 登入密碼 | 第 4 欄標籤 |
| Edit4Property | PASSWORD | 第 4 欄屬性名 |
| Edit4Visible | True | 密碼模式自動遮罩 |
CustomActions。System.Configuration.Install、System.Configuration、System.Xml。SetSqlConnection.cs 繼承 Installer 並標記 [RunInstaller(true)]。
using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Xml;
using System.IO;
namespace CustomActions {
[RunInstaller(true)]
public partial class SetSqlConnection : Installer {
public SetSqlConnection() { InitializeComponent(); }
public override void Install(IDictionary stateSaver) {
base.Install(stateSaver);
// 從 CustomActionData 取得用戶輸入
string server = Context.Parameters["SqlServerIP"];
string db = Context.Parameters["DataBase"];
string user = Context.Parameters["UserName"];
string pwd = Context.Parameters["Password"];
string targetDir = Context.Parameters["TargetDir"]; // 結尾含反斜線
// 組裝連接字串
string connStr = $"Data Source={server};Initial Catalog={db};User ID={user};Password={pwd};Persist Security Info=True";
// 修改目標目錄下的 WinUI.exe.config
string configPath = Path.Combine(targetDir, "WinUI.exe.config");
UpdateConnectionString(configPath, "ConnectionString", connStr);
}
public static void UpdateConnectionString(string configPath, string name, string connStr) {
XmlDocument doc = new XmlDocument();
doc.Load(configPath);
XmlNode node = doc.SelectSingleNode($"configuration/connectionStrings/add[@name='{name}']");
if (node != null) {
node.Attributes["connectionString"].InnerText = connStr;
doc.Save(configPath);
}
}
public override void Uninstall(IDictionary savedState) { base.Uninstall(savedState); }
public override void Commit(IDictionary savedState) { base.Commit(savedState); }
public override void Rollback(IDictionary savedState) { base.Rollback(savedState); }
}
}
CustomActions 專案主輸出。
/SqlServerIP=[SQLSERVER_NAME] /DataBase=[DATABASE_NAME] /UserName=[USERNAME] /Password=[PASSWORD] /TargetDir="[TARGETDIR]\"
注意:中括號內為步驟 4 定義的 EditXProperty 值;[TARGETDIR] 為安裝目錄,末尾必須加反斜線並用雙引號包裹,防止路徑含空格截斷。
SetupProject.msi。WinUI.exe.config:
<add name="ConnectionString" connectionString="Data Source=DBServer;Initial Catalog=szitrDB;User ID=szitr.com;Password=szitr.com;Persist Security Info=True" providerName="System.Data.SqlClient" />
| 問題 | 原因 | 解決 |
|---|---|---|
| 安裝後 config 未變 | CustomActionData 屬性名拼寫錯誤或缺少反斜線 | 嚴格對照 EditXProperty、[TARGETDIR]\, 用安裝日誌 msiexec /i setup.msi /l*v log.txt 排查 |
| 「存取被拒」無法寫入 config | 安裝目錄需管理員權限,CustomAction 未以管理員執行 | 專案屬性 → 「需要管理員權限」勾選,或將 config 寫入 AppData 目錄 |
| 密碼含特殊字元導致連接失敗 | CustomActionData 傳遞時特殊字元未轉義 | 建議安裝後由程式首次啟動加密存儲,或用 MSI 屬性 SecureCustomProperties 加密傳遞 |
| 64 位系統註冊表重定向 | 自定義動作以 32 位運行,讀寫 64 位註冊表失敗 | 類別庫目標平台設為 x64,或在 Install 方法中用 RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) |
Install 中執行 SqlConnection + CREATE DATABASE 腳本,失敗時拋出 InstallException 觸發回滾。connectionStrings 節點。VS 安裝專案雖非主流,但「零學習成本、整合 IDE、支援自定義 UI 與動作」對中小團隊極具性價比。掌握 對話框屬性 → CustomActionData → Installer.Context.Parameters → XML 修改 Config 這條鏈路,即可解決 90% 桌面應用「安裝時配置」需求。若專案後期需支援自動更新、差分補丁、多語言、權限提升等高階場景,再遷移至 WiX Toolset 或 NSIS 不遲。