从本机 lib 读取流到 C#

Posted

技术标签:

【中文标题】从本机 lib 读取流到 C#【英文标题】:Reading a stream from native lib to C# 【发布时间】:2021-07-10 04:51:26 【问题描述】:

我有以下原生 c++ 函数:

// Decode binary format from file 'filename' into stream 'output'
bool read_private_format(const char * filename, std::ostringstream & output);

阅读StringBuilder 和delegate 上关于 SO 的上一篇文章,我创建了一个中间 C 函数以暴露给 C# 层:

extern "C" 
  typedef char *(*StringBuilderCallback)(int len);
  __attribute__ ((visibility ("default")))
  bool c_read_private_format(const char * filename, StringBuilderCallback ensureCapacity, char *out, int len) 
    std::ostringstream oss;
    if( read_private_format(filename, oss) ) 
      const std::string str = oss.str();
      if( str.size() > len )
        out = ensureCapacity(str.size());
      strcpy(out, str.c_str());
      return true;
    
    return false;
  

在 C# 方面:

private delegate System.Text.StringBuilder StringBuilderEnsureCapacity(int capacity);
[System.Runtime.InteropServices.DllImport(NativeLibraryName, EntryPoint="c_read_private_format")]
private static extern bool c_read_private_format(string filename, System.IntPtr aCallback, System.Text.StringBuilder data, int size);

private static System.Text.StringBuilder callback(int capacity)

    buffer.EnsureCapacity( capacity );
    return buffer;


public static string readIntoString(string filename) 
  StringBuilderEnsureCapacity del = new StringBuilderEnsureCapacity(callback);
  System.IntPtr ptr = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(del)
  if( c_read_private_format( ptr, buffer, buffer.Capacity ) ) 
    string str = buffer.ToString();
    return str;
  
  return null;

由于某种原因,这不能按预期工作,当打印callback 返回的char* 的地址时,它的作用就像返回的指针是调用EnsureCapacity 之前的指针一样(我可以通过以下方式验证进行第二次调用,此时C层的char*不同)。

我的问题是:

如何在 .NET SDK (5.0.202) 中有效地从 C 中检索 UTF-8 字符串?

我事先不知道字符串会有多长。从技术上讲,我可以高估 StringBuilder 的容量,以便我可以在我的文件中重复使用,但感觉好像有更好的方法可以将不断增长的流传递到 c 层。

【问题讨论】:

在被调用者中分配内存并在调用者中释放内存在这里可能是最好的。要么使用共享分配器,要么导出本机解除分配。在我看来,回调的想法很笨拙。 我同意回调看起来很笨重,但是我不同意内存被调用者/调用者模式是最好的。为什么不在 C 中实现 OFstream 接口,该接口映射到 StringStream C# 这显然取决于您如何执行此操作。对于这种互操作,我倾向于使用低级 C 风格的接口,并以我使用的任何一种语言来使用本机库的更惯用的方式将其包装起来。关键是 pinvoke 只是一个垫脚石,更高级别的代码看不到。但随心所欲。 【参考方案1】:

尝试优化发布的代码毫无意义,因为根据定义,pinvoke 层缺少最重要的一点:

❌ 避免使用 StringBuilder 参数。 StringBuilder 总是编组 创建本机缓冲区副本。因此,它可以是非常 效率低下。

https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices#string-parameters

【讨论】:

以上是关于从本机 lib 读取流到 C#的主要内容,如果未能解决你的问题,请参考以下文章

从本机调用 C# 以检索字符串

从 c# 访问 C++ .lib 库

*** 关于从 C# 调用本机 C++

本机 C++ 程序在使用 C++/CLI 和 C# 互操作 DLL 启动时崩溃

如何将整数从 C# 方法复制到本机 C DLL 函数

通过 Marshal.GetFunctionPointerForDelegate 从本机 (C++) 线程调用托管函数 (C#)