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

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 (クラス識別子)が追加された同じ名前の構造として定義されGUIDGUIDは、 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_MACSYS_WIN16SYS_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_DISPATCHIDispatch.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 

ここで、 vtVARENUM列挙の値の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コンパイラの知識がなくても、すべての面倒なコードにもかかわらず、タイプライブラリをプログラムで作成できることを示しています。


ICreateTypeLib2ICreateTypeInfo2を使用ICreateTypeLib2と、COMインターフェイスとクラスの記述だけでなく、DLLから通常の関数も作成できます。 このアプローチはVisual Basic 6で使用され、インポートテーブルを介してDLLにバインドします。


コードを簡素化するために、エラーチェックは削除されました。 深刻なプログラムでHRESULT戻り値のHRESULT常に確認し、何か問題が発生した場合は対処する必要があります。



Source: https://habr.com/ru/post/J354648/


All Articles