Automation での早期バインディングと遅延バインディングの使用

概要

Automation サーバーにバインドする方法は、パフォーマンス、柔軟性、保守性など、プログラム内の多くのことに影響を与える可能性があります。

この記事では、Automation クライアントで使用できるバインドの種類について説明し、各メソッドの両側の重み付けを行います。

詳細情報

Automation は、1 つのソフトウェア コンポーネントが Microsoft のコンポーネント オブジェクト モデル (COM) を使用して別のソフトウェア コンポーネントと通信したり制御したりするプロセスです。 これは、Visual Basic やVisual Basic for Applicationsなどの言語で使用されるほとんどのコンポーネント間通信の基礎であり、ほとんどのプログラムの通常の一部となっています。

従来、Automation オブジェクトは、IDispatch インターフェイスをサポートする任意のオブジェクトです。 このインターフェイスを使用すると、クライアントは、デザイン時に通信している正確なオブジェクトを知らなくても、実行時にメソッドとプロパティを呼び出すことができます。遅延バインディングと呼ばれるプロセス。 ただし、現在、Automation オブジェクトという用語は、IDispatch をサポートしていないものであっても、事実上すべての COM オブジェクトに適用できます (したがって遅延バインドすることはできません)。 この記事では、Automating のオブジェクトが両方のバインド メソッドをサポートしていることを前提としています。

バインドとは何ですか?

バインディングは、プログラマによって記述された関数呼び出しを、関数を実装する実際のコード (内部または外部) と照合するプロセスです。 これは、アプリケーションがコンパイルされるときに実行され、コードで呼び出されるすべての関数をバインドしてからコードを実行する必要があります。

このプロセスを理解するには、書籍を発行するという点で"バインド" について考えてください。 コードが書籍のテキストのようなものであるとします。特定の段落で、"詳細については、第 12 章のページ x を参照してください。ブックが終了するまでページ番号がわからないので、段落を意図したとおりに読み取る前に、ブックのすべてのページを連結し、段落に正しいページ番号を挿入する必要があります。 ブックの他の部分を参照する前に、ブックが "バインド" されるのを待ちます。

バインド ソフトウェアは似ています。 コードは、コードを "読み取る" 前にプルする必要がある部分で構成されています。バインドとは、関数名をメモリ アドレス (より正確にはメモリ オフセット) に置き換える操作で、関数の呼び出し時にコードが "ジャンプ" されます。 COM オブジェクトの場合、アドレスはオブジェクトが保持するポインター (v テーブルと呼ばれる) のテーブル内のメモリ オフセットです。 COM 関数がバインドされると、v テーブルを介してバインドされます。

COM オブジェクトの構造は単純です。 コードがオブジェクトへの参照を保持すると、v テーブルの先頭への間接ポインターが保持されます。 v テーブルは、各エントリがそのオブジェクトで呼び出すことができる異なる関数であるメモリ アドレスの配列です。 COM オブジェクトで 3 番目の関数を呼び出すには、テーブル内の 3 つのエントリをジャンプダウンし、そこに指定されたメモリの場所にジャンプします。 これで関数のコードが実行され、完了すると、次のコード行を実行する準備が戻ります。

+-[Code]------------+  +.................................[COM Object]...+
|                   |  : +-------------+                                :
|Set obj = Nothing -|--->| obj pointer |                                :
|                   |  : +-|-----------+                                :
+-------------------+  :   |   +-----------------+                      :
                       :   +-->| v-table pointer |                      :
                       :       +--|--------------+                      :
                       :          |                                     :
                       :          |  +----------------------------+     :
                       :  (3rd)   |  | Function 1 Address pointer |     :
                       : (Offset) |  +----------------------------+     :
                       :          |  | Function 2 Address pointer |     :
                       :          |  +----------------------------+     :
                       :          +->| Function 3 Address pointer |     :
                       :             +----------------------------+     :
                       +................................................+

上の例は、COM オブジェクトを解放するときに何が起こるかを示しています。 すべての COM オブジェクトは IUnknown から継承されるため、テーブルの最初の 3 つのエントリは IUnknown のメソッドです。 オブジェクトを解放する必要がある場合、コードは v テーブルの 3 番目の関数 (IUnknown::Release) を呼び出します。

さいわい、この作業は Visual Basic によってバックグラウンドで行われます。 Visual Basic プログラマは、v テーブルを直接処理する必要はありません。 ただし、この構造は、すべての COM オブジェクトをバインドする方法であり、バインドの内容を理解しておくことが重要です。

早期バインディング

