Перечисление USB HID-устройств с использованием SetupAPI в 64-битном приложении
Я использую Delphi XE2 и пытаюсь обновить нашу usb-связь dll до 64 бит. Мы используем модули JAPCL SetupAPI и Hid. Все работает отлично, используя 32-битный компилятор и может видеть мое подключенное устройство HID. Я переключаюсь на 64-разрядную версию и больше не вижу ни одного устройства HID, которое, как я знаю, подключено.
Я сталкивался с людьми, упоминавшими о необходимости изменить размер некоторых структур данных по-разному для 64-разрядных (см. https://forums.embarcadero.com/thread.jspa?messageID=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;
2 ответа
Теперь изменения внесены в JVCL, пожалуйста, используйте последний контент SVN.
По сути, необходимо было исправить SetupApi, чтобы он использовал "заполнитель" в x64 для выравнивания.
Это было проверено и хорошо работает здесь.
Итак, после большой кропотливой работы я получил эту работу. Возможное исправление не слишком сложное, хотя мне пришлось углубиться в модуль SetupApi JVCL и изменить некоторые типы переменных структуры.
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, я был бы очень признателен.