In my case, I need both the full names and COM port addresses. I have physical serial ports, USB serial ports, and com0com virtual serial ports.
Like the accepted answer suggests, I use WMI calls. SELECT * FROM Win32_PnPEntity
find all devices. It returns physical devices like this, and address can be parsed from Caption
:
Serial Port for Barcode Scanner (COM13)
However, for com0com ports Caption
is like this (no address):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort
returns addresses (DeviceID
), as well as full names (Name
). However, it only finds physical serial ports and com0com ports, not USB serial ports.
So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort
(address is DeviceID
) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'
(address can be parsed from Caption
). I have narrowed down the Win32_PnPEntity
call, because it only needs to find devices that were not found in the first call.
This C++ code can be used to find all serial ports:
// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
std::map<int, std::wstring> result;
HRESULT hres;
hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (SUCCEEDED(hres)) {
IWbemServices *pSvc = NULL;
// Connect to the rootcimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
bstr_t(L"ROOT\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (SUCCEEDED(hres)) {
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (SUCCEEDED(hres)) {
// Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
// This is done first, because it also finds some com0com devices, but names are worse
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp;
pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);
// Name should be for example "Serial Port for Barcode Scanner (COM13)"
const std::wstring deviceName = vtProp.bstrVal;
const std::wstring prefix = L"(COM";
size_t ind = deviceName.find(prefix);
if (ind != std::wstring::npos) {
std::wstring nbr;
for (size_t i = ind + prefix.length();
i < deviceName.length() && isdigit(deviceName[i]); i++)
{
nbr += deviceName[i];
}
try {
const int portNumber = boost::lexical_cast<int>(nbr);
result[portNumber] = deviceName;
}
catch (...) {}
}
VariantClear(&vtProp);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
// Use Win32_SerialPort to find physical ports and com0com virtual ports
// This is more reliable, because address doesn't have to be parsed from the name
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp1, vtProp2;
pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);
const std::wstring deviceID = vtProp1.bstrVal;
if (deviceID.substr(0, 3) == L"COM") {
const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
const std::wstring deviceName = vtProp2.bstrVal;
result[portNumber] = deviceName;
}
VariantClear(&vtProp1);
VariantClear(&vtProp2);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
}
pSvc->Release();
}
pLoc->Release();
}
}
CoUninitialize();
}
if (FAILED(hres)) {
std::stringstream ss;
ss << "Enumerating serial ports failed. Error code: " << int(hres);
throw std::runtime_error(ss.str());
}
return result;
}
При программировании COM портов полезно иметь возможность получать список доступных портов на компьютере. Эта задача встречается настолько часто, что я решил затронуть ее в своем блоге.
Существует несколько различных способов решения этой задачи. Их все можно условно разделить на «некрасивые» и «красивые».
«Некрасивые» решения
Первый вариант заключается в том, чтобы зашить в программу самые распространенные номера портов (обычно COM1, COM2, COM3 и некоторые другие). Недостатком этого подхода является то, что вы не всегда можете охватить все доступные порты. Например, я видел системы, на которых встречается порты COM22, COM23, COM24 (порты были виртуальными, но суть вопроса это не меняет). Не будете же вы зашивать столько номеров. А если завтра встретится COM100?
Второй вариант заключается в проверке большого количества портов на их доступность. Мы просматриваем порты COM1, COM2, ….., COM100 и пытаемся открыть каждый из них. Если это удалось, значит, порт есть. У этого подхода несколько недостатков.
Во-первых, такой перебор занимает время, а регулярные вызовы функции CreateFile напрасно расходуют системные ресурсы.
Во-вторых, у данного метода возможны ложные срабатывания. Предположим, пользователь запустил программу, которая заняла порт COM3. После этого он запустил вашу программу. Разумеется, при попытке открыть порт COM3 возникнет ошибка, так как он занят другой программой. В результате чего программа решит, что такого порта нет. А он есть.
В-третьих, он не решает проблему охвата всего многообразия возможных портов.
«Красивое» решение
Красивое решение основывается на том факте, что информация о доступных COM портах (в том числе виртуальных) в системах Windows хранится в реестре. Точнее в ветке HKEY_LOCAL_MACHINEHARDWAREDEVICEMAPSERIALCOMM. Там COM порты хранятся в виде строк: «COM1», «COM2», «COM3» и т.д. Отсюда сразу возникает решение – нужно перебрать все строковые параметры в данном разделе реестра.
Ниже приводится полный исходный код примера, демонстрирующего данный метод.
#include <windows.h> #include <tchar.h> void ShowCOMPorts() { int r = 0; HKEY hkey = NULL; //Открываем раздел реестра, в котором хранится иинформация о COM портах r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\DEVICEMAP\SERIALCOMM\"), 0, KEY_READ, &hkey); if (r != ERROR_SUCCESS) return; unsigned long CountValues = 0, MaxValueNameLen = 0, MaxValueLen = 0; //Получаем информацию об открытом разделе реестра RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &CountValues, &MaxValueNameLen, &MaxValueLen, NULL, NULL); ++MaxValueNameLen; //Выделяем память TCHAR *bufferName = NULL, *bufferData = NULL; bufferName = (TCHAR*)malloc(MaxValueNameLen * sizeof(TCHAR)); if (!bufferName) { RegCloseKey(hkey); return; } bufferData = (TCHAR*)malloc((MaxValueLen + 1)*sizeof(TCHAR)); if (!bufferData) { free(bufferName); RegCloseKey(hkey); return; } unsigned long NameLen, type, DataLen; //Цикл перебора параметров раздела реестра for (unsigned int i = 0; i < CountValues; i++) { NameLen = MaxValueNameLen; DataLen = MaxValueLen; r = RegEnumValue(hkey, i, bufferName, &NameLen, NULL, &type, (LPBYTE)bufferData, &DataLen); if ((r != ERROR_SUCCESS) || (type != REG_SZ)) continue; _tprintf(TEXT("%sn"), bufferData); } //Освобождаем память free(bufferName); free(bufferData); //Закрываем раздел реестра RegCloseKey(hkey); } int main() { ShowCOMPorts(); return 0; }
В заключение лишь отмечу, что я использую этот подход во многих своих проектах, и он еще ни разу не подводил меня.
In my case, I need both the full names and COM port addresses. I have physical serial ports, USB serial ports, and com0com virtual serial ports.
Like the accepted answer suggests, I use WMI calls. SELECT * FROM Win32_PnPEntity
find all devices. It returns physical devices like this, and address can be parsed from Caption
:
Serial Port for Barcode Scanner (COM13)
However, for com0com ports Caption
is like this (no address):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort
returns addresses (DeviceID
), as well as full names (Name
). However, it only finds physical serial ports and com0com ports, not USB serial ports.
So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort
(address is DeviceID
) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'
(address can be parsed from Caption
). I have narrowed down the Win32_PnPEntity
call, because it only needs to find devices that were not found in the first call.
This C++ code can be used to find all serial ports:
// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
std::map<int, std::wstring> result;
HRESULT hres;
hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (SUCCEEDED(hres)) {
IWbemServices *pSvc = NULL;
// Connect to the rootcimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
bstr_t(L"ROOT\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (SUCCEEDED(hres)) {
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (SUCCEEDED(hres)) {
// Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
// This is done first, because it also finds some com0com devices, but names are worse
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp;
pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);
// Name should be for example "Serial Port for Barcode Scanner (COM13)"
const std::wstring deviceName = vtProp.bstrVal;
const std::wstring prefix = L"(COM";
size_t ind = deviceName.find(prefix);
if (ind != std::wstring::npos) {
std::wstring nbr;
for (size_t i = ind + prefix.length();
i < deviceName.length() && isdigit(deviceName[i]); i++)
{
nbr += deviceName[i];
}
try {
const int portNumber = boost::lexical_cast<int>(nbr);
result[portNumber] = deviceName;
}
catch (...) {}
}
VariantClear(&vtProp);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
// Use Win32_SerialPort to find physical ports and com0com virtual ports
// This is more reliable, because address doesn't have to be parsed from the name
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp1, vtProp2;
pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);
const std::wstring deviceID = vtProp1.bstrVal;
if (deviceID.substr(0, 3) == L"COM") {
const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
const std::wstring deviceName = vtProp2.bstrVal;
result[portNumber] = deviceName;
}
VariantClear(&vtProp1);
VariantClear(&vtProp2);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
}
pSvc->Release();
}
pLoc->Release();
}
}
CoUninitialize();
}
if (FAILED(hres)) {
std::stringstream ss;
ss << "Enumerating serial ports failed. Error code: " << int(hres);
throw std::runtime_error(ss.str());
}
return result;
}
In my case, I need both the full names and COM port addresses. I have physical serial ports, USB serial ports, and com0com virtual serial ports.
Like the accepted answer suggests, I use WMI calls. SELECT * FROM Win32_PnPEntity
find all devices. It returns physical devices like this, and address can be parsed from Caption
:
Serial Port for Barcode Scanner (COM13)
However, for com0com ports Caption
is like this (no address):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort
returns addresses (DeviceID
), as well as full names (Name
). However, it only finds physical serial ports and com0com ports, not USB serial ports.
So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort
(address is DeviceID
) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'
(address can be parsed from Caption
). I have narrowed down the Win32_PnPEntity
call, because it only needs to find devices that were not found in the first call.
This C++ code can be used to find all serial ports:
// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
std::map<int, std::wstring> result;
HRESULT hres;
hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (SUCCEEDED(hres)) {
IWbemServices *pSvc = NULL;
// Connect to the rootcimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
bstr_t(L"ROOT\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (SUCCEEDED(hres)) {
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (SUCCEEDED(hres)) {
// Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
// This is done first, because it also finds some com0com devices, but names are worse
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp;
pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);
// Name should be for example "Serial Port for Barcode Scanner (COM13)"
const std::wstring deviceName = vtProp.bstrVal;
const std::wstring prefix = L"(COM";
size_t ind = deviceName.find(prefix);
if (ind != std::wstring::npos) {
std::wstring nbr;
for (size_t i = ind + prefix.length();
i < deviceName.length() && isdigit(deviceName[i]); i++)
{
nbr += deviceName[i];
}
try {
const int portNumber = boost::lexical_cast<int>(nbr);
result[portNumber] = deviceName;
}
catch (...) {}
}
VariantClear(&vtProp);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
// Use Win32_SerialPort to find physical ports and com0com virtual ports
// This is more reliable, because address doesn't have to be parsed from the name
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp1, vtProp2;
pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);
const std::wstring deviceID = vtProp1.bstrVal;
if (deviceID.substr(0, 3) == L"COM") {
const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
const std::wstring deviceName = vtProp2.bstrVal;
result[portNumber] = deviceName;
}
VariantClear(&vtProp1);
VariantClear(&vtProp2);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
}
pSvc->Release();
}
pLoc->Release();
}
}
CoUninitialize();
}
if (FAILED(hres)) {
std::stringstream ss;
ss << "Enumerating serial ports failed. Error code: " << int(hres);
throw std::runtime_error(ss.str());
}
return result;
}
makavelly 0 / 0 / 0 Регистрация: 16.09.2010 Сообщений: 23 |
||||
1 |
||||
16.09.2010, 10:29. Показов 16412. Ответов 4 Метки нет (Все метки)
Пытаюсь определить и вывести куда-нибудь список всех com портов системы, раньше работал в билдере, там код был такой
Сейчас то же самое хочу сделать в Visual Studio 2008, но никак не получается разобраться с RegOpenKeyEx, RegEnumValue… Или надо что-то другое использовать? Не поможете?
__________________
0 |
dimasamchenko 336 / 269 / 21 Регистрация: 30.03.2009 Сообщений: 500 |
||||
16.09.2010, 12:03 |
2 |
|||
Определение портов (COM) системы
где cbPorts комбобокс для индикации номеров портов!
1 |
0 / 0 / 0 Регистрация: 16.09.2010 Сообщений: 23 |
|
16.09.2010, 13:05 [ТС] |
3 |
Спасибо, работает!
0 |
0 / 0 / 0 Регистрация: 16.10.2015 Сообщений: 1 |
|
16.10.2015, 20:51 |
4 |
Подскажите пожалуйста! мне нужно вывести доступные ком порты на экран желательно на комбо бокс. ктоможет помочь? и где его писать?? я просто новичок)) писать где то здесь?? private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) } ????
0 |
djrenko 0 / 0 / 0 Регистрация: 28.03.2015 Сообщений: 11 |
||||||||
13.02.2017, 15:24 |
5 |
|||||||
Лучше всего это сделать при загрузке формы.
Не забудьте подключить:
А потом, если вдруг устройство отключится или подключится новое, обновлять этот список. Для этого лучше использовать события SerialPort, но можно и по таймеру.
0 |
На сегодняшний день популярными портами в новых компьютерах стали USB, а СОМ – порт почти полностью вышел из эксплуатации. Тем не менее, устройства использующие СОМ – порт не остались в стороне. Они поддерживаются производителями и не потеряли популярности на производствах. Где нужен обмен информацией между различными современными устройствами (сбор информации, контроль управления).
В недалеком прошлом для работы с последовательными портами (COM) в среде .Net 1.1, необходимо было использовать либо «Windows API», либо управление из сторонних библиотек. В среде .Net 2.0 (и в более поздних версиях .NET) компания Microsoft реализовала весь необходимый функционал для работы с последовательным портом включением класса «SerialPort» как части пространства имен «System.IO.Ports».
Последовательный порт (англ. serial port, COM-порт, англ. communications port) — сленговое название интерфейса стандарта RS-232, которым массово оснащались персональные компьютеры. Последовательным данный порт называется потому, что информация через него передаётся по одному биту, бит за битом (в отличие от параллельного порта).
Для получения списка всех COM-портов компьютера можно воспользоваться статическим методом «GetPortNames» класса «SerialPort» и оператора «foreach». Этот оператор обрабатывает все элементы массива имен последовательных портов для текущего компьютера в том порядке, в котором его возвращает метод «GetPortNames».
foreach (string str in System.IO.Ports.SerialPort.GetPortNames())
В результате этого вызова при обработке массива в переменной «str» будет содержаться имя COM-порта локальной машины. Перед проверкой конкретного COM-порта нужно сначала создать объект класса SerialPort с указанием имени порта.
System.IO.Ports.SerialPort Port; Port = new System.IO.Ports.SerialPort(str);
Для проверки состояния порта, необходимо воспользоваться методом «SerialPort.Open», данный метод открывает новое соединение последовательного порта. После открытия нового соединения необходимо воспользоваться свойством «SerialPort.IsOpen», возвращающее значение, указывающее состояние объекта SerialPort — открыт или закрыт. После проверки состояния необходимо закрыть соединение, вызвав метод «Close».
Port.Close();
Создайте проект «Windows Form» в «Microsoft Visual Studio» и добавьте на главную форму вашего проекта следующие элементы управления:
- System.Windows.Forms.Button button1 – элемент управления для запуска процесса поиска открытых портов;
- System.Windows.Forms.ListBox listBox1 – элемент управления для вывода имен открытых портов.
У вас получится приведенный ниже пример.
Сделайте двойной клик левой клавишей мыши по элементу управления «button1». Вы перейдете в редактор кода с автоматической установкой курсора в созданном методе «button1_Click», события «Click», возникающего при нажатии на кнопку. Добавьте в тело данного метода приведенный ниже листинг, вызова метода проверки открытых портов компьютера.
//Выполняем очистку listBox1 listBox1.Items.Clear(); //Если порты найдены, то //программа выдаст сообщение if (IsPortOpen()) MessageBox.Show("Найдены открытые порты!", "Сканирование портов", MessageBoxButtons.OK, MessageBoxIcon.Information); else MessageBox.Show("Открытые порты не найдены!", "Сканирование портов", MessageBoxButtons.OK, MessageBoxIcon.Information);
Так же добавьте в листинг главной формы сам метод.
public bool IsPortOpen() { //Создаем переменную для возврата //состояния получения портов. //true в случае успешного получения //false если порты не найдены. bool available = false; //Представляет ресурс последовательного порта. System.IO.Ports.SerialPort Port; //Выполняем проход по массиву имен //последовательных портов для текущего компьютера //которые возвращает функция SerialPort.GetPortNames(). foreach (string str in System.IO.Ports.SerialPort.GetPortNames()) { try { Port = new System.IO.Ports.SerialPort(str); //Открываем новое соединение последовательного порта. Port.Open(); //Выполняем проверку полученного порта //true, если последовательный порт открыт, в противном случае — false. //Значение по умолчанию — false. if (Port.IsOpen) { //Если порт открыт то добавляем его в listBox listBox1.Items.Add(str); //Уничтожаем внутренний объект System.IO.Stream. Port.Close(); //возвращаем состояние получения портов available = true; } } //Ловим все ошибки и отображаем, что открытых портов не найдено catch (Exception ex) { MessageBox.Show(ex.ToString(), "Ошибка при сканировании!", MessageBoxButtons.OK, MessageBoxIcon.Error); } } //возвращаем состояние получения портов return available; }
Запустите проект, нажав на клавишу «F5». После успешной компиляции и запуска вашего проекта у вас откроется главная форма вашего проекта, нажмите кнопку «Сканировать». После завершения сканирования, вы получите сообщение с информацией о результате, а так же список открытых портов, если такие были найдены.
В случае если COM-порт уже используется другим процессом, то при попытке его открыть вы получите исключение: Невозможно открыть com порт. Чтобы определить, кто именно держит порт, нужно воспользоваться утилитой для мониторинга ресурсов процессов.
Я уже как то писал о работе с последовательным портом компьютера на языке C++. И не то чтобы я не любил этот язык, или мне было лень написать на пару строк больше кода.. но стал посматривать в сторону C#. Есть устойчивое впечатление, что язык станет развиваться семимильными шагами.
Решил написать утилиту, которая будет мониторить регистры устройства на Modbus линии. А для этого конечно нужно наладить связь с этим самым устройством.
Решено было писать на C#.
Для работы понадобиться указать, что будем использовать класс для работы с портами.
using System.IO.Ports;
Теперь можно использовать класс SerialPort. Писать будем прямо в Main созданный нам средой.
Сразу же посмотрим какие есть порты в системе и предложим выбрать.
SerialPort port; // получаем список доступных портов string[] ports = SerialPort.GetPortNames(); Console.WriteLine("Выберите порт:"); // выводим список портов for (int i=0; i<ports.Length;i++) { Console.WriteLine("[" + i.ToString() + "] "+ports[i].ToString()); }
Создаем объект порта и считываем с консоли номер, который выбрал пользователь.
port = new SerialPort (); // читаем номер из консоли string n = Console.ReadLine (); int num = int.Parse (n);
Наполняем класс работы с com портом настройками и пытаемся открыть.
try { // настройки порта port.PortName = ports [num]; port.BaudRate = 256000; port.DataBits = 8; port.Parity = System.IO.Ports.Parity.None; port.StopBits = System.IO.Ports.StopBits.One; port.ReadTimeout = 1000; port.WriteTimeout = 1000; port.Open (); } catch(Exception e) { Console.WriteLine ("ERROR: невозможно открыть порт:" + e.ToString ()); return; }
Пишем в порт строку и закрываем порт.
port.Write ("Hello from C#"); port.Close ();
Получается вот так:
Для чтения можно пользоваться методом Read(), который принимает в качестве параметров буфер и количество данных, которые нужно принять.
Вот собственно и все. Есть еще сигналы о наличии данных и прочие плюшки. Хорошая справка по этому поводу на msdn.
Получение
списка COM-портов в системе
Следующий
код позволит вам получить список COM-портов в системе (программа
консольная):
#include
<windows.h>
#include
<TCHAR.H >
#include
<conio.h>
#include
<stdio.h>
//——————————————————————————
void
EnumerateSerialPorts()
{
//
В какой системе работаем?
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize =
sizeof(OSVERSIONINFO);
BOOL bGetVer =
GetVersionEx(&osvi);
//
В NT используем API QueryDosDevice
if(bGetVer
&& (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT))
{
//
Используем QueryDosDevice для просмотра всех устройств похожих на
COMx.
//
Это наилучшее решение, так как порты не требуется открывать
TCHAR szDevices[65535];
DWORD dwChars =
QueryDosDevice(NULL, szDevices, 65535);
if(dwChars)
{
int
i=0;
for
(;;)
{
//
Получаем текущее имя устройства
TCHAR* pszCurrentDevice =
&szDevices[i];
//
Если похоже на «COMX» выводим на экран
int
nLen =
_tcslen(pszCurrentDevice);
if(nLen
> 3 && _tcsnicmp(pszCurrentDevice, _T(«COM»), 3)
== 0)
{
printf(pszCurrentDevice);
printf(«n»);
}
//
Переходим к следующему символу терминатору
while(szDevices[i]
!= _T(»))
i++;
//
Перескакиваем на следующую строку
i++;
//
Список завершается двойным симмволом терминатором, так что если
символ
//
NULL, мы дошли до конца
if(szDevices[i]
== _T(»))
break;
} //
for (;;)
} //
if(dwChars)
} //
if(bGetVer && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT))
else
{
//
В 95/98 открываем каждый порт для определения его существования
//
Поддерживается до 255 COM портов, так что мы проходим пл всему списку
//
Если мы не можем открыть порт, или происходит ошибка при открытии,
//
получаем access denied или общую ошибку все эти случаи указывают на
//
то, что под таким номером есть порт.
for
(UINT i=1; i<256; i++)
{
//
Формируем сырое имя устройства
char
sPort[10];
sprintf(sPort,»\\.\COM%d»,
i);
//
Пытаемся открыть каждый порт
BOOL
bSuccess = FALSE;
HANDLE hPort =
::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING, 0, 0);
if(hPort
== INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
//
Смотрим что получилось при открытии
if(dwError
== ERROR_ACCESS_DENIED || dwError == ERROR_GEN_FAILURE)
bSuccess
= TRUE;
}
else
{
//
Порт открыт успешно
bSuccess = TRUE;
//
Не забываем закрывать каждый открытый порт,
//
так как мы не собираемся с ним работать…
CloseHandle(hPort);
} //
if(hPort == INVALID_HANDLE_VALUE)
//
Выводим на экран название порта
if(bSuccess)
{
printf(sPort);
printf(«n»);
}
} //
for (UINT i=1; i<256; i++)
} //
if(bGetVer && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT))
}
//——————————————————————————
int
main()
{
EnumerateSerialPorts();
getch();
return
0;
}
//——————————————————————————