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

从C++ DLL向C#客户端传递字节数组时返回随机值的问题排查

问题根源

嘿,我一眼就看出问题出在哪了——你在C++函数里返回了局部数组的指针!

当passBytes()函数执行完毕后,栈上的局部变量bytes会被销毁,对应的内存空间会被操作系统回收或覆盖。这时候你返回的指针bptr就变成了“野指针”,指向的是已经无效的内存区域,所以C#这边用Marshal.Copy读取时,拿到的自然是随机垃圾值。

解决方案

下面给你三种可行的修复方案,按推荐程度排序:

方案3:由C#提供缓冲区(最安全推荐)

这种方式让C#预先分配好字节数组,把数组指针传递给C函数,C直接写入缓冲区,完全避免内存管理问题,是业界更推荐的做法:

修改后的C++ DLL代码

extern "C" __declspec(dllexport) void __stdcall passBytes(char* buffer, int bufferSize) {
    cout << "\n passBytes DLL code START \n";
    // 先检查缓冲区大小是否足够
    if (bufferSize >= 2) {
        buffer[0] = 0x07;
        buffer[1] = 0x08;
    }
    cout << "bytes to write: " << endl;
    cout << "byte 0 = 7" << endl;
    cout << "byte 1 = 8" << endl;
    cout << "\n passBytes DLL code END \n";
}

修改后的C#客户端代码

namespace sc_client {
    class Program {
        const string libname = "C:/work/x64/Release/libMxN.dll";
        
        // 用MarshalAs让CLR自动处理数组与指针的转换
        [DllImport(libname)]
        static extern void passBytes([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] buffer, int bufferSize);

        static void Main(string[] args) {
            byte[] cbytes = new byte[2];
            // 直接传递数组即可,无需手动处理指针
            passBytes(cbytes, cbytes.Length);
            
            Console.WriteLine("C# client output: ");
            for ( int i = 0; i < cbytes.Length; i++) {
                Console.WriteLine("client byte " + i + " = " + (int)cbytes[i]);
            }
        }
    }
}

方案2:动态分配内存(需手动释放)

在C++里用malloc动态分配内存,C#使用完后调用DLL的释放函数回收内存,避免泄漏:

C++ DLL代码

extern "C" __declspec(dllexport) char* __stdcall passBytes() {
    cout << "\n passBytes DLL code START \n";
    char* bytes = (char*)malloc(2); // 动态分配2字节内存
    bytes[0] = 0x07;
    bytes[1] = 0x08;
    cout << "bytes array size: 2" << endl;
    for (int i = 0; i < 2; i++) {
        cout << "byte " << i << " = " << (int)bytes[i] << endl;
    }
    cout << "\n passBytes DLL code END \n";
    return bytes;
}

// 新增释放内存的函数
extern "C" __declspec(dllexport) void __stdcall freeBytes(char* ptr) {
    free(ptr);
}

C#客户端代码

namespace sc_client {
    class Program {
        const string libname = "C:/work/x64/Release/libMxN.dll";
        
        [DllImport(libname)]
        static extern IntPtr passBytes();
        
        [DllImport(libname)]
        static extern void freeBytes(IntPtr ptr);

        static void Main(string[] args) {
            IntPtr ptr = passBytes();
            byte[] cbytes = new byte[2];
            Marshal.Copy(ptr, cbytes, 0, 2);
            
            Console.WriteLine("C# client output: ");
            for ( int i = 0; i < cbytes.Length; i++) {
                Console.WriteLine("client byte " + i + " = " + (int)cbytes[i]);
            }
            
            freeBytes(ptr); // 必须调用释放,否则会内存泄漏
        }
    }
}

方案1:使用静态数组(简单但有线程风险)

把C++的局部数组改成static,数组会被分配在静态存储区,函数返回后内存不会释放,但静态数组是全局共享的,多线程调用会有数据覆盖风险,仅适合单线程场景:

C++ DLL代码

extern "C" __declspec(dllexport) char* __stdcall passBytes() {
    cout << "\n passBytes DLL code START \n";
    static char bytes[] = { 0x07, 0x08 }; // 添加static关键字
    cout << "bytes array size: " << sizeof(bytes) << endl;
    for (int i = 0; i < sizeof(bytes); i++) {
        cout << "byte " << i << " = " << (int)bytes[i] << endl;
    }
    char* bptr = bytes;
    cout << "\n passBytes DLL code END \n";
    return bptr;
}

内容的提问来源于stack exchange,提问作者34n

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:02:39