4

ネイティブC++COMサーバーdllからのインターフェイスを使用するC#クライアントがあります。DLLは4つのインターフェイスを実装します。これらの4つのインターフェイスは、DLLの4つのコクラスによって実装されます。ただし、クライアントに公開されるコクラスは1つだけです。インターフェイス2、3、4は、インターフェイス1のメソッドの1つによってクライアントに返されます。

C ++ COMサーバー:

interface IFace1: IUnknown{
HRESULT CreateOtherInterface([in] REFIID iidFace, [out, iid_is(iidFace)] void** ppOut);
};

coclass ClassIFace1
{
    [default] interface IFace1;
};

C#クライアント:

ClassIFace1 Face1Obj = new ClassIFace1();

IFace1 Face1Ctrl = (IFace1)Face1Obj; 

IFace2 Face2Ctrl = null;
IntPtr Face2IntPtr = new IntPtr();

Face1Ctrl.CreateOtherInterface(Face2Guid, out Face2IntPtr);
Face2Ctrl = (IFace2)Mashal.PtrToStructure(Face2IntPtr);

//Consume Face2Ctrl

if(Face1Obj != null)
{
    Marshal.ReleaseComObject(Face1Obj);
}

IFace2、IFace3、およびIFace4はIFace1と同じコクラスを共有しないため、Marshal.ReleaseComObject(Face1Obj)行はClassIFace1オブジェクトのみを破棄し、ClassIFace2、ClassIFace3、ClassIFace4オブジェクトは破棄せず、メモリリークが発生する可能性があります。これを解決する方法はありますか?または、Marshal.ReleaseComObject(Face1Obj)は実際に他のCOMオブジェクトも破棄しますか?

4

3 に答える 3

1

ハンスが言ったことのように、CreateOtherInterface非常に奇妙に見えます。通常、自分で作成する必要はありません。クライアントが4つのコクラスすべてにアクセスできることを確認するだけです。次に、Activator.CreateInstanceまたはネイティブCoCreateInstanceがあなたのために正しいことをします。もう1つの選択肢は、1つのコクラスを公開し、その1つのコクラスで4つのインターフェイスすべてをサポートすることです。

ただし、1つのコクラスのみがクライアントに公開されているとおっしゃっていたので、クライアントによって消費されたTLBファイルが他の3つのコクラスを認識しない、または他の3つのコクラスが正しく登録されていないがによって発見されたという奇妙な理由があると思います。いくつかの独自の方法での最初のコクラス。また、サーバー側の実装は変更できないと想定しています。

これらすべての仮定を前提として、ここに私の答えがあります。参照カウントは、4つの剰余類内で独立して維持されます。したがって、最初のコクラスで参照を解放しても、他の3つのコクラスの参照カウントはデクリメントされません。

注意が必要なことがいくつかあります。Marshal.ReleaseComObject(Face1Obj)最初のコクラスをリリースするために使用しています。最初のコクラスがRuntimeCallableWrapper(RCW)によってラップされたため、これを行うことができます。マーティンが言ったように、あなたが呼び出さなくてもMarshal.ReleaseComObject()、.NETランタイムはgarbabgeコレクションが発生したときにあなたのためにそれを行います。

ただし、Face2Ctrlの取得方法は異なります。RCWでラップされていません。返されたポインタを構造体として直接扱っています。メモリアライメントとデータマーシャリングに問題がある可能性があるため、これは私には正しく聞こえません。あなたがしたいことはMarshal.GetObjectForIUnknownあなたのためにRCWを返す呼び出しであるかもしれません。RCWを入手したらMarshal.ReleaseComObject()、タイムリーにRCWをリリースするために電話をかけることができます。

の実装CreateOtherInterfaceがのようである場合QueryInterface、これは常に返されるインターフェイス上にあります。Face2Objを使い終わったら、返されたインターフェイスAddRefを呼び出す必要があります。 RCWによって追加された参照カウントを解放するだけなので十分ではありませんが、この場合は、さらに1回呼び出す必要があります。Marshal.ReleaseMarshal.ReleaseComObject()IUnknown.Release

于 2012-07-16T02:13:13.350 に答える
1

COMオブジェクトの通常の動作では、インターフェイスからのみアクセスします。コクラスは、COMオブジェクトの新しいインスタンスを作成するためにのみ必要であり、コクラスに直接アクセスすることはありません。だから私はあなたの例がそのように見えるべきだと思います:

IFace1 face1Ctrl = new ClassIFace1();

メソッドCreateOtherInterface()は私には少し奇妙に見えます、それはと同じ署名を持っているQueryInterface()ので、私はそれが同じことをするべきだと思います(私はC ++に精通していません):

IFace2 face2Ctrl;
face1Ctrl.CreateOtherInterface(IFace2, out face2Ctrl);

これでうまくいくと思います。試してみてください。通常のQueryInterfaceメソッドの場合、次のようなインターフェイスを取得できるはずです。

IFace2 face2Ctrl = face1Ctrl as IFace2;

COMオブジェクトは参照カウントされ、インターフェイスへの最後の参照を破棄するとすぐに解放されます。ガベージコレクターがCOMオブジェクトへの参照を使用して変数を破棄するとすぐに、COMオブジェクトはそれ自体を解放します。これはC#で問題になる可能性があります。これは、ガベージコレクターを待機する必要があり、解放の順序を決定できないためです。特定の時点でCOMオブジェクトを解放する必要がある場合は、を使用できますMarshal.ReleaseComObject()が、通常は、ガベージコレクターが参照カウンターをデクリメントするのを待つだけです。

COMオブジェクトの実装がわからない限り、各インターフェイスに独自のコクラスがあるのか​​、1つのコクラスが複数のインターフェイスを実装しているのかはわかりません。COMオブジェクトを使用する場合は、この知識は必要ありません。クエリインターフェイスは、新しいコクラスを作成してそのインターフェイスを返すか、それ自体を返し、参照カウンターを増やすことができます。

于 2012-07-15T20:20:41.767 に答える
0

さらに、インターフェースの取得方法にいくつかの間違いがあります。C#クライアントの完全なソリューションは次のとおりです。

//======Create IFace1 and IFace2 interface===============
Type consoleType = Type.GetTypeFromCLSID(Face1CoCLSID);
Object Face1Obj = Activator.CreateInstance(consoleType);
IFace1 Face1Ctrl = (IFace1)Face1Obj;

Guid IFace2Guid = typeof(IFace2).GUID;
IntPtr Face2IntPtr = IntPtr.Zero;

//Face2 object's ref count will go up 1
Face1Ctrl.CreateOtherInterface(Face2Guid, out Face2IntPtr); 

//Face2 object's ref count will go up 2. One by "GetObjectForIUnknown()" 
//and one by "as", since the "as" will trigger .Net to call QueryInterface()
IFace2 Face2Ctrl = Marshal.GetObjectForIUnknown(Face2IntPtr) as IFace2; 

//=============Consume Face2Ctrl=========================

//======Destroy IFace1 and IFace2 interface===============

if (Face2Ctrl != null)
{
//Release 3 times as there were 3 RefCount obtained.

    Marshal.Release(Face2IntPtr);
    Marshal.Release(Face2IntPtr);
    Marshal.Release(Face2IntPtr);
    Face2Ctrl = null;     
}

if(Face1Obj != null)
{
//both Face1 object and Face2 object will get FinalRelease() after
//this line.
    Marshal.ReleaseComObject(Face1Obj); 
    Face1Obj = null;
}
于 2012-07-17T07:27:56.097 に答える