c# - 如何正确地将长生命周期缓冲区从 C# 发送到 C++

标签 c# c++

任务 - 提供用于从 C++ DLL 注销并从 C# 使用它的缓冲区

我做了什么(而且是错的) C++伪代码:

wchar_t* _out;
int _outsize;
extern "C" __declspec(dllexport) void setOut(wchar_t* out, int outsize){
  _out = out;
  _outsize = outsize;
}
void logOut(wstring& message){
    const wchar_t* m = message.c_str();
    wcscat_s(_out,_outsize,m);
}
extern "C" __declspec(dllexport) void worker() {
    logOut(L"start");
    doWork();
    logOut(L"finish");
}

我在 C# 中做了什么(通常用于 outstring)

[DllImport(LibraryName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
extern public static void setOut([MarshalAs(UnmanagedType.LPWStr)]StringBuilder buf, int size);

[DllImport(LibraryName, CallingConvention = CallingConvention.StdCall)]
extern public static void worker();

public void Run(){
    const int bufsize = 1024;
    var buf = new StringBuilder(bufsize);
    setOut(buf, bufsize);
    doWork();
    Assert.True(buf.ToString().Contains("start"));
}

失败 - 有两个变体 - 有时进程中止,有时缓冲区为空。

在调试期间,我发现这是我的大错误 - setOut 中的 wchar_t* 参数仅在第一次调用 setOut 时是有效指针(因此,如果我要在其中写一些东西 - 我将在调用者中捕获数据).但在它之后,在第二次调用发生之前 (worker()) 指针变得无效。

因此我可以看到 StringBuilder 编码为单个 PInvoke 周期的临时指针。

那么,是否可以通过某种方式提供长期有效的 CLI <-> C++ 字符串缓冲区? (我知道我可以使用文件或映射文件或 tcp 套接字,但它不是很好,问题是是否可以使用 StringBuilder 或 char[] 或 byte[])?

最佳答案

编码分配一个单独的临时缓冲区(这是 out 指向的内容)然后在函数退出时将其内容复制到 StringBuilder 中。

由于这种行为,out 指针仅可用于通过将字符串复制到该指针来传回数据,例如通过从内部调用 wcscat_s(out, outsize,m)相同或嵌套的功能;

要修复代码,请考虑更改 worker() 签名以将 out 参数直接传递给该函数:

 void worker(wchar_t* out, size_t outsize) 
 {
     setOut(out, outsize);
     ....
     setOut(NULL, 0);
}

另一个[不太优雅]的选择是在代码中显式编码:

  [DllImport(LibraryName, CallingConvention = CallingConvention.StdCall)]
  extern public static void setOut(IntPtr buf, int size);


 char[] buffer = new char[bufferSize];

 // Specify size in bytes leaving room for terminal \0 
 IntPtr outPtr = Marshal.AllocHGlobal((bufferSize + 1) * sizeof(char) );
 try 
 {
    setOut(outPtr, bufferSize);
    worker();
    setOut(IntPtr.Zero, 0);
    Marshal.Copy(outPtr, buffer, 0, bufferSize); 
 }
 finally
 {
    Marshal.FreeHGlobal(outPtr);
 }

 // use buffer.

关于c# - 如何正确地将长生命周期缓冲区从 C# 发送到 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33507164/

相关文章:

c# - 使用第一对作为种子聚合

c# - 旋转后更改 WPF 元素的父元素(设置新坐标问题)

c# - 如何使用本地名称 XPATH 在 C# 上使用冒号管理 XML 标记

c++ - _read 函数返回文件句柄的过时值

c++ - 二维数组CUDA问题

C#:在类清理期间捕获测试失败

c# - WPF文本框字符串格式忽略小数

c++ - qt creator 看不到 qwt header

c++ - LNK2019 当包含 asio header 时,使用 cmake 生成的解决方案

c++ - 从 std::signal handler 调用函数;信号机