1

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.

1
  • 1
    I don't remember the details from the top of my mind, but iirc you can create either a custom marshaller or add the MarshalAs flags to ParamStrings etc. (These seem to be missing.) Commented Mar 2, 2016 at 12:22

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! I realized myself after asking that MarshalAs-decorations were missing, but wasn't sure how to write them. Unfortunately I didn't understand the explanation about the ParamStrings.
The thing is, that char Params[10][STR_MAX] is a two dimensional array, and the C# marshaller cannot handle that automatically. It's laid out in memory in the same way as a one dimensional array of length 10*STR_MAX. So that's what my MarshalAs does. But now you need to find the 10 strings contained within that flattened one dimensional array, and convert them to C# strings.
Thanks. I understood what you mean. But does this indicate that I can't call Marshal.PtrToStructure at all? That I got to marshal all members separaterly? Marshal.PtrToStructure results in System.AccessViolationException so I guess that I have to marshal all separately somehow.
There should be no problem with PtrToStructure. You'll just end up with a byte[] array for ParamStrings that will take a little extra work to decode.
Thanks a lot! Now it works. I sort this byte[] to string[] out and I'm done.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.