从C#传递结构到C++

10

我在C++中有以下结构:

extern "C" __declspec(dllexport) struct SnapRoundingOption
{
    double PixelSize;
    bool IsISR;
    bool IsOutputInteger;
    int KdTrees;
};

这是我的C++函数声明:

extern "C" __declspec(dllexport) void FaceGenerationDummy(SnapRoundingOption snapOption);

这是相应的C#代码:
// I also tried not specifying Pack, but the same error occurred.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SnapRoundingOption
{
    public  double PixelSize;
    public bool IsISR;
    public bool IsOutputInteger;
    public int KdTrees;

    public SnapRoundingOption(double pixelSize, bool isISR, bool isOutputInt, int kdTrees)
    {
        PixelSize = pixelSize;
        IsISR = isISR;
        IsOutputInteger = isOutputInt;
        KdTrees = kdTrees;
    }
}

[DllImport("Face.dll")]
public static extern void FaceGenerationDummy(SnapRoundingOption snapRoundingOption);

然而,当我使用以下测试调用FaceGenerationDummy时:
[Test]
public void DummyTest()
{
    SimpleInterop.FaceGenerationDummy(new SnapRoundingOption(10, true, false, 1));
}

我发现在C++中,KdTrees的值为0,而不是传入的1。

我做错了什么?

编辑1:我在Windows 7 32位上使用Visual Studio 2008。

编辑2:两个sizeof(SnapRoundingOption)返回相同的数字——16。


1
你确定在 C++ 中打包对齐方式也是 1 吗? - Jon
2
@Graviton: 哦哦...你的 struct 中的单词“struct”在哪里啊?那是笔误还是你实际上就是这样写的?! - user541686
1
查看C++中结构体的内存表示,并使用适当的“Pack”和“MarshalAs”属性/参数。 - Jaroslav Jandek
@Graviton:哦,还有试着在两个版本中说sizeof(SnapRoundingOption)并检查它们是否相同。 - user541686
@Mehrdad,不是的。我没有在任何类或结构体内声明它们。即使我这样做了,很可能我会得到一个找不到入口点的异常。 - Graviton
显示剩余6条评论
2个回答

18

这里的问题是您如何编排bool字段。在C++中,它们是单个字节,因此需要进行以下编排:

[StructLayout(LayoutKind.Sequential)]
public struct SnapRoundingOption
{
    public double PixelSize;
    [MarshalAs(UnmanagedType.U1)]
    public bool IsISR;
    [MarshalAs(UnmanagedType.U1)]
    public bool IsOutputInteger;
    public int KdTrees;
}

在 C++ 的一侧进行匹配:

struct SnapRoundingOption
{
    double PixelSize;
    bool IsISR;
    bool IsOutputInteger;
    int KdTrees;
};
我删除了打包设置,以便结构具有符合平台自然对齐方式的对齐方式。 您还应确保调用约定一致。 目前看来,C++代码使用 cdecl ,而C#代码使用 stdcall 。例如:
[DllImport("Face.dll", CallingConvention=CallingConvention.Cdecl)]

将两侧的接口对齐。


@David:同感...它们让我更加思考。 :) (我特别喜欢COM接口问题... 那些 可以变得非常棘手!) - user541686
@Graviton 为什么你没有做C++部分呢?这很重要!不管怎样,我为此编写了一个测试,并成功地在两个方向上传递了这个结构体。 - David Heffernan
你不是刚才说过你不是 C# 程序员吗?现在你在维护这样一个应用程序,被迫学习 P/Invoke 语法了吗? - Cody Gray
1
@David:这就是我在40分钟前评论中提出的建议 :-P。@Graviton:无论你使用C++还是C#,打包对齐方式应该都是相同的。它应该可以使用Pack = 0或没有它来工作。 - Jaroslav Jandek
2
@David:你确定WinAPI使用1字节对齐吗?微软建议使用默认的对齐方式(8),这样数据类型可以根据其大小进行对齐。此外,我只看到微软在数组(内部生成)中使用pack=1... @Graviton: http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx - Jaroslav Jandek
显示剩余5条评论

5

1
+1:由于您已经说明了bool被编组为什么(这是David Heffernan在他的答案中忘记的一个要点 :-)) - mmmmmmmm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接