無効なバリアントクラッシュ
質問
C#で最終的に使用するために、ネイティブC ++ DLLをC ++ / CLIでラップした状況があります。
実行時にいくつかの問題を引き起こしているコールバック関数がいくつかあります。特に、次の例外が発生します。
型の未処理の例外 'System.Runtime.InteropServices.InvalidOleVariantTypeException' ToadWrapTest.dllで発生しました
追加情報:指定されたOLE バリアントが無効です。
このコード行(C ++ / CLI):
public delegate int ManagedCallbackFunction (Object^ inst, const Object^ data);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
System::IntPtr ip1 = IntPtr(pInstance);
System::IntPtr ip2 = IntPtr(temp);
Object^ oInst = Marshal::GetObjectForNativeVariant(ip1);
Object^ oData = Marshal::GetObjectForNativeVariant(ip2);
//invoke the callback to c#
//return m_callbackFn::Invoke(oInst, oData);
return 0;
};
この「中間コールバック」を作成した理由デリゲートをC#からネイティブC ++コードに直接マップしようとしたときにスローされる無効なバリアント例外を回避する試みでした。回避策として、C#側でデリゲートを宣言し、そのfuncptrをC ++ / CLIラッパーに渡します。次に、中間funcptrをネイティブC ++に渡し、呼び出しをデイジーチェーン接続します。
私が知っているのは、すべてがネイティブC ++の世界で機能するということです。問題は、void *をマネージワールドにマッピングすることです。次のコードは、コールバックのネイティブC ++バージョンを示しています。
int (*CallbackFunction) (void *inst, const void *data);
ここで誰か助けていただければ、本当にありがたいです。
解決
pInstanceとpDataは本当にさまざまですか?もしそうなら、コールバック関数がより強く型付けされると期待します:
int (*CallbackFunction)(VARIANT *inst, VARIANT *data);
その場合は、コードで実際の VARIANT を手で確認してください。本当にVARIANTを取得していない場合(つまり、実際には単にvoid *ポインターを取得している場合)、それらに固有の意味はないため、C#オブジェクトに変換しようとしないでください。これらはIntPtrとして渡される必要があります。他のタイプの固有の意味を持つ必要があることがわかっている場合は、適切なタイプとしてマーシャリングする必要があります。
他のヒント
台座に感謝します!このようなサードパーティの楽しみに対処しなければならない他の人に、最終的な解決策を以下に投稿しています!コードの最適化が完了していないので、気軽に批判してください。これはまだ解決策を迂回するためのものかもしれません。
最初に、コールバック関数は次のようになりました:
public delegate int ManagedCallbackFunction (IntPtr oInst, IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
これに関する大きな小道具。 void *からObject ^に直接キャストしようとすると、単純に機能しません。 IntPtrと中間コールバックを使用して:
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
return m_callbackFn->Invoke(IntPtr(pInstance), IntPtr(temp));
};
C#側で、オブジェクトをいくつかマッサージして、最終的に作業モデルを取得します。
public static int hReceiveTestMessage(IntPtr pInstance, IntPtr pData)
{
// provide object context for static member function
helloworld2 hw = (helloworld2)GCHandle.FromIntPtr(pInstance).Target;
if (hw == null || pData == null)
{
Console.WriteLine("hReceiveTestMessage received NULL data or instance pointer\n");
return 0;
}
// populate message with received data
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataPacketWrap(pData)));
DataPacketWrap dpw = (DataPacketWrap)GCHandle.FromIntPtr(ip2).Target;
uint retval = hw.m_testData.load_dataSets(ref dpw);
// display message contents
hw.displayTestData();
return 1;
}
「マッサージ」に言及しますオブジェクトは、デリゲートがこのコールバック関数に固有ではなく、実行時に(デリゲートPOVから)pDataがどのオブジェクトになるかわからないためです。この問題のため、pDataオブジェクトを使用して追加の作業を行う必要があります。基本的に、IntPtrを受け入れるには、ラッパーのコンストラクターをオーバーロードする必要がありました。完全な" clarity"のためのコードが提供されています:
DataPacketWrap (IntPtr dp)
{
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};