2016-03-02 110 views
1

我有一個本機的結構:如何從C調用C#傳遞數組作爲成員的結構數組?

typedef struct 
{ 
    char Message[STR_MAX]; 
    char Params[10][STR_MAX]; 
    int GetParamStr[10]; 
    int ParamCount; 
} FormattedMessage_t; 

和回調類型:

typedef void(*FormatMsgCB_t)(FormattedMessage_t *FormatMsgs, int FormatMsgCount); 

靜態數組:

static FormattedMessage_t gFormattedMessages[10]; 

回調函數集:

extern "C" __declspec(dllexport) void DllGuiSetFormatMsgCB(FormatMsgCB_t pCB) 
{ 
    gFormatMsgCB = pCB; 
} 

呼叫從原產地到管理:

void DllGuiSetFormatMessage() 
{ 
    gFormatMsgCB(gFormattedMessages, gFormattedMsgIndex); 
} 

在託管:

[StructLayout(LayoutKind.Sequential)] 
    public struct FormattedMessage_t 
    { 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxStrLength)] 
     public string Message; 

     public string[] ParamStrings; 
     public int[] GetParamStrs; 
     public int ParamCount; 

     public const int MaxStrLength = StrMax; 
    } 

    public static T[] GetArray<T>(IntPtr aTblPtr, int nRows) 
    { 
     var entrySize = Marshal.SizeOf(typeof(T)); 
     IntPtr oneRowPtr = new IntPtr(aTblPtr.ToInt64()); 
     T[] array = new T[nRows]; 

     for (int i = 0; i < nRows; i++) 
     { 
      array[i] = (T)Marshal.PtrToStructure(oneRowPtr, typeof(T)); 
      oneRowPtr = new IntPtr(oneRowPtr.ToInt64() + entrySize); 
     } 

     return array; 
    } 

    private void OnSetFormatMsg(IntPtr formatMsg, int nFormatMsg) 
    { 
     var array = GetArray<FormattedMessage_t>(formatMsg, nFormatMsg); 
     foreach (var msg in array) 
     { 
      var str = msg.Message; 
      // and so on 
     } 
    } 

此工作的getArray對於簡單的類型,結構成員。這超出了我的P/Invoke和Native Interop技能。

這在很多方面可能都是錯誤的。任何提示如何做到這一點(改變這兩個結構是沒有問題的)將不勝感激。

+1

我不記得我頭腦中的細節,但是iirc可以創建一個自定義編組器或將'MarshalAs'標誌添加到'ParamStrings'等。(這些似乎缺失。) – atlaste

回答

3

你需要申報的C#結構像這樣:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct FormattedMessage_t 
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxStrLength)] 
    public string Message; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10*MaxStrLength)] 
    public byte[] ParamStrings; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 
    public int[] GetParamStrs; 

    public int ParamCount; 

    public const int MaxStrLength = StrMax; 
} 

然後你需要通過手動進行索引的每個項目從ParamStrings挑出來。價值從指數i*MaxStrLength(i+1)*MaxStrLength-1運行。您需要挑選出來,找到空終止符,並使用Encoding.Default.GetString

+0

謝謝!在詢問MarshalAs-decorations缺失後,我意識到自己的存在,但不知道如何編寫它們。不幸的是我不明白關於ParamStrings的解釋。 –

+0

問題是,'char Params [10] [STR_MAX]'是一個二維數組,而C#編組無法自動處理。它的存儲方式與長度爲10 * STR_MAX的一維數組相同。這就是我的'MarshalAs'所做的。但是現在你需要找到這個扁平化的一維數組中包含的10個字符串,並將它們轉換爲C#字符串。 –

+0

謝謝。我明白你的意思。但是這是否表明我根本無法調用Marshal.PtrToStructure?我得到了所有成員的統一分開? Marshal.PtrToStructure導致System.AccessViolationException異常,所以我想我必須以某種方式分別編組所有元素。 –