Перечисление USB HID-устройств с помощью SetupAPI в 64-битном приложении

Я использую Delphi XE2 и пытаюсь обновить нашу usb comms dll до 64-разрядной версии. Мы используем модули JVCL SetupAPI и Hid. Все работает отлично с использованием 32-битного компилятора и может видеть мое подключенное HID-устройство. Я переключаюсь на 64-разрядную версию и больше не вижу подключенных устройств HID.

Я встречал людей, упоминающих о необходимости изменить размер некоторых структур данных для 64-битной версии (см. https://forums.embarcadero.com/thread.jspa?messageID=408473#408473), и это помогло, но теперь я официально в тупике.

В настоящее время мой код возвращает 0 байтов, прочитанных из функции SetupDiGetDeviceInterfaceDetail. Закомментированный SizeOf () работал для 32-битной версии, но не для 64-битной.

Любая помощь приветствуется.

repeat
  TmpDeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
  TmpDeviceInterfaceData.cbSize := 32;  // SizeOf(TmpDeviceInterfaceData);
  TmpSuccess := SetupDiEnumDeviceInterfaces(TmpDevInfo, nil, TmpDevHidGuid, TmpDevn, TmpDeviceInterfaceData);
  if TmpSuccess then
  begin
    TmpDevData.cbSize := 32; //SizeOf(TmpDevData);
    showmessage(inttostr(tmpdevdata.cbsize));
    TmpBytesReturned := 0;
    SetupDiGetDeviceInterfaceDetail(TmpDevInfo, @TmpDeviceInterfaceData, nil, 0, TmpBytesReturned, @TmpDevData);
    showmessage('bytes returned = ' + inttostr(TmpBytesReturned));
    if (TmpBytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
    begin
      // showmessage('hello');
      TmpFunctionClassDeviceData := AllocMem(TmpBytesReturned);
      TmpFunctionClassDeviceData.cbSize := sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);

      TmpFunctionClassDeviceData.cbSize := 8;
      // showmessage(inttostr(TmpFunctionClassDeviceData.cbSize));
      if SetupDiGetDeviceInterfaceDetail(TmpDevInfo, @TmpDeviceInterfaceData, TmpFunctionClassDeviceData, TmpBytesReturned, TmpBytesReturned, @TmpDevData) then
      begin
        // showmessage('here');
        try
          begin
          //try to obtain PID and VID information about the HID devices
          TmpDeviceHandle := CreateFile(@TmpFunctionClassDeviceData.DevicePath,
                            GENERIC_READ OR GENERIC_WRITE,
                            FILE_SHARE_READ OR FILE_SHARE_WRITE,
                            NIL, OPEN_EXISTING, 0 , 0);
          TmpAttributes.Size := Sizeof(TmpAttributes);
          HidD_GetAttributes(TmpDeviceHandle, TmpAttributes);
          If (vid = TmpAttributes.VendorID) then
          begin
            PIDlistStr := PIDlistStr + Inttostr(TmpAttributes.ProductID) + ',';
          end ;


          if TmpDeviceHandle <> INVALID_HANDLE_VALUE then
           begin
            CloseHandle(TmpDeviceHandle);
            TmpAttributes.ProductID := 0;
            TmpAttributes.VendorID := 0;
           end;
          TmpDeviceHandle := INVALID_HANDLE_VALUE;
          end
        except
          // ignore device if unreadable
        end;
        Inc(TmpDevn);
      end
    else
      showmessage('error in SetupDiGetDeviceInterfaceDetails');
      FreeMem(TmpFunctionClassDeviceData);
    end;
  end;
until not TmpSuccess;

person Steve Magness    schedule 08.11.2011    source источник


Ответы (2)


Изменения сейчас внесены в JVCL, пожалуйста, используйте последнее содержимое SVN.

По сути, возникла необходимость исправить SetupApi, чтобы он использовал «наполнитель» в x64 для выравнивания.

Это было протестировано и здесь хорошо работает.

person OBones    schedule 13.06.2012

Итак, после долгой тяжелой работы у меня все заработало. Возможное исправление не слишком сложно, хотя мне пришлось вникнуть в модуль JVCL SetupApi и изменить некоторые типы переменных структуры.

repeat
  TmpDeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
  // showmessage('TSPDeviceInterfaceData: ' + inttostr(SizeOf(TSPDeviceInterfaceData)));
  TmpSuccess := SetupDiEnumDeviceInterfaces(TmpDevInfo, nil, TmpDevHidGuid, TmpDevn, TmpDeviceInterfaceData);
  if TmpSuccess then
  begin
    TmpDevData.cbSize := SizeOf(TmpDevData);
    // showmessage('TmpDevData: ' + inttostr(tmpdevdata.cbsize));
    TmpBytesReturned := 0;
    SetupDiGetDeviceInterfaceDetail(TmpDevInfo, @TmpDeviceInterfaceData, nil, 0, TmpBytesReturned, @TmpDevData);
     //showmessage('bytes returned = ' + inttostr(TmpBytesReturned));  // = 170 in 32 bit app
    inc(i);
    if (TmpBytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
    begin
      // showmessage('i did this ' + inttostr(i) + ' times');
      TmpFunctionClassDeviceData := AllocMem(TmpBytesReturned);

      {$ifdef CPUX64}
         TmpFunctionClassDeviceData.cbSize := 8;
         // showmessage('64 bit compiler used');
      {$else}
         TmpFunctionClassDeviceData.cbSize := 6;
         // showmessage('32 bit compiler used');
      {$endif}

      // showmessage('TmpFunctionClassDeviceData:' + inttostr(sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A)));
      if SetupDiGetDeviceInterfaceDetail(TmpDevInfo, @TmpDeviceInterfaceData, TmpFunctionClassDeviceData, TmpBytesReturned, TmpBytesReturned, @TmpDevData) then
      begin
        try
          begin
          //try to obtain PID and VID information about the HID devices

          s := '';
          for i := 0 to 999 do
           begin
             s := s + TmpFunctionClassDeviceData.DevicePath[i];
           end;

          TmpDeviceHandle := CreateFile(PChar(s),
                            GENERIC_READ OR GENERIC_WRITE,
                            FILE_SHARE_READ OR FILE_SHARE_WRITE,
                            0, OPEN_EXISTING, 0 , 0);

          TmpAttributes.Size := Sizeof(TmpAttributes);
          // showmessage('TmpAttributes: ' + inttostr(Sizeof(TmpAttributes)));

          HidD_GetAttributes(TmpDeviceHandle, TmpAttributes);

          // showmessage(inttostr(TmpAttributes.VendorID) + ' ; ' + inttostr(TmpAttributes.ProductID));

          If (vid = TmpAttributes.VendorID) then
          begin
            PIDlistStr := PIDlistStr + Inttostr(TmpAttributes.ProductID) + ',';
          end ;

          if TmpDeviceHandle <> INVALID_HANDLE_VALUE then
           begin
            CloseHandle(TmpDeviceHandle);
            TmpAttributes.ProductID := 0;
            TmpAttributes.VendorID := 0;
           end;
          TmpDeviceHandle := INVALID_HANDLE_VALUE;
          end
        except
          // ignore device if unreadable
        end;
        Inc(TmpDevn);
      end;
    //else
      //showmessage('bob ' + inttostr(GetLastError));
      FreeMem(TmpFunctionClassDeviceData);
    end;
  end;
until not TmpSuccess;

Чтобы узнать об изменениях в SetupAPI.pas, см. Мою запись в системе отслеживания проблем джедаев здесь: http://issuetracker.delphi-jedi.org/view.php?id=5706

Если кто-нибудь может сказать мне, почему DevicePath необходимо явно скопировать в локальную строку перед передачей в CreateFile или почему я не могу использовать SizeOf для TmpFunctionClassDeviceData.cbSize, я был бы очень признателен.

person Steve Magness    schedule 10.11.2011
comment
›Почему DevicePath необходимо явно скопировать, как говорит здесь ms: msdn.microsoft.com/en-us/library/windows/hardware/› DevicePath ›Строка с завершающим нулем, содержащая путь к интерфейсу устройства. Этот путь можно передать функциям Win32, таким как CreateFile. szDevicePath: = PChar (@ TmpFunctionClassDeviceData.DevicePath [0]); используйте это значение для перехода к CreateFile. - person X-Ray; 21.08.2014