.NET Core 3.1 Linux下调用C原生库Marshal编码异常求助
问题描述
在Debian系统的.NET Core 3.1应用中引入第三方C原生库
library.so,该库与Windows下对应的library.dll方法签名已确认一致。适配SDK代码后,调用Marshal.PtrToStructure<FoundEntityStruct>时首次循环抛出异常:No mapping for the Unicode character exists in the target multi-byte code page. (0x80070459)。推测是原生库使用ANSI编码而非UTF-8,但修改DllImport的CharSet为Ansi/Unicode/Auto均无效,求解决思路。
代码示例
[DllImport("library.so", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] internal static extern uint FindEntites(out FoundResultsStruct foundResult); ... internal ArrayList FillCollection() { var innerList = new ArrayList(); FoundResultsStruct results; var code = FindEntites(out results); IntPtr ptr = results.Value; if (ptr == IntPtr.Zero) throw new Exception(); int num = Marshal.SizeOf(typeof(FoundEntityStruct)); for (long index = 0; index < results.Count; ++index) { innerList.Add(new CollectionEntity(Marshal.PtrToStructure<FoundEntityStruct>(ptr))); //异常触发点 ptr = (IntPtr)((long)ptr + (long)num); } return innerList; }
解决思路
手动处理字符串编码,绕过自动Marshaling
直接在FoundEntityStruct中将字符串字段替换为固定长度byte[],比如:[StructLayout(LayoutKind.Sequential)] public struct FoundEntityStruct { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public byte[] EntityName; // 其他字段... }转换时用原生库实际使用的编码(比如GBK、ISO-8859-1)手动解析:
var entity = Marshal.PtrToStructure<FoundEntityStruct>(ptr); string name = Encoding.GetEncoding("GBK").GetString(entity.EntityName).TrimEnd('\0');避免依赖
.NET跨平台下CharSet的不一致行为。强制结构体内存布局对齐
Linux与Windows的结构体默认对齐规则可能不同,在FoundEntityStruct和FoundResultsStruct上显式指定布局和pack值,确保与原生库内存结构完全匹配:[StructLayout(LayoutKind.Sequential, Pack = 1)] // 或对应原生库的pack值,如4 public struct FoundEntityStruct { // 字段定义 }布局不匹配会导致读取错误字节序列,间接触发编码异常。
修正调用约定
Linux下C原生库默认调用约定为Cdecl,而非Windows常用的StdCall。修改DllImport的调用约定:[DllImport("library.so", CallingConvention = CallingConvention.Cdecl)]错误的调用约定会破坏栈结构,导致读取到无效内存数据。
验证原生库返回的内存字节
用Marshal.Copy导出内存数据,手动分析字符串编码:byte[] buffer = new byte[num]; Marshal.Copy(ptr, buffer, 0, num); // 打印buffer十六进制内容,确认字符串部分的实际编码格式精准定位原生库使用的编码类型,再针对性处理。
内容的提问来源于stack exchange,提问作者Jonik