上記の例は、早期 (または v テーブル) バインディングと呼ばれるものです。 すべての COM オブジェクトでは、COM オブジェクトの IUnknown インターフェイスが呼び出されるたびに、この形式のバインディングが行われます。 しかし、オブジェクトの他の関数についてはどうでしょうか。 Refresh メソッドまたはその Parent プロパティを呼び出す方法 これらは通常、オブジェクトに固有のカスタム関数です。 v テーブル内のそれらの場所を想定できない場合、それらを呼び出すために必要な関数アドレスを見つけるにはどうすればよいでしょうか。

もちろん、その答えは、オブジェクトの v テーブルの外観を事前に把握しているかどうかによって異なります。 そうする場合は、IUnknown メソッドに対して行ったのと同じ早期バインディング プロセスをオブジェクトのカスタム メソッドに実行できます。 これは、一般的に "早期バインディング" を意味します。

オブジェクトに対して早期バインディングを使用するには、その v テーブルがどのように表示されるかを把握する必要があります。 Visual Basic では、オブジェクト、そのインターフェイス (v テーブル)、およびオブジェクトで呼び出すことができるすべての関数を記述するタイプ ライブラリへの参照を追加することで、これを行うことができます。 完了したら、オブジェクトを特定の型として宣言し、v テーブルを使用してそのオブジェクトを設定して使用できます。 たとえば、早期バインディングを使用して Microsoft Office Excel を自動化する場合は、Project から "Microsoft Excel 8.0 オブジェクト ライブラリ" への参照を追加します|[参照] ダイアログを開き、変数を "Excel.Application" 型として宣言します。それ以降、オブジェクト変数に対して行われたすべての呼び出しは、事前バインドされます。


' Set reference to 'Microsoft Excel 8.0 Object Library' in
' the Project|References dialog (or Tools|References for VB4 or VBA).

' Declare the object as an early-bound object
  Dim oExcel As Excel.Application

  Set oExcel = CreateObject("Excel.Application")

' The Visible property is called via the v-table
  oExcel.Visible = True

このメソッドはほとんどの場合うまく機能しますが、デザイン時に使用する正確なオブジェクトがわからない場合はどうなりますか? たとえば、複数のバージョンの Excel と通信する必要がある場合や、"不明な" オブジェクトと完全に通信する必要がある場合はどうでしょうか。

遅延バインディング

COM には IDispatch が含まれています。 IDispatch を実装するオブジェクトには、dispinterface (サポートしている唯一のインターフェイスの場合) またはデュアル インターフェイス (事前にバインドできるカスタム インターフェイスがある場合) があると言われます。 IDispatch にバインドするクライアントは、呼び出す正確なプロパティまたはメソッドが実行時に IDispatch のメソッドを使用して特定されるため、"遅延バインド" と呼ばれます。 前の本の例に戻って、ページ番号をテキストに既に印刷するのではなく、"読み取り時" に "検索" する必要がある目次に向かう脚注のように考えてください。

インターフェイスのマジックは、GetIDsOfNames と Invoke の 2 つの関数によって制御されます。 最初の関数名 (文字列) は、関数を表す識別子 (dispid と呼ばれます) にマップされます。 呼び出す関数の ID がわかったら、Invoke 関数を使用して呼び出すことができます。 この形式のメソッド呼び出しは"遅延バインディング" と呼ばれます。

繰り返しになりますが、Visual Basic では、オブジェクトのバインド方法をオブジェクト宣言で指定します。 オブジェクト変数を "Object" として宣言する場合、実際には、IDispatch を使用するように Visual Basic に指示しているため、バインドが遅れています。

' No reference to a type library is needed to use late binding.
' As long as the object supports IDispatch, the method can 
' be dynamically located and invoked at run-time.

' Declare the object as a late-bound object
  Dim oExcel As Object

  Set oExcel = CreateObject("Excel.Application")

' The Visible property is called via IDispatch
  oExcel.Visible = True

ご覧のように、コードの残りの部分は同じです。 初期バインディングと遅延バインディングの唯一の違い (記述するコードに関して) は変数宣言にあります。

"遅延バインド" とは呼び出される関数であり、呼び出し方法ではない点に注意することが重要です。 バインディング全般に関する前述の説明から、IDispatch 自体が "早期バインド:" であることに注意してください。つまり、Visual Basic では、任意の COM 呼び出しと同様に、v テーブル エントリ (IDispatch::Invoke) を使用して Visible プロパティを設定する呼び出しが行われます。 COM オブジェクト自体は、Excel を表示するための正しい関数への呼び出しの転送を担当します。 この間接参照を使用すると、Visual Basic クライアントをコンパイルできます (つまり、有効な関数アドレスにバインドされます)。ただし、実際に作業を行う正確な関数はまだわかりません。

