3

最終的に 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);

誰かがここで助けてくれれば、本当に感謝しています。

4

2 に答える 2

2

pInstance と pData は本当に VARIANT ですか? もしそうなら、私はあなたのコールバック関数がより強く型付けされることを期待します:

int (*CallbackFunction)(VARIANT *inst, VARIANT *data);

その場合は、コードで実際のVARIANTを見て、それを手で確認できるはずです。VARIANT を実際に取得していない場合 (つまり、実際には単に void * ポインターを取得している場合) は、固有の意味がないため、それらを C# オブジェクトに変換しようとしないでください。それらは IntPtr として渡される必要があります。他のタイプの固有の意味を持つ必要があることがわかっている場合は、それらを適切なタイプとしてマーシャリングする必要があります。

于 2009-07-30T14:10:44.933 に答える
2

これについて台座に感謝します!このようなサードパーティの楽しみに対処しなければならない他の人に、以下の最終的な解決策を投稿しています! コードの最適化はまだ終わっていないので、気軽に批評してください。これはまだ解決策を迂回する可能性があります。

まず、コールバック関数は次のようになりました。

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 を受け入れるには、基本的にラッパーでコンストラクターをオーバーロードする必要がありました。完全な「明確さ」のためにコードが提供されています。

DataPacketWrap (IntPtr dp)
{ 
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};
于 2009-07-31T02:56:40.750 に答える