2

我正在尝试从某些 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 作为错误参数的缓冲区传递,可以正常工作。我可以在 DLL 中设置它的内容,如下所示:

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

当 C# 对foo的调用返回时,字符串错误变量具有我们在 DLL 中设置的文本。

但是,我无法弄清楚如何编组StringBuilder[] 列表参数。

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

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

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

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

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

4

2 回答 2

0

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

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

于 2013-01-28T13:17:46.500 回答
0

好的,我找到了解决问题的方法。看起来答案在于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 中取回字符串数组。

于 2013-01-29T12:18:22.680 回答