1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Windows.h]接続されているCOMデバイスとその詳細を取得(備忘録)

Last updated at Posted at 2023-04-01

初めに

Windows.hを使うことでレジストリにアクセスすることができる。
今回は、現在接続されているデバイスのCOMポート一覧の取得方法をまとめました。

windows限定です

プログラム

int serchDevice()関数は接続されているデバイスの数を返します。x64環境で作成しております。

勉強中なので、プログラムにはミスが含まれている可能性があります。

#include <iostream>
#include <string>
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <setupapi.h>

#pragma comment(lib,"hid.lib")
#pragma comment(lib,"setupapi.lib")

int serchDevice(){
    HDEVINFO hDevInfo;
	int max = 0;

	// デバイス一覧の情報を取得
	hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
	if (hDevInfo == INVALID_HANDLE_VALUE) { printf_s("ERROR:COMPORT NOT FOUND\n"); return -1; }

    // デバイス情報セット
	SP_DEVINFO_DATA Data = { sizeof(SP_DEVINFO_DATA) };
	Data.cbSize = sizeof(Data);

	// 個々のデバイスの情報を取得
	while (SetupDiEnumDeviceInfo(hDevInfo, max, &Data)) {
		char *port = nullptr;
		char name[256] = { 0 };

		// 個々のデバイスの構成情報のレジストリキーを開く
		HKEY key = SetupDiOpenDevRegKey(hDevInfo, &Data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
		if (key != INVALID_HANDLE_VALUE) {
			DWORD type = 0;
			DWORD size = 256;

			//デバイス情報(レジストリキー)の情報群からCOMPortの名前を取り出す
			RegQueryValueEx(key, "PortName", NULL, &type, (LPBYTE)name, &size);
			port = name;
		}else continue;
		
		DWORD size = 0;
		char *buf = nullptr;

		// デバイスの詳細の取得
        // デバイスの詳細に必要な文字数をsizeに収納
		SetupDiGetDeviceRegistryProperty(hDevInfo, &Data, SPDRP_DEVICEDESC, NULL, NULL, 0, &size); 
		buf = new char [size * 2];

        BOOL success = FALSE;
		// デバイスの詳細の文字列をbufに収納
		success = SetupDiGetDeviceRegistryProperty(hDevInfo, &Data, SPDRP_DEVICEDESC, NULL, (PBYTE)buf, size, &size);

        if (success == FALSE) printf_s("ERROR:CANT GET THE DETAIL\n");
        
        // ここで、COMポートの名前(port)と詳細(buf)が取得できる。ここを改良して出力してください
		printf_s("%s, %s\n", port, buf); 

		delete[] buf;
		max++;
	}
    SetupDiDestroyDeviceInfoList(hDevInfo); // デバイス情報セットの解放
    return max;
}

実行例

ターミナルの出力

COM20, Bluetooth リンク経由の標準シリアル
COM1, 通信ポート
COM21, Bluetooth リンク経由の標準シリアル

デバイスマネジャー
デバイスマネージャー.png

解説

ほとんどMicrosoftのWINAPIリファレンスから引用です。

間違えている可能性が高いです。レジストリなどよくわかっていません…参考でお願いします。

SetupDiGetClassDevs(const GUID *ClassGuid, PCSTR Enumerator, HWND hwndParent, DWORD Flags)

SetupDiGetClassDevsA 関数 (setupapi.h)

/*デバイス一覧の情報を取得
HDEVINFO SetupDiGetClassDevs(
    デバイスの種類(GUID_DEVINTERFACE_COMPORT=COMPORTのみ), 
    PnP(コンピュータに差し込む装置)を識別するID,USBやPCMCIA等(NULL可),
    ユーザーインターフェースの最上ウィンドウのハンドル? (NULL可),
    出力するデバイスのフィルター (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE = 現在存在するデバイス ∧ デバイスインターフェースのみ)
);
戻り値:引数に一致するデバイス一覧を返す
*/
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

SP_DEVINFO_DATA

SP_DEVINFO_DATA構造体 (setupapi.h)
詳細不明です…Dataには最終的に取得した個々のデバイスの情報(GUIDなど)が入ります。

// デバイス情報セット
SP_DEVINFO_DATA Data = { sizeof(SP_DEVINFO_DATA) };
Data.cbSize = sizeof(Data);

SetupDiEnumDeviceInfo(HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData)

SetupDiEnumDeviceInfo 関数 (setupapi.h)

/*個々のデバイスの情報を取得
BOOL SetupDiEnumDeviceInfo(
    デバイス一覧,
    デバイスのインデックス,
    デバイス一覧から、インデックスで指定された個々のデバイス情報を渡す
);
戻り値:成功したか否か(成功=TRUE)
*/
SetupDiEnumDeviceInfo(hDevInfo, max, &Data)

SetupDiOpenDevRegKey(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Scope, DWORD HwProfile, DWORD KeyType, REGSAM samDesired);

SetupDiOpenDevRegKey 関数 (setupapi.h)

/*個々のデバイスの構成情報のレジストリキーを開く
HKEY SetupDiOpenDevRegKey(
    デバイス一覧,
    デバイス一覧から一つを指定するためのSP_DEVINFO_DATA構造体,
    開くレジストリキーを選択(DICS_FLAG_GLOBAL=構成情報を取得),
    ハードウェアの情報の選択(0=現在の状態のハードウェアプロファイル)
    開くレジストリキーの種類(DIREG_DEV デバイスのハードウェアキー),
    開くキーに使うレジストリセキュリティアクセス
);
返り値:レジストリキー
*/
HKEY key = SetupDiOpenDevRegKey(hDevInfo, &Data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);

RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);

