Visual Basic 4.0
Entreprise Edition
リモートオートメーションで見えてきたCairoの分散オブジェクト環境
酒井 法雄
●Enterprise Editonの機能
Visual Basic 4.0には、従来のStandardおよびProfessional
Editionに加えて、 Enterprise Editonがある。このEnterprise Editonは、ネットワーク環境を積極的に利用するための仕組みが含まれているものだと言える。具体的には、次の3つの機能だと考えてよいだろう。
1. リモートオートメーション
OLE Automationを異なるマシン間でネットワークを介して行うための仕組み、および実現のためのツール。
2. リモートデータオブジェクト(RDO)
Visual Basic 3.0やAccessで遅いと不評だったDAO(データアクセスオブジェクト)を使ったクライアント/サーバー型データベースへのアクセスに代わり、リモートデータ専用のオブジェクトであるRDO(リモートデータオブジェクト)が追加された。
3. Visual SourceSafe
複数プログラマーでの開発時にサーバーにソースを配置して管理するためのVisual SourceSafeが追加された(図 )
これらの機能のいずれもが、ネットワーク環境を前提にしたものである。いずれも昨今のダウンサイジングやWindows 95にともなうクライアント/サーバー環境の普及を考えると、きわめて重要なトピックである。
●リモートオートメーションとネットワークOLE
Enterprise Editionの機能から、今回はリモートオートメーションについて述べることにしよう。
●VisualBasic
4.0 Enterprise Editoon のフォルダー
図はEnterprise Editonのグループである。ここには、Professional Editionには含まれていないツールがあることがわかる。これらのツールは、主にリモートオートメーションのためのツールである。
では、いったいリモートオートメーションとはどういうものだろうか。
現在のOLE Automationは、OLEサーバーと呼ばれる他のアプリケーションをOLEコンテナと呼ばれるアプリケーションからVisual Basicの言語仕様で制御するものだ。この機能をうまく使ってカスタムコントロールとしたのかOCXと呼ばれるOLEベースのカスタムコントロールである。このように、OLE AutomationはOLEを語る上で非常に重要なトピックである。WordにExcelを貼り付けるなどというものは、OLEのひとつの側面でしかないのだ。
そのOLE自体も、今後さらに変貌していくという。というのも、MicrosoftはOLEの機能を次期主力OSであるCairo(Windows NT 4.0)に取り入れようとしているからだ。この時点で、OLEは完全にOSと一体化し、Windows NTはオブジェクト指向OSに変貌するという。例によってオブジェクト指向OSという抽象的な表現が出てきてしまった。これは、OSの持つ機能がすべてオブジェクト化されていて、それをVisual Basicの言語仕様でOLE Automationで使うことができるということだ。現在のWindows NTでも多分にオブジェクトという言葉は使われているが、内容はかなり違ったものになる。
さらに、ここで重要なことは、ネットワークをサポートするということだ。そもそもが、Windows NTは、従来のホストと端末というネットワーク環境から、クライアント/サーバー型へ、さらにはワークグループでの分散処理も統括していこうというものである。ここからも容易に予想がつくように、OLE Automationでネットワークに接続されている他のマシン上にあるオブジェクトまでも自在に操作できるようになるということである。
現在のクライアント/サーバー環境でも、複数の人間がサーバー上にあるリソースをデータあるいはプログラムとして共有することはできる。しかし、サーバー以外にあるリソースを使おうとすると、セキュリティなどの問題もあってスマートには実現できてないのが現状だ。
Cairoでは、OSがネットワークのセキュリティも含めて従来型のプログラムやデータのほかに、OLEオブジェクトも管理することができるようになるという。そうなると、ネットワークを使ったソリューションにまたひとつ幅が広がってくることになるだろう。
しかし、そのCairoの話を初めて聞いてから数年が経過し、Windows 95の大騒ぎがあったものの、基本的にはWindows NTのスペックダウンとMacintoshのマネにすぎなかった。いつになったらMicrosoftの言うオブジェクト指向OSが現れるのかと、そろそろ待ちくたびれてきたところへ、Visual Basic 4.0のリモートオートメーションの話が出てきた。
これは、まぎれもなくCairoへの布石である。ただし、CairoではネットワークOLEサーバーと呼んでいるものを、今回のVisual Basic 4.0ではリモートオートメーションと呼んでいる。つまり、似たような機能を提供するものではあるが、その構造は異なっているということだ。これは、OS自体に装備された機能ではなく、現状のOS上で実現するためのアドオンとなっているからであろう。しかし、そこで実現できる機能、そしてそこから先にあるオブジェクト分散環境は、Visual Basic 4.0のリモートOLEでも十分にかいま見ることができる。
●環境情報を得るアプリケーションの作成
では、カンタンなサンプルプログラムを作って、リモートオートメーションを実現する過程を紹介しよう。
まず、注意点を述べる。リモートオートメーションは32bit版でのみサポートされる機能である。よって、OSも32bit版を使うことのできるWindows 95かWindows NT 3.51以降が必要だ。ここでは、Windows 95 PreRelease版(製品版と同等)とWindows NT 3.51βのいずれも日本語版を使用した。Visual Basic 4.0も日本語β版を使用した。すべてが正式版ではないため、画面や細かい動作などについてはリリース版と違う可能性があることをお断りしておく。
本来、リモートオートメーションを使えばオブジェクトを複数のマシンに分散することができる。そこをうまく活かすようなプログラムでないと、リモートオートメーション自体が意味をなくすことになる。実際にどういうものがリモートオートメーションに有効であるかを考えると、いろいろと難しい側面もある。そこで、そういう話は後回しにして、ここではとにかくリモートオートメーションを実感できるものとして、コンピュータの名前、ログインしているユーザー名、OLEサーバーの起動パス名を返すようなリモートオートメーションサーバーを作成してみよう。
Visual Basic 4.0でリモートオートメーションサーバーを作るのは、実にカンタンだ。というのも、形式的には通常のOLEオートメーションサーバーを作るのとまったく同じであるからである。ひとつだけ違いがあるとすれば、実行形式ファイル作成時にリモートオートメーションサーバーであることを指定するだけである。
では、通常のオートメーションサーバーを作る手順はというと、Visual Basic Magazine Vol. 3の特集に詳細を述べたので、詳しくはそちらを読んでいただきたい。ここでは復習もかねて、その手順を踏んで作ってみることにしよう。
1. 単体で動作するアプリケーションの作成
OLE Automationサーバーは、コンテナとサーバーの二つの実行モジュールを組み合わせて使うことになる。ということは、単純化すればひとつのアプリケーションとして作成できるわけだ。まずは、後からコンテナとサーバーに分けることを念頭に置きながら、単体で動作するアプリケーションを作成する。
このアプリケーションでは、次のWin32 APIを使用する。
GetComputerName コンピュータ名の取得
GetUserName ユーザー名の取得
それぞれの API宣言は、リストの通り。ちなみに、Win32 APIをVisual Basic 4.0から使うときの注意があるが、これについては今回のメインテーマではないものの、重要な問題があるのでコラムにまとめた。
これらのAPIを使ってコンピュータ名、ユーザー名を、Visual Basicの機能を使って起動パスを表示するようなコードをリスト○に示す。実際の動作結果を図に示す。
図からもわかるように、このマシンの名前は「Rollei」、ユーザー名は「norio」、起動パスは「D:\Program Files\Microsoft Visual
Basic\x\myname」である。このmynameはこのモジュールのプロジェクト名だが、EXE形式にしても拡張子までは表示しない。DLLも作成できるようになったためだろうか。
※コラム Win32 APIの使用
Visual BasicではWindows APIをコールしてVisual
Basicだけではできない処理を実現することができる。しかし、従来の16bit APIと違って、32bitの Win32 APIを使うためにはいくつか押さえておかなくてはならないことがある。次にこれらをまとめた。
・Win32 APIについて
32bit APIセットには、大きく分けて次の3つがある。
Win32s 16bit Windowsで32bit APIを追加して使うサブセット。
Win32c Windows 95の APIセット。Windows NT独自の一部のAPIセットは含まれないが、Windows 95で拡張された部分がある。
Win32 32bit APIのすべてのはず。現実的にはWindows NTのAPIセット。Windows 95の主としてインターフェイスに関係するAPIセットなどは含まれない。
Win32s は Win32に含まれる。しかし、Win32cはWindows 95用の主としてインターフェイスまわりをサポートするために拡張された部分があり、それらのAPIはフルセットであるはずのWin32には含まれない。つまり、フルセットであるはずのWin32にはないAPIがあるということだ。逆に、Win32cにもWindows NT独自の高度な機能を実現するためのAPIセットが抜けている。したがって、ターゲットとなるOSによって動作しないことがありうる。開発時には、ターゲットとなるOSにあるAPIであるかを確認しておく必要がある。
ただし、Windows NTの3.51の次のバージョン(3.52?)では完全にWin32cを取り込むことになる予定だ。
ちなみに、Windows 95でのWin32 APIは、user32.dll(32bit API)とuser.exe(16bit
API)のサイズを見ても明らかなように、かなりのモノがそこから16bit APIをコールしているだけという情けない状態だ。ドライバをいくら32bitにしても、Windows 95はしょせん16bit OSなのである。
・宣言の記述
APIの宣言は、Professional Editionに付属しているWin32Api.Txtファイルに記述されている。このファイルは、同じくProfessional Editionに付属している APIビューワーで検索し、複数の検索結果をクリップボードに転送することができる(図 fig2.doc)。
テキストファイル内の検索だけではなく、テキストファイルの内容をMDB形式にコンバートする機能もあり、高速に検索することができる。検索時にはインクリメンタルサーチが使えるが、直接リストボックスにフォーカスしてキー入力しなくてはならないので、使い勝手はいまいちだ。このような検索ツールは私も自作して使っているし、フリーソフトウェアにも存在しているようだ。せっかくMDBまで使えるようになったのだから、もうちょっと使い勝手がいいと嬉しかった。また、この宣言の内容に結構ウソがあるようなので気をつけないといけない。
それはともかく、この宣言は従来のVisual Basicならば、(general) (declarations)の位置に記述すればよかった。しかし、Visual Basic 4.0からは言語仕様が変わったために、定数、固定長文字列、配列とともに、Declareステートメントは、フォームやクラスモジュールのPublicメンバーとすることができなくなった。したがって、標準モジュール(従来のコードモジュール)に記述することになる。ただし、キーワード Privateを頭に記述すれば、フォームモジュールにも記述することができる。単に Declareと記述すると、Publicとみなされるわけである。
Win32を使うと、16bit APIとソックリなものも多いことに気づかれるだろう。ただし、従来はIntegerであったものがLongになっている。基本的には、Win32は16bit API + αと考えられるので、同じようなAPIも多数あるのだ。ただし、32bitのVisual Basicから16bit APIは直接呼び出すことはできない。必ず、32bit版のAPIを使わなくてはならないのだ。問題なのは、32bitからは呼び出すことのできない、すなわち廃止または変更されたAPIがあることだ。
・Win32 API のお作法
バッファをあらかじめ用意しておいて、APIを呼び出してそのバッファに何らかの内容を得るという方法は、16bit Windows APIでもよくあった。ただし、このとき戻り値に実際にAPIがコピーした長さを返すというのが16 bit APIの作法だった。したがって、戻り値とバッファの内容を元にして、"BufRes = LeftB(Buf, ret)"というようなコードを書くのがスマートであった。
しかし、Win32 APIではこういうやり方をしないのが一般的だ。Win32 APIでは戻り値は成功か失敗かを示すTrueかFalseである。通常はFalseは0、Trueは0以外という意味になるのだが、Visual BasicではTrueは-1とされているから、戻り値はTrueであるかを調べるのではなく、Falseではないのが成功だったとしなくてはならない。
また、実際に必要な文字列の長さは、引数のひとつとして渡した変数に返すのが一般的だ。16bit APIのときも、用意したバッファサイズを越えないように、バッファの大きさをバッファのアドレスと同時に指定した。これは、16bit APIでは直接定数値を指定してもよかったのだが、Win32 APIでは変数にしておく。ここに実際に必要な長さがセットされるのである。
正確に言うならば、これはサイズを示すのではなくサイズを示す変数のアドレスを指定するのである。したがって、ByValはついていない。
Declare Function... (ByVal lpBuffer As String, nSize As Long) As Long
~~~~~~~~~~~
CやC++などでは一度サイズ0のバッファをセットしてAPIをコールし、戻ってきた長さでバッファを確保し直してから、再度APIをコールするといった手段をとることが多い。
Visual Basicでも、もちろんそうした方が安全ではあるのだが、とりあえずある程度の大きさのバッファを取得してAPIをコールしてもよいだろう。次に例を示す。
Dim x As Long
Dim buf As String
Dim ln As Long
ln = 127
buf = String$(ln, " ")
If GetComputerName(buf, ln) <> False Then
Label1 = Left(buf, ln)
End If
・文字列型データとUniCode
先ほどの例で「おかしいな」と思った方は鋭い。このプログラムでは文字列操作関数にLeftBを使っていない。「APIの戻り値はバイト単位のハズ。これはバグってますね」なんて思ってしまったアナタはWindows 95やWindows NTの勉強をしなくてはいけない。
Windows 95やWindows NTでは、内部的にUniCodeが使われている。UniCodeとは世界中のあらゆる言語の文字を表すことができるというコード体系であり、どんな文字でも2バイトである。ASCII コードの "a"も2バイトなのだ。したがって、UniCodeシステム上ではバイト単位での文字の処理などというものは通常はあり得ないということになる。唯一の例外として、上位bitが0ならばきっとASCIIコードだろうというような簡易見分け方に必要な程度だろう(いや、もっと深いものがあるかもしれないが)。ちなみに、AscBといった怪しい関数も増えているのは、このためである。
そういうわけで、今後はバイト単位での処理ならなんとかB系の関数などと妙な気を回す必要はなくなったわけだ。LenB関数などを使うと、128文字(バイト指定はできない!)と指定した固定長文字列型変数が256バイトあると返してくるのである。
Dim s As String * 128
Caption = LenB(s) ' 256が返る
2. クラスを作成して組み込む
クラスを作成する。このクラスには、動いたコードを元にして、コンピュータ名、ユーザー名、起動パスをそれぞれ返すプロシージャをメンバー関数として組み込む(EnvInfo.Cls)。これにともない、呼び出し元のコードも修正する(EnvInfo.Frm)。
こうして作成したプログラムも、同様に実行することができる(図)。
3. OLEコンテナの作成
単独で動作していたアプリケーションから、クラスに隠蔽した処理を外して、フォームモジュールのみにする。このとき、オブジェクトの作成をするコードを次のように書き直す。
旧 Set ei = New EnvInfo
新 Set ei
= CreateObject("EnvSrv.EnvInfo") ' New EnvInfo
従来のコードはプロジェクト内のクラスEnvInfo型のオブジェクトを作成していたのだが、新しいコードではプロジェクト外にあるOLEサーバー"EnvSrv.EXE"(またはEnvSrv.DLL)にあるEnvInfo型のオブジェクトを新規に作成して、オブジェクト変数eiにセットするものだ。
4. OLEサーバーの作成
単独で動作していたアプリケーションから、クラスに隠蔽した処理を、OLEサーバーとして動作するように書き直す。これには次のステップを踏む。

