複数バージョンの Excel を WSH 等のスクリプト内で使い分ける方法の検討

約一年前に Excel 2003 と Excel 2007 を共存させる場合の留意点 なんてエントリを書きました。Office 2003 を残したまま Office 2007 をインストールする方法は下記の通りです。(※前エントリの再掲)

デフォルトのアップグレードをしてしまうと、旧バージョンの Office をすべて削除して上書きされます。インストール済みの Office アプリケーションを残す場合は、「ユーザ設定」→「削除するアプリケーションを指定する」で、削除しない Office アプリケーションを選択してアップグレードします。この設定は、2007 の初回インストール時にのみ有効なので注意が必要です。

複数バージョンをインストールした場合、通常の使い方としてはユーザが明示的にショートカット等で Excel 2003 と Excel 2007 を使い分けていることでしょう。しかしながら WSH 等のスクリプト内では話が別です。

たとえばスクリプト内で Excel を使いたい場合は、

CreateObject("Excel.Application") とか ActiveXObject("Excel.Application")

で Excel インスタンスを生成します。引数として渡しているのは「COM コンポーネント名」なのですが、Office オートメーションの設計上?問題がありバージョン管理ができないため基本的に最後にインストールしたバージョンの Excel のインスタンスが生成されてしまいます。

- スポンサーリンク -

これも前エントリの再掲になりますが、

たとえば、Excel 2000 には Excel.Application というバージョン固有の PROGID と {00024500-0000-0000-C000-000000000046} という CLSID があります。Excel 2000 には、次の COM 関連のレジストリ エントリがあります。
 HKEY_CLASSES_ROOT\Excel.Application\CLSID
 デフォルト値 : {00024500-0000-0000-C000-000000000046}
 HKEY_CLASSES_ROOT\CLSID\{00024500-0000-0000-C000-000000000046}\LocalServer32
 デフォルト値 : C:\PROGRA~1\MICROS~1\Office\EXCEL.EXE /automation
COM は、PROGID から CLSID までのレジストリ キーの値によって Excel の実行可能ファイルのインストール場所を確認し、そのファイルでオートメーションを開始できます。システムに複数バージョンの Office をインストールしているときの Office オートメーションは、一般的にバージョン固有の特定の PROGID を使用して読み込むバージョンを指定できると認識されています。たとえば、
"Excel.Application.9" の場合は Excel 2000、
"Excel.Application.10" の場合は Excel 2002、
"Excel.Application.11" の場合は Office Excel 2003
これは正しくありません。Excel 2000、2002、および 2003 は同一の CLSID を共有しているため、これらの PROGID を使用して読み込まれるバージョンは、単にどのバージョンが最後にインストールされたかということで決まります。


なんて前回書いたことは把握しつつもどうしてもバージョンを分けて起動をする必要が出てきました。さて前置きはこのくらいにしてどのように解決したかのソースを記載します。 excel_2003or207.wsf (右クリックで保存下さい)
<job id="excel_2003or207">
    <script language="JScript">

        var excel;
        var book;
        var ExcelPath
        var Shell = WScript.CreateObject('WScript.Shell');
        var runMode = Shell.Popup("「はい」:Excel2003 「いいえ」:Excel2007", -1, "Excel起動モードの選択", 36);

        // excel2003 or 2007 の起動を分ける裏技。excel = new ActiveXObject('Excel.Application.12'); を使わない
        if(runMode==6){ // excel 2003 を起動
            ExcelPath = Shell.RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Excel.AddIn\\shell\\Open\\command\\");
        }
        else {          // excel 2007 を起動
            ExcelPath = Shell.RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Excel.AddInMacroEnabled\\shell\\Open\\command\\");
        }
        ExcelPath = ExcelPath.toLowerCase();
        ExcelPath = ExcelPath.replace(/^"(.+\\excel\.exe).+$/, "$1");
        excel = Shell.Exec('"' + ExcelPath + '"');
        while(!Shell.AppActivate(excel.ProcessID)){ WScript.Sleep(2000); }  // excel の起動を待ちます

        // 最新の Book名 (=今開いたヤツのハズ)のオブジェクトを取得
        var findflg = false;
        while(!findflg){
            var i = 1;
            try { while(GetObject("Book" + i)) { i++; } }
            catch(e) {}
            i--;
            if(i>=1){
                book = GetObject("Book" + i);
                Shell.AppActivate("Book" + i);
                findflg = true;
            }
            WScript.Sleep(800);
        }

        // テストで何かデータを書いてみる
        var sheet = book.worksheets.add;
        sheet.cells(1,1) = "this is test";

    </script>
</job>

えーっと・・・どうなっているかというと WshShell オブジェクトの Exec メソッドを使って Excel 2003 / Excel 2007 を起動します。Exec メソッドには起動する Excel のフルパス名を指定します。フルパス名はレジストリからよろしく取得します。Office アプリケーションのパスを調べる方法 なんかにフルパス名の取得方法が記載されていますが、この方法はオートメーションに登録されている Office のフルパスになるので他のレジストリキーから Office11 と Office12 が出てくるキーを探して適当に取得してでっち上げています。

ちょっと手抜きで今起動した Excel のオブジェクトを取得するために最新の Book 名を検索する方法をとっています。見つからなければ終了します。Book 名は Excel を起動する毎に通番号で増えていくはずなので最後の Book が今立ち上げたものという”見なし取得”です。このアイデアの元ネタは

を参考にさせて頂きました。この手法で起動までは当然うまく行くのですが、Book の取得で失敗してしまう端末がいくつかでてしまいました。同じ環境でうまく行く端末といかない端末があるので謎です。専門分野でないのであまり深追いする気力がありません・・・。

- スポンサーリンク -