2015-03-28 3 views
3

Я пытаюсь написать хорошую P/Invoke подписи для Windows SetupAPI вызовов, и я столкнулся со следующей проблемой с упаковкой структур SetupAPI в:Определение C# P/Invoke Структура Alignment во время выполнения

// 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# во время выполнения?

ответ

3

Нет, упаковка представляет собой концепцию времени компиляции, поскольку она определяет (помимо всего прочего) общий размер типа. Это не может измениться во время выполнения.

В этом случае, если вы не желаете иметь отдельные сборки для 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); 
    } 
} 
+1

Не нужно взломать IntPtr, вы можете просто объявить перегрузку функции, которая принимает другой тип структуры. –

+0

Тонкая настройка, предлагаемая @HansPassant, звучит достаточно элегантно. Должны ли мы добавить его к ответу? – DoomMuffins

-1

я сделал в C++ и C#, как на x86 и x64, и я скажу, что «пакет» не имеет никакого эффекта на этой структуре, так что вам нужно только

[StructLayout(LayoutKind.Sequential)] 

на x86 как C++ и # структуры C имеют sizeof/Marshal.SizeOf 28 байт, в 64 из 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; 
}; 

размеры 56 на x86 и x64 64 в (так точно двойных, дополнение нулей). Разница в размере между x86 и x64 равна 4 байтам, поэтому разница в размере .

+0

Ухаживать за нижним нитом? – xanatos