SafeArrayGetElementを使用してVT_UNKNOWNのSafeArrayに適切にアクセスする

StackOverflow https://stackoverflow.com/questions/1609025

  •  05-07-2019
  •  | 
  •  

質問

マネージコードには実装とインターフェイス定義が存在するが、ネイティブコンポーネントによって駆動されるCOMコンポーネントがあります。管理対象コンポーネントは、次のメソッド宣言を介してネイティブコードにSafeArrayを返しています。

interface IExample {
  <return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
  object[] DoSomeOperation()
}

生成されたネイティブ署名は、これをIUnknownとして適切に返します。

コードのレビュー中に、SafeArrayGetElementを使用して結果の配列を呼び出すことについていくつかの質問を思いつきました。問題は、SafeArrayGetElementがAddRefされた<=>インスタンスを返すかどうかです。基本的には、次のうちどれが正しいかを要約します

例1:

CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));

例2:

IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));

この主題に関するドキュメントは非常に薄いです。次の行のみが含まれます。

  

データ要素が文字列、オブジェクト、またはバリアントの場合、関数は要素を正しい方法でコピーします。

正しいの定義は少しあいまいです。

役に立ちましたか?

解決

最初の方法は正しくなければならず、COM全体のオブジェクトの処理と一致するはずです。おそらく、あなたが見つけた定義は、消費者が正しい方法を知っていることを前提としています。

記載されている他のアイテムにはそれが必要です。 VARIANTまたはSAFEARRAYをコピーすると、オブジェクトが含まれている場合に暗黙的なAddRef()が実行されます。ただし、VT_BYREFが存在する場合、VARIANTはそれを必要としません。

VariantCopy @ MSDN
SafeArrayCopy @ MSDN

この動作は、COMでパラメーターを処理するルールの一部であるため、SAFEARRAYまたはVARIANTに固有のものではありません。ただし、誰かがルールを回避しようとすることを妨げるものは何もありません。

入力パラメーターの場合、AddRef()の呼び出し先は、今後の使用のためにインターフェイスポインターを保持するつもりがない限り、それを行いません。ただし、パラメーターを使用する他のケースではそれが必要です。

たとえば、VARIANTまたは他のコンテナに配置されたインターフェイスには、少なくとも1つのAddRef()呼び出しを適用する必要があります。そうしないと、データ/参照の転送が一方向であるため、COMメソッドからの出力パラメーターとしてVARIANTを使用するときに問題が発生します元のオブジェクトは、コールが宛先に到着するまでに期限切れになる可能性があります。同様に、インターフェイスをストリームにマーシャリングするには、AddRef()も必要です。

同様に、参照による呼び出しには、少なくとも1つのAddRef呼び出しも必要です。これが当てはまらない場合、適切な長時間の呼び出し(たとえば、DCOM経由)は、参照先のオブジェクトがまだ生きているという保証があり、宛先に到着しない可能性があります。ただし、呼び出しスコープ内または呼び出しスコープの前で作成されるため、オブジェクトは既に1+になっているはずなので、AddRef()/ Release()の追加呼び出しはここでは頻繁にスキップされます。

コンポーネントを変更することが可能で、呼び出しがインプロセスである場合、代わりにGITを使用することが望ましい場合があります。これにより、代わりにトークンを渡すことができ、COMアパートメント間でインターフェイスをマーシャリングするのが簡単になります。関係するオブジェクトの存続期間は、呼び出しの期間を通じて呼び出し元の責任となり、オブジェクトをマーシャリングできなかった場合をトラップできます。

グローバルインターフェイステーブル@ MSDNの作成

BSTRの脚注も興味深いものです。

  

BSTR参照パラメーターを受け取る関数の実装がパラメーターに新しいBSTRを割り当てる場合、以前に参照されたBSTRを解放する必要があります。

文字列操作関数(COM)

他のヒント

AddRef:edである必要がありますが、そのような直接的な情報はありません(たとえば、ソースを読んでいません)。

ドキュメントはかなり明確だと思います-インターフェイスポインタを「正しく」コピーすることはAddRef:ingです。

本当に確認したい場合は、IUnknownを実装する超シンプルなATL COMオブジェクトを構築し、それらの多くをSAFEARRAYに詰めて、CComObjectBase<>::InternalAddRefにブレークポイントを配置します(私の記憶が役立つ場合)。次に、SafeArrayGetElementへの呼び出しをデバッグし、ブレークポイントにヒットするかどうかを確認します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top