【问题标题】:Marshalling from C# to a DLL with a TCHAR** x[] parameter使用 TCHAR** x[] 参数从 C# 编组到 DLL
【发布时间】:2013-01-28 12:59:00
【问题描述】:

我正在尝试从某些 C# 调用 Win32 DLL。 DLL函数声明如下:

extern "C" __declspec(dllexport) UINT foo(TCHAR** list[], int& listSize, TCHAR* error);

在 C# 中,我这样声明外部函数:

[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
static extern uint foo(StringBuilder[] list, ref int listSize, StringBuilder error);

我的问题与 list 参数有关,它是 StringBuilder 类的数组。

我调用DLL函数的一些测试代码如下: (注意:我意识到缺少边界检查等。在这个阶段,我只是想让事情正常进行)。

// allocate a buffer for a possible error message being returned
StringBuilder error = new StringBuilder(1000);

// allocate buffers for the list of strings returned
StringBuilder[] list = new StringBuilder[10];
for (int i = 0; i < 10; i++)
{
    list[i] = new StringBuilder(1000);
}

int listSize = list.Length;

uint result = foo(list, ref listSize, error);

将 StringBuilder 作为 error 参数的缓冲区传递,可以正常工作。我可以在 DLL 中设置它的内容如下:

_tcscpy(error, _T("Something went wrong");

当 C# 调用 foo 返回时,string error 变量具有我们在 DLL 中设置的文本。

但是,我不知道如何编组 StringBuilder[] 列表 参数。

在 C++ 中,我可以设置 list 数组的内容:

_tcscpy((*list)[0], _T("First item")); 
// and so on...

而且,即使没有由于任何可能的编组问题而发生崩溃,但 C# 中的 list 在对 foo() 的调用返回时,确实不包含我们在 C++ 中设置的任何字符串。

我猜测问题出在 DllImport 上,以及我如何声明 list 参数应该被编组。

顺便说一下,示例 DLL 有一个匹配函数,然后调用该函数以释放上面显示的示例 foo 返回的“TCHAR** 列表”。

【问题讨论】:

  • TCHAR** list[]?你有一个字符串数组,还是一个字符串数组的数组?
  • 指向 TCHAR 指针的指针数组。令人困惑,我知道。
  • 那么为什么将托管签名定义为StringBuilder[]?我猜这相当于TCHAR **。但两者都不适用于您的情况:)。
  • 好吧,也许我的定义也是错误的。这都与我的问题有关,我正在努力工作。
  • 对于error参数,StringBuilder效果很好,就是一个TCHAR*。所以,我假设对于 TCHAR** list[],StringBuilder 是正确的。

标签: c# .net dll marshalling dllimport


【解决方案1】:

我已经有一段时间没有这样做了,但是您应该尝试以下操作以正确编组 list 数组:

添加一个 [MarshalAs(UnmanagedType.LPArray, ArraySubType.LPTStr, SizeParamIndex=1)] list 的属性,你应该添加一个out,因为数据是在 C++ 代码中分配的。

【讨论】:

    【解决方案2】:

    好的,我找到了解决问题的方法。看起来答案就在 IntPtr 中。 C 函数在 "TCHAR** list[]" 中创建并返回一个字符串数组。

    首先,我将C#中的声明改为:

    [DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
    static extern uint foo(out IntPtr[] list, ref int listSize, StringBuilder error);
    

    然后,当对 DLL 的调用返回时,我需要从数组中提取字符串:

    string[] returnedList = new string[listSize];
    IntPtr[] ptrArray = new IntPtr[listSize];
    
    Marshal.Copy(list, ptrArray, 0, listSize);
    
    for (int i = 0; i < listSize; i++)
    {
        returnedList[i] = Marshal.PtrToStringAuto(ptrArray[i]);
    }
    

    这导致从 C DLL 中取回字符串数组。

    【讨论】:

    • 如果这可行,那么您很可能会泄漏内存,而不是释放字符串缓冲区。通过调用函数一百万次来测试这一点,并确保内存使用不会爆炸。
    • DLL 有一个关联函数,用户需要调用该函数来释放上述 foo 示例返回的“TCHAR** list[]”。
    • 请在问题中提及此类细节。这让我们不知道这样一个 api 函数如何可能按预期工作并产生对任何人都无用的错误答案。
    猜你喜欢
    • 1970-01-01
    • 2018-02-24
    • 1970-01-01
    • 2021-01-29
    • 2012-12-01
    • 1970-01-01
    • 2021-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多