char m
char m

Reputation: 8336

How to call C# from C to pass array of structs having arrays as members?

I have a native struct:

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

and callback type:

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

static array:

static FormattedMessage_t gFormattedMessages[10];

callback set function:

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

call from native to managed:

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

In Managed:

    [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
        }
    }

This GetArray works for simple types as struct members. This is way beyond my P/Invoke and Native Interop skills.

This is probably wrong in many ways. Any hints how this should be done (changing both structs is no problem) would be highly appreciated.

Upvotes: 1

Views: 760

Answers (1)

David Heffernan
David Heffernan

Reputation: 612794

You'll need to declare the C# struct like so:

[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;
}

Then you'll need to pick out each item from ParamStrings by performing the indexing manually. The ith value runs from indices i*MaxStrLength to (i+1)*MaxStrLength-1. You'll need to pick that out, find the null terminator, and use Encoding.Default.GetString.

Upvotes: 3

Related Questions