基本的にはこの手順でOLEサーバーにすることができる。なお、デバッグ時には二つのVisual Basicを起動して、それぞれコンテナ側とサーバー側のプログラムをインタープリタで実行してデバッグすることができる。
ここでは、これだけでは面白くないので、サーバー側にひとつフォームを追加し、ここにクラスの持つメソッドが呼び出された時刻を表示するようにした。また、コンピュータ名を返すComputerNameメソッドには、何回呼び出されたかをStatic型変数に記憶しておいて、それも同時に返すようにしてみた。コードをリスト○に示す。
ただし、これはあくまでも実験のためにしたことである。リモートマシンにあるOLEサーバーオブジェクトにフォームがあると、処理中に余計な動作を加えられたり、勝手に終了させられることも考えられる。したがって、このようなフォームを表示させることは一般的には危険である。
5. 実行
それぞれのEXEファイルを作成したら、サーバー側のEXEだけ単体で一度実行しておく。この時点で自動的にシステムのレジストリにOLEサーバーとしての情報が登録される。もしDLLとして作成したときには、作成時にレジストリが登録される。
あとは実行するだけである。
● instancingプロパティの意味

こうして作成したアプリケーションの実行図を示す。ここでは、クラスEnvInfoの「instancing」プロパティは"2 - Creatable MultiUse"に設定してある。これは、OLEサーバーが複数のコンテナから呼び出されたとき、ひとつのサーバーでそれぞれに対応した処理をするという意味だ。
●クライアント側環境情報取得プログラム

