【VB.NET】Windows Formで設定値をDebugとReleaseで切り替えたい

タイトル通りのことをやりたかったけど、思ったより苦労したのでそのメモ。
Visual Studio 2017

# 失敗：ASP.NETのApp.configを切り替える機能を使う
ASP.NETではビルド構成に合わせてApp.configを切り替える機能があるが、Windows Formにはない。
検索すると、WindowsアプリでもASP.NETの機能を流用してconfigファイルを置き換える手段が見つかる。

[App.configをビルドターゲットによって書き換える - Qiita](https://qiita.com/m2tmk/items/c24e4d0eb30d820dd7b5)

しかし、実際にやってみてもビルド後のconfigファイルが置き換わらない。
```<appSettings>```タグも「設定」関連のどちらも置き換わらず。

Settingsファイルを2つ作り、ビルド時に構成によって切り替えようとしたけど、それもダメだった。

```xml
<None Include="Settings.Debug.settings" Condition=" '$(Configuration)' == 'Debug' ">
  <Generator>SettingsSingleFileGenerator</Generator>
</None>
<None Include="Settings.Release.settings" Condition=" '$(Configuration)' == 'Release' ">
  <Generator>SettingsSingleFileGenerator</Generator>
</None>
```

condition属性が有効にならないし、ファイル名＝クラス名になってしまうので置換になっていない。

プロジェクト直下にDebugとReleaseのフォルダを作成し、同じSettings1.settingsというファイルを作ってみたところ、お互いが競合してapp.configとsettingsファイルを上書きしあってしまう。

# ビルド後スクリプトでconfigファイルの内容を置き換える
Visual Studioには、ビルド前、ビルド後に実行させたいことを指定できる。
[方法: ビルド イベントを指定する (Visual Basic) - Visual Studio | Microsoft Docs](https://docs.microsoft.com/ja-jp/visualstudio/ide/how-to-specify-build-events-visual-basic?view=vs-2019)

これを使い、ビルド後にconfigファイルを置き換える処理を自作した。

## configファイルを用意する
オリジナルのApp.configと構成毎のconfigファイルを用意する。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/4f44a0e8-1e1c-d1c5-464e-40957e0eac9e.png)
3つ並ぶのが鬱陶しいなら、vbprojファイルをいじって、以下のようにできる。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/50a2ab9a-9029-730f-f59a-0f4581b6f21e.png)

```xml:vbprojファイル抜粋
<Content Include="App.config">
  <SubType>Designer</SubType>
</Content>
<Content Include="App.Debug.config">
  <DependentUpon>App.config</DependentUpon>
  <SubType>Designer</SubType>
</Content>
<Content Include="App.Release.config" >
  <DependentUpon>App.config</DependentUpon>
  <SubType>Designer</SubType>
</Content>
```


## PowerShellでconfigファイルの内容を上書きする
.NETにはconfigファイルを読み書きするためのクラス等が用意されているが、「設定」の部分は扱えないようなので、普通のXMLファイルとして読み書きする。

↓```appSettings```タグしか使用しないなら有用な方法。
[PowerShellでアプリケーション構成ファイル(app.config)を読み書きする - しばたテックブログ](https://blog.shibata.tech/entry/2014/11/04/235639)

App.configをXMLファイルとして読み取り、以下で囲んだ部分を読み取って置き換える。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/4c11b8ec-a43d-0f7f-4d07-7ad2b6569b90.png)

PowerShellに渡すパラメータとして、以下が必要：

- オリジナルのApp.configのファイルパス
- 置き換えたい内容が含まれているApp.configのファイルパス(App.Debug.configなど)
- プロジェクト名 (MySettingsが定義されているタグ名にプロジェクト名が入っている)

```powershell:サンプルスクリプト(一部)
Param($targetAppConfigPath, $sourceAppConfigPath, $outputPath, $projectName)

Add-Type -AssemblyName System.Configuration

if (!(Test-Path $targetAppConfigPath) -or !(Test-Path $sourceAppConfigPath)) {
    return
}

# ファイル読込
[xml]$targetAppConfig = Get-Content $targetAppConfigPath -Encoding UTF8
[xml]$sourceAppConfig = Get-Content $sourceAppConfigPath -Encoding UTF8

function main() {

    copySettings "configuration/applicationSettings/$projectName.My.MySettings"
    copySettings "configuration/userSettings/$projectName.My.MySettings"
    copyConnectionStrings "configuration/connectionStrings"
    copyAppConfigs "configuration/appSettings"

    $targetAppConfig.Save($outputPath)
}
```

## PowerShellをプロジェクトに配置する
プロジェクト内にps1ファイルを置く。サンプルでは「BuildScripts」フォルダに置いた。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/27fec0c0-7525-f180-24e8-da06ac8e5490.png)

## PowerShellをビルド後に実行する
プロジェクトのプロパティを開き、「コンパイル」タブの「ビルドイベント」を押す。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424975/16ef0168-9879-37ba-e8f6-65433bbb7a24.png)


以下のコマンドを、ビルド後のコマンドに追加する。

```
powershell -NoProfile -ExecutionPolicy Unrestricted -file "$(ProjectDir)BuildScripts\ReplaceSettings.ps1" "$(ProjectDir)App.config" "$(ProjectDir)App.$(ConfigurationName).config" "$(TargetDir)$(TargetFileName).config" $(ProjectName)
```

通常、PowerShellを呼び出すときに```-file```は無くても良いはずだが、ここでは付けないとエラーになる。

これで、出力フォルダには構成毎の設定値が出力されるようになる。

このスクリプトを使ったサンプルをGitHubに保管した。
https://github.com/vicugna-pacos/vbnet_separate_config