タイプライブラリをプログラムで作成する

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インターフェイスは、上記の原則に基づいて構築されていますが、追加の制限があります。









仮想関数のテーブルだけでなく、スクリプトプログラミング言語の関数名でもインターフェイスを操作するには、 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つを渡す必要があります。







エンティティタイプ、TYPEKIND 説明
TKIND_ALIAS 別のタイプのエイリアスであるタイプ。
TKIND_INTERFACE 純粋な仮想関数、つまり実装されていない関数を持つインターフェース。
TKIND_COCLASS インターフェイスから継承されたクラス。
TKIND_DISPATCH IDispatch.Invoke



介して利用できるメソッドとプロパティのセット。 デフォルトでは、デュアルインターフェイスはTKIND_DISPATCH



返しTKIND_DISPATCH



TKIND_ENUM 列挙。
TKIND_MAX 転送の終了のマーク。
TKIND_MODULE 静的関数とデータのみを含むことができるモジュール(たとえば、DLL)。
TKIND_RECORD メソッドなしの構造。
TKIND_UNION オフセットがゼロの変数の結合。


クラスとインターフェースのみを追加することを検討します。 戻り値は、結果のエンティティを構成する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



はフラグが割り当てられていないことを示します。 フラグは、次の値の組み合わせを取ることができます。







パラメーターのフラグと戻り値 説明
IDLFLAG_FIN インバウンドパラメーター。
IDLFLAG_FOUT 発信パラメーター。呼び出されたオブジェクトから呼び出し元のオブジェクトに情報を返します。通常はポインターです。
IDLFLAG_FRETVAL 関数の「実際の」戻り値は、通常IDLFLAG_FOUT



フラグと組み合わされます。
IDLFLAG_NONE インストールされていません。


場合によっては、オブジェクトにプロパティがある場合があります。 外部からは、プロパティは変数のように見えますが、内部からは、set関数とreturn関数を使用して提供されます。 関数は、次の値のいずれかで説明できます。







行動機能の種類、INVOKEKIND 説明
INVOKE_FUNC 通常の機能。
INVOKE_PROPERTYGET 値を返すプロパティ。
INVOKE_PROPERTYPUT 値を設定するプロパティ。
INVOKE_PROPERTYPUTREF 参照によって値を設定するプロパティ。


追加の機能は実装ごとに分けられます。







実装による機能の種類、FUNCKIND 説明
FUNC_STATIC 実装を伴う静的関数、呼び出しコンテキストなし。 このような関数は通常DLLに存在します。
FUNC_NONVIRTUAL 静的関数は、暗黙的な呼び出しコンテキストを内部的に受け入れる実装を持つクラスのメンバーです。
FUNC_VIRTUAL 仮想関数は、暗黙的な呼び出しコンテキストを内部的に受け入れる実装を持つクラスのメンバーです。
FUNC_PUREVIRTUAL 暗黙の呼び出しコンテキストを内部的に受け入れる純粋な仮想関数。
FUNC_DISPATCH この関数はIDispatch.Invoke



を介してのみ使用可能です。


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



常に確認し、何か問題が発生した場合は対処する必要があります。








All Articles