При программировании под Windows есть очень простая, можно сказать, стандартная задача - построение списка COM-портов и вывод списка в комбобокс.
Ниже приведу два варианта построения такого списка и объясню, почему с использованием только Qt это сделать невозможно.
Вариант 1. С использованием Qt. Не работающий.
Для работы с реестром Windows средствами Qt можно использовать класс QSettings, который позволяет работать не только с ini-файлами, но и с несколькими другими форматами хранения настроек. В этом случае функция построения списка компортов могла бы выглядеть так.
#include <QSettings.h>
QStringList getComPortList()
{
QString path = "HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM";
QSettings settings(path, QSettings::NativeFormat);
QStringList keys = settings.childKeys();
QStringList list;
for (int i = 0; i < keys.count(); i++)
{
QVariant var = settings.value(keys.at(i));
list.append(var.toString());
}
return list;
}
Список ключей keys будет выглядеть примерно так: ["/Device/Serial0" "/Device/VCP0"]
Как можно заметить, Qt заменяет бэкслэши (backslash) на слэши (slash), т.к. использует бэкслэши для разделения подключей. Как гласит документация на QSettings:
Do not use slashes ('/' and '\') in section or key names; the backslash character is used to separate sub keys (see below). On windows '\' are converted by QSettings to '/', which makes them identical.
Поэтому на Qt невозможно прочитать ключи с бэкслэшами --> пичалька. Приведенный вариант работать не будет.
Вариант 2. С использованием WINAPI. Готов к использованию.
#include <windows.h>
QStringList getComPortList()
{
QStringList list;
HKEY hKey = 0;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), NULL, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD cbMaxValueNameLen, cbMaxValueLen;
if (RegQueryInfoKey(hKey, // дескриптор ключа
NULL, // адрес буфера для имени класса
NULL, // адрес размера буфер для имени класса
NULL, // зарезервировано
NULL, // адрес буфера для количества подключей
NULL, // адрес буфера для наибольшего размера имени подключа
NULL, // адрес буфера для наибольшего размера имени класса
NULL, // адрес буфера для количества вхождений значений
&cbMaxValueNameLen, // адрес буфера для наибольшего размера имени значения
&cbMaxValueLen, // адрес буфера для наибольшего размера данных значения
NULL, // адрес буфера для длины дескриптора безопасности
NULL // адрес буфера для получения времени последней записи
) == ERROR_SUCCESS)
{
cbMaxValueNameLen++;
cbMaxValueLen++;
TCHAR ValueName[cbMaxValueNameLen];
DWORD Type = REG_SZ;
BYTE Data[cbMaxValueLen];
char temp[cbMaxValueLen];
DWORD cbValueNameLen, cbValueLen;
DWORD index = 0;
cbValueNameLen = cbMaxValueNameLen;
cbValueLen = cbMaxValueLen;
while (RegEnumValue(hKey, // дескриптор запрашиваемого ключа
index++, // индекс запрашиваемого значения
ValueName, // адрес буфера для имени значения
&cbValueNameLen, // адрес переменной с размером буфера для имени значения
NULL, // зарезервировано
&Type, // адрес переменной с типом данных
Data, // адрес буфера для данных значения
&cbValueLen // адрес переменной с размером буфера для данных
) == ERROR_SUCCESS)
{
wcstombs(temp, (WCHAR*)Data, cbValueLen/2);
list.append(temp);
cbValueNameLen = cbMaxValueNameLen;
cbValueLen = cbMaxValueLen;
}
}
RegCloseKey(hKey);
}
return list;
}
Это полностью готовый вариант. Можно использовать :)
Большое спасибо!
ОтветитьУдалитьПриятно когда можно тупо скопипастить и работает из коробки, а потом по живому разбираться. Спасибо
ОтветитьУдалитьАндрей, сейчас, когда QSerial уже есть в Qt5, всё гораздо проще.
Удалить