前回、旧VB ( Visual Basic 6.0 ) で作成したアプリを、Visual Studio 2010 で作成したインストーラと統合する方法を紹介しました。この準備が整っている前提で、旧VB に .net Framework で機能を追加していきます。前回同様、Light Cutter 5 を例として取り上げます。
旧VBに機能を追加するには、COMクラスを作成します。
今回は .net Framework 4.0 で作成した COM クラスを、Visual Basic 6.0 から利用することで、最新技術を使って旧VBアプリに機能追加が出来てしまう!という感じです。
目次
Visual Studio 2010 で COM クラスを作成
まず、Visual Studio 2010 で新しいクラスライブラリを作成します。バージョンは何でも良いのですが、今回は .net Framework 4.0 を選択しました。(前回最後に少し書いたとおり、VS2012が使えないので 4.5 が難しく、今回は Windows 8 対応のアップデートを意識しているので、最新の 4.0 が良いかな、と思いました)
プロジェクトを作成したら、新しい項目の追加で COM クラスを選びます。
ここでビルドするとエラーと警告が出るはずです。
エラーは、COMクラスをテストできるようにシステムに登録するには、Visual Studio を「管理者権限」で起動するように、ということです。ビルド時にCOMクラスを登録してくれるようになっているので、簡単にテストができます。そのために管理者権限で起動しましょう。
警告は、一つもパブリックメソッドが無いのは、COMクラスとしてシステムに登録できないよ、と言ってます。これは後でメソッドを追加したら出なくなります。
COMクラスを追加すると、プロジェクトのプロパティで「COM相互運用機能の登録」というチェックがONになります。また、追加したクラスの先頭にアウトライン(#Region)でCOMクラスに必要な宣言が挿入されます。これらは変更しないように注意します。
もう一つ、準備で必要なのが、構成マネージャーで、ビルドのプラットフォームを AnyCPU から x86 に変更しておきます。今回作成する COM は x86 からしか使用しないので、不要なのかもしれませんが、変更しておきます。
準備が出来たら、いよいよメインのメソッドを追加します。今回は、非常にシンプルに、「クリップボードの画像をデスクトップに PNG 画像として保存する」メソッド SaveToDesktop を追加しました。
Light Cutter 5 で画面を切り取った後、クリップボードに画像がコピーされます。これまでのバージョンではその後、何もしないか、特定のアプリを起動する事が選択できました。今回はここで、さらに、画像をデスクトップに保存するのを選択できるようにしようと思います。(本当はもっと追加したい機能があるのですが、それは説明がわかりづらくなるので、今回はブログ用にこの機能を追加することにしました)
COMクラスのテスト
メソッドが出来たらテストします。
もちろん Visual Studio 標準の「単体テスト」でテストして問題ありません。が、それだけでは足りません。今回は、ちゃんと COM クラスとして作成できているか?確認をします。この作業を怠って先に進むと、あとで問題が合ったときに、どこが原因でちゃんと動いていないのか?非常に悩むことになります。一つずつちゃんと進むために、この手順を必ずやっておきましょう。
まず、テストするメソッド用の VBScript ファイルを追加します。
ソリューションエクスプローラから新しいテキストファイルを追加し、クラス名.Test.vbs の様な名前をつけます。
追加できたら、ファイルメニューの「保存オプションの詳細設定」で シフトJIS として保存します。
VBScript の内容は、先ほど作った COM クラスを CreateObject でメモリーにロードして、テストしたい SaveToDesktop メソッドを実行するだけ、となっています。
Dim a : Set a = CreateObject("Namespace.ClassName")
Call a.MethodName()
VBScript は VB6 とほぼ同じ文法なので、オブジェクトを変数に設定するときは Set を忘れずに書きます。また、VBScript は VB6 と異なり、型の宣言が出来ないので、Dim の変数の後ろに As は書けません。私は習慣的にこの2行を : (コロン)でつないで、Dim と Set をセットで書くようにしています。
またメソッドの呼び出しは Call を書きます(無くても大丈夫です)。
次に、Visual Studio 2010 を使う開発環境が x64 環境の場合は、必ず、次のバッチファイルを追加します。さっきと同様に、(クラス名).Test.bat のような名前でテキストファイルを追加して、シフトJISで保存します。
こちらの内容は C:\Windows\SysWow64\wscript.exe に、さっきの VBS を実行しろ、という処理を書いています。その後結果が見えるように pause をかけていて、最初に @echo off を書いて画面を見やすくしています。
@echo off
C:\Windows\SysWow64\wscript.exe ClassName.Test.vbs
pause
x64 環境では、VBScript ファイルをダブルクリックすると、なんと、x64 用の Windows Scripting Host (って名前だったかな?)が動いて、COM クラスのロード、つまり CreateObject が出来ません。CreateObject が出来るのは x86 環境のみなのです。
ということで、x86 環境では VBS ファイルをダブルクリックすればテストできますが、x64 環境では、x86 用のスクリプトホストにVBSを実行させる、というバッチファイルを用意して、それでテストを実行します。
最終的なプロジェクト構成はこんな感じになりました。
では早速、バッチファイルを実行して、作成したCOMクラスをテストしてみます。今回は画像をクリップボードにコピーしてからテストする必要があるので、既存の LightCutter で画面を切り取ってからバッチファイルを実行しました。無事、デスクトップにファイルが保存されました。
ついでに、クリップボードに画像をコピーしないで実行したとき(テキストをコピーしたときなど)はどうなるか?エラーケースも確認しておきます。私の今回の実装はエラーを握りつぶさないので例外がそのままスローされます。バッチファイルから実行してもそのエラーがそのまま表示されました。これを確認しておくと、後で、旧VBとの連携がうまく行かないときも手がかりになります。
旧VB開発環境テスト用にCOMクラスのインストーラを作成
ここまで出来たら、Visual Basic 6.0 用の開発環境に、COMクラスをインストールするためのインストーラを作成します。先ほどの Visual Studio 2010 で作成しているときはビルド時にシステムに COM クラスが登録されたのでちゃんと動作しました。しかし、ただ DLL をコピーしただけではこの COM クラスは動作しません。なので、旧VBの開発環境に持って行くには、ちゃんとインストーラを作る必要があります。
※コマンドでもインストールできますが、何度もテストするのが面倒で、また、時間が空いてコマンドを忘れてしまうとさらに面倒なので、インストーラを作っておくのがお奨めです。
ここで作成するインストーラは、前回作成した統合インストーラではなく、COMクラスを旧VB環境に配置するためだけの専用インストーラなので、別に作ります。そうすることで、実際は、統合インストーラが完成していない時でも、旧VB環境に .net で開発したDLLを持ち込んでテストすることが出来ます。
新しいプロジェクトを追加します。「セットアップウィザード」を選ぶと簡単です。
作ったクラスライブラリのプライマリ出力を追加します。
構成はこんな感じに。
ビルドして出来た msi ファイルを、旧VB環境にコピーします。
msi ファイルと一緒に、テスト用の VBScript ファイルもコピーしておきます。
インストールが出来たら VBScript ファイルを実行して、実際に動作していることを確認します。この手順が非常に大事です。
Visual Basic 6.0 アプリから .net COMクラスを利用する
ここから旧VBのコードを改修します。
Light Cutter 5 ではオプションでこんな感じに画面を切り取った後の動作が指定できます。そこに「デスクトップに PNG 画像を保存する」を追加します。懐かしのコントロール配列ですね(同じ名前のコントロールを複数配置すると、配列として扱えるという機能が旧VBにはありました)。
さらに、目的の箇所にコードを追加します。
このコードはさっきの VBScript のテストコードと、ほぼ、同じ物です。
違うのは、Dim の宣言で変数の後に As Object が定義されていることだけ。実は、VB6 では As 以降を省略すると Variant 型で初期化されます。Variant 型は Object 型と異なり、(むしろ .net の Object に近いかな?)基本データ型でもクラスでも何でも格納できてしまいます。しかし、その反面動作が As Object で書くより1万倍遅かったような・・・(VB4の頃の資料の記憶なので正確ではない気がしますが)。なので、念のため、 As Object を書いておきます。VBScript は全て Variant 型で動作しているのでこの心配はありません。ていうか、VBScript が遅くなければ、VB6でも大丈夫なんですけど~・・・
では、VBの F5 で動作を確認します。exe を作成して確認してもOKです。ちゃんと動作することが確認できたら、旧VBでの改修は完了です。前回同様の手順で、Visual Studio Installer 1.1 でセットアップモジュールを作成して、ソース管理に登録しておきます。
統合インストーラの作成
前回同様、ソース管理経由で Visual Studio 2010 環境に、旧VBアプリのマージモジュールを持ち込んだら、今度はテスト用ではなく、アプリ全体のインストーラを作成します。
統合インストーラのソリューションを開いて、今日作った COM クラスのプロジェクトをソリューションに追加します。
ソリューションに追加できたら、今度はインストーラのプロジェクトに COM クラスの「プロジェクト出力」を追加します。
プライマリ出力を追加します。
追加するとこんな感じになります。インストーラのプロジェクトに、COMクラスのプロジェクトのプライマリ出力と、旧VBアプリのマージモジュール(msmファイル)が一緒に追加されます。
依存関係も .net Framework 4.0 が追加されます。セットアップの「起動条件」とプロジェクトのプロパティで「必須コンポーネント」もそれぞれ、.net 4.0 に変更しておきましょう。
・・・
一緒に追加される依存関係の tlb ファイルは、Visual Basic 6.0 や Office のマクロ(Visual Basic for Application; VBA)からCOMクラスを「参照設定」することが出来るようにするためのファイルです。これがあれば、なんと、CreateObject しなくてもちゃんと参照設定出来るのですね。
しかし、私は参照設定はお奨めしていません。というのも、CreateObject は失敗するとその時点でエラーになりますが、参照設定は全然違うところでエラーになる可能性があります(型を宣言しているとそれがロードされる最初のタイミングなのかな?)。これが非常につらくて、何でうまくいっていないのか?エラー処理をするのが難しい。今回は、COM クラスを使用する時に On Error Resume Next でエラーをただ握りつぶしているだけですが、これをちゃんと処理するようにするには、参照設定よりも、CreateObject の方が楽というのがあります。
後もう一点、CreateObject すると型情報が参照できないので、Visaul Basic 6 でコードを書くのが非常に面倒ですよね。それがイイんです。
なまじ書けてしまうと、どんどん旧VBのコードが増えてしまう。旧VB側で処理をしたくなってしまいます。いやいやいやいや。「ラストスパートVB6」なのですよ?それはやってはダメでしょう。あくまで、旧VBのアプリをそのまま引き継いで、どうしても機能追加したいところだけ、新 .net でCOMクラスを作ってしまおう、というのが今回の作戦です。なので、参照設定できなくて良いんです。
ちなみに tlb ファイルはインストールから除外しても大丈夫です(ちゃんとCOM登録されます)。そのままでも悪さはしないので(他人から簡単に使えてしまう、以外は)、どちらでも大丈夫です。
・・・
これで統合インストーラも完成です。早速インストールして動作を確認します。
おぉ。ちゃんと動作しますね。よかった。
こんな感じで、なるべく旧VB部分を変更しないで機能を追加していくことが出来ます。.net Framework は Windows 8 なら 4.5、Windows 7 なら 3.5、Vista なら 3.0 が標準でインストールされています。お客様の要件が許せば、気兼ねなく機能を追加できるので、是非試してみてください。
次回は、まだ考えてあるネタのどれを書くか?決めていませんが、まだ連載は続きます。
Visual Basic 6.0 でマウスホイール
おまけです。第1回のセットアップの時に忘れました。
このページからダウンロードできる ZIP ファイルをどこかに展開し、その内容を VB6 をインストールしたフォルダの隣とかに置きます。regファイルをダブルクリックで実行してレジストリ登録したら、VB6 を起動して、アドオンを自動的にロードするように設定します。
これをやらないと、マウスホイールが動作しません。
Visual Basic 6.0 が発売された当時のマウスホイールは現在とは別の機構で動作していたらしく、途中で動作しなくなり、このファイルが公開されるようになりました。