「Bootstrapperはもう終わり」と思っていたのですが、WiX Bootstrapperは気持ちの悪い終わり方をしたし、どうしてもこのまま終了にするのが嫌なので、結局やるところまでやることにしました。
まず、マイクロソフトがインストールの方法として、最近はClickOnceを推奨しているので、ClickOnceがどんなものか使ってみることにしました。
簡単なサンプルを作り、ClickOnceの長短をまとめた記事を読んでみました。短所は、細かい指示ができそうにないということで、今後は、改良されてインストール手段のメインになるかもしれませんが、「今は、よしわかった」でClickOnceは終わりにします。
WiX以外のBootoStrapperがあるという情報を得たので、探し出し使ってみました。AutoItです。これを使っていて、はたとわかったことは、「要するに、ターゲットコンピュータに、私の場合Accessがインストールされているかどうか調べて、なければ、所定のURLから用意したAccessRuntimeをダウンロードし、インストールすればいいのだから、このプログラムをVBでもC#でもいいから、自作すればいいだけだ」ということです。
ごく当たり前のことなのに、難しく考えていました。
C#はあまり経験ないので、この際C#で書いてみました。
ターゲットコンピュータのOSのバージョン、32/64ビット版の確認、さらに、Accessがインストールされているかレジストリーをしらべ、なければRunTimeをダウンロード、インストール、それが済んだら自分のセットアップソフト(MyApp.msi)をダウンロードしインストールします。
正味200行もないと思います。
難しいことはありませんが、注意すべき点がいくつかあります。
1. RunTimeの存在を調べるとき、32ビットOSと64ビットOSで調べる場所が異なる点です。
調べるAccessは2つあります。
Access2010でもいいし、Access2013でもいいです。
Access2010のProgIDは[Access.Application.14]、Access2013のProgIDはAccess.Application.15です。
これらがインストールされているかどうかは、レジストリーのLOCAL_MACHINESOFTWAREClassesを探します。
これだけでいいと思いますが、更に念のためここで得られたCLSIDからインストールの状況を調べました。
このとき、32ビットOSではSOFTWAREClassesCLSID、64ビットOSではSOFTWAREWOW6432NodeClassesCLSIDを調べる必要があります。
2. ファイルのダウンロードが完了後に、そのインストールを始めなければいけません。ダウンロードが完了していないのに、インストールを始めては当然いけません。
ダウンロードにはWebClientクラスを使います。
WebClient.DownloadFileメソッドは同期をとっていると思うのですが、作業の終了を確認しないとエラーがでます。
WebClient.IsBusyの間は、次の処理に移行しないようにしました。
これでOKでした。
また、MyAppのインストールの前に、AccessRuntimeのインストール作業を終了しておかなければいけません。
これはProcessを使い、Process.WaitForExit()で同期をとればOKです。
次にコードの一部を示します。
System.Diagnostics.Process p = System.Diagnostics.Process.Start(AccessRuntime);
p.WaitForExit();
private bool AccessRuntimeInstall() { bool res = false; int osbit = System.Environment.Is64BitProcess ? 64 : 32; RegistryKey BaseKey = null; RegistryKey Key = null; // Access.Application.14 Access2010 BaseKey = Registry.LocalMachine; Key = BaseKey.OpenSubKey("SOFTWARE\Classes\Access.Application.14\CLSID"); bool SearchKeyRes = SearchKey(BaseKey, Key, osbit); if (SearchKeyRes == false) { // Access.Application.15 Access2013 Key = BaseKey.OpenSubKey("SOFTWARE\Classes\Access.Application.15\CLSID"); SearchKeyRes = SearchKey(BaseKey, Key, osbit); } if (SearchKeyRes == true) { res = true; return res; } else { String Access = "AccessRunTime.exe"; try { WebClient client = new WebClient(); if (osbit == 32) { client.DownloadFile("http://www.aa.bb/cc/AccessRuntime_x86_ja-jp.exe", Access); } else { client.DownloadFile("http://www.aa.bb/cc/AccessRuntime_x64_ja-jp.exe", Access); } while (client.IsBusy == true) { System.Threading.Thread.Sleep(1000); } Using (System.Diagnostics.Process p = System.Diagnostics.Process.Start(Access)) { p.WaitForExit(); } res = true; } catch (Exception ex) { Console.WriteLine(ex.Message); res = false; } finally { System.IO.File.Delete(Access); } } return res; } private bool SearchKey(RegistryKey BaseKey, RegistryKey Key, int osbit) { RegistryKey CLSKey; if (Key != null) { String CLSID = (String)Key.GetValue(""); if (osbit == 32) { CLSKey = BaseKey.OpenSubKey("SOFTWARE\Classes\CLSID\" + CLSID); } else { // HKEY_LOCAL_MACHINESoftwareWOW6432NodeClassesCLSID で CLSID をキーとして を探す CLSKey = BaseKey.OpenSubKey("SOFTWARE\WOW6432Node\Classes\CLSID\" + CLSID); } if (CLSKey != null) { return true; } } return false; } }