專案需自動將自身加入 Windows 防火牆「允許的應用程式」清單。採用呼叫 netsh advfirewall (或舊版 netsh firewall) 指令列工具,封裝為可重用類別,避免直接操作 COM 介面 (INetFwPolicy2) 的複雜性與版本相容問題。
public enum FirewallScope { All, Subnet, Custom }
public enum FirewallMode { Enable, Disable }
public class AllowedProgram {
public string ProgramPath { get; set; } // 程式完整路徑 (必要)
public string Name { get; set; } // 顯示名稱 (選填,預設取檔名)
public FirewallMode Mode { get; set; } = FirewallMode.Enable;
public FirewallScope Scope { get; set; } = FirewallScope.All;
public string CustomAddresses { get; set; } // Scope=Custom 時的 IP/CIDR 清單
// 依賴外部 RunProcess 類別執行指令並捕獲輸出/錯誤
public void AddOrUpdate() {
if (string.IsNullOrEmpty(ProgramPath)) throw new ArgumentException("ProgramPath required");
if (string.IsNullOrEmpty(Name)) Name = Path.GetFileNameWithoutExtension(ProgramPath);
if (Scope == FirewallScope.Custom && string.IsNullOrEmpty(CustomAddresses))
throw new ArgumentException("Custom scope requires addresses");
var cmd = $"advfirewall firewall add rule name=\"{Name}\" program=\"{ProgramPath}\" " +
$"action={(Mode == FirewallMode.Enable ? "allow" : "block")} " +
$"direction=in profile=any";
if (Scope != FirewallScope.All)
cmd += $" remoteip={(Scope == FirewallScope.Subnet ? "localsubnet" : CustomAddresses)}";
var result = RunProcess.Execute("netsh", cmd);
if (!result.Success) throw new Exception($"netsh failed: {result.Error}");
}
public void Remove() {
if (string.IsNullOrEmpty(ProgramPath)) throw new ArgumentException("ProgramPath required");
var cmd = $"advfirewall firewall delete rule name=\"{Name}\" program=\"{ProgramPath}\"";
var result = RunProcess.Execute("netsh", cmd);
if (!result.Success) throw new Exception($"netsh failed: {result.Error}");
}
}
public class ProcessResult {
public bool Success { get; set; }
public string Output { get; set; }
public string Error { get; set; }
public int ExitCode { get; set; }
}
public static class RunProcess {
public static ProcessResult Execute(string fileName, string arguments) {
var psi = new ProcessStartInfo(fileName, arguments) {
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
};
using (var proc = Process.Start(psi)) {
string output = proc.StandardOutput.ReadToEnd();
string error = proc.StandardError.ReadToEnd();
proc.WaitForExit();
return new ProcessResult {
Success = proc.ExitCode == 0,
Output = output,
Error = error,
ExitCode = proc.ExitCode
};
}
}
}
private void btnAllow_Click(object sender, EventArgs e) {
try {
var rule = new AllowedProgram {
ProgramPath = txtPath.Text,
Mode = chkEnable.Checked ? FirewallMode.Enable : FirewallMode.Disable,
Scope = (FirewallScope)cmbScope.SelectedItem,
CustomAddresses = txtAddresses.Text
};
rule.AddOrUpdate();
MessageBox.Show("防火牆規則已套用");
} catch (Exception ex) {
MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnRemove_Click(object sender, EventArgs e) {
try {
new AllowedProgram { ProgramPath = txtPath.Text }.Remove();
MessageBox.Show("規則已移除");
} catch (Exception ex) {
MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
requireAdministrator 或安裝時由 MSI 自訂動作執行。advfirewall;XP/Server 2003 需用 firewall 子指令,參數語法不同。RunProcess 捕獲非零結束碼與 stderr,轉為例外拋出,上層統一處理。FirewallAPI.dll (COM) 直接操作 INetFwPolicy2,功能更豐富 (如啟用/停用規則、列舉現有規則) 但部署較繁瑣。