2010-10-12 249 views
2

我有兩個C++結構,我必須從C#調用DLL方法時作爲參數發送。MarshalAs嵌套結構

例如,讓我們將它們定義爲:

struct A 
{ 
    int data; 
} 

struct B 
{ 
    int MoreData; 
    A * SomeData; 
} 

,我需要從C#調用具有以下簽名的方法:

int operation (B * data); 

(請注意,我沒有控制在這些C++結構或方法上)。

在C#中,我將這些結構定義爲類:

[StructLayout(LayoutKind.Sequential)] 
class A 
{ 
    public int data; 
} 

[StructLayout(LayoutKind.Sequential)] 
class B 
{ 
    public int MoreData; 

    [MarshalAs(UnmanagedType.Struct)] 
    public A SomeData; 
} 

我已經創建了一個「調試dll」從C#調用,以確保在C++方法中正確接收所有數據。到目前爲止,只有在嵌套結構指針之前發送的數據才能正確發送。

當我嘗試從嵌套結構(B-> A->數據)讀取數據時,出現讀取違例錯誤(AccessViolationException)。

如何編組嵌套結構以便我可以在C++方法中讀取它?

回答

4

您的C#聲明不等效,它會內聯生成SomeData字段。換句話說,等效的原生聲明是:

struct B 
{ 
    int MoreData; 
    A SomeData; 
} 

P/Invoke編組器不能處理指針的成員。這是一個內存管理問題,擁有指針並負責刪除它是非常模糊的。爲了使這項工作在所有的,你必須聲明的結構是這樣的:

struct B { 
     public int MoreData; 
     public IntPtr SomeData; 
    } 

而且自己名帥它。就像這樣:

 var b = new B(); 
     b.MoreData = 0x12345678; 
     var a = new A(); 
     a.Data = 0x789abcde; 
     int len = Marshal.SizeOf(a); 
     b.SomeData = Marshal.AllocCoTaskMem(len); 
     try { 
      Marshal.StructureToPtr(a, b.SomeData, false); 
      someFunction(ref b); 
     } 
     finally { 
      Marshal.FreeCoTaskMem(b.SomeData); 
     } 

在這段代碼中隱含的是,指針由託管代碼擁有和它釋放內存(FreeCoTaskMem調用)。如果本機代碼複製傳遞的結構,那麼這將是一個問題,當它試圖取消引用指針時,它將讀取不良數據或炸彈。 不是釋放內存也不是一個選項,它會產生不可避免的內存泄漏。因爲本機代碼不能使用free()函數來釋放它。如果你最終在這個礦區,那麼你將不得不用C++/CLI語言編寫一個封裝器,以便你可以使用CRT的malloc()函數。

+0

這來得太遲了,剛完成自己的封送。不過,這是正確的答案。不管怎樣,謝謝你! – 2010-10-12 14:44:08