はじめに
弊社が運営している ArcGIS Blog で 紹介した「ArcGIS Desktop 楽々インストール ~バッチ ファイルの作成方法をご紹介~」、「ArcGIS Desktop 10.6, Pro 2.1 楽々インストール – バッチ ファイルを追加」では、ArcGIS Desktop, Engine, Pro 用のバッチファイルの作り方を紹介するとともに、サンプルのバッチファイルを GitHub 上で公開しています。
上記の記事中では触れませんでしたが、実は [Unistall_Batchfiles] の階層下にアンインストール用のサンプル バッチファイル、[Ps_Scripts] の階層下にメンテナンス用の PowerShell スクリプトも密かに公開しています。
本記事では、上記のメンテナンスで使用している PowerShell のスクリプトのように、MSI や MSP のアンインストール用コマンドを取得する C# のコードとツールをご紹介します。
通常、MSI や MSP のファイル内の情報へアクセスするには、Windows SDK に含まれる orca をインストールして利用することが多いと思いますが、本記事のようにツールを作成しておけば、msi や msp のアンインストール コマンドを、orca なしで作成することができます。
MSIファイルの アンインストールコマンド
Windows Installer(*.msi)のアンインストールのコマンドは次の通りです。
msiexec.exe /x <xyz\xyz.msi>
もしくは msiexec.exe /x <ProductGUID>
|
ここで必要になのは msi ファイルか、product-code の GUID です。msi ファイルをインストール終了後に、ファイルを保持しておかなくてもよいという点において、GUID で指定するコマンドの方が汎用的に利用できます。
MSI の GUID の値については、「orca」を使ってアクセスした場合には、 *.msiを開き、[Tables] > [Property] > [ProductCode]の値を確認することが可能です。
一方で、C# でアクセスする方法は、次のようなコードになります。
<SPAN class="comment token">///</SPAN>
<SPAN class="comment token">/// 呼び出し側で、Unistallのコマンド取得するには、propertyの引数に"ProductCode"を指定。</SPAN>
<SPAN class="comment token">///</SPAN>
<SPAN class="comment token">/// <summary></SPAN>
<SPAN class="comment token">/// Msi ファイルからアンインストール時に必要な情報を取得するメソッド</SPAN>
<SPAN class="comment token">/// </summary></SPAN>
<SPAN class="comment token">/// <param name="msiFile"></param></SPAN>
<SPAN class="comment token">/// <param name="property"></param></SPAN>
<SPAN class="comment token">/// <returns></returns></SPAN>
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">static</SPAN> <SPAN class="keyword token">string</SPAN> <SPAN class="token function">GetMsiProperty</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">string</SPAN> msiFile<SPAN class="punctuation token">,</SPAN> <SPAN class="keyword token">string</SPAN> property<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">string</SPAN> retVal <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">string</SPAN><SPAN class="punctuation token">.</SPAN>Empty<SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// インストーラのインスタンスを作成</SPAN>
Type classType <SPAN class="operator token">=</SPAN> Type<SPAN class="punctuation token">.</SPAN><SPAN class="token function">GetTypeFromProgID</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"WindowsInstaller.Installer"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
Object installerObj <SPAN class="operator token">=</SPAN> Activator<SPAN class="punctuation token">.</SPAN><SPAN class="token function">CreateInstance</SPAN><SPAN class="punctuation token">(</SPAN>classType<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
Installer installer <SPAN class="operator token">=</SPAN> installerObj <SPAN class="keyword token">as</SPAN> Installer<SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// 読み込みのために msi ファイルをオープン</SPAN>
<SPAN class="comment token">// OpneDatabase method</SPAN>
<SPAN class="comment token">// 0 - Read, 1 - Read/Write</SPAN>
Database database <SPAN class="operator token">=</SPAN> installer<SPAN class="punctuation token">.</SPAN><SPAN class="token function">OpenDatabase</SPAN><SPAN class="punctuation token">(</SPAN>msiFile<SPAN class="punctuation token">,</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// SQL文でプロパティをクエリして取得</SPAN>
<SPAN class="comment token">// OpneView method</SPAN>
<SPAN class="keyword token">string</SPAN> sql <SPAN class="operator token">=</SPAN> String<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Format</SPAN><SPAN class="punctuation token">(</SPAN>
<SPAN class="string token">"SELECT Value FROM Property WHERE Property ='{0}'"</SPAN><SPAN class="punctuation token">,</SPAN> property<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
WindowsInstaller<SPAN class="punctuation token">.</SPAN>View view <SPAN class="operator token">=</SPAN> database<SPAN class="punctuation token">.</SPAN><SPAN class="token function">OpenView</SPAN><SPAN class="punctuation token">(</SPAN>sql<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// Execute method and Fetch method</SPAN>
view<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Execute</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">null</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// 取得したレコードの読み込み</SPAN>
Record record <SPAN class="operator token">=</SPAN> view<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Fetch</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// StringData property</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN>record <SPAN class="operator token">!=</SPAN> <SPAN class="keyword token">null</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token">// プロパティの値を返す</SPAN>
<SPAN class="comment token">//retVal = string.Format("{0}: {1}", property, record.get_StringData(1));</SPAN>
<SPAN class="comment token">// アンインストール用のコマンドの形式で返す</SPAN>
<SPAN class="comment token">// mxiexec /X <PruductGUID></SPAN>
retVal <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">string</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">Format</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"mxiexec /X '{0}'"</SPAN><SPAN class="punctuation token">,</SPAN> record<SPAN class="punctuation token">.</SPAN><SPAN class="token function">get_StringData</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
retVal <SPAN class="operator token">=</SPAN> retVal<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Replace</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"'"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"\""</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> retVal<SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN> MSP ファイルの アンインストールコマンド
Windows Installer Patch(*.msp)のアンインストールのコマンドは次の通りです。
msiexec.exe /I <ProductGUID> MSIPATCHREMOVE=<PatchGUID>
msp の GUID の値は、「orca」を使ってアクセスした場合は、 *.mspを開き、[View]メニュー > [Summary Information] の画面の [Patch Code]=<PatchGUID>、[Targets]=<ProductGUID> の値を指定します。
一方で、C# でアクセスする場合は次のようなコードになります。
<SPAN class="comment token">/// <summary></SPAN>
<SPAN class="comment token">/// MSP ファイルからアンインストール時に必要な情報を取得するメソッド</SPAN>
<SPAN class="comment token">/// </summary></SPAN>
<SPAN class="comment token">/// <param name="mspFile"></param></SPAN>
<SPAN class="comment token">/// <returns></returns></SPAN>
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">static</SPAN> <SPAN class="keyword token">string</SPAN> <SPAN class="token function">GetMspSummaryInfo</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">string</SPAN> mspFile<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">string</SPAN> retVal <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">string</SPAN><SPAN class="punctuation token">.</SPAN>Empty<SPAN class="punctuation token">;</SPAN>
Type classType <SPAN class="operator token">=</SPAN> Type<SPAN class="punctuation token">.</SPAN><SPAN class="token function">GetTypeFromProgID</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"WindowsInstaller.Installer"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
Object installerObj <SPAN class="operator token">=</SPAN> Activator<SPAN class="punctuation token">.</SPAN><SPAN class="token function">CreateInstance</SPAN><SPAN class="punctuation token">(</SPAN>classType<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
Installer installer <SPAN class="operator token">=</SPAN> installerObj <SPAN class="keyword token">as</SPAN> Installer<SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// 読み込みのために msp ファイルをオープン</SPAN>
<SPAN class="comment token">// OpneDatabase method</SPAN>
<SPAN class="comment token">// 32 - msiOpenDatabaseModePatchFile</SPAN>
Database database <SPAN class="operator token">=</SPAN> installer<SPAN class="punctuation token">.</SPAN><SPAN class="token function">OpenDatabase</SPAN><SPAN class="punctuation token">(</SPAN>mspFile<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">32</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// orca での 「Patch Summary Information」画面</SPAN>
<SPAN class="comment token">//</SPAN>
<SPAN class="comment token">// Summary Property IDsの解説:</SPAN>
<SPAN class="comment token"><SPAN>// </SPAN><A class="jive-link-external-small" href="https://community.esri.com/external-link.jspa?url=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fwindows%2Fdesktop%2FMsi%2Fsummaryinfo-summaryinfo" target="_blank">https://docs.microsoft.com/en-us/windows/desktop/Msi/summaryinfo-summaryinfo</A></SPAN>
<SPAN class="comment token">//</SPAN>
<SPAN class="comment token">// プロパティの値を返す</SPAN>
<SPAN class="comment token">//int propCnt = database.SummaryInformation.PropertyCount;</SPAN>
<SPAN class="comment token">//retVal = string.Format("Targets: {0}", database.SummaryInformation.Property[7]); // [7]は Targets:</SPAN>
<SPAN class="comment token">//retVal += " , " + string.Format("Patch Code: {0}", database.SummaryInformation.Property[9]); // [9]は Patch Code:</SPAN>
<SPAN class="comment token">// アンインストール用のコマンドの形式で返す</SPAN>
<SPAN class="comment token">// msiexec / I < ProductGUID > MSIPATCHREMOVE =< PatchGUID ></SPAN>
retVal <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">string</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">Format</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"msiexec /I '{0}' MSIPATCHREMOVE='{1}'"</SPAN><SPAN class="punctuation token">,</SPAN> database<SPAN class="punctuation token">.</SPAN>SummaryInformation<SPAN class="punctuation token">.</SPAN>Property<SPAN class="punctuation token">[</SPAN><SPAN class="number token">7</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> database<SPAN class="punctuation token">.</SPAN>SummaryInformation<SPAN class="punctuation token">.</SPAN>Property<SPAN class="punctuation token">[</SPAN><SPAN class="number token">9</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
retVal <SPAN class="operator token">=</SPAN> retVal<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Replace</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"'"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"\""</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> retVal<SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN> 完成形
上記のような msi および msp のアンインストール用のコマンドの取得を、GUI 画面から操作可能なようにした実行ファイル、および ソースコードは GitHub 上で公開しています。
orca なしでアンインストールのコマンドを取得したい場合などにご利用ください。

参考サイト
なお、PowerShell および C# での msi や msp にアクセスする方法は、以下のようなサイトを参考にさせてもらいました。
参考1 : Enumerating installed MSI products with PowerShell and msi.dll
https://marcinotorowski.com/2018/03/04/enumerating-installed-msi-products-with-powershell/
参考2 : [VB.NET] MSI Manipulation
https://www.neowin.net/forum/topic/968772-vbnet-msi-manipulation/
参考3 : Read Properties from an MSI File
http://www.alteridem.net/2008/05/20/read-properties-from-an-msi-file/
参考4 : 1で COM Microsoft Windows Installer Object Library を参照しようとした時のエラー回避方法
http://www.reinholdt.me/2015/07/29/problem-adding-a-c-reference-to-windowsinstaller-com-object/
%WINDIR%\system32\msi.dll をかわりに追加するとよい
参考5 : Windows SDK Components for Windows Installer Developers に含まれる View Installer Script の解説
https://docs.microsoft.com/en-us/windows/desktop/Msi/view-installer-script