月別アーカイブ: 2015年06月

2 posts

セットアッププロジェクト : 結論

2月から色々やってみましたが、結論を出しました。

私の場合、セットアッププロジェクトに求められる条件は二つです。

一つは、デモ版と製品版を作成して、製品版ではプロダクトキーの入力を促す

二つ目は、ターゲットコンピュータにMyAppの動作に必要なソフトがインストールされているかどうか調べて、もしなければ自動でインストールする

ということです。

しかし考えてみれば、製品登録はインストールの途中で実行しなくてもよい。インストール後のMyAppの中で登録するようにすれば、セットアッププロジェクトにカスタムアクションを組み込むことはない。

アプリケーションの中で製品登録し、その情報をどこかに保存するのは、難しいことではない。それに、そもそもデモ版と製品版を作る必要はない。

登録情報は、レジストリに書き込む、ファイルに書き込む、インターネット経由でサーバーに書き込む等いくらでも方法があります。登録情報を暗号化することも簡単です。

また、ターゲットコンピュータの必須プログラムの存在の確認は、前回書きましたように、VBでもC#でも簡単なプログラムを書けば十分です。

となるとMyAppのセットアッププロジェクトは単純でいいので、手っ取り早くつくればいい。

二つ方法があります。

一つはVS2010のセットアッププロジェクトを使う。もう一つは、[InstallShield Limited Edition]を使うです。

VS2010のセットアッププロジェクトは、至って簡単にmsiファイルを作成できますが、欠点は、VS2010以降のVSでサポートしていないことです。

2015/7/27 修正
VS2015では再び、Microsoftの[Installer Projects]が復活しました。

まだPreviewer版ですので、製品版のことは分かりませんが。VS2010のものより改良されているようです。

[InstallShield Limited Edition]はマイクロソフトのセットアッププロジェクトに代わるもので、最新のVSでも使えるのですが、私のテストではうまくいきませんでした。多分依存ソフトの設定に問題があると思いますが、これ以上の研究はやめにします。

と言う訳で、プロダクトキーの設定はアプリケーションMyApp本体の中で、MyAppのセットアップ・プログラムmsiの作成はVS2010のセットアッププロジェクトで、ターゲットコンピュータの必須ランタイムの調査およびそのインストール、最後にはmsiを起動するプログラムは独自に作成する。
という方式を結論にします。

結局WiXは使わないことになりました。

Bootstrap その後

「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;
        }
    }