Dispid バインディング

一部の Automation クライアント (最も顕著な MFC および Visual Basic 3.0 だけでなく、ActiveX コントロールに関して Visual Basic 5.0 および 6.0) では、dispid バインディングと呼ばれる遅延バインディングのハイブリッド形式が使用されます。 COM オブジェクトがデザイン時にわかっている場合は、実行時に GetIDsOfNames を呼び出す必要なく、呼び出される関数のディピッドをキャッシュして IDispatch::Invoke に直接渡すことができます。 これは、関数ごとに 2 つの COM 呼び出しを行う代わりに、1 つだけを行う必要があるため、パフォーマンスを大幅に向上させることができます。

Dispid バインディングは、Visual Basic 5.0 または 6.0 で通常選択できるオプションではありません。 これは、タイプ ライブラリで参照されているが、カスタム インターフェイスを含まないオブジェクト (つまり、dispinterface のみを持つオブジェクトの場合) と集計された ActiveX コントロールに使用されますが、一般に Visual Basic では、通常はディピッド バインディングを使用する任意の場所で早期バインディングを使用します。

どの形式のバインドを使用する必要がありますか?

この質問に対する答えは、他の何よりもプロジェクトの設計に大きく依存します。 Microsoft では、ほぼすべてのケースで早期バインディングをお勧めします。 ただし、遅延バインディングを選択する理由が考えられます。

早期バインディングが推奨される方法です。 アプリケーションが呼び出される関数のアドレスに直接バインドされ、実行時参照を実行する際に余分なオーバーヘッドがないため、最適なパフォーマンスを実現できます。 全体的な実行速度に関しては、遅延バインディングの少なくとも 2 倍の速度です。

早期バインディングでは、型の安全性も提供されます。 コンポーネントのタイプ ライブラリへの参照を設定すると、Visual Basic は IntelliSense のサポートを提供し、各関数を正しくコーディングするのに役立ちます。 また、Visual Basic では、パラメーターまたは戻り値のデータ型が正しくない場合に警告が表示され、コードの作成とデバッグに多くの時間が節約されます。

遅延バインディングは、デザイン時にオブジェクトの正確なインターフェイスが不明な場合でも役立ちます。 アプリケーションが複数の不明なサーバーと通信しようとしている場合、または名前で関数を呼び出す必要がある場合 (たとえば、Visual Basic 6.0 CallByName 関数を使用)、遅延バインディングを使用する必要があります。 遅延バインディングは、バージョン間のインターフェイスを不適切に変更または調整したコンポーネントの複数のバージョン間の互換性の問題を回避する場合にも役立ちます。

早期バインディングに与えられる利点により、可能な限り最適な選択になります。

複数のバージョン間の互換性を維持する

セットアップ パッケージと共に再配布しないコンポーネントを使用し、実行時に通信する正確なバージョンを保証できない場合は、すべてのバージョンのコンポーネントと互換性のあるインターフェイスへの早期バインド、または (場合によっては) 遅延バインディングを使用して特定のバージョンに存在する可能性のあるメソッドを呼び出し、そのメソッドの場合は正常に失敗することに特に注意する必要があります。は、クライアント システムにインストールされているバージョンに存在しません。

Microsoft Office アプリケーションは、このような COM サーバーの良い例を提供します。 通常、Office アプリケーションはインターフェイスを拡張して、新しい機能を追加したり、バージョン間の以前の欠点を修正したりします。 Office アプリケーションを自動化する必要がある場合は、クライアントのシステムにインストールできると思われる最も古いバージョンの製品に早期にバインドすることをお勧めします。 たとえば、Excel 95、Excel 97、Excel 2000、Excel 2002 を自動化できる必要がある場合は、Excel 95 (XL5en32.olb) のタイプ ライブラリを使用して、3 つのバージョンすべてとの互換性を維持する必要があります。

また、Office アプリケーションでは、大きなデュアル インターフェイスを持つオブジェクト モデルが、一部のプラットフォームでマーシャリングの制限を受ける可能性があることを示しています。 すべてのプラットフォームでコードを最適に動作させるには、IDispatch を使用します。

関連情報

COM、v テーブル、および Automation の使用の詳細については、次の書籍を参照してください。

ロジェソン、Dale、Inside COM、MSPRESS、ISBN: 1-57231-349-8。

Curland、Matt、Advanced Visual Basic 6、DevelopMentor、0201707128。