●サーバー側環境情報取得プログラム
図でも、二つのコンテナではそれぞれ1回と2回サーバーを呼び出しており、サーバー側のリストボックスにも3回呼び出された履歴が残っている。
ちなみに、正確には一方のコンテナから2回呼び出してから、もう一方のコンテナを起動して1回呼び出した。つまり、それぞれのコンテナに対してStaticな変数がそれぞれ別に処理されていることが分かる。
一方、次の図は「instancing」プロパティを"1 - Creatable SingleUse"に設定した例である。二つのコンテナに対応してそれぞれOLEサーバーが起動し、それぞれの回数に合わせた処理がされていることが分かる。

●InstancingがCreatable Multi UseのOLEサーバーは単一のサーバーで複数のクライアントごとに情報を配布する

●クライアントに対応した複数のサーバーが情報を配布する(VisualBasicのインタープリタではInstancingがCreatable Single UseのOLEサーバーは複数の不可)
●In-Process とOut-of-Process OLEサーバー
OLEサーバーを作成するに当たって、もう一つ注意しておかなくてはならないことがある。それは、In-Process OLEサーバーとOut-of-Process OLEサーバーの違いである。これは、Visual BasicではそのままOLEサーバーの拡張子の違いと考えてよい。すなわち、EXE形式として作成したOLEサーバーはOut-of-Process OLEサーバーであり、DLL形式として作成したOLEサーバーはIn-Process OLEサーバーである。
Out-of-Processとは、OLEサーバーを呼び出すコンテナのプロセス空間と、呼び出されたOLEサーバーのプロセス空間は別になっているという意味である。したがって、プロセス間でデータをやり取りするのは通常のOLEのLRPC(Lightweight Remote Procedure Call)が使われる。これは、違うプロセスにあるプロシージャを呼び出す仕組みである。この仕組みはなかなかのものなのだが、残念ながらオーバーヘッドが大きい。
一方、In-Process とは、OLEサーバーを呼び出すコンテナのプロセス空間と、呼び出されたOLEサーバーのプロセス空間が同じであるという意味だ。つまり起動されたオートメーションサーバーは、起動元であるOLEコンテナのプロセスで管理される。したがって、データのやり取りもプロセス内でスタックを使って行われるために、Out-of-Process型に比較してきわめて高速である。
しかし、複数のプロセスを起動したときには、それぞれのプロセス内にOLEサーバーが起動されることになる。これは、リモートオートメーションを使おうとすると、問題になる。なぜかと言えば、OLEサーバーのコードをOLEコンテナ側のマシンに転送しなくてはならないからである。これは大変なオーバーヘッドになる。そこで、リモートオートメーションでは、In-Process OLEサーバーを直接扱うことはできないことになっている。ただし、リモートマシンにあるOut-of-Process OLEサーバーを起動し、そこを経由して同じリモートマシン上にあるIn-Process OLEサーバーのプロパティやメソッドにコーディング上は直接アクセスすることは可能だ。これならば、同じマシンにあるOLEオブジェクトだから、少なくともそこではオーバーヘッドは起きないわけだ。もっとも、Out-of-Process OLEサーバーを呼び出すときのオーバーヘッドはいかんともしがたい。
また、一般のデバッグでも問題が生じる。Visual Basicを複数起動してデバッグするとき、Visual Basic自体はEXE形式であり、他のVisual Basicのプロセスに入って実行をすることはできない。したがって、厳密にはIn-Process OLEサーバーのデバッグをインタープリタではできないということになる。しかし、この状態をある程度シミュレーションして、In-Process OLEサーバーをデバッグするときと同様の制限を加えることができるようになっている。これには、[ツール][オプション]メニューを選び[詳細設定]タブの「OLE DLL制限の使用」をチェックしておけばよい(図)。
●インプロセスOLEサーバー使用時に近い制限を設定
●リモートオートメーションサーバーの作成
話が少々横道にそれたが、実際にリモートオートメーションサーバーを作成してみよう。
●リモートOLEサーバーの作成

