我不知道这里有什么问题。
我有大量的p / invoke调用正常工作......除了这个。
我设法将我的问题减少到以下示例代码。
如果我删除结构成员(double或int),它可以正常工作。
我假设问题与结构的布局有某种关系 - 但是当我在C中执行sizeof()和在C#中执行Marshal.SizeOf()时,它们都返回相同的值...所以如果结构大小在C#和C中是相同的,问题是什么?
我显然在这里缺少一些基本的东西。
SampleDLLCode.c
#pragma pack(1)
typedef struct SampleStruct {
double structValueOne;
int structValueTwo;
} SampleStruct;
__declspec(dllexport) SampleStruct __cdecl SampleMethod(void);
SampleStruct SampleMethod(void) {
return (SampleStruct) { 1, 2 };
}
构建脚本
gcc -std=c99 -pedantic -O0 -c -o SampleDLLCode.o SampleDLLCode.c
gcc -shared --out-implib -o SampleDLL.dll SampleDLLCode.o
C#代码
using System;
using System.Runtime.InteropServices;
namespace SampleApplication
{
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SampleStruct {
public double structValueOne;
public int structValueTwo;
}
class Program
{
[DllImport("SampleDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern SampleStruct SampleMethod();
static void Main(string[] args)
{
SampleStruct sample = SampleMethod();
}
}
}
答案 0 :(得分:9)
首先,请允许我祝贺你提出一个非常好的问题。曾经很高兴收到重现问题所需的所有代码。
问题是由于gcc和Microsoft工具用于函数返回值的ABI略有不同。对于可以适合寄存器的返回值,例如int
返回值没有差异。但是,由于您的结构太大而无法放入单个寄存器中,因此在这种情况下API之间存在差异。
对于较大的返回值,调用者将隐藏指针传递给该函数。这个隐藏的指针被调用者推入堆栈。该函数将返回值写入该隐藏指针指定的内存地址。 ABI的区别在于谁将隐藏指针弹出堆栈。 Microsoft工具使用ABI,要求调用者弹出隐藏指针,但默认的gcc ABI要求被调用者执行此操作。
现在,gcc几乎可以无限配置,有一个开关可以让你控制ABI。并且您可以使gcc使用与Microsoft工具相同的规则。这样做需要callee_pop_aggregate_return
function attribute。
将您的C代码更改为:
__declspec(dllexport) SampleStruct __cdecl SampleMethod(void)
__attribute__((callee_pop_aggregate_return(0)));
// specifies that caller is responsible for popping the hidden pointer
SampleStruct SampleMethod(void) {
return (SampleStruct) { 1, 2 };
}