如何从 C 调用 C# 以传递具有数组作为成员的结构数组?
How to call C# from C to pass array of structs having arrays as members?
我有一个原生结构:
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 和本机互操作技能。
这在很多方面可能都是错误的。任何提示应该如何完成(更改两个结构都没有问题)将不胜感激。
您需要像这样声明 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
中挑选出每个项目。第 ith 个值从索引 i*MaxStrLength
到 (i+1)*MaxStrLength-1
。您需要将其挑出来,找到空终止符,然后使用 Encoding.Default.GetString
.
我有一个原生结构:
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 和本机互操作技能。
这在很多方面可能都是错误的。任何提示应该如何完成(更改两个结构都没有问题)将不胜感激。
您需要像这样声明 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
中挑选出每个项目。第 ith 个值从索引 i*MaxStrLength
到 (i+1)*MaxStrLength-1
。您需要将其挑出来,找到空终止符,然后使用 Encoding.Default.GetString
.