2011-07-07 54 views
2

我有一些問題處理布爾類型,並在C#和C之間來回編譯這個結構。我在C中生鏽,但希望沒有什麼關鍵那部分是錯誤的。一些P/Invoke C#到C編組問題在結構中使用布爾值

據我讀過/看出,.NET布爾和C#bool類型是4個字節長C類布爾僅1個字節。對於內存佔用的原因,我不會在C代碼中使用定義的BOOL 4字節版本。

下面是一些簡單的測試代碼,希望將我的問題說清楚:


C代碼:

typedef struct 
{ 
     double SomeDouble1; 
     double SomeDouble2; 
     int SomeInteger; 
     bool SomeBool1; 
     bool SomeBool2; 
} TestStruct; 

extern "C" __declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs); 

__declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs) 
{ 
    return structs; 
} 

我打電話使用下面的定義在C#這樣的代碼:

[StructLayout(LayoutKind.Explicit)] 
    public struct TestStruct 
    { 
     [FieldOffset(0)] 
     public double SomeDouble1; 
     [FieldOffset(8)] 
     public double SomeDouble2; 

     [FieldOffset(16)] 
     public int SomeInteger; 

     [FieldOffset(17), MarshalAs(UnmanagedType.I1)] 
     public bool SomeBool1; 
     [FieldOffset(18), MarshalAs(UnmanagedType.I1)] 
     public bool SomeBool2; 
    }; 

    [DllImport("Front.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
    public static extern IntPtr TestGetBackStruct([MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] TestStruct[] structs); 

a ND這裏是C#中的實際測試功能:

[Test] 
    public void Test_CheckStructParsing() 
    { 
     var theStruct = new TestStruct(); 
     theStruct.SomeDouble1 = 1.1; 
     theStruct.SomeDouble2 = 1.2; 
     theStruct.SomeInteger = 1; 
     theStruct.SomeBool1 = true; 
     theStruct.SomeBool2 = false; 

     var structs = new TestStruct[] { theStruct }; 

     IntPtr ptr = TestGetBackStruct(structs); 

     var resultStruct = (TestStruct)Marshal.PtrToStructure(ptr, typeof(TestStruct)); 
    } 

這工作,因爲我得到一個struct回(使用調試器來檢查它)的意義上說,但是完全錯誤的價值觀。即編組根本不起作用。我嘗試過不同版本的C#結構,但沒有成功。因此,這裏是我的問題(1 & 2最重要的):

  1. 是C函數正確,此目的是什麼?
  2. 如何正確地寫入結構以便將結構中的正確值返回給C#?(甚至有必要使用FieldOffset值來定義具有StructLayout(LayoutKind.Explicit)屬性的結構,還是可以使用StructLayout(LayoutKind.Sequential))?
  3. 由於我返回了一個指向C中TestStruct的指針,我猜應該有可能在C#中得到TestStructs的一個數組。但是使用Marshal.PtrToStructure函數似乎不可能。有可能以其他方式嗎?
  4. 通過使用相同的FieldOffset屬性值使多個結構字段指向相同的內存分配,可以使用名爲聯合的C中的某些東西。我明白這一點,但我仍然還沒有得到,當這種情況下會有用。請賜教。
  5. 是否有人推薦一本關於C#P /調用C/C++的好書?我有點厭倦了在網上獲取各種信息。

非常感謝您對這些問題的幫助。我希望他們不是太多。

回答

6

停止使用LayoutKind.Explicit並擺脫FieldOffset屬性和您的代碼將工作。你的偏移量沒有正確對齊字段。

public struct TestStruct 
{ 
    public double SomeDouble1; 
    public double SomeDouble2; 
    public int SomeInteger; 
    [MarshalAs(UnmanagedType.I1)] 
    public bool SomeBool1; 
    [MarshalAs(UnmanagedType.I1)] 
    public bool SomeBool2; 
}; 

聲明在C#中的功能是這樣的:

public static extern void TestGetBackStruct(TestStruct[] structs); 

默認的編組將匹配你的C++聲明(你的代碼是C++而不是C的事實),但你必須確保你的分配在調用函數之前在C#代碼中調用參數TestStruct[]。通常情況下,您還需要將數組的長度作爲參數傳遞,以便C++代碼知道有多少個結構。

請不要嘗試從函數返回結構數組。使用structs參數作爲輸入/輸出參數。

我知道沒有強調P/Invoke的書。這似乎是一種黑色藝術!

+0

參數不是一個真正的數組。它應該被宣佈爲'out TestStruct retval' –

+0

感謝您的信息!但是我確實認爲我正在分配TestStruct []參數:var structs = new TestStruct [] {theStruct};在調用函數之前。另外,我注意到,在resultStruct上的所有結構字段的末尾添加Console.WriteLine時,所有參數(預計整數)實際上都會正確返回。但調試器仍然提供完全關閉的值。那是怎麼回事? :(而且,quriosity殺死了這個貓:是什麼讓我的代碼比C更像C++? – Toby999

+0

'extern「C」'是一個告訴符號。你實際上是否需要一個結構數組,或者像@Hans所說的那樣你只需要一個單獨的項目? –