2009-07-30 103 views
3

我有一種情況,我用C++封裝了一個本機C++ DLL,以便在C#中最終使用。變量崩潰無效

有幾個回調函數在運行時導致一些問題。特別是,我得到以下異常:

型 '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); 

如果任何人都可以在這裏幫助,我會很感激。

回答

2

pInstance和pData真的是VARIANT嗎?如果是這樣,我希望將更多的強類型的回調函數:

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

如果是這樣的話,在你的代碼,你應該能夠看到實際VARIANT手工檢查。如果你真的沒有獲得VARIANT(也就是說,你真的只是得到void *指針),你不應該試圖將它們變成C#對象,因爲它們沒有固有的含義。他們應該通過IntPtr傳遞。如果你知道它們應該有其他類型的內在含義,你需要將它們編組爲適當的類型。

+0

回調是由第三方開發者提供了一個頭文件中定義。如果我可以改變它到VARIANT,我會的。我會嘗試將IntPtr傳遞給C#,然後看看會發生什麼。更新即將發佈... – TomO 2009-07-30 23:37:25

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; 
} 

我提到「按摩」的對象,因爲委託是不是特定於此回調函數,並且我不知道pData將在運行時間之前(來自代表POV)。由於這個問題,我必須對pData對象做一些額外的工作。我基本上不得不重載構造函數在我的包裝接受一個I​​ntPtr。提供代碼,以便完整的「清晰度」:

DataPacketWrap (IntPtr dp) 
{ 
DataPacket* pdp = (DataPacket*)(dp.ToPointer()); 
m_NativeDataPacket = pdp; 
};