2011-03-24 37 views
3

我想在C#中使用interop調用一些遺留的C代碼。 我不太熟悉C#如何互操作,但必須處理一些令人困惑的結構。 我得到了一部分工作,但當我嘗試將結構體置入C層時,地址混亂了。Interop C#到C++結構

我試圖傳遞結構爲C代碼,它會做一些事情,而我需要得到一個結果返回

我在C#這些結構

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct RETURNBUFFER 
    public IntPtr records; //this is the struct RECORD 
    public IntPtr infoA; // this is the struct INFO 
    public IntPtr infoB; 
    public int number; 
} 
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct INFO 
{ 
    public IntPtr doc; //this is a handle in C code 
    public int cpFirst; 
    public int cpLim; 
} 
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct RECORD 
{ 
    public int size; 
} 

記錄實際的指針在C#這樣定義的另一結構STATS,

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct STATS 
{ 
    public int size; 
    public int a; 
    public int b; 
    public int c; 
    public int d; 
    public int e; 
    public int f; 
    public int g; 
} 
在C#層

,我創建像下面

0的結構
 RETURNBUFFER returnBuffer = new RETURNBUFFER(); 
     returnBuffer.infoA = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO))); 
     returnBuffer.infoB = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO))); 
     returnBuffer.records = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(STATS))); 

當我運行我的代碼,我只能檢索returnBuffer的第一個項目是returnBuffer.records, 所有包括returnBuffer int值的其他項目搞砸了。

我試圖通過它來調試,並期待到地址值, 我發現,當從C#代碼的代碼 - > C中的地址轉移

我不知道爲什麼地址是關閉的,

下面是64位環境下所發生的事情爲例

C# 
&ReturnBuffer 
0x00000000046f05f8 
&ReturnBuffer.records 
0x00000000046f05f8 
&ReturnBuffer.infoA 
0x00000000046f0600 
&ReturnBuffer.infoB 
0x00000000046f0608 
&ReturnBuffer.number 
0x00000000046f0610 
用C

,讓說,我呼籲正在參數RETURNBUFFER * pReturnBuffer功能,

我得到這些地址,

pReturnBuffer 
0x00000000046F05F8 
&pReturnBuffer->records 
0x00000000046F05F8 
&pReturnBuffer->infoA 
0x00000000046F0600 
&pReturnBuffer->infoB 
0x00000000046F0610 **This address is off by 8** 
&pReturnBuffer->number 
0x00000000046F0620 **this is off by 16** 

所以,作爲一個結果, 當代碼移動回C#的功能,

我可以正確地構建returnBuffer.records, 但沒能構建既不infoA也不infoB也得到正確的值returnBuffer.number

不知道我在這裏錯過了什麼。

============================================== =

我修改了代碼的幫助下,樂門Pieng

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] 
public struct CRB 
{ 
    [FieldOffset(0)] 
    public IntPtr pcstat;//CSTAT 
    [FieldOffset(8)] 
    public IntPtr caProc;//NLCA 
    [FieldOffset(24)] 
    public IntPtr caSent;//NLCA 
    [FieldOffset(40)] 
    public int cnlch; 
} 

現在,地址相匹配時,它進入C# - > C++ - > C# 但是我仍然得到一些垃圾數據備份。

我做了一些調查,這裏是我發現的不規律行爲。

在C#代碼

我作出這樣

IntPtr的文本=元帥呼叫。StringToCoTaskMemUni(「我在這裏」);

legacyFunction(文字,裁判returnBuffer)

在這裏

,當我打電話的GetHashCode的功能,我在從函數返回得到以下值

returnBuffer.records.GetHashCode() 473881344 
returnBuffer.infoA.GetHashCode() 473898944 
returnBuffer.infoB.GetHashCode() 473898784 

text.GetHashCode() 468770816 

,這些散列值的變化,

returnBuffer.records.GetHashCode() 543431240 
returnBuffer.infoA.GetHashCode() 473799988 
returnBuffer.infoB.GetHashCode() 473799988 

text.GetHashCode() 473799988 

現在,我其實可以做, Marshal.PtrToStringUni(checkReturnBuffer.infoA),我得到 「我在這裏」

C#現在認爲,infoA和infoB都與文本相同。

============================================== ====== 第二編輯

C++的結構實際上是在

typedef struct RETURNBUFFER 
{ 
    RECORD *precord; 
    INFO infoA;  
    INFO infoB;  
    UINT number; 
} CRB; 

感謝您的答覆,這的確是我的問題。

我是莫名其妙的印象, 在C++ 每一個結構/類/對象我必須做出同等的IntPtr在C#

最後一個問題,而我在這裏,所以我不必須重新定義所有結構在一個新的問題中,

intPtr in struct INFO。 在C++中,它是類型的手柄

我在這裏糾正它定義爲IntPtr?它只是一個句柄,但它不是一個*處理思想,或者我應該讓它成爲一個uint值?

這是我從MSDN站點寫道:「記着,返回或接受手柄真的不透明指針工作的任何API函數。您的代碼應該在Windows元帥手柄爲System.IntPtr值」

如果我將它定義爲IntPtr,

我該如何爲它分配內存?以下是否正確?

returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size); 

解組

Marshal.PtrToStructure(returnBuffer.infoA.doc的typeof(IntPtr的));

這是正確的做法嗎?

謝謝你這麼多

回答

2

這可能是因爲以下任何一種:

  1. 您的C++編譯爲16個字節對準
  2. 您的類型INFOA是C++和C#,或類型的大小之間的不同是不同的

可能的解決方案包括:

[FieldOffset(24)] public IntPtr infoB; 

OR

在C++


比較IntPtr.Sizesizeof(infoB)編輯:看來INFOA是16個字節,但你的信息的聲明不是16個字節。很可能您的C#和C++聲明是不同的。如果你可以在問題中包含你的C++聲明,那將是一件好事。

在此期間,我只能猜測的最佳匹配是:

public struct RETURNBUFFER 
{ 
    public RECORD records; //this is the struct RECORD 
    public INFO infoA; // this is the struct INFO 
    public INFO infoB; 
    public int number; 
} 
+0

in C#IntPtr.Size是8,在C++ sizeof(pReturnBuffer)= 8,而sizeof(pReturnBUffer-> infoA)= 16和sizeof(pReturnBuffer-> records)= 8。可以告訴我你是什麼意思使用Int64? – freshWoWer 2011-03-24 03:00:01

+0

@freshWoWer,很抱歉,Int64部分是來自其他地方的失控文本。不應該在這篇文章中。懷疑,你的infoA不是一個64位的指針。我想你應該在你的問題中包含你的C++結構聲明。顯然,它與C#版本不匹配。 – 2011-03-24 03:17:52

0

儘量確保在Win32結構和C#的結構是位(大小)的映射。這可以通過爲win32類型使用精確的c#類型來實現。

2

你的假設,即RETURNBUFFER包含結構的指針已經是錯誤的。這是infoA和infoB佔用16個字節的唯一方法。 INFO結構當然是16字節長,我看不到infoB的類型。因此:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct RETURNBUFFER 
    public IntPtr records; 
    public INFO infoA; 
    public DUNNO infoB; 
    public int number; 
} 

如果仍有問題,請用C語言更新您的問題。應該很容易從他們身上看到。