Выделение динамических структур в 32- и 64-разрядных средах выполнения - PullRequest
1 голос
/ 09 марта 2012

Я звоню SetupDiGetDeviceInterfaceDetail () здесь , а структура SP_DEVICE_INTERFACE_DETAIL_DATA неправильно маршалирует.Определение структур можно найти здесь .Я попытался использовать определение для этой структуры из PInvoke.net, здесь , но безрезультатно.

До сих пор, когда вызов функции завершается успешно (т.е. маршалер не 'выдает ошибку), возвращаемое значение равно 1784 (INVALID_USER_BUFFER).Главное, когда этот код выполняется из 32-битного процесса на моем компьютере, все это работает просто отлично.Когда он запускается в 64-битном процессе, у меня возникает эта проблема.

Моя текущая подпись SetupDiGetInterfaceDetailData () выглядит следующим образом:

[DllImport(@"c:\Windows\System32\SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
    SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
    ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
    IntPtr deviceInterfaceDetailData,
    uint deviceInterfaceDetailDataSize,
    IntPtr requiredSize,
    IntPtr deviceInfoData);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
    public UInt32 cbSize;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string DevicePath;
}

В настоящее время я выделяю память с помощью Marshal.AllocHGlobal() и запись / чтение данных из этого буфера с использованием семейства функций Marshal. *.

Для справки, вот что я делаю:

public string GetPathToDevice(SafeHandleZeroOrMinusOneIsInvalid hDevList,
                              SP_DEVICE_INTERFACE_DATA devIntfData)
{
    uint sizeNeeded = 0;
    // get's the size needed
    SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
                                                 ref devIntfData,
                                                 IntPtr.Zero,
                                                 0,
                                                 ref sizeNeeded,
                                                 IntPtr.Zero);

    IntPtr pBuffer = Marshal.AllocHGlobal((int)(sizeNeeded + 4)); // +4 for cbSize
    SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
                                                 ref devIntfData,
                                                 pBuffer,
                                                 sizeNeeded,
                                                 IntPtr.Zero,
                                                 IntPtr.Zero);

    // copy bytes from unmanaged space in pBuffer to a manged byte array
    // free unmanaged memory

    return theStringParsedFromByteArray;
}

Как я уже говорил, яЯ попытался определить структуру, описанную PInvoke.net для SP_DEVICE_INTERFACE_DETAIL_DATA (см. ссылку выше), и создал новую сигнатуру метода PInvoke для обработки этого.При запуске из 64-битной системы я получаю ту же проблему, то есть функция возвращает 1784. Причина, по-видимому, заключается в том, что ссылки в C # при работе в 64-битной среде выровнены по 8 байтов (обнаружено, что в другойСтатья StackOverflow).Я пробовал различные макеты для этой структуры, пытаясь заставить макет (используя явное смещение поля) в 4-байтовую выровненную структуру, но у меня это тоже не сработало.У меня были проблемы со временем компиляции.

Я пытался использовать различные декорации для параметров сигнатуры метода PInvoke.Например, MarshalAs (UnmanagedType.LPStruct), с которым я постоянно неправильно соединяюсь.Теперь я дошел до того, что мне нужна помощь в этом.

Что я действительно не понимаю, так это почему это вообще происходит.Даже если он работает на моем компьютере при работе в 32-битной среде, разве 64-битная среда просто не подключит меня к правильным 64-битным версиям Setup API?В чем проблема?

Спасибо за любую помощь, Энди

Проблема решена

Хорошая вещь, она решена сейчас, раздражающая вещьЯ не люблю исправлять вещи в течение часа или двух после публикации здесь.Итак, проблема заключалась в том, что это была проблема 64 бит.Код ошибки от Marshal.GetLastWin32Error () сообщал мне о проблеме.Значение cbSize было неверным.Я изменил его на 8, и теперь все работает.

Пожалуйста, кто-нибудь, объясните мне, почему размер теперь 8 на 64 бит?Структура теперь выше (комментатор попросил меня включить ее).Структура состоит из двух членов, одного DWORD и TCHAR [ANYSIZE_ARRAY].ANYSIZE_ARRAY оценивается как 1, TCHAR всегда является WCHAR, если Unicode и char в противном случае.DWORD - это всегда 32-битное количество (4 байта), а один TCHAR для Unicode - 2 байта.Итак, 4 + 2 = 6. Почему это 8?Это из-за выравнивания байтов для этой структуры в 64 битах?Мне бы очень хотелось это понять.

В любом случае, установка элемента cbSize на 8 для 64-битной и 6 для 32-битной работает, и я могу использовать структуру, определенную выше, вместоНеобработанное выделение / освобождение памяти и маршалинг.

1 Ответ

0 голосов
/ 06 марта 2014

Я также упал из-за этой проблемы, потому что я скопировал код из этого ответа: https://stackoverflow.com/a/2937588/1070906, который содержит ошибку в определении структуры.Может быть, вы сделали ту же ошибку.

Глядя на определение SP_DEVINFO_DATA на http://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx это последний параметр является указателем, а не Uint, как в другом посте.

Так как яизменил определение структуры на:

[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
    /// <summary>Size of the structure, in bytes.</summary>
    public uint cbSize;
    /// <summary>GUID of the device interface class.</summary>
    public Guid ClassGuid;
    /// <summary>Handle to this device instance.</summary>
    public uint DevInst;
    /// <summary>Reserved; do not use.</summary>
    public IntPtr Reserved;
}

это работает!

Marshall.SizeOf (новый SP_DEVINFO_DATA ()) теперь возвращает 32 вместо 28, и отображаются мои последовательные порты.

...