Перейти к содержанию
    

Определить номер COM-порта

Ситуация:

есть USB-COM устройство. Определить его можно (прочитав VID и PID всех подключенных к компьютеру USB устройств)

Также я могу определить все COM-порты компьютера

но что-то никак не соображу, как скрестить это дело

 

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

При извлечении устройства освободить порт и ждать нового подключения

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это зависит от операционной системы, но в любоой из двух можно узнать всю информацию об устройстве (она хранится в системе) и перебором найти нужный VID/PID.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

При извлечении устройства освободить порт и ждать нового подключения

 

Вот полный текст функции определения порта по VID и PID.

На вход даем VID, PID и пустой список строк.

На выходе получаем заполненный список с перечнем доступных портов и результат в виде индекса искомого порта в списке.

 

unit USB_COM_enumeration;

interface

uses Windows, Classes, SysUtils, JvSetupApi;

function EnumerateUsbCom(VID, PID: Integer; Ports: TStrings): Integer;

const
 PortsGUID: TGUID = '{4D36E978-E325-11CE-BFC1-08002BE10318}'; // ports

implementation

function EnumerateUsbCom(VID, PID: Integer; Ports: TStrings): Integer;
var
 GUID: TGUID;
 PnPHandle: HDevInfo; // handle на базу данных драйверов, раздел ports
 i, j: DWORD;
 DeviceInfoData: SP_DEVINFO_DATA;
 Err: Integer;
 RequiredLength: DWORD;
 DevicePath: string;
 RegType: DWORD;
 Name: string;
 s: string;
 DevPID: Word;
 DevVID: Word;
 RegKey: HKey;
begin
 Ports.Clear;
 Result := 0;
 GUID := PortsGUID;

 // получаем handle на базу данных портов присутствующих в системе
 // win7 compat: с флагом DIGCF_DEVICEINTERFACE в некоторых компах с семеркой
 // перечисляются только нативные ком-порты
 PnPHandle := SetupDiGetClassDevs(@GUID, nil, 0, DIGCF_PRESENT { or  DIGCF_DEVICEINTERFACE } );

 if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then // не можем открыть базу
raise Exception.Create(SysErrorMessage(GetLastError));
 try
