Unity中C#传递含字符串结构体到非托管代码时StructLayout失效问题
问题根源
Unity使用的Mono运行时与.NET 5的CoreCLR在P/Invoke结构体封送的处理逻辑上存在差异。你在C#结构体中直接用string[]对应C++的char* array[218],这种写法在CoreCLR中可以自动处理,但Mono不支持将结构体中的字符串数组直接封送,必须手动通过IntPtr中转处理。
解决方案
1. 修改C#结构体定义
将string[]替换为IntPtr[],因为IntPtr可以直接对应C++中的char*指针:
using System; using System.Runtime.InteropServices; using UnityEngine; using System.Collections.Generic; public class DLLTEST : MonoBehaviour { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct MOVES_IMPORT { public int size; // 改为public,确保Marshal能访问成员 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)] public IntPtr[] Array; }; [DllImport("LittleChessEngine.dll")] static extern int EXP_LEGAL_MOVES(ref MOVES_IMPORT mvs); // 如果C++中用malloc分配了字符串内存,需要添加释放函数的导入 // [DllImport("LittleChessEngine.dll")] // static extern void EXP_FREE_MOVE_STRING(IntPtr strPtr); void Start() { var mvs = new MOVES_IMPORT(); // 初始化IntPtr数组,避免Mono报错 mvs.Array = new IntPtr[218]; var size = EXP_LEGAL_MOVES(ref mvs); // 手动将IntPtr转换为C#字符串 List<string> legalMoves = new List<string>(); for (int i = 0; i < mvs.size; i++) { if (mvs.Array[i] != IntPtr.Zero) { string move = Marshal.PtrToStringAnsi(mvs.Array[i]); legalMoves.Add(move); // 释放C++分配的内存(如果需要) // EXP_FREE_MOVE_STRING(mvs.Array[i]); } } // 这里可以使用legalMoves列表做后续处理 foreach(var move in legalMoves) { Debug.Log("合法走法:" + move); } } }
2. 注意内存泄漏问题
如果你的C++引擎中是通过malloc/new分配的char*内存,必须在DLL中提供对应的释放函数,并在C#中调用释放每个指针,否则会造成内存泄漏。
C++侧释放函数示例:
extern "C" __declspec(dllexport) void EXP_FREE_MOVE_STRING(char* str) { if(str != nullptr) { free(str); // 如果用new分配则用delete[] } }
为什么.NET 5能正常运行?
.NET 5使用的CoreCLR对结构体中字符串数组的封送做了特殊处理,会自动将C++的char*指针数组转换为C#的string[]。但Unity的Mono运行时没有实现这个自动转换逻辑,必须手动处理指针到字符串的转换。
内容的提问来源于stack exchange,提问作者bogdane
相关产品推荐
相关产品推荐

