返回结构时,PInvoke 仅适用于 64 位

PInvoke only works in 64 bit, when returning a struct

我有以下 C++ 代码,可编译为 dll:

typedef struct _RGB {
    unsigned char R, G, B;
} RGB;

extern "C" __declspec(dllexport) RGB __stdcall TestMethod1() {
    RGB rgb{1,2,3};
    return rgb;
}

我在 C# 中调用它使用:

static void Main(string[] args)
{
    var res = TestMethod1();
}

[DllImport(@"D:\Develop\res\VSProjects\ConsoleApp1\Debug\Dll1.dll", CallingConvention = CallingConvention.StdCall)]
static extern RGB TestMethod1();

[StructLayout(LayoutKind.Sequential)]
struct RGB { public byte R, G, B; }

当运行它为x86时,在将dll构建为x86后,我得到一个错误Attempted to read or write protected memory.。在 x64 中它工作正常。

当我使用 managed/native 调试器时,我看到它在 return rgb; 上崩溃。

将 return 类型更改为 long(C# 中的 int)时,即使在 x86 下也能正常工作。

RGB 结构 is blittable 为什么我会遇到这个问题?

不要对 "complex" return 类型使用结构,更喜欢这样的东西:

C++:

extern "C" __declspec(dllexport) void __stdcall TestMethod2(RGB *prgb) {
    prgb->R = 1;
    prgb->G = 2;
    prgb->B = 3;
}

C#:

[DllImport(@"D:\smo\source\repos\ConsoleApplication4\Debug\Dll1.dll")]
static extern void TestMethod2(ref RGB rgb);

static void Main(string[] args)
{
    var rgb = new RGB();
    TestMethod2(ref rgb);
}

请注意,在您的特定情况下,它会失败,因为结构大小为 3,因此如果您像这样更改结构,则可以使其正常工作,例如:

C++:

typedef struct _RGB {
    unsigned char R, G, B, A;
} RGB;

C#

[StructLayout(LayoutKind.Sequential)]
struct RGB { public byte R, G, B, A; }

根据此定义,大小将为 4,因此 C++ 编译器将生成一个代码,该代码将 return 一个 int32 值而不是 returning - 可能 - 对某些内部存储器的引用当执行到达 .NET 端时,它将消失。这纯属运气(或 hack),我猜取决于 C++ 编译器。