将复杂结构从 C# 编组到 C
Marshalling a complex structure from C# to C
我正在阅读有关 64 位环境中 C 和 C# 之间的结构编组的所有内容,但没有成功。
我需要做的是传递结构
typedef struct DescStructTa
{
char* pszNa;
unsigned long ulTId;
char* pszT;
unsigned short usRId;
unsigned long ulOff;
unsigned long ulSi;
char szAcc[2];
unsigned char bySSize;
} DescStruct;
从 C# 应用程序到不是我们调用方法制作的 C DLL
MT_DLL_DECL long GetAll(UINTPTR ulHandler, DescStruct **ppList, unsigned long *pulNumS);
经过长时间的搜索,我编写了以下代码:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct PlcSymbolDescStruct
{
[MarshalAs(UnmanagedType.LPStr)]
string pszNa;
UInt32 ulTId;
[MarshalAs(UnmanagedType.LPStr)]
string pszT;
[MarshalAs(UnmanagedType.U2)]
ushort usRId;
UInt32 ulOff;
UInt32 ulSi;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
string szAcc;
[MarshalAs(UnmanagedType.U1)]
byte bySSize;
}
[DllImport("DataSource.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.U8)]public static extern long GetAll(ulong ulHandler, out DescStruct [] ppList,
out uint pulNumS);
不幸的是,这仍然给我造成堆损坏。
如果我尝试打包少于 8 个,它会(如预期的那样)出现访问冲突,但我预计这会起作用,但我无法弄清楚可能出了什么问题。
有什么建议吗?
提前致谢!
我不确定你是否需要 Pack = 8
。默认打包似乎或多或少在 C# 和 C++ 之间兼容。
szAcc
字段可能使用 Unicode。要切换,请在 [StructLayout]
中指定 CharSet=CharSet.Ansi
最后,如果您的 C API 具有指向它们数组的双指针,则您必须以特殊方式分配该内存,CoTaskMemAlloc 或 LocalAlloc,忘记是哪个了。
相反,实现 2 个函数更容易,也更有效。一是获得所需大小的数组。另一个将它们写入调用者分配的缓冲区,C++ 中的单个指针而不是双指针,以及 C# 中的 UnmanagedType.LPArray
。
更新
allocation should not be necessary since the documentation states that the call returns a pointer to it's internal data structure
运行时不知道该文档中写了什么,无论如何尝试释放,然后崩溃。您可以在 C# API 中使用 out IntPtr
,并手动编组。
如果您有现代 .NET,请使用不安全,将 IntPtr 转换为 PlcSymbolDescStruct*
原始指针,并从该原始指针构造 ReadOnlySpan<PlcSymbolDescStruct>
。
如果您坚持使用旧的桌面 .NET,请在循环中调用 Marshal.PtrToStructure<PlcSymbolDescStruct>
,在循环迭代之间将指针递增 Marshal.SizeOf<PlcSymbolDescStruct>()
字节。在这种情况下无需使用不安全代码。
我正在阅读有关 64 位环境中 C 和 C# 之间的结构编组的所有内容,但没有成功。
我需要做的是传递结构
typedef struct DescStructTa
{
char* pszNa;
unsigned long ulTId;
char* pszT;
unsigned short usRId;
unsigned long ulOff;
unsigned long ulSi;
char szAcc[2];
unsigned char bySSize;
} DescStruct;
从 C# 应用程序到不是我们调用方法制作的 C DLL
MT_DLL_DECL long GetAll(UINTPTR ulHandler, DescStruct **ppList, unsigned long *pulNumS);
经过长时间的搜索,我编写了以下代码:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct PlcSymbolDescStruct
{
[MarshalAs(UnmanagedType.LPStr)]
string pszNa;
UInt32 ulTId;
[MarshalAs(UnmanagedType.LPStr)]
string pszT;
[MarshalAs(UnmanagedType.U2)]
ushort usRId;
UInt32 ulOff;
UInt32 ulSi;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
string szAcc;
[MarshalAs(UnmanagedType.U1)]
byte bySSize;
}
[DllImport("DataSource.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.U8)]public static extern long GetAll(ulong ulHandler, out DescStruct [] ppList,
out uint pulNumS);
不幸的是,这仍然给我造成堆损坏。 如果我尝试打包少于 8 个,它会(如预期的那样)出现访问冲突,但我预计这会起作用,但我无法弄清楚可能出了什么问题。
有什么建议吗?
提前致谢!
我不确定你是否需要 Pack = 8
。默认打包似乎或多或少在 C# 和 C++ 之间兼容。
szAcc
字段可能使用 Unicode。要切换,请在 [StructLayout]
CharSet=CharSet.Ansi
最后,如果您的 C API 具有指向它们数组的双指针,则您必须以特殊方式分配该内存,CoTaskMemAlloc 或 LocalAlloc,忘记是哪个了。
相反,实现 2 个函数更容易,也更有效。一是获得所需大小的数组。另一个将它们写入调用者分配的缓冲区,C++ 中的单个指针而不是双指针,以及 C# 中的 UnmanagedType.LPArray
。
更新
allocation should not be necessary since the documentation states that the call returns a pointer to it's internal data structure
运行时不知道该文档中写了什么,无论如何尝试释放,然后崩溃。您可以在 C# API 中使用 out IntPtr
,并手动编组。
如果您有现代 .NET,请使用不安全,将 IntPtr 转换为 PlcSymbolDescStruct*
原始指针,并从该原始指针构造 ReadOnlySpan<PlcSymbolDescStruct>
。
如果您坚持使用旧的桌面 .NET,请在循环中调用 Marshal.PtrToStructure<PlcSymbolDescStruct>
,在循环迭代之间将指针递增 Marshal.SizeOf<PlcSymbolDescStruct>()
字节。在这种情况下无需使用不安全代码。