2013-03-18 177 views
4

我有一個從非託管代碼傳遞字符串到託管的問題。 在我的非託管類(unmanagedClass.cpp)我有一個指針從託管代碼的功能:從非託管代碼傳遞字符串到託管

TESTCALLBACK_FUNCTION testCbFunc; 

TESTCALLBACK_FUNCTION接受一個字符串,並沒有返回值:從ITest接口

typedef void (*TESTCALLBACK_FUNCTION)(char* msg); 

非託管類inherites其中只有一種方法:

STDMETHOD(put_TestCallBack) (THIS_ 
        LPVOID FnAddress  
      ) PURE; 

在managedClass.cs中,我編寫了此代碼:

public class ManagedClass 
{ 
    ITest unmanaged = new unmanagedClass(); 
    public delegate void TestDelegate(string info); 
    ManagedClass() 
    { 
     unmanaged.put_TestCallBack(new TestDelegate(this.Test)); 
    } 
    void Test(string info) 
    { 
      MessageBox.Show(info); 
    } 
} 

[ComImport, Guid("<my guid here>")] 
public class unmanagedClass 
{ 
} 

[ComImport, System.Security.SuppressUnmanagedCodeSecurity, 
Guid("<my guid here>"), 
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
public interface ITest 
{ 
    [PreserveSig] 
    int put_TestCallBack([MarshalAs(UnmanagedType.FunctionPtr), In] Capture.TestDelegate func); 

}

要調用從非託管代碼測試FUNC我用這個

(*testCbFunc)("Func Uragan33::Execute has been started!"); 

但是,當從managedClass.cs測試方法被稱爲我總是收到字符串。 爲什麼會發生?

提前致謝!

回答

5

您在調用約定時不匹配。 C++代碼中的typedef使用默認的調用約定(__cdecl)聲明瞭一個函數指針。但託管代碼中委託的默認值是__stdcall。

您將需要一個屬性來告訴pinvoke編組否則。讓它看起來像這樣:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void TestDelegate(string info); 

刪除函數聲明中的[MarshalAs]。固定在你的C++代碼的類型定義可能是可取的,如果可以的話,顯然讓一切保持一致是首選的解決方案:

typedef void (__stdcall * TESTCALLBACK_FUNCTION)(char* msg); 

無關的,這你需要一個bug修復:

unmanaged.put_TestCallBack(new TestDelegate(this.Test)); 

您創建的委託對象對垃圾收集器不可見。如果將在下一個GC上收集,當本機代碼進行回調時,您的代碼將崩潰。您必須將委託對象存儲在某處,以便GC始終可以看到參考。既可以作爲類中的字段,也可以附加要求類對象需要保持足夠長的時間或靜態變量。

請注意,當您聲明回調接口而不是委託時,所有這些問題都會消失。 COM方式。

+0

非常感謝你! – Alekstim 2013-03-19 08:27:43

相關問題