i := 0; // первый порт
DeviceInfoData.cbSize := SizeOf(DeviceInfoData);
while SetupDiEnumDeviceInfo(PnPHandle, i, DeviceInfoData) do
// получаем последовательно порты, пока они есть.
begin
  DevicePath := '';
  Name := '';
  // получаем размер строчки HardwareID
  SetupDiGetDeviceRegistryProperty(PnPHandle, DeviceInfoData, SPDRP_HARDWAREID, RegType, nil, 0, RequiredLength);
  Err := GetLastError;
  if Err = ERROR_INSUFFICIENT_BUFFER then
  // только эта ошибка должна возникнуть - все другое - что-то не так
  begin
	if Length(Name) < RequiredLength div SizeOf(Char) then
	  // если буфер маленький, то
	  SetLength(Name, RequiredLength div SizeOf(Char));
	// устанавливаем размер буфера

	if not SetupDiGetDeviceRegistryProperty(PnPHandle, DeviceInfoData, SPDRP_HARDWAREID, RegType, @Name[1], RequiredLength, RequiredLength) then
	// получаем HardwareID
	begin
	  inc(i); // если ошибка, то смотрим следущий порт
	  Continue;
	end;
  end
  else
	raise Exception.Create(SysErrorMessage(Err));

  Name := UpperCase(Name);
  // чтобы сравнивать строки, переводим все в заглавные буквы
  if Copy(Name, 1, 3) = 'USB' then
  // если первые три символа HardwareID = 'USB' - то это у нас виртуальный порт
  begin
	j := pos('VID_', Name) + 4; // ищем где у нас VID
	s := '';
	while Name[j] <> '&' do // получаем VID
	begin
	  s := s + Name[j];
	  inc(j);
	end;
	DevVID := StrToInt('$' + s);
	// OutputDebugString(PChar('vid = ' + IntToHex(DevVID, 4)));
	j := pos('PID_', Name) + 4; // ищем PID
	s := '';
	while (Name[j] <> '&') and (Name[j] <> #0) do // получаем PID
	begin
	  s := s + Name[j];
	  inc(j);
	end;
	DevPID := StrToInt('$' + s);
	// OutputDebugString(PChar('pid = ' + IntToHex(DevPID, 4)));
	if (DevVID = VID) and (DevPID = PID) then // если VID и PID - наши, то
	begin
	  SetLength(DevicePath, 10);
	  // 10 символов на название ком-порта - хватит (максимальный COM999999 [последний символ = #0])
	  RegKey := SetupDiOpenDevRegKey(PnPHandle, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
	  // получаем Handle на раздел реестра экземпляра устройства
	  if RegKey = INVALID_HANDLE_VALUE then
	  begin
		inc(i); // если ошибка - следущий порт
		Continue;
	  end;
	  try
		RequiredLength := 10 * SizeOf(Char);
		if RegQueryValueEx(RegKey, 'PortName', nil, @RegType, @DevicePath[1], @RequiredLength) <> ERROR_SUCCESS then
		// в PortName записано название порта (например - СОМ5)
		begin
		  inc(i);
		  Continue;
		end;
		DevicePath := Copy(DevicePath, 1, RequiredLength div SizeOf(Char) - 1);
		// в RequiredLength - размер полученной строки, минус 1 - последний ноль нам не нужен
		Ports.Add(DevicePath); // добавлеяем имя порта
		inc(Result);
		// результат функции - количество портов для данного VID&PID
	  finally
		RegCloseKey(RegKey); // Handle надо закрыть, даже в случае ошибки
	  end;
	end;
  end;
  inc(i); // следущий порт
end;
 finally
SetupDiDestroyDeviceInfoList(PnPHandle); // освобождаем занятую память.
 end;
end;


end.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот полный текст функции определения порта по VID и PID.

Спасибо!

буду его под CPP перелопачивать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если пишите на C++ можете попробовать библиотеку Qt. Там в составе есть класс QSerialPortInfo - он позволяет получить список доступных в системе портов + кое-какую информацию о них (vid, pid, description): http://doc.qt.io/qt-5/qserialportinfo.html

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот полный текст функции

у меня не срабатывает вот это место:

        while (SetupDiEnumDeviceInfo(PnPHandle, i, &DeviceInfoData))

нет ни одного вхождения в цикл, хотя порт в системе точно есть

 

предваряет это все такая последовательность действий:

const TGUID PortsGUID = {0x4D36E978, 0xE325, 0x11CE, {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}}; // ports
TGUID GUID;

    GUID = PortsGUID;
    PnPHandle = ::SetupDiGetClassDevs(&GUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );

UPD

GetLastError() == ERROR_NO_MORE_ITEMS

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

убрал DIGCF_DEVICEINTERFACE, оставил только

PnPHandle = ::SetupDiGetClassDevs(&GUID, NULL, NULL, DIGCF_PRESENT)

и заработало

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если пишите на C++ можете попробовать библиотеку Qt. Там в составе есть класс QSerialPortInfo - он позволяет получить список доступных в системе портов + кое-какую информацию о них (vid, pid, description): http://doc.qt.io/qt-5/qserialportinfo.html
Это vendorIdentifier/productIdentifier? Не знал что такое есть...

А description что пишет? Интересует, можно ли использовать чужой VID/PID но засовывать свой description?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Самые интересные танцы начинаются когда к компу подключено несколько одинаковых устройств USB.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Самые интересные танцы начинаются когда к компу подключено несколько одинаковых устройств USB.

попробовал открыть - не получилось - берем следующий в списке

а вот когда встает задача "схватить" некий конкретный девайс - вот тут да... только в какой-то последовательности подключать одинаковые устройства и ждать события подключения

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...