秋月 巌 AKIZUKI,Iwao
|
Visual Basic 6.0に 何を望むか |
|---|
Visual Basic 6.0のリリースが具体的になってきた.この原稿を書いている時点で,私はプレリリースのどのバージョンも受け取っていないし,技術的な情報を何も受け取っていない.知っていることは,本誌98年3月号で米Microsoft社のWillis氏が語っていた,DHTML開発をサポートするらしいということだけである.
Visual Basic 6.0の進化ついて,何を望むべきか,私は明確な意見をもっていない.それは現行のVisual Basicにとくに不満を感じていないという意味でもある.実は,私はVisual Basic 4.0に対してもさして不満は感じていなかったのである.データベースシステムを作る上でとくにパフォーマンスの問題は気にしていなかったし,ActiveXコントロールの作成機能についても,Webアプリケーションを作成することにさえならなければ必要ではなかった.あえて言えば,Visual Basic 5.0 Enterprise Editionに付属のRDOが,バッチアップデートをサポートしていたことに魅力を感じたことくらいである.
|
新しいバージョンも 使い慣れれば… |
|---|
Webアプリケーションやサーバーアプリケーションを作らないならば,私は今でもとくにVisual Basic 5.0の必要性は感じないだろう.しかし実際のVisual Basic 5.0は,以前のバージョンよりもはるかに高い完成度を誇っている.また,新しく搭載された機能によってWebアプリケーションが作成できるようになったことは,Visual Basicの可能性を広げることになった.
だからVisual Basic 6.0も,入手してしばらく経てば,その新機能の有用性に合点がゆくのかもしれない.しかし今の時点では次のバージョンへの期待は大きくない.それ以上に新たなる障害への対応や,移行期間中に2つのバージョンを利用できる環境をどのように確保するかの心配の方が大きい.
今のところ,Visual Basicの新しいバージョンのリリース間隔は,16ヶ月程度である.この間隔が最適なものなのかどうかはわからない.個人的には2年間くらい一定のバージョンが継続した方が安心して使えるような気もする.ただ,1年もすると新しい開発環境に慣れてくる以上に飽きがくるので,適時のバージョンアップは,ユーザーを繋ぎ止めるうえで,必要なことなのかもしれない.
この連載で使用しているテクニックは,Visual Basic 5.0で搭載された機能にその多くを依存している.もしこれらの機能がなかったら,おそらく全く別の方法で実装することになっただろう.大切なのは,使える方法の中で最善の方法を選択することである.目的を達成するための特定の機能がバグで使えないならば,代替えの手段を見つける必要がある.そのような札をどれだけ持っているかがエンジニアのスキルだということもできる.
| ActiveX EXEの使用 |
|---|
前号では,Type4 DB Serverのマルチユーザーマネージメントサーバー(DBsvrRB.exe)について解説した.今回はデータベースアクセスの要であるDBアクセスサーバー(DBsvr.exe)とクライアントアプリケーションについて説明する.
DBアクセスサーバーの内部的な仕組みは,Type3 DB Serverとほとんど同様であることは説明した.違うのは,標準EXEではなく,ActiveX EXEとして実装されていることである.
それではActiveX EXEと標準EXEとの違いについて簡単にまとめておこう.ActiveX EXEも標準EXEも,どちらもクライアントアプリケーションから起動することが可能であるが,ActiveX EXEは,起動後にもクライアントアプリケーション(起動元の呼び出しアプリケーション)との同期が可能である.標準EXEをVisual Basicアプリケーションから起動する場合にShell関数を利用するのに対し,ActiveX EXEはCreateObject関数やSet Newステートメントを使用する.
起動後,標準EXEがクライアントアプリケーションとは無関係に動作するのに対して,ActiveX EXEでは同期処理が行なわれる.またActiveX EXEはメソッド,プロパティ,イベントを公開することでクライアントからそれらのメソッドを使用することができる.あるいは起動後に標準EXEをクライアントアプリケーションから操作する場合には,DDEなどのプロセス間通信メカニズムを別途用意しなければならない.
簡単に言えば,ActiveX EXEは通信メカニズムつきの標準EXEだということだ.その通信機能により,コンポーネントとしての性質ももつのである.
|
ActiveXコントロールと ActiveXコンポーネントの違い |
|---|
ActiveX DLLとActiveX EXEは,単にクライアントアプリケーションと同一のプロセスによって動作するか否かの違いでしかない.また,ActiveXコントロールとはActiveX DLLの一種であるということを理解すれば,ActiveX EXEがメソッド,プロパティ,イベントといったActiveXコントロールと同様の特徴をもつことも合点がゆくだろう.
では,ActiveXコンポーネント(ActiveX EXEとActiveX DLL)とActiveXコントロールの違いは何なのだろうか.まずひとつは,ActiveXコントロールはインプレースアクティベーション,つまり,フォーム内でのオブジェクトの表示が可能だということである.インプレースアクティベーションを備えていないActiveXコンポーネントをコードコンポーネントというのだ.インプレースアクティベーションは,本来ActiveXコンポーネントでも可能なのだが,Visual Basicで作成するActiveXコンポーネントは,その機能を備えていない.もっとも,他のツールでインプレースアクティベーションが可能なActiveXコンポーネントを作成するのも容易ではない.そのような機能を必要とするときには,Visual Basicであるか否かにかかわらず,ActiveXコントロールとして作成すべきだろう.
| インスタンス化の方法 |
|---|
プログラミングをする上でのActiveXコンポーネントとActiveXコントロールの違いは,ActiveXコントロールを使用するには,フォームにコントロールをドラッグ&ドロップで配置するのに対して,ActiveXコンポーネントは前述のようにCreateObject関数やSet Newステートメントを使用する.つまり,フォームのLoadイベントプロシージャにこれらの処理を記述すれば,非ビジュアルのActiveXコントロールと同様にActiveXコンポーネントを使用できることになる.
歴史的にはActiveXコントロールの前身であるVisual Basicカスタムコントロール(VBX)は,ActiveXのようなCOMアーキテクチャとは,まったく別の構造をもっていた.それをCOMアーキテクチャと統合したのが,OLEカスタムコントロール(OLE)である.結果としてVisual Basic 2.0のカスタムコントロールは,Visual Basic 4.0の32bit版では使えなくなった.Visual Basicが5.0にバージョンアップされたときに,OLEカスタムコントロールが改称されたのがActiveXコントロールであり,この2つは同じものと考えてよい.また,ActiveXコンポーネントとCOMコンポーネントは同義で捉えて問題はない.
| DLLとActiveX DLLの違い |
|---|
では,従来のDLLに対して,ActiveX DLLがもつメリットは何なのだろうか? Visual Basicを基準で考える場合,この二者の違いは明確である.Visual Basicでは標準DLLが作成できないからである.Visual Basicを使ったプログラムで特定のモジュールを別ファイルに切り出すには,ActiveX DLL化する必要がある.しかし,C言語のように標準DLLが作成できるツールで作成する場合のメリットは,Microsoftが喧伝するほどに明確ではない.あえていえば,好みの問題だということができる.これはアプリケーションに依存する情報を,INIファイルとレジストリのどちらに保存すべきかという問題に類似しているかもしれない.
個人的にはActiveX DLLのもつ最大のメリットは,Internet Explorerによる自動配信が可能な点だと考えている.標準DLLも自動配信は可能だが,その場合,利用するアプリケーションも同様にInternet Explorerによって配信したものでなければならない.それは標準DLLは自分の存在する物理的な位置を知らせる方法をもたないからである.その点,Internet Explorerによって配信されたActiveX DLLはレジストリに自動的にエントリされるため,どのアプリケーションからも利用することができる.
| ActiveX EXEとActiveX DLLの違い |
|---|
ActiveX EXEとActiveX DLLとの違いは,実行されるプロセス空間の違いだけだと説明した.この説明はアーキテクチャを説明する上では正解だが,実際のActiveX EXEのふるまいはActiveX DLLといくつかの点で異なっている.まず,ActiveX EXEはActiveX DLLとして,アプリケーションとして完結する可能性をもつということである.Microsoft Excelは完結したアプリケーションであると同時に,ActiveX EXEコンポーネントでもある.ActiveX DLLはあくまでもコンポーネントであり,それを呼び出すクライアントを必ず必要とする.
ActiveX EXEのもつ,もうひとつの固有の性格は,マルチスレッドで動作することである.もちろん,マルチスレッド用に設計されたActiveX DLLは適切なコンテナで実行すれば,マルチスレッドで動作する.しかし,Visual Basicはマルチスレッド用のコンテナを作成できない.ActiveX EXEは独自のプロセスで実行されるため,コンテナとは無関係にマルチスレッドで動作が可能である.ここにVisual Basicでマルチスレッドアプリケーションを作成するチャンスがある.しかし,ActiveX EXEコンポーネントを作成して,Visual Basicで作成したコンテナから呼び出しただけではマルチスレッドアプリケーションとしては動作しない.それはActiveXクライアント(コンテナ)とActiveXサーバー(コンポーネント)が同期をとるため,シングルスレッドで動作するクライアントに,サーバーが同期してしまうためである.もっとも,この連鎖を断ちきる方法はそれほど難解ではない.これについてはいずれ説明する.
図1:プロジェクトタイプの選択
|
| ActiveX EXEの作成 |
|---|
ActiveX EXEを作成するには,メニュー[ファイル]-[新しいプロジェクト]を選択し,表示されるダイアログ(図1)から,ActiveX EXEを選択すればよい.あるいは最初は標準EXEとして作成したプログラムを後でActiveX EXEに変換するのは難しいことではない.ActiveX EXEの新しいプロジェクトを作成し,そこに既存のフォームやモジュールをインポートすればよい.その場合,もしActiveX EXEとしてだけでなくアプリケーションとしても利用したい場合は,[プロジェクト]-[プロパティ]の[スタートアップの設定]に[Sub Main]を指定し(図2),Mainプロシージャに起動のための処理を記述する.大抵はフォームのロードをすることになるだろう.
| スタートアップの設定 |
|---|
DBアクセスサーバー(DBsvr.vbp)はアプリケーションとして利用する予定はないので,[スタートアップの設定]は(なし)に設定する.ただ,開発時のデバッグには単体で起動した方が便利な場合がある.そのため,Mainプロシージャには起動のためのコードが残っているが,このコードは[スタートアップの設定]を(なし)にした時点で呼び出されなくなる.
スレッドモデルにオブジェクトごとのスレッドを選択しているが,このプロジェクトの唯一のクラスであるclsDBaccessクラスだ.ここではInstancingプロパティをSingleUseに指定しているので,この設定は無効である(図3).しかし, InstancingプロパティをMultiUseに設定した場合には,この設定が意味をもってくる.DBアクセスサーバーはマルチプロセスサーバーを目指しているため,SingleUseに設定するのだが,このプロパティの設定をMultiUseに変えてコンパイルしなおすだけでマルチスレッドサーバーに変更することができる.
クラスのInstancingプロパティをMultiUseに設定した場合,プロジェクトのプロパティでスレッドプールを選択し,スレッドの数に1を選択するとシングルプロセスのシングルスレッドモデルとして動作する.スレッドの数を複数に設定した場合,マルチスレッドとして動作するが,指定した数以上のインスタンスが作成された場合,実行スレッドの数は指定した数値に制限される.
プロジェクトにフォームを含めた時点で,左側の「マルチスレッド」チェックボックスは設定不可能になる.しかし,ここがチェックされていなくても,スレッドモデルが適切に設定されていれば,マルチスレッドコンポーネントとして動作する.
図2:ActiveX EXEをアプリケーションとして使用する場合のプロジェクトプロパティ![]() |
図3:ActiveX EXEとして使用する場合のプロジェクトプロパティ
|
Private Sub Winsock1_DataArrival(Index As Integer, ByVal bytesTotal As Long)
' クライアントからデータを受信
Dim DBaccessObj As clsDBaccess
Dim strData As String
Dim intStr As Integer
Dim sKey As String
' データの取得
Winsock1(winsockNo).GetData strData, vbString
intStr = InStr(1, Trim(strData), ",")
' 接続クライアント名
txt_host.Text = Left(Trim(strData), intStr - 1)
' ODBCデータソース名
txt_dsn.Text = Mid(Trim(strData), intStr + 1, Len(Trim(strData)))
Set DBaccessObj = New DBsvr.clsDBaccess
sKey = strData
' clsDBaccessクラスのConnCliメソッドを実行
DBaccessObj.ConnCli txt_host, txt_dsn, LOCAL_HOST, sKey
Set DBaccessObj = Nothing
Winsock1(winsockNo).Close
Unload Winsock1(winsockNo)
Debug.Print "after ConnCli"
End Sub
|
|
DBアクセスサーバーの インスタンス化 |
|---|
プロジェクトのタイプにActiveX EXEを指定した場合,最低ひとつ以上のクラスが必要である.DBアクセスサーバーのクラス名はclsDBaccessである(図4).このプロジェクトにはDBsvrという名前を指定してあるので,DBアクセスサーバーをインスタンシングするためのコードは,次のようになる.
Set DBaccessObj = New DBsvr.clsDBaccess
このコードは,前回解説したマルチユーザーマネージメントサーバーのWinsock1_DataArrivalプロシージャに記述されている.結果としてユーザーからリクエストがきた時点で,clsDBaccessクラスのインスタンスが作成される.そして,DBアクセスサーバーが起動する.データベースへのアクセス処理が記述されたフォーム(図5)がロードされ,DBアクセスサーバーが処理を開始するのは,マルチユーザーマネージメントサーバーに記述されている次のコードが実行されてからである.
DBaccessObj.ConnCli txt_host, txt_dsn, LOCAL_HOST, sKey
図4:SingleUseに設定されたInstancingプロパティ![]() |
図5:デザインモード時のDBアクセスサーバーのフォーム,clsDBaccessクラスから呼び出される
|
|
公開されたメソッドの 定義と呼び出し |
|---|
次のコードは,clsDBaccessクラスが公開しているConnCliメソッドを呼び出している.ConnCliメソッドはclsDBaccessクラスモジュールで以下のように定義されている.
Public Sub ConnCli (ByVal hostName As String, ByVal DSN As String, _
ByVal LocalHostNM As String, ByVal sKey As String)
frm_WinsockDummy.PConnectCli hostName, DSN, LocalHostNM, sKey
clsKey = sKey
End Sub
ConnCliメソッドが行なっている処理は多くない.フォームモジュール内に記述されているPConnectCliプロシージャに引数をパスしているだけである.
Public Sub PConnectCli (ByVal hostName As String, ByVal DSN As String, _
ByVal LocalHostNM As String, ByVal sKey As String)
' DBsvrRBからリモートホスト名とODBCデータソース名を受け取る
Set en = rdoEnvironments(0)
Set cn = en.OpenConnection(DSName:=DSN, Prompt:=rdDriverCompleteRequired)
Winsock1.RemoteHost = hostName
Winsock1.Connect
txt_LocalHost = LocalHostNM
txt_RemoteHost = hostName
txt_dsn = DSN
clsKey = sKey
End Sub
PConnectCliプロシージャでは,RDOの初期化とクライアントへの接続を行なう.コールバックである.Winsockコントロールはクライアントとして機能しているので,この接続処理を行なう時点で,クライアントが接続要求を受けつける準備が完了している必要がある.
| SQLリクエストの受けつけと返信 |
|---|
接続が完了した後で,クエリーの要求を受けつけるのはリスト2のWinsock1_DataArrivalプロシージャである.このプロシージャはWinsockコントロールのDataArrivalイベントプロシージャとして定義されている.
検索処理を行なっているのは,このイベントプロシージャから呼び出されるs_searchプロシージャ内の次のOpenResultsetメソッドである.
Set rs = cn.OpenResultset (strData, rdOpenKeyset, rdConcurReadOnly, rdExecDirect)
取得した結果をストリングに整形し,100レコードずつ返信するメカニズムはType3 DB Serverと変わらない.
リスト2:DBアクセスサーバーのWinsock1_DataArrivalプロシージャと,検索処理を行なうs_searchプロシージャ
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
' クライアントからSQL文を受け取る
Winsock1.GetData strData, vbString
txt_sql.Text = Trim(strData)
Dim RetResult As String
Dim i As Integer
Dim MaxfieldCount As Integer
Dim RecCount As Integer
s_search Trim(strData) ' 検索を実行
MaxfieldCount = rs.rdoColumns.Count ' 項目数を取得
RetResult = ""
RecCount = 0
Do While Not rs.EOF
RecCount = RecCount + 1
For i = 0 To MaxfieldCount - 1
RetResult = RetResult & rs(i) & ";"
Next
RetResult = RetResult & vbCrLf
rs.MoveNext
If RecCount = 100 Or rs.EOF Then
RecCount = 0
Winsock1.SendData RetResult
RetResult = ""
If rs.EOF Then
Exit Do
End If
End If
Loop
rs.Close
Set rs = Nothing
End Sub
Private Sub s_search(strData)
mlngStartTime = timeGetTime() ' 計測を開始
' 検索を実行
Set rs = cn.OpenResultset(strData, rdOpenKeyset, rdConcurReadOnly, rdExecDirect)
mlngEndTime = timeGetTime() ' 計測を終了
lbl_Time.Caption = (mlngEndTime - mlngStartTime) / 1000
End Sub
|
図6:デザインモード時のType4用DBクライアントフォーム![]() |
|
コールバックを待機する クライアントアプリケーション |
|---|
コールバックを受ける必要があるため,当然,クライアントプログラムもType3 DB Serverのクライアントとは異なっている.
図6を見るとわかるように2つのWinsockコントロールが配置されているが,ひとつはマルチユーザーマネージメントサーバーと接続するためのものであり,もうひとつはDBアクセスサーバーからのコールバックを受けるためのものである.当然Socket用のポートは,2つ使うことになる.
コールバックを受けるためのコントロールの初期化は,次のようにフォームのLoadイベントによって行なわれている.
Private Sub Form_Load() Winsock2.Listen End Sub
これでフォームがロードされた段階で,コールバックを受ける準備が始まる.このとき,Winsockコントロールはサーバーとして動作しているため,特定のポートを占有している.クライアントアプリケーションを同一のマシン内で2つ起動しようとすると図7のようなエラーが発生するのはそのためである.Type4 DB Serverを実運用で使うときには,プログラムが二重起動できないような処理を記述すべきだろう.
コールバックを受けた後の動作は,Type3 DB Serverと変わらない.SQL文を送信すれば,結果を受信することができる.
図7:Socketポートが競合するために発生するエラー![]() |
| 今後の展開 |
|---|
Type4 DB Serverを動作させてみると,十分に実用的な能力をもっていることがわかるだろう.もちろん,このサーバーアプリケーションは機能が不足している.Type5 DB Serverではログイン時のマルチユーザー対応と,SELECT以外のSQL文に対応する.これは次号で紹介できる予定である.
現在思案しているのは,それ以降の展開である.ひとつはクライアントの実装を進める展開である.現在のDB ServerはSELECT文を発行すると結果をテキストで取得することができる.DAO(データアクセスオブジェクト)のレコードセットのような高機能なクライアントカーソルに慣れたプログラマには,テキストによる結果セットは機能が不十分に感じられるだろう.
もうひとつの展開は,正式にマルチスレッド対応モデルに変更することである.現在のようにサーバーに不定のSocketポートを割り当てることは,やはりサーバーアプリケーションとしては問題がある.
ただどちらにしても,Visual Basic 6.0に付属するコンポーネントを確認してから,実験を進めることになるだろう.クライアントを強化するには,グリッドコントロールに依存することになるが,現在のVisual Basicに付属しているグリッドコントロールは入力ができない.DBグリッドコントロールは非連結で入力処理することが可能だが,プログラムが猥雑になってしまう.
もちろん,サードパーティ製品を使うことも検討するが,Visual Basic 6.0でよいコントロールが付属するならば,それに越したことはない.
|
マルチスレッド化と データベースアクセスの将来 |
|---|
また,マルチスレッドへの対応は将来的には必須項目である.というのは,Type4 DB Serverを使用するとわかると思うが,ユーザーからの各接続ごとに6MBのメモリを消費する.これは128MBのメモリが搭載されているサーバーマシンで10接続程度が現実的な数値だということになる.現在はメモリが低価格なので,データベースサーバーのクライアントライセンス料と比較して,決して高価ではない.
clsDBaccessクラスのInstancingプロパティをMultiUseに変更すると,1接続あたりのメモリ消費は200KB程度におさえられる.これはマルチスレッドで動作するからである.しかし,現時点でこの方法は薦めていない.それはJetデータベースエンジンが本来,1プロセスを独占して使用するように設計されているからである.
しかし,Jetデータベースエンジンの後継データベースエンジンは,Microsoft SQL Serverと共通化される.ということは当然,マルチスレッドを前提に設計されていることになる.DB Serverをマルチスレッド化するに際してこれは朗報だ.このエンジンが次期Visual Basicに付属するのか,あるいは次期Office製品からバンドルされるのか私は知らない.インターフェイスは標準ではADO(ActiveXデータオブジェクト)になるだろうが,ODBCやDAOもサポートするだろう.しかし,エンジン自体がOLE DBプロパイダとして実装されるのだから,ぜひADOを使いたいものである.そうなると,現在RDO(リモートデータオブジェクト)で実装している部分を変更することが必要になるだろう.しかし,大きなコストは発生しないはずである.
もっとも,多くのベンダーがOLE DBへの対応を発表しない現在では,RDOのまま使用してODBCデータベースを使用することも現実的な選択である.RDOはアパートメントモデルのコンポーネントとして実装されているが,ADOはフリースレッドモデルとして実装されている.これは,マルチプロセッサを使用したときに有利である.もっとも,マルチプロセッサのコンピュータを持っている人を私は知らないので,あまり関係ない.それでも「マルチプロセッサ対応」という言葉の響きは,ちょっと格好いい.
ADOだってODBCブリッジを用意しているから,ADOに移植したとしても問題はない.要するに何でもいいのである.
冒頭ではVisual Basic 6.0には期待していないと書いた.しかしいろいろと考えると,やはり,期待はあるのである.