如何在PINVOKE中传递带字符串数据的struct?

时间:2013-06-29 12:40:29

标签: c# c++ pinvoke

我通过将带有C#代码的字符串数据传递给C ++ dll而陷入困境。

c ++代码

typedef struct 
{
    LPCSTR lpLibFileName;
    LPCSTR lpProcName;
    LPVOID pPointer1;
    LPVOID pPointer2;
} ENTITY, *PENTITY, *LPENTITY;
extern "C" __declspec(dllexport) int Test(LPENTITY entryList, int size);

int Test(LPENTITY entryList, int size)
{
    for (int i = 0; i < size; i++)
    {
        ENTITY e = entryList[i];
        // the char* value doesn't get passed correctly.
        cout << e.lpLibFileName << e.lpProcName << endl;
    }
    return 0;
}

c#c​​ode

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private class Entity
{
    public string lpLibFileName;
    public string lpProcName;
    public IntPtr pPointer1;
    public IntPtr pPointer2;
}

[DllImport("cpp.dll")]
private static extern int Test(
    [In, Out, MarshalAs(UnmanagedType.LPArray)]Entity[] entities,
    int size);

static void Main(string[] args)
{
    var entries = new[]
        {
            new Entity
                {
                    lpLibFileName = "comdlg32",
                    lpProcName = "PrintDlgExW",
                    pPointer1 = Marshal.GetFunctionPointerForDelegate(new PrintDlgEx(PrintDlgExCallback)),
                    pPointer2 = IntPtr.Zero,
                },
            new Entity
                {
                    lpLibFileName = "shell32",
                    lpProcName = "ShellAboutW",
                    pPointer1 = Marshal.GetFunctionPointerForDelegate(new ShellAbout(ShellAboutCallback)),
                    pPointer2 = IntPtr.Zero,
                },
        };
    var ret = Test(entries, entries.Length);
}

PINVOKE已被触发,但是像lpLibFileName和lpProcName这样的char *数据无法正确传递。我错过了什么?如何纠正?

感谢。

2 个答案:

答案 0 :(得分:0)

在传递自定义结构数组之类的参数时,在定义自己的数据结构时使用'struct'而不是'class'。在我将其更改回struct之后,一切正常。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct Entity
{
    public string lpLibFileName;
    public string lpProcName;
    public IntPtr pPointer1;
    public IntPtr pPointer2;
}

答案 1 :(得分:0)

您的代码将C#类映射到本机结构上。因为C#类是引用类型,所以它将被编组为引用。因此,您的代码会传递一组引用,这些引用会被编组到本机端的指针数组中。

但是本机代码需要一个指向结构数组的指针,这些结构是值类型。因此,最简单的解决方案是将Entity的声明更改为struct而不是class

我可以看到的其他问题:

  1. 本机代码似乎使用cdecl调用约定。您需要更改C#代码才能匹配。
  2. 您正在使用Out修饰数组参数。您无法将对string字段的修改编组回托管代码。
  3. 您需要确保将传递给GetFunctionPointerForDelegate的代表保持活力以阻止他们被收集。