在运行时确定C#P / Invoke结构对齐

时间:2015-03-28 17:17:43

标签: c# pinvoke setupapi

我正在尝试为一些Windows setupapi调用编写好的P / Invoke签名,并且我在setupapi的结构打包时遇到了以下问题:

// Excerpt from setupapi.h
#if defined(_WIN64)
#include <pshpack8.h>   // Assume 8-byte (64-bit) packing throughout
#else
#include <pshpack1.h>   // Assume byte packing throughout (32-bit processor)
#endif

现在,这意味着我不能只将StructLayoutAttribute.Pack属性设置为常量值。

我尝试了以下操作:

[StructLayout(LayoutKind.Sequential, Pack = Environment.Is64BitProcess ? 8 : 1)]
public struct SP_DEVINFO_DATA
{
    public uint cbSize;
    public Guid ClassGuid;
    public uint DevInst;
    public IntPtr Reserved;
}

正如所料,这会因以下编译错误而失败:

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

我真的想避免#if并设置不同的编译平台,而不是Any CPU。我可以在运行时确定C#结构的打包吗?

2 个答案:

答案 0 :(得分:3)

不,打包是一个编译时的概念,因为它决定了(除其他外)该类型的整体大小。这在运行时无法改变。

在这种情况下,如果您不愿意为x86和x64分别构建,那么您必须自己进行编组。感谢@Hans Passant以实现这一目标:

  • 定义两个结构,例如SP_DEVINFO_DATA32SP_DEVINFO_DATA64
  • 为每个结构定义方法重载
  • 在运行时,选择要创建的结构并调用适当的重载。

它看起来像这样:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SP_DEVINFO_DATA32 { /* stuff */ }
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct SP_DEVINFO_DATA64 { /* stuff */ }

[DllImport("setupapi.dll")]
public static extern void Function(ref SP_DEVINFO_DATA32 data);

[DllImport("setupapi.dll")]
public static extern void Function(ref SP_DEVINFO_DATA64 data);

public void DoStuff()
{
    if (Environment.Is64BitProcess)
    {
        var data = new SP_DEVINFO_DATA64 
        { 
            cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA64)) 
        };
        Function(ref data);
    }
    else
    {
        var data = new SP_DEVINFO_DATA32 
        {
            cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA32)) 
        };
        Function(ref data);
    }
}

答案 1 :(得分:-1)

我已经在C ++和C#中完成了x86和x64,并且我会说&#34; pack&#34;对该结构没有任何影响,因此您只需要

[StructLayout(LayoutKind.Sequential)]

在x86,C ++和C#结构的sizeof / Marshal.SizeOf为28个字节,x64为32个字节。如果我创建

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    public SP_DEVINFO_DATA a;
    public SP_DEVINFO_DATA b;
}

和C

struct MyStruct
{
    SP_DEVINFO_DATA a;
    SP_DEVINFO_DATA b;
};

x86处的大小为56,x64处的大小为64(所以正好是双倍,零填充)。 x86和x64之间的大小差异是4个字节,因此IntPtr的大小差别正是如此。