You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++导出含char*[]的结构体供C#调用时乱码崩溃如何解决

问题根因

你的封送逻辑存在三个核心错误,直接导致乱码和崩溃:

  • 初始C#结构体定义内存布局完全不匹配:C++侧的char* iAdapterName[6]是6个char类型指针构成的数组,32位程序下总长度为24字节(每个指针4字节),64位下总长度为48字节(每个指针8字节);你定义的char[] iAdapterName是6个单字节字符构成的数组,总长度仅6字节,内存错位必然导致读取乱码。
  • 字符串类型匹配错误:你后续用UnmanagedType.LPTStr对应C++的char*类型,LPTStr会随C#项目字符集设置自动映射为ANSI或Unicode宽字符类型,和固定为ANSI编码的char*不匹配,读取宽字符会直接访问非法内存。
  • 参数传递和内存逻辑错误:用out传参时,封送器会等非托管函数返回后才初始化托管结构体,C侧写入数据时结构体内部的数组成员根本没有分配有效内存;同时封送器默认会尝试释放非托管传入的字符串指针,但你C侧的字符串是适配器信息的内部缓存,不是用COM内存分配器申请的,释放非法内存直接触发程序崩溃。
正确实现方案

优先使用手动控制指针封送的方案,完全规避封送器的自动内存处理逻辑,32/64位下全兼容,不会出现乱码和崩溃:

  1. 修正结构体定义,用IntPtr数组对应C++的char*指针数组,保证内存布局完全匹配
[StructLayout(LayoutKind.Sequential)]
public struct Adapters
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] iAdapterIndex;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] iBusNumber;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] iDeviceNumber;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public IntPtr[] iAdapterName;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public IntPtr[] iDisplayName;
}
  1. 修正DLL导入声明,将out改为ref,允许提前初始化结构体内部数组
[DllImport("Example.dll", EntryPoint = "GET_ADAPTERS_INFO", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern int GET_ADAPTERS_INFO(ref Adapters adapters);
  1. 调用前提前初始化所有数组成员为固定长度6,调用完成后手动将IntPtr转换为ANSI编码字符串
// 初始化结构体,为所有数组分配固定长度内存
Adapters adapters = new Adapters();
adapters.iAdapterIndex = new int[6];
adapters.iBusNumber = new int[6];
adapters.iDeviceNumber = new int[6];
adapters.iAdapterName = new IntPtr[6];
adapters.iDisplayName = new IntPtr[6];

// 调用非托管接口
int result = GET_ADAPTERS_INFO(ref adapters);

// 读取有效适配器的字符串信息
for (int i = 0; i < 6; i++)
{
    if (adapters.iBusNumber[i] > -1) // 和C++侧有效适配器判断逻辑保持一致
    {
        string adapterName = Marshal.PtrToStringAnsi(adapters.iAdapterName[i]);
        string displayName = Marshal.PtrToStringAnsi(adapters.iDisplayName[i]);
        // 业务逻辑处理adapterName、displayName即可
    }
}
可选简化方案

如果不想手动处理指针转换,可以直接用string[]类型,但必须严格指定子类型为ANSI字符串,且同样提前初始化数组:

[StructLayout(LayoutKind.Sequential)]
public struct Adapters
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] iAdapterIndex;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] iBusNumber;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] iDeviceNumber;
    // 必须指定为LPStr对应ANSI char*,不能用LPTStr
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 6)]
    public string[] iAdapterName;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 6)]
    public string[] iDisplayName;
}

调用时需要提前将string数组初始化为长度6的空数组,同样用ref传参。该方案依赖封送器默认行为,不同.NET版本下可能存在兼容性差异,优先使用手动指针转换方案。

内容的提问来源于stack exchange,提问作者Yuri Bubliy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:48:15