我有这个问题,我需要使用反向pinvoke(从C代码转换为C#代码的委托),但是此委托返回一个字符串数组,我将从C代码中读取该字符串。这是函数指针的typdef。
typedef wchar_t** (__cdecl TestPassingString)();
这是关联的委托
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate string[] TestPassingString();
在C代码中,有一个对该函数指针的调用,例如:
wchar_t** array = callbackTestPassingString();
但是我有一个AccessViolationException,如何解决这个问题?
谢谢。
最佳答案
C端代码:
typedef wchar_t** (__cdecl *TestPassingString)();
// The variable containing the callback to C#
TestPassingString callbackTestPassingString;
// The allocator that can be used by C#. The C code must
// use the corresponding deallocator (free in this case) to
// free the memory.
__declspec(dllexport) void* Allocate(size_t bytes)
{
return malloc(bytes);
}
// Method used to set the callback. You can change it however you want
__declspec(dllexport) void SetCallbackTestPassingString(TestPassingString callback)
{
callbackTestPassingString = callback;
}
// Test method that uses the callback. Note the free-ing!
__declspec(dllexport) void Test()
{
wchar_t** array = callbackTestPassingString();
const int arraySize = 5;
for (int i = 0; i < arraySize; i++)
{
wprintf(L"%s\n", array[i]);
}
// Here we deallocate the array and its elements
// Here I'm using free()... But the only important thing
// is that you use the corresponding free-er of Allocate()
for (int i = 0; i < arraySize; i++)
{
free(array[i]);
}
free(array);
}
C#端代码:
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Allocate(IntPtr bytes);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr TestPassingString();
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCallbackTestPassingString(TestPassingString callback);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Test();
// Simple method to copy a string to a IntPtr, including the terminating \0
public static void CopyStringUnicodeToPtr(string str, IntPtr ptr)
{
for (int j = 0; j < str.Length; j++)
{
Marshal.WriteInt16(ptr, j * sizeof(char), str[j]);
}
// \0 terminator
Marshal.WriteInt16(ptr, str.Length * sizeof(char), 0);
}
public IntPtr MyTestPassingString()
{
string[] strings = new[]
{
"Foo",
"Bar",
"FooBar",
"Baz",
"FooBarBaz",
};
IntPtr ptr = Allocate((IntPtr)(strings.Length * IntPtr.Size));
for (int i = 0; i < strings.Length; i++)
{
string str = strings[i];
// The +1 is for the terminating \0
IntPtr ptr2 = Allocate((IntPtr)((str.Length + 1) * sizeof(char)));
Marshal.WriteIntPtr(ptr, i * IntPtr.Size, ptr2);
CopyStringUnicodeToPtr(str, ptr2);
}
return ptr;
}
然后在C#端初始化委托:
private TestPassingString TestPassingStringDelegate;
public void InitializeDelegate()
{
TestPassingStringDelegate = MyTestPassingString;
SetCallbackTestPassingString(TestPassingStringDelegate);
}
一些注意事项:
C端我正在导出C分配器(
Allocate
方法),以便C#端可以使用它来分配内存C端,我正在导出
SetCallbackTestPassingString
方法,C#可使用该方法将C委托设置为C#方法。C#端:非常重要:
TestPassingStringDelegate
字段的生存期必须是C端可以调用C#委托的时间>=
。如果TestPassingStringDelegate
丢失(通常是因为包含TestPassingStringDelegate
字段的对象已被垃圾回收),则如果C端调用委托,您将得到一个错误。解决此问题的最简单方法是将TestPassingStringDelegate
设置为static
字段。