2011-04-12 30 views
4

夥計們我在調用DLL中的函數後檢索struct成員值時遇到困難。我試圖將C++代碼轉換爲C#,但我不確定它是否正確。請幫我理解我的錯誤(如果有的話)以及如何糾正。如何將聯合C++結構轉換爲C#?

我在這裏的問題是我無法正確地檢索後,我從DLL調用ReceiveMessage函數後INNER STRUCTS(聯盟)的值。例如,m_objMsg.MsgData.StartReq.MsgID始終爲0. 但是,當我嘗試使用C++ .exe程序時,MsgID具有正確的值。 (不爲0)

C++代碼:

extern int ReceiveMessage(SESSION, int, Msg*); 

typedef struct 
{ 
    char SubsId[15]; 
    int Level; 
    char Options[12]; 
} ConxReq; 

typedef struct 
{ 
    char MsgId[25]; 
} StartReq; 


typedef struct 
{ 
    long Length; 
    short Type; 
    union 
    { 
    ConxReq oConxReq; 
    StartReq oStartReq; 
    } Data; 
} Msg; 


///////////////////////////////////////////////////// 
Msg oMsg; 
int rc=ReceiveMessage(Session, 0, &oMsg); 

switch(rc) 
{ 
    case 0: 
    switch(oMsg.Type) 
    { 
     case 0: // ConxReq 
     … 
     break; 

     case 1: // StartReq 
     … 
     break; 
    … 
} 

這裏是我嘗試將其轉換成C#:

[DllImport("MyDLL.dll", 
    CallingConvention = CallingConvention.Cdecl, 
    CharSet = CharSet.Ansi)] 
    protected static extern Int32 ReceiveMessage(IntPtr session, 
    Int32 nTimeOut, 
    [MarshalAs(UnmanagedType.Struct)] ref Msg ptrMsg); 


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct ConxReq 
    {    
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)] 
    public string SubsId; 

    public Int32 Level; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] 
    public string Options; 
    } 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]   
    public struct StartReq 
    {    
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)] 
    public string MsgId; 
    } 


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    protected struct Msg 
    { 
    public int Length; 
    public Int16 Type; 
    public Data MsgData; 
    } 

    StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] 
    public struct Data 
    { 
    [FieldOffset(0)] 
    public ConxReq oConxReq; 

    [FieldOffset(0)] 
    public StartReq oStartReq; 
    } 


    Msg m_objMsg = new Msg(); 
    m_objMsg.MsgData = new Data(); 
    m_objMsg.MsgData.oConxReq = new ConxReq(); 
    m_objMsg.MsgData.oStartReq = new StartReq(); 

    int rc = ReceiveMessage(m_Session, nTimeOut, ref m_objMsg); 


    then the SWITCH Condition 

,如果我加入這個結構的UNION內的C++和C#。 .. 我有一個錯誤,說明了「......正確對齊」或「......重疊......」

C++

ConxNack oConxNack; 

typedef struct 
{ 

    int Reason; 

} ConxNack; 


[StructLayout(LayoutKind.Sequential)]   
public struct ConxNack 
{    
    public int nReason; 
} 

[FieldOffset(0)] 
public ConxNack oConxNack; 

預先感謝您這麼多的時間,並幫助......

回答

1

阿卡什是正確的,看看這裏:http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3

另一種選擇是創建兩個結構,一旦你知道哪些類型是使用合適的演員。

HTH

馬里奧

+0

感謝您的回覆和鏈接。根據鏈接,聯盟內部有兩個結構。他們所做的是在Union中明確提取兩個結構和佈局的所有成員變量。在我的轉換中,我更傾向於將Union中每個結構的起始FieldOffSet都設置爲0.是不可能的? – Ryan 2011-04-12 06:28:07

+0

唯一的區別是我仍然把每個結構中的所有成員變量。 – Ryan 2011-04-12 06:30:03

+0

你是什麼意思創建兩個結構和只是演員取決於類型?對不起,要求... – Ryan 2011-04-12 06:50:06

1

你必須使用StructLayout(LayoutKind.Explicit)和FieldOffsets使工會。

+1

