TestPassingStringDelegate

TestPassingStringDelegate

我有这个问题,我需要使用反向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字段。

10-07 21:46