福岡寿和 FUKUOKA, Toshikazu
富士通SSL
COMとDCOMはよく同一視されます.DCOMが分散COMを意味するのですから,当然と言えば当然です.少々乱暴ですが,ActiveX EXEをリモートに配置するか否かがCOMとDCOMの違いであるともいえるでしょう.しかし,COMとDCOMでは,その技術が内因している問題点やクリアしなければならない技術的な課題が大きく異なります(DCOMにおける認証の煩雑さ,COM+の出現からわかるように複雑化しすぎてしまったCOM).COMについていえば,このような問題点はCOM以外の技術も多かれ少なかれ内包しているものですし,COM使った製品が実際に流通している点を考えると現実的なソリューションとしての魅力も備えています.しかし,DCOMはどうでしょうか.現実的なソリューションとしての魅力はあるのでしょうか.そこで今回は基本に立ち戻って,どのような局面でCOM/DCOMを使うのかを探ると共にCOM/DCOMを使わないときの限界を考えてみたいと思います.
| COMを使わない方法 |
COMを使う利点として,モジュールの再利用性が取り上げられますが,ソースコードが提供されているときなどは,実際にCOMを使うほどのこともありません.ソースコードが提供されているときの,モジュールの再利用性を向上する方法は目新しいものではありませんが,COMを使った再利用性向上方法とコンセプトは同じ物です.
図1:フォームモジュール分割![]() |
モジュール分割〜フォームモジュール
大規模なプログラムを作成するときには,機能単位にモジュールを分割し,そのモジュール単位で作成してゆく方法が取られます.モジュール分割と聞くと「難しそう」と思うかもしれませんが,VisualBasicを使っていれば,画面を分割することで,自然にフォームモジュール分割を行なってくれます.モジュール分割の観点から見れば,画面に含まれる機能は単一である方がよく,それは直感的でわかりやすい画面構成であることを意味します.以上のことを踏まえると,VisualBasicを使った一番簡単なモジュール分割方法は,フォームモジュールの分割であるといえるでしょう(図1).
フォームモジュールの分割を行なうときに注意しなければならないのは,どのようにフォームモジュール間で情報をやり取りするかです.標準モジュールにパブリック変数を定義して,複数のフォームモジュール間で利用する方法もありますが,今回は,クラス化の事を考慮して,パブリック関数やパブリックサブルーチンをフォームモジュールに定義し,その関数やサブルーチンを経由して情報をやり取りする方法を紹介します(リスト1,リスト2).
Private Sub tmrLoad_Timer()
Dim strConn As String
tmrLoad.Enabled = False
fdlgODBCLogon.Show vbModal ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(1)
If fdlgODBCLogon.pblnConn(strConn) Then ・・・・・・・・・・・・・・・・・・(2)
rdcMulti.SQL = "select * from authors"
rdcMulti.Connect = strConn
rdcMulti.Caption = rdcMulti.SQL
rdcMulti.Refresh
Set fdlgODBCLogon = Nothing ・・・・・・・・・・・・・・・・・・・・・・・・・・・(3)
Else
Set fdlgODBCLogon = Nothing
Unload Me
End If
End Sub
------------------------------------------------------------
呼び出し側
(1) 共通フォームモジュールを呼び出す
(2) 共通フォームモジュールのインターフェイス関数を呼び出す
(3) 情報を取得終了後,共通フォームモジュールを破棄する
|
リスト2 共通フォームモジュール(Logon.frmより抜粋) |
Private Sub cmdOK_Click()
Dim strConnect As String ''接続文字列
Dim strDSN As String ''データソース名
strConnect = strConnect & "UID="& txtUID.Text & ";"
strConnect = strConnect & "PWD="& txtPWD.Text & ";"
strConnect = strConnect & "Driver={"& cboDrivers.Text & "};"
strConnect = strConnect & "Server=" & txtServer.Text & ";"
strConnect = strConnect & "DSN='" & strDSN & "';"
mstrConnect = strConnect ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(1)
mblnConnect = True
On Error Resume Next
If mblnConnect Then
Unload Me
End If
End Sub
Public Function pblnConn(rstrConnect As String) As Boolean ・・・(2)
rstrConnect = mstrConnect ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(3)
pblnConn = mblnConnect
End Function
---------------------------------------------------------------
共通フォームモジュール側
(1) インターフェイス関数で返却する値を確保する.コントロールの値を返却
するときもこのようにフォーム共通変数に転記しておく
(2) パブリック関数としてインターフェイス関数を宣言する
(3) フォーム共通変数に転記しておいた値をインターフェイス関数に設定する.
この関数内でコントロールやフォームオブジェクトのプロパティを参照する
とフォームがロード&表示されてるので注意が必要
|
このように,フォーム単位であれば,自然とモジュール分割とモジュール共有が行なえるでしょう.これは,1画面=1機能というくくりが直感的に理解しやすいからだと思います.ですから,まずはフォームモジュール分割を通して,モジュール分割の勘を養うのもよいかもしれません.
モジュール分割〜標準モジュール
本格的なアプリケーションをプログラムしていると,ひとつのイベントの中に複数の機能を含める必要があったり,複数のイベントで共通的な機能を含める必要があったりします.このようなときは,それぞれのイベントの中にすべての機能を記述するのではなく,機能ごとに関数やサブルーチンに分割して,イベントの中では,その分割したものを呼び出すように記述し機能を階層化します(図2).そして,ある範囲の機能ごとに標準モジュールを作成します.もちろん,なるべく標準モジュール間の依存性は疎であり(パブリック変数やパラメタが少ない),標準モジュール内の依存性は密であるのが理想的ですが,フォームモジュールとは異なり,かなり意識して作成しないと理想とするものからは無意味にずれてしまうので,気をつけて分割してください.
図2:機能の階層化![]() |
vbpの一括コンパイル
ソースコードレベルのモジュール再利用で問題になってくるのは,あるモジュールを修正したときに,そのモジュールを使っているプログラムについて,実行ファイルを再コンパイルしなければならないことです.Visual Basicのプロジェクトを構成するファイル(vbp,bas,frm,clsファイルなど)が少ないときには,さほど問題になりませんが,大規模開発などを行なっていて,構成するファイルが1000や2000という数になってくると再コンパイルは一大事業になってしまいます.また障害発生状況を管理していると,構成モジュールが変更されていないのに無闇に再コンパイルすることで実行ファイルのファイル更新日付を変更したくありません.エクスプローラ上で右クリックをしてファイルのプロパティを表示し,バージョンを確認すればよいのかもしれませんが,バージョン確認の第一歩としてファイル更新日付の整合が保たれているのは重要なことです.
話が横道にそれてしまいますが,Visual Basicマガジン誌上で古橋氏が指摘していたように(1996年4月発売Vol.6 品切),Visual Basic 4.0のセットアップウィザードは,その肝心のファイル更新日付をウィザード利用日時に書き換えてしまいます.そのため,セットアップウィザードを使ったアプリケーションをインストールして,自分のPCの環境がよくわからない状況に陥ってしまう現象が時々見られます.私はどんなに素晴らしいソフトウェアでもインストーラがセットアップウィザードで作られているものは導入しないことにしています.
さて,ファイル更新日付を考慮しつつ再利用を行なうためには,実行ファイルよりもそれを構成するファイルが新しいときのみ再コンパイルするような方針をたてなければなりません.依存ファイルを使って,Visual
C++付属にnmakeやフリーソフトのmakeプログラムを使う方法もありますが,依存ファイルのメインテナンスも大変なことがあります.そこで,Visual Basicを使って,Visual Basicのプロジェクトを構成するファイル(vbp,bas,frm,clsファイルなど)が実行ファイルよりも新しいときは,再コンパイルするプログラム(Vbmake.exe)を作成しました.VB4用とVB5用にわかれていますがロジックなどはまったく同一です.ただし,ActiveXコントロールをVisual Basic自体で作成しており,複数のVisual Basicプロジェクトが依存関係にあるときはコンパイル順番などを意識しなければならず,そのためにはVBPファイルの解析を再帰的に呼び出すなどのロジックを工夫しなければなりません.今回は,そこまでの対応がなされていないので,VBMake暫定版となっています.
| バージョン間の互換性 COLUMN | |
|---|---|
| ご存知の方も多いと思いますが,ActiveXコンポーネント(DLL,EXE,Documents,Control)には,CLSIDというIDが振られます.OS自体はこのCLSIDでコンポーネントを認識しています.Visual Basic開発環境の[プロジェクトプロパティ]-[コンポーネント]タグで[バージョン間の互換性]をバイナリ互換にして,コンポーネントの実行ファイルとVBPファイルを関連づけておかないと,ファイルサーバー上などにソースコードを配置していたときなどには,マシンが変わるごとに新しいCLSIDが振られ,レジストリ上に不要なCLSIDが増加してしまいます.一度でも実行ファイルを作成したときは,必ずバイナリ互換を指定してください(図8).この指定さえしておけば,インターフェイスなどが変更されない限りは,同一のCLSIDであり続けます. | 図8:バージョン間の互換性
|
| COMを使う局面 |
ソースコードが手元にあるときは,COMを使うよりもVisual Basicのプロジェクトファイルにモジュールを追加して,一枚岩の実行ファイルとすることができました.しかし,ソースコードが手元になかったり,開発言語が異なっているためにひとつの実行ファイルにすることができない状況では,COMなしでは何も始まりません.
また,一部分だけ入れ替えて,何種類もの製品を作るときも同様です.このときは,ソースコードが手元にあるのですから,それぞれ実行ファイルを作れば良さそうですが,同一ファイル名で機能が異なる実行ファイルを並行開発/管理してゆくのは,ともすれば配布ミスによる障害発生の原因になります.共通機能部分と個別機能部分をCOMで接続して,製品ごとにチョイスできるようにします.
| COMを使う局面 |
| マルチDB対応アプリの作成(その1) |
MDB,SQL ServerとOracleに対応したアプリケーションを作成するとき,DAO/Jetにより問題解決するケースが多いようです.しかし,本来は,
・MDB :DAO/Jet ・SQL Server :RDO ・Oracle :Oracle Objects for OLE
がそれぞれに適したミドルウェアです.DAO/Jetにこだわるのではなく,ActiveX
DLLを作ってミドルウェアをラッピングすることでマルチDB対応アプリケーションを作成するのがよいでしょう.
DAO/Jet対応版を作成する
DAO/Jet対応版のクラスモジュールを含んだvbpファイルと共通機能部分のvbpファイルをプロジェクトグループにします(図5).このとき,DAO/Jet対応部分とプロジェクトグループは,共通機能部分と別ディレクトリにしておくのがよいでしょう(図6).
共通機能部分のvbpでは,DAO/Jetのプロジェクトを参照設定することで,クイックヒント(図7)やパラメータヒントを活用できます(リスト4).本来,共通機能部分のvbpで参照設定を行なうと,その参照先の個別対応部分専属になってしまうので参照設定は行ないませんが,ある程度プログラムが完成してから参照設定を外せば問題ありません.なお,参照設定フォームに表示される内容ですが,同一プロジェクトグループ内では,ActiveXDLLのvbpが参照設定の対象となり[プロジェクト名]が一覧に表示され,他のプロジェクトグループからはActiveX DLLのdllが参照設定の対象となり[プロジェクトの説明]が一覧に表示されます.
DAO/Jet対応版のActiveX DLLは,ひとつのクラスモジュールとひとつのフォームモジュールから構成されています.フォームモジュールはMDBの選択を行なう画面です.DLLのロジックの中心であるクラスモジュール(リスト5)には,
(1) データベース領域を確保.
クラスの外からの参照はないので,プライベート変数
(2) DBと接続する機能:lngLogon
(3) 結果セットを解放する機能:lngClose
(4) DBを解放する機能:lngLogoff
(5) カラム数を返却する機能:lngColCount
(6) 結果セット生成機能:lngCreateDynaset
(7) 列の値を取得する機能:strFIelds
(8) カーソル位置を判断する機能:blnEof
(9) カーソルを移動する機能:subMoveNext
の機能があります.
Private Sub cmdRun_Click()
Dim objDb As clsDAO
Dim objDs As Object
Dim iintCol As Integer
Dim strResult As String
Dim lngRet As Long
On Error GoTo errRun:
lstResult.Clear
Set objDb = New clsDAO
'(1) DBにログインする
objDb.lngLogon
If lngRet > 0 Then
Err.Raise lngRet
End If
Me.MousePointer = vbHourglass
Me.Refresh
'(2) SELECT SQL文を実行して結果セットを作成する
lngRet = objDb.lngCreateDynaset(objDs, Trim$(txtSQL.Text))
If lngRet > 0 Then
Err.Raise lngRet, "OBJECT"
End If
'(3) 結果セットを取得する
Do While Not objDb.blnEof(objDs) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(4)
strResult = ""
For iintCol = 0 To objDb.lngColCount(objDs) - 1 ・・・・・・・・・・・・・(5)
strResult = strResult & _
objDb.strFields(objDs, iintCol) & vbTab ・・・・・・・・・・・・・・(6)
Next
lstResult.AddItem strResult
Call objDb.subMoveNext(objDs) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(7)
Loop
exitRun:
On Error Resume Next
'(8) 結果セットを開放する
objDb.lngClose (objDs)
'(9) DBを開放する
objDb.lngLogoff
Me.MousePointer = vbDefault
Exit Sub
errRun:
If Err.Source <> "OBJECT" Then
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
End If
Resume exitRun:
Resume Next
End Sub
|
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "clsDAO"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit
Private dbsLocal As Database ・・・・・・・・・・・・・・・・・・・・・・・・・・(1)
Public Function lngLogon() As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・(2)
Dim strDBName As String
On Error GoTo errLogon:
lngLogon = 0
frmDAO.Show vbModal
strDBName = frmDAO.strFileNameGet
Set frmDAO = Nothing
Set dbsLocal = Workspaces(0).OpenDatabase(strDBName)
exitLogon:
On Error Resume Next
Set frmDAO = Nothing
Exit Function
errLogon:
lngLogon = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitLogon:
End Function
Public Function lngClose(rrdynlocal As Recordset) As Long ・・・・・・(3)
On Error GoTo errClose:
lngClose = 0
rrdynlocal.Close
exitClose:
On Error Resume Next
Exit Function
errClose:
lngClose = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitClose:
End Function
Public Function lngLogoff() As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(4)
On Error Resume Next
dbsLocal.Close
lngLogoff = 0
End Function
Public Function lngColCount(rrdynlocal As Recordset) As Long ・・・・・・(5)
lngColCount = rrdynlocal.Fields.Count
End Function
Public Function lngCreateDynaset( _
rrdynlocal As Recordtset, rstrSQL As String) As Long ・・・・・・・・・・・(6)
On Error GoTo errCreateDynaset:
lngCreateDynaset = 0
Set rrdynlocal = dbsLocal.OpenRecordset(rstrSQL,dbOpenDynaset)
exitCreateDynaset:
On Error Resume Next
Exit Function
errCreateDynaset:
lngCreateDynaset = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitCreateDynaset:
End Function
Public Function strFields( _
rrdynlocal As Recordset, rintIndex As Integer) As String ・・・・・・(7)
If IsNull(rrdynlocal.Fields(rintIndex).Value) Then
strFields = ""
Else
strFields = rrdynlocal.Fields(rintIndex).Value
End If
End Function
Public Function blnEof(rrdynlocal As Recordset) ・・・・・・・・・・・・・・・・(8)
blnEof = rrdynlocal.EOF
End Function
Public Sub subMoveNext(rrdynlocal As Recordset) ・・・・・・・・・・・・・・・・(9)
rrdynlocal.MoveNext
End Sub
|
図5:プロジェクトグループ![]() |
図6:ディレクトリ構造![]() |
図7:事前バインド時のクイックヒント![]() |
共通機能部分も汎用化する
リスト4では,DAO/Jet対応コンポーネントを参照設定することで,Visual Basic 5.0の編集サーポート機能を使うことができました.しかし,このままでは他のコンポーネントと簡単に切りかえることができません.そこで,SetステートメントにNewを指定して,クラスの新しいインスタンスを生成するのを止めて,CraeteObjectを使ってインスタンスを生成するように変更します.
Set objDb = CreateObject("vbpDAO.clsDAO")
CreateObjectのパラメタは文字列定数または変数が指定できるので,実行ファイルの起動時パラメタなどで使用するクラスを指定して,再コンパイルなしにプログラムの機能を変更することができます.ただし,実行ファイルをパラメタ付きで起動するためには,[スタート]/[ファイル名を指定して実行]メニューからプログラムを起動するか,ショートカットで定義するか,DOSプロンプトから起動するかの方法をとる必要があります.
SQL Server対応版を作成する
共通機能部分はそのままで,ActiveX DLL部分を変更します.まず,参照設定を[Microsoft DAO 3.5 Object Library]から[Microsoft Remote Data Object 2.0]に切り替えます.そして,機能のインターフェイスは変更せずに内部処理をRDO用に変更します(リスト6).また,MDBファイルの選択画面をODBCデータソースのログオン画面に変更します.
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "clsRDO"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit
Private rdcnnSrv As rdoConnection ・・・・・・・・・・・・・・・・・・・・・(1)
Public Function lngLogon() As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(2)
Dim strConn As String
On Error GoTo errLogon:
lngLogon = 0
fdlgODBCLogon.Show vbModal
If fdlgODBCLogon.pblnConn(strConn) Then
Set rdcnnSrv = New rdoConnection
rdcnnSrv.Connect = strConn
rdcnnSrv.CursorDriver = rdUseOdbc
rdcnnSrv.EstablishConnection
End If
exitLogon:
On Error Resume Next
Set fdlgODBCLogon = Nothing
Exit Function
errLogon:
lngLogon = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitLogon:
End Function
Public Function lngClose(rrdynlocal As rdoResultset) As Long ・・・・(3)
On Error GoTo errClose:
lngClose = 0
rrdynlocal.Close
exitClose:
On Error Resume Next
Exit Function
errClose:
lngClose = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitClose:
End Function
Public Function lngLogoff() As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・・(4)
On Error Resume Next
rdcnnSrv.Close
lngLogoff = 0
End Function
Public Function lngColCount(rrdynlocal As rdoResultset) As Long ・・・(5)
lngColCount = rrdynlocal.rdoColumns.Count
End Function
Public Function lngCreateDynaset( _
rrdynlocal As rdoResultset, rstrSQL As String) As Long ・・・・・・・・・・(6)
On Error GoTo errCreateDynaset:
lngCreateDynaset = 0
Set rrdynlocal = rdcnnSrv.OpenResultset(rstrSQL)
exitCreateDynaset:
On Error Resume Next
Exit Function
errCreateDynaset:
lngCreateDynaset = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitCreateDynaset:
End Function
Public Function strFields( _
rrdynlocal As rdoResultset, rintIndex As Integer) As String ・・・・(7)
If IsNull(rrdynlocal.rdoColumns(rintIndex).Value) Then
strFields = ""
Else
strFields = rrdynlocal.rdoColumns(rintIndex).Value
End If
End Function
Public Function blnEof(rrdynlocal As rdoResultset) ・・・・・・・・・・・・・・(8)
blnEof = rrdynlocal.EOF
End Function
Public Sub subMoveNext(rrdynlocal As rdoResultset) ・・・・・・・・・・・・・・・(9)
rrdynlocal.MoveNext
End Sub
|
Oracle対応版を作成する
共通機能部分はそのままで,ActiveX DLL部分を変更します.まず,参照設定を[Microsoft
Remote Data Object 2.0]から[OracleInProcServer2.2 Type Library]に切り替えます.そして,機能のインターフェイスは変更せずに内部処理をoo4o用に変更します(リスト7).また,ODBCデータソースのログオン画面をオラクル用のログオン画面に変更します.
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "clsOo4o"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit
Const mcOLE = "OracleInProcServer.XOraSession"
Private oraSess As OraSession ・・・・・・・・・・・・・・・・・・・・・・・・・・・・(1)
Private oraDb As OraDatabase ・・・・・・・・・・・・・・・・・・・・・・・・・・・・(1) '
Public Function lngLogon() As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(2)
Dim strHost As String
Dim strConn As String
Dim strErrText As String
On Error GoTo errLogon:
lngLogon = 0
frmOraLogon.Show vbModal
If frmOraLogon.pblnResult(strHost, strConn) Then
Set oraSess = CreateObject(mcOLE)
Set oraDb = oraSess.DbCreateDatabase(strHost, strConn, 3&)
End If
exitLogon:
On Error Resume Next
Set frmOraLogon = Nothing
Exit Function
errLogon:
lngLogon = Err
If oraSess.LastServerErr = 0 Then
If oraDb.LastServerErr = 0 Then
strErrText = Error$
Else
strErrText = oraDb.LastServerErrText
oraDb.LastServerErrReset
End If
Else
strErrText = oraSess.LastServerErrText
oraSess.LastServerErrReset
End If
MsgBox strErrText, vbOKOnly + vbExclamation, _
App.EXEName, App.HelpFile, 1
Resume exitLogon:
End Function
Public Function lngClose( _
rrdynlocal As OraDynaset) As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・(3)
On Error GoTo errClose:
lngClose = 0
set rrdynlocal = Nothing
exitClose:
On Error Resume Next
Exit Function
errClose:
lngClose = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitClose:
End Function
Public Function lngLogoff() As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・・(4)
On Error Resume Next
Set oraDb = Nothing
Set oraSess = Nothing
lngLogoff = 0
End Function
Public Function lngColCount( _
rrdynlocal As OraDynaset) As Long ・・・・・・・・・・・・・・・・・・・・・・・・・・・(5)
lngColCount = rrdynlocal.Fields.Count
End Function
Public Function lngCreateDynaset( _
rrdynlocal As Recordset, rstrSQL As String) As Long ・・・・・・・・・・・(6)
On Error GoTo errCreateDynaset:
lngCreateDynaset = 0
Set rrdynlocal = oraDb.CreateDynaset(rstrSQL, 4&)
exitCreateDynaset:
On Error Resume Next
Exit Function
errCreateDynaset:
lngCreateDynaset = Err
MsgBox Error$, vbOKOnly + vbExclamation, App.Title
Resume exitCreateDynaset:
End Function
Public Function strFields( _
rrdynlocal As OraDynaset, rintIndex As Integer) As String ・・・・・・・・(7)
If IsNull(rrdynlocal.Fields(rintIndex).Value) Then
strFields = ""
Else
strFields = rrdynlocal.Fields(rintIndex).Value
End If
End Function
Public Function blnEof(rrdynlocal As OraDynaset) ・・・・・・・・・・・・・・・・・(8)
blnEof = rrdynlocal.EOF
End Function
Public Sub subMoveNext(rrdynlocal As OraDynaset) ・・・・・・・・・・・・・・・・・(9)
rrdynlocal.MoveNext
End Sub
|
| COMを使う局面 |
| マルチDB対応アプリの作成(その2) |
このように対象となるDBのネイティブインターフェイスをラッピングすることで,ミドルウェアの相性問題を回避することが可能です.しかし,インターフェイスを同一にしたとしても,SQL文がRDBMSにより異なることもあります.
そこで,COMを使って提供する機能レベルをもう少し高機能にして,
(1) DBと接続する
(2) 顧客一覧を取得する
(3) DBと切り離す
のようにして,SQL文すらもラッピングしてしまう方法が考えられます.つまり,ビジネスロジックのオブジェクト化です.
| COMを使う局面 |
| ActiveX EXEの活用 |
ActiveX EXEではActiveX DLLと異なり,呼び出し元とは別プロセスで動作します.そのため,どのようにプロセスを別にするかを決定するインスタンシングの指定が可能です.
SingleUseインスタンシング
ActiveX EXEが呼び出されると,同じActiveX EXEが起動中でも,別個のプロセスとして起動されます.メモリなどのリソースを消費する代わりに,同じActiveX
EXEを呼び出している他の実行ファイルの影響を受けることはありません.
MultiUseインスタンシング
図9:スレッドモデルの指定![]() |
MultiUseは,[プロジェクト]-[プロパティ]のスレッドモデルの指定(図9)によりパブリック変数の扱いや他の実行ファイルの影響を受けるかどうかが決まります.
図10:MultiUseサンプル画面
|
図11:DBコネクションプーリング
|
ActiveX EXEのサンプルプログラム(Multi.exe)のスレッドモデルを変更してコンパイルし,呼び出し元の実行ファイル(MultiUse.exe)を複数起動することで,パブリック変数の使われ方がご理解いただけると思います(図10).[CreateObject]ボタンによりActiveX EXEを呼び出して,[Count Get]ボタンでパブリック変数の取得,[CountUp]ボタンで変更を行ないます.
DBコネクションプーリング
大規模な開発を行なっていると機能ごとに実行ファイルをわける必要に出会うときがあります.このとき,複数の実行ファイル間でDBとの接続情報をどのように受け渡していくが,システム全体の使い勝手を左右します.
リスト8:コネクションプーリング (パブリック変数部:ConnPool.bas) |
Attribute VB_Name = "basConnPool" Option Explicit Public poraSess As OraSession Public poraDb As OraDatabase Public pblnConn As Boolean |
リスト9:コネクションプーリング (ConnPool.cls抜粋) |
Public Function lngLogon() As Long
Dim strHost As String
Dim strConn As String
Dim strErrText As String
lngLogon = 0
If Not pblnConn Then '未接続のときのみログイン
On Error Resume Next
Set poraDb = Nothing
Set poraSess = Nothing
pblnConn = False
On Error GoTo errLogon:
frmOraLogon.Show vbModal
If frmOraLogon.pblnResult(strHost, strConn) Then
Set poraSess = CreateObject(mcOLE)
Set poraDb = poraSess.DbCreateDatabase( _
strHost, strConn, 3&)
End If
pblnConn = True
End If
exitLogon:
On Error Resume Next
Set frmOraLogon = Nothing
Exit Function
End Function
Public Function lngLogoff() As Long '実切断は行なわない
lngLogoff = 0
End Function
|
DCOMの問題点は,インストールが煩雑な点とセキュリティの設定が難しい点です.そして,NT4.0が前提となっているテクノロジーなので,DCOMのエラーがイベントログとして出力されることを考えると,Windows
95では,DCOMサーバーとして使うことは論外としても,DCOMクライアントとしても使用を控えた方がよいでしょう. VBRファイルは,サーバーコンポーネントのインストールではなく,クライアントコンポントのインストールに使用されます.
運用を考えたとき,DCOMはやはりCOMに比べて扱いにくいものです.そこで,COMで接続していたDBコネクションプーリングをDCOMにより接続するための手順を追ってみることで,DCOMを使うときの注意点を探ってみたいと思います.
[動作確認]
DCOMを使わない方法
図12:UDP/IP3階層システム