[ファイル][EXEファイルの作成]または、[OLE DLLファイルの作成]を選ぶ。ここから[オプション]ボタンを選ぶと、それぞれ図のようなダイアログが表示される。ここにある「リモートサーバーサポートファイル」を選べば、リモートサーバーになることができる。
カンタンである。しかし、カンタンなのはここまてで、この後が結構ややこしい。フォルダーにあった例のツール群を使いこなさないといけないのである。
●リモートオートメーションのためのツール群
リモートオートメーションでは、コンテナ側のアプリケーションもサーバー側のアプリケーションも、通常のローカルで実行されるOLE Automationのアプリケーションと基本的にはなんら変わりがない。唯一あるのが、サーバー側の実行モジュール作成時のオプションだけである。
では、どうして通常のOLEアプリケーションと同じものがリモートで使えるかというと、OLEコンテナとOLEサーバーの間に入って、いかにもローカルに実行しているように見せかけるしかけがあるのだ。このしかけをしているのが、次の3つのツールである。
それぞれについて、機能と使い方の概要を述べよう。
●オートメーションマネージャ
リモートマシンとなるサーバー側には、オートメーションマネージャを起動しておく。このプログラムは、リモートオートメーションの仕組み上、もっとも重要な役割を担うものだ。起動すると、リモートオートメーションを行うための各種のプロトコルを走らせる。それが一通り終わるとアイコン化されるが、元のサイズに戻すと図のような小さなウィンドウになる。ここには、接続数と接続中のオブジェクト数が表示されるようになっている。
このツールは、コンテナ側からネットワークを介して送られてきたデータを元にして、自分が走っているリモートマシン上のレジストリを調べ、起動すべきOLEサーバーを探し出す。そして、いかにも自分が呼び出したコンテナアプリケーションであるというふりをして(もちろん、実際にコンテナアプリケーションではあるのだが)OLEサーバーを起動し、制御するのである。
したがって、オートメーションマネージャを起動する前に、少なくとも一度はOLEサーバーアプリケーションを実行するなどして、レジストリを登録しておく必要がある。これについては、また後ほど述べる。
●オートメーションマネージャの起動
なお、この図はWindows NT上のウィンドウであることが分かるだろう。
●RemAuto接続マネージャ
RemAuto接続マネージャは、コンテナ側とサーバー側でそれぞれ設定する必要がある。
★コンテナ側
クライアントとなるコンテナ側のマシンでの動作は、次のようなものだ。OLEコンテナアプリケーションがOLEサーバーアプリケーションを起動し、制御しようとしたときに、レジストリデータベースにある、本来なら呼び出されるハズのOLEサーバーを起動しないで、リモートにあるOLEサーバーを呼び出して制御を渡してしまう。
したがって、コンテナ側では次の設定をする。
・リモート接続するOLEサーバーの指定
システムのレジストリデータベースから、OLEサーバーの一覧が表示される。この中から、リモート接続の設定をしたいものを指定する。それぞれのOLEサーバーごとの指定項目は、以下の通り。
・リモートOLEサーバーのあるマシン名
OLEサーバーのあるマシン名を指定する。
・ネットワークのプロトコル
接続されているネットワークのプロトコルを設定する。プロトコルによっては制限があるので注意が必要だ。
・認証レベル
無条件に接続できるだけでなく、コネクトのみや呼び出しのみなどの設定をすることができる。
・リモート接続するかどうか
●リモート接続マネージャのクライアント側設定
リモート接続するか、ローカル接続するかを指定する。図はリモート接続時の例。
以上の設定をして、[Apply]ボタンを押すと、リモート接続を設定することができる。
★サーバー側
サーバー側では、リモート接続のセキュリティを設定する。図にもあるように、リモートでオブジェクトを作成できるか、アクティブにできるかを組み合わせて指定する。 
●リモート接続マネージャのサーバー側設定
●リモート接続マネージャのサーバー側設定このとき、OSによって機能が異なる。Windows 95では、あくまでもレジストリのキー(CLSIDのサブキーがAllowRemoteActivation=Y)のみによってセキュリティが設定される。Windows NTでは、これに加えてACLの機能を使い、ユーザーごとに細かい設定をすることができる(図)。セキュリティを考えたら、Windows NTがやはり安心だ。
Windows NTでのACLを使ったセキュリティの管理
以上の設定で、リモートオートメーションができるようになる。この手順でリモート接続したときのコンテナ側とサーバー側の図をそれぞれ示す
●リモートOLEサーバーからの情報取得