謝謝阿卡什您的回覆。我沒有看到LayoutKind.Absolute,我使用.Explicit並將所有FieldOffSet設置爲(0)使其成爲UNION。 – Ryan 2011-04-12 04:20:32

+0

謝謝阿卡什!我已經在你的共同想法下提供了一個答案。 – Ryan 2011-04-14 02:49:43

1

在C++中,我們知道,UNION的所有成員共享相同的存儲器塊,並且只能有一個對象中的一個部件在一個時間。 爲了在C#中實現這一點,我們需要使用LayoutKind來顯式化並將每個成員的所有起始點設置爲0.

在我之前的示例中,顯示一條錯誤消息,指出對象的偏移量類型被錯誤地對齊或被非對象類型重疊。

答案是我們無法將所有成員設置爲FieldOffSet爲0,因爲不允許將引用類型與值類型組合。 - 感謝Hans Passant的解釋

我所做的是創建UNION成員Structs的副本並將所有字符串成員變量的類型更改爲字節。 我使用了字節,因爲這是一個值類型,所以我可以把這個結構放入FieldOffSet(0)。 請注意,我調整了下一個成員變量的FieldOffSet,所以我仍然可以獲得與我的字符串變量相同的大小。 而且還因爲我有字節成員在最後的結構大小。 感謝Akash Kava和Mario The Spoon爲我提供了一個主意併爲我提供了一個有用的鏈接。

在調用DLL中的函數並傳遞了這個Struct Obj(ref m_objMsg)作爲參數之後,我需要提取這些值。 一種方法是有一個指向非託管內存中結構地址的指針,並將該指針轉換爲帶有相應成員變量(我的原始結構)的新的 結構。

NEW STRUCTS (BYTES) 

//////////////////////////////////////////////////////////////// 

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 31)] 
public struct ConxReq 
{    
    [FieldOffSet(0)] 
    public byteSubsId; 

    [FieldOffSet(15)] 
    public Int32 Level; 

    [FieldOffSet(19)] 
    public byte Options; 
} 

[StructLayout(LayoutKind.Explicit, Size = 4)]   
public struct ConxNack 
{    
    [FieldOffSet(0)] 
    public int nReason; 
} 

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 25)]   
public struct StartReq 
{    
    [FieldOffSet(0)]  
    public byte MsgId; 
} 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
protected struct Msg 
{ 
    public int Length; 
    public Int16 Type; 
    public Data MsgData; 
} 

StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] 
public struct Data 
{ 
    [FieldOffset(0)] 
    public ConxReq oConxReq; 

    [FieldOffset(0)] 
    public ConxNack oConxNack; 

    [FieldOffset(0)] 
    public StartReq oStartReq; 
} 

//////////////////////////////////////////////////////////////// 



MY ORIGINAL STRUCTS 

//////////////////////////////////////////////////////////////// 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct MyConxReq 
{    
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)] 
    public string SubsId; 

    public Int32 Level; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] 
    public string Options; 
} 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]   
public struct MyStartReq 
{    
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)] 
    public string MsgId; 
} 

[StructLayout(LayoutKind.Sequential)]   
public struct MyConxNack 
{    
    public int nReason; 
} 

/////////////////////////////////////////////////////////////// 


Since I have a Msg.Type, i know what kind of struct (type) I could cast the object. 
Like for example 

ReceiveMessage(m_Session, nTimeOut, ref oMsg); 


switch (oMsg.Type) 
{ 
    case 0: // ConxReq 
     IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(oMsg.MsgData.ConxReq); // use the new struct (bytes) 
     Marshal.StructureToPtr(oMsg.MsgData.ConxReq, ptr, false); 
     MyConxReq oMyConxReq = new MyConxReq; 
     oMyConxReq = (MyConxReq) Marshal.PtrToStructure(ptr, typeof(MyConxReq)); // convert it to the original struct 
     Marshal.FreeHGlobal(ptr); 

Then you can use now the oMyConxReq object to acccess the member variables directly. 

請讓我知道,如果你有其他的或更好的方式來做到這一點......如果我所做的是正確的或 請告知,如果我錯過了什麼。

非常感謝你! :)