セキュリティ必要ですか?
今回の特集では,DBを使うという観点からCOM/DCOMを評価しています.そこで,DBを使うことを前提としたときに,DCOMが提供しているセキュリティが本当に必要でしょうか. ユーザー認証については,DBのユーザー認証を使えば済みますし,LAN上を流れるデータの暗号化についても必要になることは少なく,もし必要ならば,市販の暗号化ライブラリを入手してもよいでしょう.DCOMのセキュリティ機能は確かに強固ですが,その裏返しとして,設定の難しさや運用時の不安定さが存在しています.強固なセキュリティが必要なシステムならば,そのような負の面をカバーしてでも構築する意味はあるかもしれませんが,そうでないときは,もっと構築しやすい方法を模索してみるのもいいと思います. たとえば,本誌97年12月号の特集で紹介したUDP/IP3階層システムなどもそのひとつの解決案になると思います(図12,リスト10).
リスト10:UPD/IP 3階層システム (UDPMNC.frm抜粋)
Private Sub cmdFunc_Click(Index As Integer)
Dim strBuf As String
tcpClient.RemoteHost = txtHost.Text
tcpClient.Port = txtPort.Text
Select Case Index
Case 0 '送金
strBuf = pcSend & vbTab & cboBank(0).Text & _
vbTab & txtAccount & vbTab & cboBank(1).text
tcpClient.SendData strBuf
Case 1 '引き出し
strBuf = pcWithDraw & vbTab & _
cboBank(0).text & vbTab & txtAccount
tcpClient.SendData strBuf
Case 2 '入金
strBuf = pcDeposit & vbTab & _
cboBank(0).Text &_vbTab & txtAccount
tcpClient.SendData strBuf
Case 3 '残高照会
strBuf = pcBalance & vbTab & cboBank(0).Text_
tcpClient.SendData strBuf
Case 4
tcpClient.SendData strBuf
End Select
End Sub
Private Sub tcpClient_DataArrival(ByVal bytesTotal As Long)
'データが届きました
Dim strBUf As String
Dim strCommand As String
'相手アプリケーションの送信データを取得します
tcpClient.GetData strBuf
lstRecv.AddItem strBuf
'追加された行が表示されるように選択しておく
lstRecv.ListIndex = lstRecv.ListCount - 1
'受信データを解析
If InStr(strBuf,vbTab) > 0 Then
strCommand = Left$(strBuf, InStr(strBuf, vbTab) - 1)
strBuf = Mid$(strBuf, InStr(strBuf, vbTav) + 1)
Select Case strCommand
Case pcBalance
txtAccount = Left$(strBuf, InStr(strBuf, "") - 1)
Case pcError
MsgBox strBuf, vbOKOnly + vbExclamation, App Title
Case pcSend
Case pcWithDraw
Case pcDeposit
Case pcBankList
cboBank(1).Clear
cboBank(1).AddItem strBuf
Case Else
End Select
End If
End Sub
DCOMを使う局面
DBコネクションプーリング
DCOMサーバーコンポーネントのセットアップディスク作成
(1) 事前の作業として,DCOMサーバーコンポーネントの[プロジェクト]-[プロパティ]-[コンポーネント]の[リモートサーバーファイル]チェックボックスをチェック(図13)してVBRファイル(リスト11)を作成します
(2) セットアップウィザードを起動してプロジェクトファイルを指定します(図14)
(3) 「サーバーコンポーネントを含める」とは,DCOMクライアントコンポーネントのセットアップを意味します(図15).DCOMサーバーコンポーネントのときは,必要に応じて(大抵,図15の画面に遷移する直前に必要なコンポーネントが表示されます)ローカルコンポーネントの追加を行ないます
(4) DCOMサーバーコンポーネントのときは,共有ActiveXアプリケーション画面では,図16のように指定します
(5) すべての指定が完了すれば,セットアップディスクが完成します(図17)
リスト11:リモートサーバーファイル(DCPOOL.VBR)
VB5SERVERINFO
VERSION=1.0.0
APPDESCRIPTION=DCOM Connection Pooling Layer for oo4o
HKEY_CLASSES_ROOT\Typelib\{619A06AB-1673-11D2-8A04-00C0D0205A69}\1.0 ⇒
=DCOM Connection Pooling Layer for oo4o
HKEY_CLASSES_ROOT\Typelib\{619A06AB-1673-11D2-8A04-00C0D0205A69}\1.0\0\win32 ⇒
= DCPOOL.exe
HKEY_CLASSES_ROOT\Typelib\{619A06AB-1673-11D2-8A04-00C0D0205A69}\1.0\FLAGS = 0
HKEY_CLASSES_ROOT\DCPool.clsConnPool\CLSID = {619A06AA-1673-11D2-8A04⇒
-00C0D0205A69}
HKEY_CLASSES_ROOT\CLSID\{619A06AA-1673-11D2-8A04-00C0D0205A69}\ProgID ⇒
= DCPool.clsConnPool
HKEY_CLASSES_ROOT\CLSID\{619A06AA-1673-11D2-8A04-00C0D0205A69}\Version ⇒
= 1.0
HKEY_CLASSES_ROOT\CLSID\{619A06AA-1673-11D2-8A04-00C0D0205A69}⇒
\Typelib = {619A06AB-1673-11D2-8A04-00C0D0205A69}
HKEY_CLASSES_ROOT\CLSID\{619A06AA-1673-11D2-8A04-00C0D0205A69}⇒
\LocalServer32 = DCPOOL.exe
HKEY_CLASSES_ROOT\INTERFACE\{619A06A9-1673-11D2-8A04-00C0D0205A69} ⇒
= clsConnPool
HKEY_CLASSES_ROOT\INTERFACE\⇒
{619A06A9-1673-11D2-8A04-00C0D0205A69}\ProxyStubClsid = ⇒
{00020420-0000-0000-C000-000000000046}
HKEY_CLASSES_ROOT\INTERFACE\⇒
{619A06A9-1673-11D2-8A04-00C0D0205A69}\ProxyStubClsid32 = ⇒
{00020420-0000-0000-C000-000000000046}
HKEY_CLASSES_ROOT\INTERFACE\⇒
{619A06A9-1673-11D2-8A04-00C0D0205A69}\Typelib = ⇒
{619A06AB-1673-11D2-8A04-00C0D0205A69}
HKEY_CLASSES_ROOT\INTERFACE\⇒
{619A06A9-1673-11D2-8A04-00C0D0205A69}\Typelib\"version" = 1.0
⇒紙幅の都合で折り返しています
VBRファイルは,サーバーコンポーネントのインストールではなく,クライアントコンポーネントのインストールに使用されます.
[プロジェクト]-[プロパティ]の[全般]タグの[マルチタスク]チェックボックスをチェックするとMSGBOXなどの出力先がイベントログに自動的に切り替わります.
図13:リモートサーバーファイルの指定