先ほどローカル実行したときの図と比較しても分かるように、マシン"ROLLEI"からログインしていた"norio"が呼び出したOLEサーバーは、リモートマシン"LEICA"のユーザー"rgb'がログインしている環境の"G:\VB422\X\ENVSRV"というモジュールである。明らかにリモートマシンのオブジェクトが使われたわけだ。
また、サーバー側の画面を見ると、接続数および接続中オブジェクト数は1になっており、呼び出された時刻もしっかり記録されている。
●セットアップウィザード
ともかくリモートオートメーションができたわけで、なんだか一安心である。実際にはマニュアルなどが完備していない状態でこの原稿を書いたので、ちゃんと動くようになるまでなんやかんやで一週間以上かかってしまった。分かってしまえばなんということはないのだが、どうにもこれらの設定は面倒である。もっとカンタンに設定できないものだろうか。
また、今回はコンテナ側とサーバー側のマシン両方にVisual Basic 4.0をインストールしたので特に問題はなかったのだが、一般的にはどちらかのマシンに後からコンテナアプリケーションだけをインストールするだろう。しかし、必要になるDLLやレジストリの設定などを手動ではやっていられない。
こういった手間を軽減してくれるのが、セットアップウィザードである。このアプリケーションを使えば、Visual Basicのプロジェクトファイル(.VBP)を指定するだけで、自動的にOLEやデータアクセスなどに必要なコンポーネントを集めてくれる。もちろん、必要ならば手動で追加や削除し、それらを圧縮し、セットアッププログラムをディスクイメージで作ってしまうというスグレモノだ。
OLEコンテナとOLEサーバーそれぞれについての例を図に示す。OLE Automationクライアントのときには、OLEサーバーのモジュールをうまく拾ってくれないのはご愛敬だ。しかし、OLEサーバーではインストール先を共有コンポーネントとして設定できるなど、Visual Basic 3.0のセットアップウィザードに比較しても格段に良くなっている。リモートオートメーションアプリケーションやデータアクセスアプリケーションの配布には、必須のアイテムである。


