我有这个问题,我需要使用反向pinvoke(从C代码到C#代码调用的委托),但是这个委托返回一个字符串数组,我将从C代码中读取。这是函数指针的typdef。
org.junit
这是与代理相关的
typedef wchar_t** (__cdecl TestPassingString)();
在C代码中调用此函数指针,例如:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate string[] TestPassingString();
但我有一个AccessViolationException,我该如何解决这个问题?
感谢。
答案 0 :(得分:0)
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#-side初始化委托:
private TestPassingString TestPassingStringDelegate;
public void InitializeDelegate()
{
TestPassingStringDelegate = MyTestPassingString;
SetCallbackTestPassingString(TestPassingStringDelegate);
}
一些注意事项:
Allocate
方法),以便C#-side可以使用它来分配内存SetCallbackTestPassingString
方法,C#可以使用该方法将C委托设置为C#方法。TestPassingStringDelegate
字段的生命周期必须是>=
C端可以调用C#委托的时间。如果TestPassingStringDelegate
丢失(通常是因为包含TestPassingStringDelegate
字段的对象被垃圾收集),那么如果C端调用委托,则会收到错误。解决问题的最简单方法是使TestPassingStringDelegate
成为static
字段。