从C#(.NET Core)向非托管C++传递结构体数组失败求助
你的核心问题在于C和C#之间的参数类型不匹配——C函数期望的是指向结构体指针的数组(LOG_SAVE_DETAILS**),但你在C#里直接传递了SaveDetails[](对应C++的连续结构体数组LOG_SAVE_DETAILS[]),这两种类型在内存布局上完全不同,自然会导致访问违例。
下面是一步步的解决方案:
1. 修正结构体定义(优先用struct而非class)
结构体是值类型,封送时更贴合C++结构体的语义,同时确保字段和布局完全匹配:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SaveDetails { [MarshalAs(UnmanagedType.LPWStr)] public string Log; public FILETIME FileTime; [MarshalAs(UnmanagedType.Bool)] public bool Saved; } // 确保FILETIME和C++的定义完全一致 [StructLayout(LayoutKind.Sequential)] public struct FILETIME { public uint dwLowDateTime; public uint dwHighDateTime; }
2. 调整委托和调用逻辑,手动构建指针数组
因为C++需要的是LOG_SAVE_DETAILS**,我们需要手动为每个结构体分配非托管内存、拷贝数据,再把这些指针组成数组传递给函数,调用完成后务必释放内存避免泄漏。
首先修改委托的参数类型:
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] private delegate Status DLogSaveFiles(IntPtr logsToSavePtrArray, string destinationPath);
然后重写SaveFiles方法:
public Status SaveFiles(SaveDetails[] logsToSave, string destinationPath) { Status result = Status.FunctionNotAvailable; if (!LogSaveFilesAvailable) return result; _dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles)); // 为每个结构体分配非托管内存并拷贝数据 IntPtr[] structPointers = new IntPtr[logsToSave.Length]; for (int i = 0; i < logsToSave.Length; i++) { structPointers[i] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SaveDetails))); Marshal.StructureToPtr(logsToSave[i], structPointers[i], false); } try { // 分配内存存储指针数组 IntPtr ptrArray = Marshal.AllocHGlobal(IntPtr.Size * structPointers.Length); try { // 把指针数组拷贝到非托管内存 Marshal.Copy(structPointers, 0, ptrArray, structPointers.Length); // 调用C++函数 result = _dLogSaveFiles(ptrArray, destinationPath); } finally { // 释放指针数组的内存 Marshal.FreeHGlobal(ptrArray); } } finally { // 释放每个结构体的非托管内存 foreach (IntPtr ptr in structPointers) { if (ptr != IntPtr.Zero) { Marshal.FreeHGlobal(ptr); } } } return result; }
3. 验证调用约定和字符集一致性
- 确保C++的
_API宏对应的是__stdcall调用约定(和C#的CallingConvention.Winapi匹配,Windows平台下Winapi等价于stdcall)。 - 字符集要统一:如果C++项目编译时用的是Unicode字符集(
LPTSTR等价于wchar_t*),那么C#的CharSet.Unicode是正确的;如果是多字节字符集,需要改成CharSet.Ansi。
额外提示
如果你的C++函数会修改结构体中的字段(比如Saved标记),调用Marshal.StructureToPtr时第三个参数要设为true,并且在函数调用完成后,用Marshal.PtrToStructure把修改后的非托管数据拷贝回托管结构体。
内容的提问来源于stack exchange,提问作者Aistis Taraskevicius
相关产品推荐
相关产品推荐