●コンポーネントマネージャ
ひとつや二つのリモートオートメーションオブジェクトならば、手動で設定もできる。一気にたくさんのオブジェクトを登録することもそんなにはないだろうから、これくらいの手間はしかたないだろう。しかし、いつのまにか膨大に増えたオブジェクトとなると、これは困ってしまう。
ひとつは、どのオブジェクトにどんなプロパティやメソッドがあったを知る、リモートオートメーションに対応したオブジェクトブラウザがないということだ。もうひとつは、新しいローカルマシンを追加したときに、マシンのレジストリに膨大なリモートオートメーションの情報を設定しなくてはならないことだ。
●コンポーネントマネージャ

これらの問題を解決するのが、コンポーネントマネージャである。このツールでは、リモートオブジェクトをJetエンジンまたは、ODBCデータソースを使ったデータベースとして管理する。図の画面左下がデータベースのテーブルの一覧である。それぞれのテーブルには、リモートオートメーションオブジェクトの構造や情報が入っており、このツールから追加、削除、変更などをすることができる。また、作成した.MDB形式のファイルにあるデータをODBCデータソースにあるリモートサーバーにエクスポートし、ネットワークに接続しているあらゆるマシンから利用することができる。

データベースには、EXEやコンポーネントカタログ、あるいはシステムのレジストリを参照してオブジェクトを追加することができる(図)
。このとき、セットアッププログラムのある位置を指定しておけば、インストール時(図)に単なるコピーではなく、セットアップをすることも可能だ。

また、各オブジェクトのプロパティ(図)や詳細情報(図)などを表示することも可能である。


