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

在运行时确定C#P/Invoke结构对齐,c#,pinvoke,setupapi,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 thr

我正在尝试为一些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
正如预期的那样,此操作失败,并出现以下编译错误:

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

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

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

在这种情况下,如果您不愿意为x86和x64单独构建,则必须自己进行编组。感谢@Hans Passant以干净的方式实现这一目标:

  • 定义两个结构,例如
    SP_DEVINFO_DATA32
    SP_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);
    }
}

我在C++和C语言中都做了,在x86和x64,我会说“包”对这个结构没有任何影响,所以你只需要

[StructLayout(LayoutKind.Sequential)]
在x86中,C++和C结构都有<代码> > sieof of <代码> >代码> MsHal.SigeOS/<代码> 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

的大小差异不需要IntPtr hack,您只需声明采用其他结构类型的函数重载即可。@HansPassant提供的调整听起来相当优雅。我们把它加到答案上好吗?
struct MyStruct
{
    SP_DEVINFO_DATA a;
    SP_DEVINFO_DATA b;
};