我试图从一些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 [] list 参数。
在C ++中,我可以设置 list 数组的内容:
_tcscpy((*list)[0], _T("First item"));
// and so on...
并且,即使由于任何可能有问题的编组而没有发生崩溃,C#中的列表,当对 foo()的调用返回时,不包含我们在C ++中设置的任何字符串。
我猜测问题出在 DllImport 上,以及我如何宣布 list 参数应该被编组。
顺便说一下,示例DLL有一个匹配函数,然后调用该函数来解除分配上面显示的示例 foo 返回的“TCHAR **列表”。
答案 0 :(得分:0)
我暂时没有这样做,但你应该尝试以下方法来正确编组数组列表:
添加一个
[MarshalAs(UnmanagedType.LPArray, ArraySubType.LPTStr, SizeParamIndex=1)]
属性为 list ,您应该添加out
,因为数据是用C ++代码分配的。
答案 1 :(得分: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中获取字符串数组。