このように、コンポーネントマネージャは便利なものであるが、いかんせん使い勝手はよくない。もともと、Cairoになればシステムのレジストリとしてリモートオートメーションオブジェクトも登録できるようになるのだろうから、このツールはやはり苦肉の策なのかもしれない。
●リモートオートメーションのメカニズム
理論より実地という視点で、とにかくリモートオートメーションについてざっと述べてきたが、やはり中身がある程度わからないと気持ちが悪いものだ。しかし、リモートオートメーションの中身を完全に理解するには、OLE自体についての理解が必要であり、これをやり始めたら本が一冊書けてしまう。そんな時間もページもないので、ここではあくまでも概要を述べることにする。
ちなみに、OLEの細かい仕組みは理解しておくにこしたことはないのだが、理解したからプログラミングできるわけではない。アーキテクチャーが云々などと議論したい人は、OLEなんか使わないでOpenDocの話でもしていた方がいい。そんなことよりも、どうやったら目的のアプリケーションを書けるかの方が重要である。VisualC++で言うならば、対応するMFCのクラスが何であるかを知って使いこなすべきである。OLEネイティブを解説するために作られたCのプログラムなど見て首をひねっても、プログラムは書けるようにならないのだ。それをすべて理解できた頃には、新しいアーキテクチャーになったOLEになっているか、(あまり考えられないが)OLEはすたれてOpenDocになっているかだろう。
図(クライアント/サーバーアプリケーション作成ガイド 3章P.3の図)を見て欲しい。上は、一般的なOLEサーバーとクライアントを示したものだ。クライアント側のプロセスにあるOLE proxyと呼ばれる部分と、サーバー側のプロセスにある OLE stubと呼ばれる部分が通信を行う。この通信を使って、クライアントはOLEサーバー上にあるオブジェクトにアクセスするのである。
下の図は、リモートオートメーション時の動作である。クライアント側マシンでは、まったく同じアプリケーションが動作する。ただし、リモート接続マネージャがローカルにある通常のOLEサーバーではなく、リモートマシン上にあるOLEサーバーにディスパッチする。ネットワークを経由してリモートマシンに行くと、オートメーションマネージャが待ちかまえている。ここで、オートメーションマネージャは受け取った通信内容を元にして、そのマシン上にあるOLEサーバーを起動して通信する。このとき、いかにもローカルにあるコンテナのふりをして、OLE proxyから通信をするのである。同じマシンにあるのだから、リモートオートメーションサーバーも、ここでは単なるローカルOLEサーバーと同じである。通常のOLE stubを使ってふつうに通信をするわけだ。
このように、間に入っているモジュールのおかげで、まるでCairoのネットワークOLEを先取りしたようなリモートオートメーションが実現しているのである。
●3分化アーキテクチャー
ぼーっとして見ていると、リモートオートメーションは「おおー、おもしろいじゃないか」で終わってしまう。しかし、これが使えるとどんなコトが実現できるかを考えると、実に面白いことがある。
ビジネスアプリケーションの分野で考えるならば、コードの積極的な共有ということがあるだろう。コンピュータのオブジェクトとは、コードとデータである。このうち、データについては、クライアント/サーバー型RDBMSでかなりのことができるようになってきた。次はコードである。
実は、コードをも共有しようという発想は、すでにRDBMSの中にある。RDBMSのストアドプロシージャやトリガーと呼ばれるものである。
たとえば、あるトランザクションを考えよう。注文が入ったら、対応する在庫データベースから在庫数を減らすというものだ。これには、現在の在庫数を得、それから注文の数があるかを調べる。足りなければ売れないし、足りていれば在庫数を減らして、他に必要な処理を行う。これに類した処理はいろいろな場面で出てくる。これをクライアント/サーバー環境で行うと、この手順通りに何度もサーバーとやりとりをしないといけない。しかし、サーバーに注文されたものの番号と数、注文先などを渡してやれば、その処理はサーバー側にあるプロシージャがやってくれればよい。後は結果を返してくれればいいだけだ。このように、サーバーRDBMS側にあるプロシージャをストアドプロシージャと呼ぶ。
同様に、常にデータベースのテーブルを監視し、在庫数が一定以下になったら注文をかける処理を開始するというようなものが、トリガーである。
Syabase、そのOEMであるMicrosoft SQL Server、Oracleなどでは、このような仕組みをRDBMS自体が持っている。しかし、これらのコードは独自の言語仕様であり、デバッグにしても容易ではない。また、RDBMSの内部にあるプロシージャであるから、外部にある何らかの情報を積極的に取得して組み合わせた処理をするといったことはできない。
これを解決するのが、このリモートオートメーションである。単純に言えば、ストアドプロシージャやトリガーをリモートオートメーションで実現してしまおうということだ。
ここでMicrosoftは、3分化アーキテクチャーという提案をしている。といっても、これもそんなに新しいものではなくて、すでにこの考えを元にいくつかの製品も出ているらしい。ただし、このアーキテクチャーではどうしても中間層、すなわちリモートオートメーションの層が複雑になり、その管理をどうするかが課題とされてきた。Microsoftは、Visual BasicとJet、そしてODBCを使ったSQL Serverを組み合わせて、この管理をコンポーネントマネージャとして実現したわけだ。もっとも、複雑なことには変わりはないような気もするが...。
それはさておき、3分化アーキテクチャーとは図(2章 P.5図2.1)のように、ユーザー、ビジネス、データの三つのサービスに階層化して考えるというものだ。
ユーザーサービスは、Visual Basicで内部処理らしいものはほとんどないカンタンなアプリケーションを作る。ボタンが押されたときの処理などは、リモートオートメーションを使ってビジネスサービス層のOLEサーバーが呼び出されることにする。
ビジネスサービスは、計算や分岐などの論理的なもの、すなわちビジネスルールを担当する処理だ。ここだけで処理が完結してもよいし、さらにODBCなどを使ってデータサービスを呼び出してもよい。
データサービスは、いわゆるRDBMSである。ビジネスサービスとは同じマシンでも違うマシンでもかまわない。モデルとしての階層が違うというだけである。したがって、ここにストアドプロシージャがあっても一向にかまわないのだ。ただし、単純化するためには、ビジネスルールはここでは扱わないというのも、ひとつの方針だろう。
このように、それぞれの層で役割を分けると、いろいろなメリットがある。特に、処理が複雑になりがちで、仕様変更が発生する可能性の高いビジネスルールは、各クライアントマシンにばらばらにあるのではなく、特定のリモートマシン上のオートメーションOLEサーバーとして、それぞれひとつだけあるわけだから、メンテナンスが楽になる。仕様変更があっても、そのマシンでのみコンパイルしなおせばいいわけだ。
このように、リモートオートメーションの可能性は広がっていくものの、オブジェクトの管理や分散オブジェクト環境での設計ノウハウ不足などの課題がある。実際には、今からがこの分野の試行錯誤の開始なのである。
●リモートオートメーションのオーバーヘッド
OLE Automation、そしてリモートオートメーション、3分化アーキテクチャー、そして次期Windows NTと、明るい未来への第一歩としてVisual Basic 4.0が登場した。しかし、本当に未来は薔薇色なのかというと、若干疑問が残るのも確かである。
というのも、OLEは遅いのではないかということだ。 Cairo ではOLE Automationは、Visual Basicの構文であらゆるオブジェクトの操作ができるというのだが、どう考えてもシステムをOLEオブジェクトにしてしまうのはオーバーヘッドがありすぎるように思えるのだ。
思えば数年前、今まで高速にDOSでアプリケーションが動いていたマシンが、Windowsを入れた瞬間から使い物にならないマシンになってしまった。やっと使い物になる速さになったのが、せいぜいここ一年くらいではないか。この上、さらにオーバーヘッドが大きくなるのかと思うと、頭が痛い。
そこで、オーバーヘッドがどの程度あるかを調べてみた。ただし、ここで使っているOS、Visual Basicともに製品版ではないので、あくまでも目安だと考えて欲しい。
次のベンチマークは、文字列型変数にTime$を代入するだけの処理を10,000回行ったものである。代入する部分だけを他のプロシージャ内に記述して、どの程度のオーバーヘッドがあるかを調べた。ただし、マルチタスクOSである以上、かなりの誤差があると考えられる。
単位はすべてmsec
ひとつのプロジェクト
| ループ10,000回 | Windows NT 3.51 | Windows 95 |
| コマンドボタンのClickイベントに直接記述 | 220 | 3045 |
| 同じモジュールに作成したSubを呼び出し | 320 | 3320 |
| 他のモジュールに作成したSubを呼び出し | 320 | 3320 |
| クラス内のSubを呼び出し | 2360 | 6510 |
| VB 2.0J | Windows 3.1 | Windows 95 |
| コマンドボタンのClickイベントに直接記述 | 1080 | 1230 |
| VB 4.0 (16bit) | Windows 3.1 | Windows 95 |
| コマンドボタンのClickイベントに直接記述 | 3170 | 3630 |
このように、クラスにしたオブジェクトを同じプロジェクト内から呼び出しただけで、通常のSub呼び出しに比較して7倍から10倍も遅くなることが分かる。
なお、Windows NTで動作させたときには抜群に速いが、Windows 95では何と10倍以上も遅くなっているデータがある。これは、Windows 95では時刻を取得するときに仮想デバイスドライバが使われるなどのオーバーヘッドが大きいからだと思われる。
しかし、クラスを使ったときにはそこまでの差は出ないからクラス呼び出しにかかるオーバーヘッドはかなりのものだということだろう。Windows 3.1で動作させたときにも、Windows NTの4倍程度は遅い。しかし、これを見るとVisual Basic 2.0が健闘していることに驚く。速さを考えたら、Windows 3.1とVisual Basic 2.0あるいは 3.0はまだ当分生き残るのではないだろうか。
このプログラムはWindows 95にとっては最悪のケースであり、一般的にはここまでの差は出ないと思われる。しかし、セキュリティや堅牢さを考えても、開発者はやはりWindows NTがよいだろう。
次のデータは、ローカルとリモートのオートメーションサーバーにしたときのものだ。
OLE Automation
| ループ100回 | Windows NT 3.51 | Windows 95 |
| ローカル (Out-of-Process) (カッコ内はインタープリタ) |
420 (450) | 675 (710) |
| ローカル (In-Process) | 30 | 46 |
| リモート(Windows 95をサーバー) | 1680 | 35000 |
| リモート(Windows NTをサーバー) | 1680 | 11000 |
Out-of-Process とIn-Process OLEサーバーの差が14倍以上あるのには驚いた。
ローカルからリモートにしたときの差は、Windows NTでは4倍程度だが、Windows 95では、50倍も悪化している。ネットワークドライバの構造に問題があるのだろうか。
それにしても、一般的にWindows 95は遅い。しかし、Windows NTからWindows 95のサーバーを呼び出したときには、Windows NT同士でのデータと変わらない。しかし、その逆は大きな差が出ており、サーバーはWindows NTの方がパフオーマンスがよい。セキュリティも考えて、リモートオートメーションのサーバーはWindows NTにした方がよさそうである。
このように、オーバーヘッドはかなりあると言える。この値は煩雑に呼び出しが行われたときには問題が発生する可能性がある。しかし、一般的にはそれぞれのモジュール内で処理は完了するものであり、相互にやりとりする少ない回数のほんの一瞬の話であるから、そんなに悩む必要もないだろう。要は、それに合わせた設計にすればいいのだ。オーバーヘッドの問題以上に開発やメンテナンスが楽になればいいのだ。
そして、ここ数年そうだったように、Cairoが発売される頃には、ハードウェアがこの問題もクリアしてくれるのだろう。かくして、我が家のPentiumマシンたちもクズになってしまうのだろう。