【问题标题】:Return string from c++ dll export function called from c#从 c# 调用的 c++ dll 导出函数返回字符串
【发布时间】:2015-10-07 11:41:04
【问题描述】:

我正在尝试从 c++ dll 导出函数返回一个字符串。我从 c# 调用这个函数。我在互联网上看到了很多例子,我真的很困惑该怎么做。

我的导出函数的 c++ 代码:

extern "C" __declspec(dllexport)  char*  __cdecl getDataFromTable(char* tableName)
{
    std::string st = getDataTableWise(statementObject, columnIndex);
    printf(st.c_str()); 

    char *cstr = new char[st.length() + 1];
    strcpy(cstr, st.c_str());
    return cstr;
} 

当我尝试从 c# 调用此函数时:

[DllImport("\\SD Card\\ISAPI1.dll")]
private static extern string getDataFromTable(byte[] tablename);
static void Main(string[] args)
{
    string str = getDataFromTable(byteArray);
    Console.writeLine(str);
}

调用它时出错。我正在为 WinCE 6.0 创建这个

已编辑-----------

有没有类似的东西,我可以将一个空缓冲区从 c# 传递给 c++,c++ 函数将填充数据,我可以在 C# 中重用它

【问题讨论】:

  • console.writeLine 出现 OutOfMemory 异常(字​​符串值)
  • 当然,没有人会调用 delete[] 来再次释放字符串缓冲区空间。最终以OOM结束。你不能这样做。首先尝试 CoTaskMemAlloc()。接下来允许调用者提供缓冲区,例如 int getDataFromTable(char* tableName, char* buffer, int buflen)。必须是 C# 端具有足够容量的 StringBuilder。

标签: c# c++ windows-ce


【解决方案1】:

我最近也遇到了这个问题,虽然我有一个解决方案给你,但遗憾的是我无法真正解释它。我还没有找到合理的解释。

我用于检索字符串的 c++ 代码是:

extern "C" { __declspec(dllexport) void __GetValue__(char* str, int strlen); }

还有我的 C# 代码:

[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void __GetValue__(StringBuilder str, int strlen);

如您所见,您可以提供一个字符串(通过使用 StringBuilder),而不是返回一个值,然后让 C++ 填充数据,如下所示:

void __GetValue__(char* str, int strlen) {
    std::string result = "Result";

    result = result.substr(0, strlen);

    std::copy(result.begin(), result.end(), str);
    str[std::min(strlen-1, (int)result.size())] = 0;
}

为了完整起见,请求字符串的 C# 代码:

public String GetValue() {
    StringBuilder str = new StringBuilder(STRING_MAX_LENGTH);

    __GetValue__(str, STRING_MAX_LENGTH);

    return str.ToString();
}

【讨论】:

    【解决方案2】:

    这个怎么样(注意,它假定正确的长度 - 你应该传入缓冲区长度并防止溢出等):

    extern "C" __declspec(dllexport)  void  __cdecl getDataFromTable(char* tableName, char* buf)
    {
        std::string st = getDataTableWise(statementObject, columnIndex);
        printf(st.c_str()); 
    
        strcpy(buf, st.c_str());
    } 
    

    然后在 C# 中:

    [DllImport("\\SD Card\\ISAPI1.dll")]
    private static extern string getDataFromTable(byte[] tablename, byte[] buf);
    static void Main(string[] args)
    {
        byte[] buf = new byte[300];
        getDataFromTable(byteArray, buf);
        Console.writeLine(System.Text.Encoding.ASCII.GetString(buf));
    }
    

    注意,这确实对您的 C++ 应用程序中的字符编码不是 unicode 做出了一些假设。如果它们是 unicode,请使用 UTF16 而不是 ASCII。

    【讨论】:

    • 不应该是private static extern string getDataFromTableprivate static extern void getDataFromTable 吗?
    【解决方案3】:

    .NET 运行时使用 unicode (wchar_t) 字符串,而不是 ascii (char),因此需要进行一些更改。 您还应该考虑到 .NET 无法释放由 C/C++ 应用程序分配的字符串,因此预先分配缓冲区并从 C# 传入是唯一安全的方法来管理此问题而不会出现内存泄漏或更糟.

    【讨论】:

      【解决方案4】:

      在 Windows 上,您可以使用BSTR 直接返回字符串(来自wtypes.h,由windows.h 包含)。 BSTR可以通过SysAllocString函数从标准宽字符串创建:

      _declspec(dllexport) BSTR getDataFromTable(const char* tableName)
      {
          return SysAllocString(L"String to return");
      }
      

      然后在 C# 中你将返回类型编组为 BStr:

      [DllImport("\\SD Card\\ISAPI1.dll", CallingConvention = CallingConvention.Cdecl )]
      [return: MarshalAs(UnmanagedType.BStr)]
      static extern string getDataFromTable(string tableName);
      

      【讨论】:

      • MarshalAs(UnmanagedType.BStr) 为我工作。谢谢
      【解决方案5】:

      这很简单:)

      _strdup 在内存中创建字符串的副本,该副本只能使用 delete 销毁。

      https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strdup-wcsdup-mbsdup?view=msvc-170

      const char* returnStringFunc()
      {
          return _strdup("String to Return");
      }
      

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 1970-01-01
      • 2014-11-06
      • 2021-12-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      相关资源
      最近更新 更多