RegQueryValueExA function (winreg.h)

/*デバイス情報(レジストリキー)の情報群から一つを選ぶ
LSTATUS RegQueryValueEx(
    レジストリキー,
    取得したい情報,
    NULL(予約済み),
    値を収納する変数へのポインタ(値)(NULL可),
    値を収納する変数へのポインタ(文字)(NULL可),
    文字を収納する場合の文字列バッファサイズ(文字ポインタがNULLのときはNULLでよい)
);
返り値:エラーコード(ERROE_SUCCESS=成功)
*/
RegQueryValueEx(key, "PortName", NULL, &type, (LPBYTE)name, &size);

SetupDiGetDeviceRegistryProperty(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize);

SetupDiGetDeviceRegistryPropertyA 関数 (setupapi.h)

/*デバイスの説明文の取得
SetupDiGetDeviceRegistryProperty(
    レジストリキー,
    取得したい情報,
    取得したい情報の選択(SPDRP_DEVICEDESC=デバイスの解説),
    返り値のデータの型(NULL可),
    返り値を入れるバッファのポインタ(NULL可),
    返り値を入れるバッファのサイズ,
    取得した文字列の長さ
);
返り値:成功したか否か(成功=TRUE)
※「返り値を入れるバッファのポインタ」と「返り値を入れるバッファのサイズの両方がNULL」のとき、「取得した文字列の長さ」には必要なバッファの大きさが返る
*/
SetupDiGetDeviceRegistryProperty(hDevInfo, &Data, SPDRP_DEVICEDESC, NULL, (PBYTE)buf, size, &size);

参考

COMポートの一覧を表示・プロパティの変更及びポートの選択をする(ダイアログボックス版)

デバイスとドライバーのインストールのリファレンス

変更履歴

  • コメントより、SetupDiGetClassDevsSetupDiOpenDevRegKeyが失敗したとき、INVALID_HANDLE_VALUEを返すのに0でチェックを行っていたところを修正
  • コメントより、例外処理をSetupDiGetDeviceRegisterProperty関数にも追加
1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?