TLBタイプライブラリには、COMコンポーネントの機能に関する情報(クラス、インターフェイス、メソッド、パラメーターの型、戻り値)を格納できます。 COMコンポーネントプログラミングチュートリアルでは、通常、midl.exe midl.exe
を使用してタイプライブラリを手動で作成する方法を説明しますが、今日はICreateTypeLib2
およびICreateTypeInfo2
してプログラムでこれを行う方法について説明しICreateTypeInfo2
。
「プログラミング言語」はFreeBASICになります。
インターフェース
フリーベースでインターフェイスを宣言するためのInterface
キーワードはありませんが、素手でこれを行うことができます。 意図の深刻さを示すために、自然画分のインターフェイスを構築し、インターフェイスをゼロから構築するすべての段階を見ていきます。
実装する必要がある一連の関数へのインターフェイスの概念を簡素化します。
データ型としてのインターフェース
自然分数のクラスを定義します。 このようなクラスには、分子と分母を指定するプロパティが必要です。また、分数を追加する操作も追加します。 その後、継承されたオブジェクトから実装された関数にアクセスする必要があるため、このようなセットは将来の関数へのポインターで構成される必要があります。 それらを構造にラップしましょう:
Type IRational Dim GetNumerator As Function()As Integer Dim SetNumerator As Sub(ByVal Numerator As Integer) Dim GetDenominator As Function()As Integer Dim SetDenominator As Sub(ByVal Denominator As Integer) Dim AddRational As Sub(ByVal pRational As IRational Ptr) End Type
呼び出しコンテキストの追加
インターフェース関数にどのオブジェクトがそれらを呼び出すかを知らせるために、それらの最初のパラメーターでインターフェースを実装するオブジェクトへのポインターを追加します。
Type IRational Dim GetNumerator As Function(ByVal this As IRational Ptr)As Integer Dim SetNumerator As Sub(ByVal this As IRational Ptr, ByVal Numerator As Integer) Dim GetDenominator As Function(ByVal this As IRational Ptr)As Integer Dim SetDenominator As Sub(ByVal this As IRational Ptr, ByVal Denominator As Integer) Dim AddRational As Sub(ByVal this As IRational Ptr, ByVal pRational As IRational Ptr) End Type
仮想メソッド表
実際には、インターフェイス関数のセットは、仮想メソッドテーブルと呼ばれる別の構造に割り当てられ、それへのリンクはインターフェイス自体に残されています。 VirtualTableという名前は、多くの場合、VtableまたはVTblに短縮されます。
Type IRationalVirtualTable Dim GetNumerator As Function(ByVal this As IRational Ptr)As Integer Dim SetNumerator As Sub(ByVal this As IRational Ptr, ByVal Numerator As Integer) Dim GetDenominator As Function(ByVal this As IRational Ptr)As Integer Dim SetDenominator As Sub(ByVal this As IRational Ptr, ByVal Denominator As Integer) Dim AddRational As Sub(ByVal this As IRational Ptr, ByVal pRational As IRational Ptr) End Type Type IRational Dim lpVtbl As IRationalVirtualTable Ptr End Type
ループバック解像度
すべてをコンパイルしようとしていますが、何らかの理由でコンパイラはそのようなコードに抵抗します。 実際には、 IRationalVirtualTable
仮想テーブルは、 IRational
発表されるIRational
インターフェイスを参照し、 IRationalVirtualTable
インターフェイスはIRationalVirtualTable
仮想テーブルを参照します。これらは交換されないため、これらから相互参照を停止しません。 この状況から抜け出すには、 Type
演算子によって導入されたインターフェイスの追加の名前が役立ち、元のインターフェイスの名前に下線を追加します。
Type IRational As IRational_ Type IRationalVirtualTable Dim GetNumerator As Function(ByVal this As IRational Ptr)As Integer Dim SetNumerator As Sub(ByVal this As IRational Ptr, ByVal Numerator As Integer) Dim GetDenominator As Function(ByVal this As IRational Ptr)As Integer Dim SetDenominator As Sub(ByVal this As IRational Ptr, ByVal Denominator As Integer) Dim AddRational As Sub(ByVal this As IRational Ptr, ByVal pRational As IRational Ptr) End Type Type IRational_ Dim lpVtbl As IRationalVirtualTable Ptr End Type
さて、これでインターフェースの定義ができました。
COMインターフェイス
すべてのCOMテクノロジーはインターフェース上に構築されています。 すべてのCOMインターフェイスは、上記の原則に基づいて構築されていますが、追加の制限があります。
- すべてのCOMインターフェイスは、
IUnknown
インターフェイスから直接または間接的に継承されます。 AddRef
メソッドとRelease
メソッドを除き、すべてのプロシージャと関数はHRESULT
データ型を返す必要がHRESULT
ます。- 「実際の」戻り値は、最後のパラメーターによって渡されたポインターに関数によって入力されます。
仮想関数のテーブルだけでなく、スクリプトプログラミング言語の関数名でもインターフェイスを操作するには、 IUnknown
からではなくIDispatch
からインターフェイスを直接継承することをお勧めします。 テーブルとIDispatch
介した関数呼び出しを同時にサポートするインターフェイスは、デュアルと呼ばれます。 また、自動化の種類に合わせて、関数のオペランドの型をInteger
からLong
Integer
型に変更します。
GUID
各インターフェイスと実装クラスには、一意の識別子が必要です。 これを行うには、一意性を保証する特別なアルゴリズムに従って計算された128ビットの数値が計算されます。 これらの番号はGUID
と呼ばれます。 ヘッダーファイルでは、 GUID
IID
(インターフェイス識別子)とCLSID
(クラス識別子)が追加された同じ名前の構造として定義されGUID
。 GUID
は、 guidgen.exe
ユーティリティまたはCoCreateGuid
関数を使用して取得できます。 インターフェイスとクラスのGUID
プログラムは、次のように記述されています。
' IRational ' {4116B36A-0B0D-48FD-8DB6-B9867F2A1A37} Dim Shared IID_IRational As IID = Type(&h4116b36a, &hb0d, &h48fd, _ {&h8d, &hb6, &hb9, &h86, &h7f, &h2a, &h1a, &h37}) ' Rational, IRational ' {DD6C5B70-592D-41C1-A391-BCB8C7F7639A} Dim Shared CLSID_Rational As CLSID = Type(&hdd6c5b70, &h592d, &h41c1, _ {&ha3, &h91, &hbc, &hb8, &hc7, &hf7, &h63, &h9a})
COM互換でIRationalインターフェイスを作り直す
これらの要件に従ってインターフェイスを変更し、ヘッダーファイルとウォッチメンを追加してコードを再度有効にしません。 最終的なIRational.bi
ヘッダーファイルは次のとおりです。
#ifndef IRATIONAL_BI #define IRATIONAL_BI #ifndef unicode #define unicode #endif #include once "windows.bi" #include once "win\ole2.bi" ' {4116B36A-0B0D-48FD-8DB6-B9867F2A1A37} Dim Shared IID_IRational As IID = Type(&h4116b36a, &hb0d, &h48fd, _ {&h8d, &hb6, &hb9, &h86, &h7f, &h2a, &h1a, &h37}) ' {DD6C5B70-592D-41C1-A391-BCB8C7F7639A} Const CLSIDS_Rational = "{DD6C5B70-592D-41C1-A391-BCB8C7F7639A}" Dim Shared CLSID_Rational As CLSID = Type(&hdd6c5b70, &h592d, &h41c1, _ {&ha3, &h91, &hbc, &hb8, &hc7, &hf7, &h63, &h9a}) Type IRational As IRational_ Type IRationalVirtualTable ' IDispatch Dim VirtualTable As IDispatchVtbl Dim GetNumerator As Function(ByVal this As IRational Ptr, ByVal pResult As Long Ptr)As HRESULT Dim SetNumerator As Function(ByVal this As IRational Ptr, ByVal Numerator As Long)As HRESULT Dim GetDenominator As Function(ByVal this As IRational Ptr, ByVal pResult As Long Ptr)As HRESULT Dim SetDenominator As Function(ByVal this As IRational Ptr, ByVal Denominator As Long)As HRESULT Dim AddRational As Function(ByVal this As IRational Ptr, ByVal pRational As IRational Ptr)As HRESULT End Type Type IRational_ Dim lpVtbl As IRationalVirtualTable Ptr End Type #endif
タイプライブラリ
タイプライブラリは、 IRational
インターフェイスIRational
と、それを実装するRational
クラスで構成されます。
準備段階
COM環境が機能するには、 CoInitialize(0)
呼び出して初期化する必要があります。 不要になったら、適切なCoUnInitialize()
呼び出します。
将来のライブラリの識別子を定義します。
' {23F94DA0-5C11-46C1-9F27-6A3FE27985CF} Dim Shared LIBID_Rational As GUID = Type(&h23f94da0, &h5c11, &h46c1, _ {&h9f, &h27, &h6a, &h3f, &he2, &h79, &h85, &hcf})
IRational
IDispatch
から継承されるため、 IDispatch
からのIRational
保存されているstdole32.tlb
ライブラリをダウンロードし、 stdole32.tlb
リンクを追加する必要があります。
CoInitialize(0) Dim pIDispatchTypeInfo As ITypeInfo Ptr = Any Dim pStdOleTypeLib As ITypeLib Ptr = Any LoadTypeLib("stdole32.tlb", @pStdOleTypeLib) pStdOleTypeLib->lpVtbl->GetTypeInfoOfGuid(pStdOleTypeLib, @IID_IDispatch, @pIDispatchTypeInfo) ' stdole32.tlb pStdOleTypeLib->lpVtbl->Release(pStdOleTypeLib)
ライブラリ作成
CreateTypeLib2
関数を使用して、ライブラリを作成するためのICreateTypeLib2
インターフェイスを取得できます。 システムのタイプ( SYS_MAC
、 SYS_WIN16
、 SYS_WIN32
またはSYS_WIN64
)、ライブラリファイル名、およびインターフェイスへのポインターの3つのパラメーターを取ります。
Dim pCreateTypeLib As ICreateTypeLib2 Ptr = Any CreateTypeLib2(SYS_WIN32, "Rational.tlb", @pCreateTypeLib) ' , GUID, , pCreateTypeLib->lpVtbl->SetName(pCreateTypeLib, "Rational") pCreateTypeLib->lpVtbl->SetGuid(pCreateTypeLib, @LIBID_Rational) pCreateTypeLib->lpVtbl->SetVersion(pCreateTypeLib, 1, 0) pCreateTypeLib->lpVtbl->SetLcid(pCreateTypeLib, 1049) ' pCreateTypeLib->lpVtbl->SetDocString(pCreateTypeLib, " ")
IRationalインターフェイスをライブラリに追加する
CreateTypeInfo
インターフェイスのCreateTypeInfo
メソッドを使用すると、インターフェイス、クラス、関数を含むモジュール、列挙、構造、関連付け、およびエイリアスをライブラリに追加できます。 これを行うには、 TYPEKIND
値の1つを渡す必要があります。
クラスとインターフェースのみを追加することを検討します。 戻り値は、結果のエンティティを構成するICreateTypeInfo
です。
Dim pCreateTypeInfoIRational As ICreateTypeInfo Ptr = Any pCreateTypeLib->lpVtbl->CreateTypeInfo(pCreateTypeLib, @IRationalInterfaceName, TKIND_INTERFACE, @pCreateTypeInfoIRational) ' IID pCreateTypeInfoIRational->lpVtbl->SetGuid(pCreateTypeInfoIRational, @IID_IRational) pCreateTypeInfoIRational->lpVtbl->SetDocString(pCreateTypeInfoIRational, @" ") ' pCreateTypeInfoIRational->lpVtbl->SetTypeFlags(pCreateTypeInfoIRational, TYPEFLAG_FDUAL Or TYPEFLAG_FOLEAUTOMATION)
継承のために、インターフェースdadへのリンクを作成する必要があります。 車輪の小さな棒:すべての父親への追加リンクのインデックスを個別に監視する必要があります。
' IDispatch Dim RefType As HREFTYPE = Any hr = pCreateTypeInfoIRational->lpVtbl->AddRefTypeInfo(pCreateTypeInfoIRational, pIDispatchTypeInfo, @RefType) ' 0 — hr = pCreateTypeInfoIRational->lpVtbl->AddImplType(pCreateTypeInfoIRational, 0, RefType) ' IDispatchTypeInfo pIDispatchTypeInfo->lpVtbl->Release(pIDispatchTypeInfo)
IRationalインターフェイスへの機能の追加
関数にはパラメーターと戻り値があります。 関数はFUNCDESC
構造体で表され、パラメーターはELEMDESC
構造体の配列で表されます。 インターフェイスのすべての関数はHRESULT
返すため、事前にすべての関数の戻り値を作成できます。
Dim HresultReturnedValue As ELEMDESC With HresultReturnedValue .tdesc.vt = VT_HRESULT .idldesc.wIDLFlags = IDLFLAG_NONE End With
ここで、 vt
はVARENUM
列挙の値の1つを定義し、 IDLFLAG_NONE
はフラグが割り当てられていないことを示します。 フラグは、次の値の組み合わせを取ることができます。
場合によっては、オブジェクトにプロパティがある場合があります。 外部からは、プロパティは変数のように見えますが、内部からは、set関数とreturn関数を使用して提供されます。 関数は、次の値のいずれかで説明できます。
追加の機能は実装ごとに分けられます。
Getnumerator関数
高レベルのプログラミング言語では、関数を呼び出すためのコンテキスト(オブジェクトへのポインター)と仮想関数のテーブルはプログラマーから隠されており、インターフェイスでは考慮されないため、パラメーターを設定するときにそれらを示しません。 GetNumerator
関数GetNumerator
説明を紹介しましょう。
Const MaxArgumentGetNumeratorNamesLength As UINT = 2 Const MaxArgumentGetNumeratorLength As SHORT = 1 ' ' , Get. Dim GetNumeratorArgumentNames(MaxArgumentGetNumeratorNamesLength - 1) As WString Ptr = Any GetNumeratorArgumentNames(0) = @"Numerator" GetNumeratorArgumentNames(1) = @"pResult" ' — «» Dim GetNumeratorArguments(MaxArgumentGetNumeratorLength - 1) As ELEMDESC Dim RetvalGetNumerator As TYPEDESC With RetvalGetNumerator .vt = VT_I4 ' Long End With With GetNumeratorArguments(0) .tdesc.vt = VT_PTR ' .tdesc.lptdesc = @RetvalGetNumerator ' .idldesc.wIDLFlags = IDLFLAG_FOUT Or IDLFLAG_FRETVAL End With
GetNumerator
関数のFUNCDESC
構造体のGetNumerator
:
Dim GetNumeratorDefinition As FUNCDESC = Any With GetNumeratorDefinition .memid = 0 ' IDispatch.Invoke, ‐ .lprgscode = 0 ' HRESULT .cScodes = 0 ' .lprgelemdescParam = @GetNumeratorArguments(0) ' .cParams = MaxArgumentGetNumeratorLength ' .cParamsOpt = 0 ' .elemdescFunc = HresultReturnedValue ' HRESULT .funckind = FUNC_PUREVIRTUAL ' .invkind = INVOKE_PROPERTYGET ' .callconv = CC_STDCALL ' STDCALL .oVft = 0 ' , FUNC_VIRTUAL .wFuncFlags = 0 End With
これで、関数をインターフェイスにプッシュできます。 ここでも、追加された関数のインデックスに従う必要があります。この場合は0です。
' pCreateTypeInfoIRational->lpVtbl->AddFuncDesc(pCreateTypeInfoIRational, 0, @GetNumeratorDefinition) ' pCreateTypeInfoIRational->lpVtbl->SetFuncAndParamNames(pCreateTypeInfoIRational, 0, @GetNumeratorArgumentNames(0), MaxArgumentGetNumeratorNamesLength) ' pCreateTypeInfoIRational->lpVtbl->SetFuncDocString(pCreateTypeInfoIRational, 0, @" ")
関数setnumerator
同様に、 Numerator
プロパティの2番目の部分を定義します。
Const MaxArgumentSetNumeratorNamesLength As UINT = 2 Const MaxArgumentSetNumeratorLength As SHORT = 1 Dim SetNumeratorArgumentNames(MaxArgumentSetNumeratorNamesLength - 1) As WString Ptr = Any SetNumeratorArgumentNames(0) = @"Numerator" SetNumeratorArgumentNames(1) = @"Numerator" ' Dim SetNumeratorArguments(MaxArgumentSetNumeratorLength - 1) As ELEMDESC With SetNumeratorArguments(0) .tdesc.vt = VT_I4 ' Long .idldesc.wIDLFlags = IDLFLAG_FIN End With Dim SetNumeratorDefinition As FUNCDESC = Any With SetNumeratorDefinition .memid = 0 ' GetNumerator .lprgscode = 0 .cScodes = 0 .lprgelemdescParam = @SetNumeratorArguments(0) ' .cParams = MaxArgumentSetNumeratorLength ' .cParamsOpt = 0 .elemdescFunc = HresultReturnedValue .funckind = FUNC_PUREVIRTUAL .invkind = INVOKE_PROPERTYPUT ' .callconv = CC_STDCALL .oVft = 0 .wFuncFlags = 0 End With
インターフェイスに関数を追加するには、再度手動でインデックスに従う必要があります。 ただし、 SetFuncAndParamNames
メソッドでは、プロパティのペアであるため、前のGetNumerator
関数のインデックスを指定SetFuncAndParamNames
必要SetFuncAndParamNames
あります。
pCreateTypeInfoIRational->lpVtbl->AddFuncDesc(pCreateTypeInfoIRational, 1, @SetNumeratorDefinition) ' 0 — pCreateTypeInfoIRational->lpVtbl->SetFuncAndParamNames(pCreateTypeInfoIRational, 0, @SetNumeratorArgumentNames(0), MaxArgumentSetNumeratorNamesLength) pCreateTypeInfoIRational->lpVtbl->SetFuncDocString(pCreateTypeInfoIRational, 1, @" ")
同様に、分母を返し、設定するために、インデックス2および3の関数が追加されます。
AddRational関数
AddRational
関数は、 IRational
へのポインター型のパラメーターを取ります。 しかし、このタイプのデータは自動化にはないため、 IDispatch
置き換えます。
Const MaxArgumentAddRationalLength As SHORT = 1 Dim AddRationalArguments(MaxArgumentAddRationalLength - 1) As ELEMDESC Dim RetvalAddRational As TYPEDESC With RetvalAddRational .vt = VT_DISPATCH End With With AddRationalArguments(0) .tdesc.lptdesc = @RetvalAddRational .tdesc.vt = VT_PTR ' IDispatch Ptr .idldesc.wIDLFlags = IDLFLAG_FIN End With
オブジェクトブラウザーでは、パラメーターはObject
として表示され、その送信はByRef
リンクに従います。
AddRational
関数AddRational
以前のものと同様に入力され、関数タイプをINVOKE_FUNC
に変更するためにのみ残ります。関数をインターフェイスにアタッチするときは、以前の追加エンティティよりも1つ多くインデックスを指定します。この場合は4です。アイテム。
IRationalインターフェイスを閉じる
将来のRational
クラスを継承できるように、 ITypeInfo
からITypeInfo
インターフェイスを取得します。
Dim pIRationalTypeInfo As ITypeInfo Ptr = Any pCreateTypeInfoIRational->lpVtbl->QueryInterface(pCreateTypeInfoIRational, @IID_ITypeInfo, @pIRationalTypeInfo)
IRational
コンテンツを設定した後、すべてのコンテンツを封印して破棄する必要IRational
ありIRational
。
pCreateTypeInfoIRational->lpVtbl->LayOut(pCreateTypeInfoIRational) pCreateTypeInfoIRational->lpVtbl->Release(pCreateTypeInfoIRational)
ライブラリへのRationalクラスの追加
クラスの追加は、機能を備えたインターフェイスよりもはるかに高速です。これは、インターフェイスのクラスにフォルダーを追加するだけでよいためです。
' Dim pCreateTypeInfoRational As ICreateTypeInfo Ptr = Any pCreateTypeLib->lpVtbl->CreateTypeInfo(pCreateTypeLib, @"Rational", TKIND_COCLASS, @pCreateTypeInfoRational) ' GUID pCreateTypeInfoRational->lpVtbl->SetGuid(pCreateTypeInfoRational, @CLSID_Rational) pCreateTypeInfoRational->lpVtbl->SetDocString(pCreateTypeInfoRational, @" ") ' IRational Dim RefType As HREFTYPE = Any pCreateTypeInfoRational->lpVtbl->AddRefTypeInfo(pCreateTypeInfoRational, pIRationalTypeInfo, @RefType) pCreateTypeInfoRational->lpVtbl->AddImplType(pCreateTypeInfoRational, 0, RefType) pIRationalTypeInfo->lpVtbl->Release(pIRationalTypeInfo) ' pCreateTypeInfoRational->lpVtbl->LayOut(pCreateTypeInfoRational) pCreateTypeInfoRational->lpVtbl->Release(pCreateTypeInfoRational)
ライブラリを保存しています
結果をディスクに保存します。
pCreateTypeLib->lpVtbl->SaveAllChanges(pCreateTypeLib) pCreateTypeLib->lpVtbl->Release(pCreateTypeLib) CoUnInitialize()
結論
この例は、IDLインターフェイス定義言語とmidl.exe
コンパイラの知識がなくても、すべての面倒なコードにもかかわらず、タイプライブラリをプログラムで作成できることを示しています。
ICreateTypeLib2
とICreateTypeInfo2
を使用ICreateTypeLib2
と、COMインターフェイスとクラスの記述だけでなく、DLLから通常の関数も作成できます。 このアプローチはVisual Basic 6で使用され、インポートテーブルを介してDLLにバインドします。
コードを簡素化するために、エラーチェックは削除されました。 深刻なプログラムでHRESULT
戻り値のHRESULT
常に確認し、何か問題が発生した場合は対処する必要があります。