Спасибо за ответ. Только это стандартное решение: перелапатить весь Enum реестра и выбрать COM-порты для нужного VID/PID. Потом прочитать DeviceMap. Сравнивая списки, получаем открытые COM-порты для нужного VID/PID. Проблема в том, что некоторые производители помещают свои VCOM-порты не в ...Enum\USB, а в другие ключи реестра, например, для сканеров Symbol информация лежит в Enum\WIP. Поэтому искался путь от обратного: получили список открытых VCOM-портов, потом посредством некого IOCTL_xxxx-вызова получить для него VID/PID, и выбрали нужный VCOM-порт. К сожалению найти не смог, поэтому пока реализовано, по типу, как Вы предлагаете. Возможно это единственный и правильный путь. Юра.
// Name_company='FTDIBUS'; VID:='6001'; PID:='0403';('FTDIBUS\VID_0403+PID_6001+A501XYRYA\0000') // Com_enabled - только доступные для работы порты (которым можно подключиться сейчас и их не кто уже не использует); function SetupEnumAvailableComPorts(Name_company,VID, PID:string; Com_enabled:boolean):TstringList; // Enumerates all serial communications ports that are available and ready to // be used.
function LowerCase(s: string): string;// малым регистром const arr_lat:string='abcdefghijklmnopqrstuvwxyz'+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; arr_rus:string='абвгдеёжзийклмнопрстуфхцчшщъыьэюя'+ 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'; var res: string; c,i: integer; p:boolean; begin try res:=s; for i := 1 to length(res) do begin p:=false; for c:=1 to 26 do if res[i] = arr_lat[26+c] then begin p:=true; res[i]:=arr_lat[c] end; if not p then for c:=1 to 33 do if res[i] = arr_rus[33+c] then begin res[i]:=arr_rus[c] end; end; result:=res except result := s; end; end; var RequiredSize: Cardinal; GUIDSize: DWORD; Guid: TGUID; DevInfoHandle: HDEVINFO; DeviceInfoData: TSPDevInfoData; MemberIndex: Cardinal; PropertyRegDataType: DWord; RegProperty: Cardinal; RegTyp: Cardinal; Key: Hkey; Info: TRegKeyInfo; S1,S2: string; hc: THandle; Name: string; Err,i: Integer; Name_company_,VID_, PID_:string; Com_enabled_:boolean; begin try Result:=Nil; //If we cannot access the setupapi.dll then we return a nil pointer. if not LoadsetupAPI then exit; try // get 'Ports' class guid from name GUIDSize := 1; // missing from original code - need to tell function that the Guid structure contains a single GUID if SetupDiClassGuidsFromName('Ports',@Guid,GUIDSize,RequiredSize) then begin //get object handle of 'Ports' class to interate all devices DevInfoHandle:=SetupDiGetClassDevs(@Guid,Nil,0,DIGCF_PRESENT); if Cardinal(DevInfoHandle)<>Invalid_Handle_Value then begin try MemberIndex:=0; result:=TStringList.Create; //iterate device list repeat FillChar(DeviceInfoData,SizeOf(DeviceInfoData),0); DeviceInfoData.cbSize:=SizeOf(DeviceInfoData); //get device info that corresponds to the next memberindex if Not SetupDiEnumDeviceInfo(DevInfoHandle,MemberIndex,DeviceInfoData) then break; //query friendly device name LIKE 'BlueTooth Communication Port (COM8)' etc RegProperty:=SPDRP_FriendlyName;{SPDRP_Driver, SPDRP_SERVICE, SPDRP_ENUMERATOR_NAME,SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,SPDRP_FRIENDLYNAME,} SetupDiGetDeviceRegistryProperty(DevInfoHandle, DeviceInfoData, RegProperty, PropertyRegDataType, NIL,0,RequiredSize); SetLength(S1,RequiredSize); if SetupDiGetDeviceRegistryProperty(DevInfoHandle,DeviceInfoData, RegProperty, PropertyRegDataType, @S1[1],RequiredSize,RequiredSize) then begin KEY:=SetupDiOpenDevRegKey(DevInfoHandle,DeviceInfoData,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ); if key<>INValid_Handle_Value then begin FillChar(Info, SizeOf(Info), 0); //query the real port name from the registry value 'PortName' if RegQueryInfoKey(Key, nil, nil, nil, @Info.NumSubKeys,@Info.MaxSubKeyLen, nil, @Info.NumValues, @Info.MaxValueLen, @Info.MaxDataLen, nil, @Info.FileTime) = ERROR_SUCCESS then begin RequiredSize:= Info.MaxValueLen + 1; SetLength(S2,RequiredSize); if RegQueryValueEx(KEY,'PortName',Nil,@Regtyp,@s2[1],@RequiredSize)=Error_Success then begin If (Pos('COM',S2)=1) then begin Name:=''; //получаем размер строчки HardwareID SetupDiGetDeviceRegistryProperty(DevInfoHandle, DeviceInfoData, SPDRP_HARDWAREID, PropertyRegDataType, nil, 0, RequiredSize); Err := GetLastError; if Err = ERROR_INSUFFICIENT_BUFFER then // только эта ошибка должна возникнуть - все другое - что-то не так begin if Length(Name) < RequiredSize div SizeOf(Char) then // если буфер маленький, то SetLength(Name, RequiredSize div SizeOf(Char)); // устанавливаем размер буфера SetupDiGetDeviceRegistryProperty(DevInfoHandle, DeviceInfoData, SPDRP_HARDWAREID, PropertyRegDataType, @Name[1], RequiredSize, RequiredSize); Err:=GetLastError; if Err=0 then // получаем HardwareID begin Name_company_:=''; VID_:=''; PID_:=''; if Name<>'' then begin i:=pos('\',Name); if i>0 then Name_company_:=copy(Name,1,i-1); i:=pos('VID_',Name); if i>0 then if length(name)>=i+7 then VID_:=copy(Name,i+4,4); i:=pos('PID_',Name); if i>0 then if length(name)>=i+7 then PID_:=copy(Name,i+4,4); end; end; end else Continue; //Test if the device can be used Com_enabled_:=false; try hc:=CreateFile(pchar('\\.\'+S2+#0),GENERIC_READ or GENERIC_WRITE,0,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if hc<> INVALID_HANDLE_VALUE then begin Com_enabled_:=true; CloseHandle(hc); end; except Com_enabled_:=false; end; if ((LowerCase(Name_company)=LowerCase(Name_company_)) or (Name_company='')) and ((LowerCase(VID)=LowerCase(VID_)) or (VID='')) and ((LowerCase(PID)=LowerCase(PID_)) or (PID='')) and ((Com_enabled_) or (not Com_enabled)) then Result.Add(Strpas(PChar(S2))+': = '+StrPas(PChar(S1))); end; end; end; RegCloseKey(key); end; end; Inc(MemberIndex); until False; //If we did not found any free com. port we return a NIL pointer. if Result.Count=0 then begin Result.Free; Result:=NIL; end finally SetupDiDestroyDeviceInfoList(DevInfoHandle); end; end; end; finally UnloadSetupApi; end; except end; end;
procedure TForm2.Button1Click(Sender: TObject); var index : integer; Name_company,VID,PID:string; Com_enabled:boolean; ComPortStringList : TStringList; begin ListBox1.Clear; Name_company:=Edit1.Text; VID:=Edit2.Text; PID:=Edit3.Text; Com_enabled:=CheckBox1.Checked;
ComPortStringList := SetupEnumAvailableComPorts(Name_company,VID,PID,Com_enabled); if (ComPortStringList <> nil) and (ComPortStringList.Count > 0) then for Index := 0 to ComPortStringList.Count - 1 do ListBox1.Items.Add(ComPortStringList[Index]); end;
USB-устройство устанавливается как виртуальный COM-порт. Открываю его HDL=CreateFile("\\\\.\\COMn",...), Вызываю DeviceIoControl(HDL,IOCTL_GET_DEVICE_DESCRIPTOR,...); и DeviceIoControl(HDL,IOCTL_GET_USB_DESCRIPTOR,...); в обоих случаях возвращает TRUE, но структуры с данными не заполняет, и счетчик байт=0. Вызовов IOCTL_SERIAL_... и IOCTL_SERENUM_..., возвращающих VID и PID не нашел. Работаю в Borland C++. Может кто-нибудь подсказать, можно получить VID и PID для виртуального COM-порта, и если да, то как.