はじめに
ClickOnceに変わる新しいアプリケーション配布の仕組みとして、MSIXがあります。
既存の.NET Frameworkで作成したアプリケーションでも、Windows アプリケーション パッケージ プロジェクト
をソリューションに追加するだけで、容易にMSIXでパッケージ化することができます。
(具体的なやり方はDocsを参照)
https://docs.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-packaging-dot-net
今回は、MSIXでパッケージングした結果、レジストリアクセスにハマってしまった事例について紹介します。
MSIXでパッケージ化したらレジストリが他のアプリと共有できなくなった
既存のアプリでレジストリに書き込みを行ない、他のアプリで同じキーを読み取るケースを想定して下さい。
例えば、以下の2つのアプリがあるとします。
アプリA・・・今回MSIXでパッケージ化したアプリ
アプリB・・・その他のアプリ
以下の流れで同じレジストリキーの値に対して読み書きを行ないます。
- レジストリキー
HKCU\SOFTWARE\HOGE
のVAL
値に「0」が設定されているとする - アプリAで
VAL
値を読み込む - アプリBで
VAL
値を読み込む - アプリAで
VAL
値を**「1」に書き替える** - アプリAで
VAL
値を読み込む - アプリBで
VAL
値を読み込む
単体のEXEとして動作させた場合、
2.・・・「0」
3.・・・「0」
5.・・・「1」
6.・・・「1」
という結果になります。当然の結果です。
ところが、この アプリA をMSIXでパッケージングして配布・動作させると、
2.・・・「0」
3.・・・「0」
5.・・・「1」
6.・・・ 「0」
という結果になります。
4.でアプリAが書き換えた「1」の値が、アプリBから読み取れていません。
MSIXでパッケージ化されたアプリからのレジストリ アクセスは仮想化される
MSに問い合わせた結果、
MSIXでパッケージ化されたアプリからのレジストリ アクセスは仮想化されており、システムや他のアプリケーションから分離された【ユーザーごと・アプリごとのプライベートな場所】にリダイレクトされる。
そのため、パッケージ化されたアプリケーションから直接他のアプリケーションで読み書きされるレジストリに書き込むことはできない。
ということでした。以下に詳細な説明があります。
https://docs.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-behind-the-scenes#registry
即ち、MSIXでパッケージ化したアプリからHKLM\Software
やHKCU
以下への書き込みを行なうと、インストール後初回の書き込み時に【ユーザーごと・アプリごとのプライベートな場所】にコピーされ、それ以降はその場所に対する読み書きとなる、という事です。
上記の例で言うと、
4.アプリAで上記のレジストリキーの値を「1」に書き替える
の時点で、書き込みを行なったレジストリキーの値がアプリA専用のプライベートな場所にコピーされ、そちらに対する書き込み・読み込みとなっていたため、他のアプリBから読み取った値と異なってしまった、という事です。
(MSの回答では、HKCU全体ではなく、書き込みを行なった「値」単位でコピーされるとのこと)
なお、値の読み込みに関しては、システムレジストリの値と、【ユーザーごと・アプリごとのプライベートな場所】の値がマージされて読み取られるそうです。
回避策
.NETのコードでレジストリの書き込みを行なうと上記の現象になりますが、reg.exeを起動して別プロセスから値を書き込むと、システムレジストリに対する書き込みとなる事がわかりました。
元々、C#のRegistryKey.SetValue
メソッドで書き込みを行なっていましたが、以下のようにプロセスを起動する事で、【ユーザーごと・アプリごとのプライベートな場所】が作られることなく、レジストリへの書き込みが行なえました。
var argument = @"ADD ""HKEY_CURRENT_USER\SOFTWARE\HOGE"" /v ""VAL"" /t REG_DWORD /d ""1"" /f";
Process.Start("reg.exe", argument);
なお、読み込みに関しては、書き込みを全てreg.exeで行なう前提であれば、RegistryKey.GetValue
メソッドで行なっても問題ありません。
(【ユーザーごと・アプリごとのプライベートな場所】ができていない、という前提)
注意点
既にMSIXで配布し、レジストリへの書き込みを行なってしまい、【ユーザーごと・アプリごとのプライベートな場所】が作成されてしまったものについては、MSIXアプリをアンインストールするしかこれを削除する方法はありません。