【问题标题】:pinvoke - pass a string[] as out parameterpinvoke - 将字符串 [] 作为输出参数传递
【发布时间】:2013-10-09 13:43:13
【问题描述】:

我无法让下面的 p/invoke 代码工作,请帮助,谢谢。

下面的顶点在 c++ 调用后保持为空。我尝试使用 IntPtr 而不是 string[],C++ 调用后 IntPtr 保持为 0。

c++代码

extern "C"  __declspec(dllexport)
float compute_similarity(char** vertices)
{       
  vertices = new char*[2];
  vertices[0] = new char[3];
  vertices[1] = new char[3];

  strcpy(vertices[0], "he");
  strcpy(vertices[1], "ha");

  return 1.01;
}

c#代码

[DllImport("demo.dll", EntryPoint = "compute_similarity", 
    CallingConvention =  CallingConvention.Cdecl,   CharSet = CharSet.Ansi)]
static extern float compute_similarity(out string[] vertices);  
//also tried 'static extern float compute_similarity(out IntPtr vertices);'  

public string Func()
{
  string[] vertices; //also tried 'IntPtr vertices'
  float sim = compute_similarity(out vertices); 
  //break point here vertices stays null(or 0 for IntPtr)
  return sim.ToString();
}

【问题讨论】:

  • 这段代码不能可靠地从 C++ 调用,当你从 C# 调用它时它不会变得更好。调用者必须调用 delete[] 来释放数组的内存。这有时适用于 C++,您需要相当多的运气并仔细控制 DLL 和客户端代码使用的编译器版本。它保证不能在 C# 中工作,它不知道如何调用 C++ delete[] 运算符。你得到 0 因为你的 C++ 代码不好,顶点参数应该是 char***。
  • compute_similarity 分配 char** 指针,但是客户端看不到这个,因为指针是按值传递的。
  • @Xin A SAFEARRAY 可能会起作用。
  • 修复 C++ 代码后,使用低级 Marshal 函数编写 C# 客户端。 IntPtr + Marshal.ReadIntPtr(IntPtr, Int32) + Marshal.PtrToStringAnsi(IntPtr) 允许像在 C 中一样使用指针。还可以编写非托管函数来正确释放该指针,并在使用由 compute_similarity 分配的资源后调用它。
  • @Medinoc 您的答案中的代码有效。谢谢

标签: c# c++ arrays pinvoke


【解决方案1】:

我认为这种方式不能正确完成,因为您无法指定 P/Invoke 必须在将内存转换为托管字符串后通过 delete[] 调用释放内存。

但是,使用 SAFEARRAYBSTRs 和 MarshalAs 属性,您可能会有机会。

extern "C"  __declspec(dllexport)
float compute_similarity(SAFEARRAY** vertices)
{
    SAFEARRAY *pArr = SafeArrayCreateVector(VT_BSTR, 0, 2);
    if(pArr != NULL)
    {
        LONG index = 0;
        BSTR bs = SysAllocString(L"he");
        SafeArrayPutElement(pArr, &index, bs);
        SysFreeString(bs);
        index = 1;
        bs = SysAllocString(L"ha");
        SafeArrayPutElement(pArr, &index, bs);
        SysFreeString(bs);
    }
    *vertices = pArr;
    return 1.01;
}
[DllImport("demo.dll", EntryPoint = "compute_similarity", CallingConvention =  CallingConvention.Cdecl)]
static extern float compute_similarity(
    [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)] out string[] vertices
);  

你们可能仍然使用IntPtr(并在 C# 端手动执行封送处理,同时导出 delete_strings 函数),但请记住,您的函数必须通过引用获取其 char**,否则它不能实际修改它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 2017-08-10
    • 2020-05-01
    • 1970-01-01
    相关资源
    最近更新 更多