図14:セットアップウィザード(サーバー)

図15:ActiveXコンポーネント(サーバー)

図16:共有ActiveXアプリケーション(サーバー)

図17:セットアップウィザードの実行

図18:分散COMの構成(クライアント)

開発環境からDCOMサーバーコンポーネントへの接続設定
このことは,リモートコンピュータを切り替えたときは,設定を変更しなければならないことを意味しています.前述のUDP/IP3階層システムは,同一サブネット内ならば,勝手にサーバーを検出することも可能です.DCOMもUDP/IPを利用しているのですから,このようなこともサポートして欲しいですね.
コンピュータ名の指定欄ですが,NT版のdcomcnfgには,ToolTipsの表示通りに[参照]ボタンが存在しますが,Win95 OSR2.5版には存在しません(図20).
図19:分散COMのプロパティ(クライアント)

図20:サーバーコンピュータの指定(クライアント)

DCOMサーバーコンポーネントの起動
図21:サーバー側でのタスクの起動

図22:サーバー側のイベントログ

DCOMクライアントコンポーネントのセットアップディスク作成
図23:セットアップウィザード(クライアント)

図24:ActiveXコンポーネント(クライアント)

以上の手順により,大抵は正常に動作しますが,セットアップ後,ユーザーが所属するセキュリティグループを切り替えたときなどには,必ず動作確認テストを行なうほうがよいでしょう.DCOMのセキュリティは,NTドメインのセキュリティと密接な関係があるので,総合的なセキュリティ方針の設計が重要になります.行き当たりばったり的な「動いたからOKOK.参照できなくなったからOKOK」というようなセキュリティの設定をしているとさっきまで動作していたDCOMコンポーネントが動作しなくなる危険性を含んでいます.
Panasonic CF-S21
Windows 95 4.00.950 C
IE4.0 4.72.2106.9(non ActiveDesktop)
Visual Basic 5.0 (SP3)
Oracle Objects for OLE 2.2.3
Pentium MMX200Mhz 128MB
Windwos NT 4.0 Server (SP3)
Oracle8 Enterprise Edition R8.0.4
サンプルプログラムのダウンロード
int21 ホームページ | PCDN ホームページ
![]()
Copyright (c) 1998 int21 Corporation
All Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp