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

C#调用C++ DLL:相同代码在Main与独立方法中结果不一致问题

问题根源与解决方案:C#调用C++ DLL返回栈内存导致的异常

核心问题分析

你的C++ fullname函数返回的是栈上的局部数组result——栈内存会在函数执行完毕后被操作系统回收或复用。

  • Main方法中第一次读取时,这段内存刚好还没被覆盖,所以能正常获取结果;
  • 调用StringFromNative方法时,函数调用会修改栈空间,原有的内存数据已经被破坏,因此读取失败。这属于C++中的未定义行为,偶然能运行只是巧合,完全不可靠。

解决方案1:C++返回堆内存(需手动释放)

修改C++代码,使用malloc在堆上分配内存(堆内存不会随函数返回自动释放):

#include <cstdlib>
char* fullname(char* firstname, char* lastname)
{
    char* result = (char*)malloc(1000);
    if (result == nullptr) return nullptr;
    
    strcpy_s(result, 1000, firstname);
    strcat_s(result, 1000, " ");
    strcat_s(result, 1000, lastname);
    
    return result;
}

C#端使用后必须手动释放堆内存,避免泄漏:

static void Main(string[] args) 
{
    string fn = "Foo";
    string ln = "United";

    IntPtr nameResult  = fullname(Encoding.Default.GetBytes(fn), Encoding.Default.GetBytes(ln));
    
    try
    {
        string res = StringFromNative(nameResult);
        Console.WriteLine(res);
    }
    finally
    {
        Marshal.FreeHGlobal(nameResult); // 释放C++堆内存
    }

    Console.ReadLine();
}

public static string StringFromNative(IntPtr native)
{
    int len = 0;
    while (Marshal.ReadByte(native, len) != 0) ++len;
    byte[] buffer = new byte[len];
    Marshal.Copy(native, buffer, 0, buffer.Length);
    return Encoding.Default.GetString(buffer);
}

解决方案2:由C#分配内存传入C++(更安全)

让C#负责内存分配,将缓冲区传给C函数,避免跨语言内存管理的混乱:
修改C
函数,接收外部传入的缓冲区:

void fullname(char* firstname, char* lastname, char* outputBuffer, size_t bufferSize)
{
    strcpy_s(outputBuffer, bufferSize, firstname);
    strcat_s(outputBuffer, bufferSize, " ");
    strcat_s(outputBuffer, bufferSize, lastname);
}

C#端的调用代码:

[DllImport("ActivatorDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern void fullname(byte[] firstname, byte[] lastname, IntPtr outputBuffer, int bufferSize);

static void Main(string[] args) 
{
    string fn = "Foo";
    string ln = "United";
    const int bufferSize = 1000;
    IntPtr outputBuffer = Marshal.AllocHGlobal(bufferSize);
    
    try
    {
        fullname(Encoding.Default.GetBytes(fn), Encoding.Default.GetBytes(ln), outputBuffer, bufferSize);
        // 直接用Marshal方法转换,无需手动计算长度
        string res = Marshal.PtrToStringAnsi(outputBuffer);
        Console.WriteLine(res);
    }
    finally
    {
        Marshal.FreeHGlobal(outputBuffer); // 确保释放内存
    }

    Console.ReadLine();
}

关键注意事项

  • 永远不要返回C++函数栈上的局部变量指针,这是严格的未定义行为;
  • 跨语言交互时,内存管理必须明确:要么由C分配后C#释放,要么由C#分配后传给C使用;
  • 使用Marshal.PtrToStringAnsi可以替代手动计算字符串长度的代码,简化逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:25:21