CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME

CM_Get_DevNode_Registry_Propertyの第2引数に、CM_DRP_PHYSICAL_DEVICE_OBJECT_NAMEを指定すると、物理デバイスオブジェクト名を取得できる。

デバイス インスタンスのプロパティへのアクセス (Vista Windows前) - Windows drivers | Microsoft Learn

C_D_P_D_O_N.cppで、

とすると、

こうなる。
こうなる。

こうなる。
DiskDriveとしての物理デバイスオブジェクト名なので、Volumeの時とかとは違う。

Sample/winapi/CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME/C_D_P_D_O_N/src/C_D_P_D_O_N at master · bg1bgst333/Sample · GitHub

CM_Get_DevNode_Registry_Property

CM_Get_DevNode_Registry_Propertyで、指定のDEVINSTのデバイスのプロパティをレジストリから取得する。

CM_Get_DevNode_Registry_PropertyW関数 (cfgmgr32.h) - Win32 apps | Microsoft Learn

C_G_D_R_P.cppで、

と書く。
今回は、CM_DRP_CLASSを指定し、クラス名を取得。

クラス名はDiskDrive
クラス名はDiskDrive

クラス名はDiskDrive。

Sample/winapi/CM_Get_DevNode_Registry_Property/C_G_D_R_P/src/C_G_D_R_P at master · bg1bgst333/Sample · GitHub

CM_Locate_DevNode

CM_Locate_DevNodeで、指定のデバイスインスタンスパスのDEVINSTを取得する。

CM_Locate_DevNodeW関数 (cfgmgr32.h) - Win32 apps | Microsoft Learn
ドライブ名からデバイス情報を取得する(DEVINST系)(UsefullCode.net)

CM_Locate_DevNode.cppで、

と書く。

Dドライブのデバイスインスタンスパス
Dドライブのデバイスインスタンスパス

Dドライブのデバイスインスタンスパス。

CM_~系もsetupapi.lib
CM_~系もsetupapi.lib

CM_~系もsetupapi.lib

デバイスインスタンスパスを渡すと、dwDevInstが返る。
バイスインスタンスパスを渡すと、dwDevInstが返る。

バイスインスタンスパスを渡すと、dwDevInstが返る。

Sample/winapi/CM_Locate_DevNode/CM_Locate_DevNode/src/CM_Locate_DevNode at master · bg1bgst333/Sample · GitHub

CM_Request_Device_Eject

CM_Request_Device_Ejectで、指定のDevInstのデバイスを取り出す。

CM_Request_Device_EjectW関数 (cfgmgr32.h) - Win32 apps | Microsoft Learn

CM_Request_Device_Eject.cppで、

と書く。

USBHDDのDドライブを取り出す
USBHDDのDドライブを取り出す

USBHDDのDドライブを取り出す。

成功らしい。
成功らしい。

成功らしい。

確かにDドライブは消えた。
確かにDドライブは消えた。

確かにDドライブは消えた。

Sample/winapi/CM_Request_Device_Eject/CM_Request_Device_Eject/src/CM_Request_Device_Eject at master · bg1bgst333/Sample · GitHub

CM_Get_Parent

CM_Get_Parentは、指定のデバイスのDevInstから、親のデバイスのDevInstを取得する。

CM_Get_Parent関数 (cfgmgr32.h) - Win32 apps | Microsoft Learn

これを使う前に、これまでのAPIなどを使って、いくつか関数を作る。
まず、

ドライブレターからボリュームのデバイスナンバーを取得する関数。
これを使って、

// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){	// main関数のTCHAR版.

	// コマンドライン引数の数.
	_tprintf(_T("argc = %d\n"), argc);	// argcを出力.
	if (argc != 2){	// 2以外はエラー.
		_tprintf(_T("error: argc != 2\n"));	// "error: argc != 2"と出力.
		return -1;	// -1を返して異常終了.
	}

	// ドライブレターからボリュームのデバイスナンバーを取得.
	int iDeviceNumber = GetVolumeDeviceNumber(argv[1]);	// GetVolumeDeviceNumberでiDeviceNumber取得.
	if (iDeviceNumber != -1){	// iDeviceNumberが-1でなければ成功.
		_tprintf(_T("iDeviceNumber = %d\n"), iDeviceNumber);	// iDeviceNumberを出力.
	}

	// プログラムの終了.
	return 0;	// 0を返して正常終了.

}

とすると、

Dドライブのデバイスナンバーは1
Dドライブのデバイスナンバーは1

Dドライブのデバイスナンバーは1。(DドライブはUSBHDD。)
次に、

バイスパスからデバイスナンバーを取得するGetDeviceNumberByDevicePath、そしてそれを呼び出すGetVolumeDeviceNumberAndDevInst、こちらはドライブレターからボリュームのデバイスナンバーとDevInstを取得する関数。

// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){	// main関数のTCHAR版.

	// コマンドライン引数の数.
	_tprintf(_T("argc = %d\n"), argc);	// argcを出力.
	if (argc != 2){	// 2以外はエラー.
		_tprintf(_T("error: argc != 2\n"));	// "error: argc != 2"と出力.
		return -1;	// -1を返して異常終了.
	}

	// ドライブレターからボリュームのデバイスナンバーとDevInstを取得.
	DWORD dwDeviceNumber = 0;	// dwDeviceNumberを0で初期化.
	DWORD dwDevInst = 0;	// dwDevInstを0で初期化.
	BOOL bRet = GetVolumeDeviceNumberAndDevInst(argv[1], dwDeviceNumber, dwDevInst);	// GetVolumeDeviceNumberAndDevInstでdwDeviceNumber, dwDevInstを取得.
	if (bRet){	// TRUEなら成功.
		_tprintf(_T("dwDeviceNumber = %d\n"), dwDeviceNumber);	// dwDeviceNumberを出力.
		_tprintf(_T("dwDevInst = %d\n"), dwDevInst);	// dwDevInstを出力.
	}

	// プログラムの終了.
	return 0;	// 0を返して正常終了.

}

とすると、

Dのデバイスナンバーが1に加えて、DevInstが3だとわかる。
Dのデバイスナンバーが1に加えて、DevInstが3だとわかる。

Dのデバイスナンバーが1に加えて、DevInstが3だとわかる。
ドライブレターからボリュームのDevInstだけ取得する方法は他にもある。

で、

// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){	// main関数のTCHAR版.

	// コマンドライン引数の数.
	_tprintf(_T("argc = %d\n"), argc);	// argcを出力.
	if (argc != 2){	// 2以外はエラー.
		_tprintf(_T("error: argc != 2\n"));	// "error: argc != 2"と出力.
		return -1;	// -1を返して異常終了.
	}

	// ドライブレターからボリュームのDevInstを取得.
	int iDevInst = GetVolumeDevInst(argv[1]);	// GetVolumeDevInstでiDevInst取得.
	if (iDevInst != -1){	// iDevInstが-1でなければ成功.
		_tprintf(_T("iDevInst = %d\n"), iDevInst);	// iDevInstを出力.
	}

	// プログラムの終了.
	return 0;	// 0を返して正常終了.

}

とすると、

こうなる
こうなる

こうなる。
そして実は、ボリュームのデバイスナンバーと、そのボリュームがあるディスクのデバイスナンバーは同一らしい。

バイスナンバーからディスクのDevInstを取得する関数で、

// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){	// main関数のTCHAR版.

	// コマンドライン引数の数.
	_tprintf(_T("argc = %d\n"), argc);	// argcを出力.
	if (argc != 2){	// 2以外はエラー.
		_tprintf(_T("error: argc != 2\n"));	// "error: argc != 2"と出力.
		return -1;	// -1を返して異常終了.
	}

	// デバイスナンバーからディスクのDevInstを取得.
	int iDeviceNumber = 1;	// iDeviceNumberを1で初期化.
	if (iDeviceNumber != -1){	// iDeviceNumberが-1でなければ.
		int iDiskDevInst = GetDiskDevInst((DWORD)iDeviceNumber);	// GetDiskDevInstでiDiskDevInstを取得.
		if (iDiskDevInst != -1){	// iDiskDevInstが-1でなければ成功.
			_tprintf(_T("iDiskDevInst = %d\n"), iDiskDevInst);	// iDiskDevInstを出力.
		}
	}

	// プログラムの終了.
	return 0;	// 0を返して正常終了.

}

これで、

ディスクのDevInstは2。
ディスクのDevInstは2。

ディスクのDevInstは2。

さて、ここまでやってきたのは、指定のドライブレターのUSBデバイスを取り出すためである。

CM_Request_Device_Eject 実行時に"コンピュータから安全に取り外すことができます。"が表示されない。

上記リンクによると、ディスクのDevInstの親のDevInstに、CM_Request_Device_Ejectを投げればいいらしい。
親のDevInstは、

CM_Get_Parent関数 (cfgmgr32.h) - Win32 apps | Microsoft Learn

CM_Get_Parentで取れる。

CM_Request_Device_Eject 実行時に"コンピュータから安全に取り外すことができます。"が表示されない。

このページのサンプルを動かしたら、ディスクのDevInstは2、親のDevInstは3だとわかった。
"3"ということは、親デバイスはボリュームなのか?
これまでの関数を全部呼び出して、

// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){	// main関数のTCHAR版.

	// コマンドライン引数の数.
	_tprintf(_T("argc = %d\n"), argc);	// argcを出力.
	if (argc != 2){	// 2以外はエラー.
		_tprintf(_T("error: argc != 2\n"));	// "error: argc != 2"と出力.
		return -1;	// -1を返して異常終了.
	}

	// ドライブレターからボリュームのデバイスナンバーを取得.
	int iDeviceNumber = GetVolumeDeviceNumber(argv[1]);	// GetVolumeDeviceNumberでiDeviceNumber取得.
	if (iDeviceNumber != -1){	// iDeviceNumberが-1でなければ成功.
		_tprintf(_T("iDeviceNumber = %d\n"), iDeviceNumber);	// iDeviceNumberを出力.
	}

	// ドライブレターからボリュームのデバイスナンバーとDevInstを取得.
	DWORD dwDeviceNumber = 0;	// dwDeviceNumberを0で初期化.
	DWORD dwDevInst = 0;	// dwDevInstを0で初期化.
	BOOL bRet = GetVolumeDeviceNumberAndDevInst(argv[1], dwDeviceNumber, dwDevInst);	// GetVolumeDeviceNumberAndDevInstでdwDeviceNumber, dwDevInstを取得.
	if (bRet){	// TRUEなら成功.
		_tprintf(_T("dwDeviceNumber = %d\n"), dwDeviceNumber);	// dwDeviceNumberを出力.
		_tprintf(_T("dwDevInst = %d\n"), dwDevInst);	// dwDevInstを出力.
	}

	// ドライブレターからボリュームのDevInstを取得.
	int iDevInst = GetVolumeDevInst(argv[1]);	// GetVolumeDevInstでiDevInst取得.
	if (iDevInst != -1){	// iDevInstが-1でなければ成功.
		_tprintf(_T("iDevInst = %d\n"), iDevInst);	// iDevInstを出力.
	}

	// デバイスナンバーからディスクのDevInstを取得.
	int iDiskDevInst = -1;	// iDiskDevInstを-1で初期化.
	if (iDeviceNumber != -1){	// iDeviceNumberが-1でなければ.
		iDiskDevInst = GetDiskDevInst((DWORD)iDeviceNumber);	// GetDiskDevInstでiDiskDevInstを取得.
		if (iDiskDevInst != -1){	// iDiskDevInstが-1でなければ成功.
			_tprintf(_T("iDiskDevInst = %d\n"), iDiskDevInst);	// iDiskDevInstを出力.
		}
	}

	// iDiskDevInstの親のDevInstを取得.
	DWORD dwDevInstParent;	// 親のDevInstであるdwDevInstParent.
	if (iDiskDevInst != -1){	// iDiskDevInstが-1でなければ.
		CM_Get_Parent(&dwDevInstParent, (DWORD)iDiskDevInst, 0);	// CM_Get_ParentでiDiskDevInstの親のデバイスのDevInstであるdwDevInstParent取得.
		_tprintf(_T("dwDevInstParent = %lu\n"), dwDevInstParent);	// dwDevInstParentを出力.
	}

	// プログラムの終了.
	return 0;	// 0を返して正常終了.

}

としてみた。
すると、

なんと、ディスクのDevInstは6、親のDevInstは7になった。
なんと、ディスクのDevInstは6、親のDevInstは7になった。

なんと、ディスクのDevInstは6、親のDevInstは7になった。
いろいろいじってみたら、どうもSetupDiGetClassDevsを使う関数を2回以上呼ぶとこうなってしまう。
そこで、

SetupDiGetClassDevsを1回しか使わない方法でやったら、

親のDevInstが3になった。
親のDevInstが3になった。

親のDevInstが3になった。
"3"はボリュームではないのか?
親のデバイスは何なのか?
謎は深まるばかり・・・。

Sample/winapi/CM_Get_Parent/CM_Get_Parent/src/CM_Get_Parent at master · bg1bgst333/Sample · GitHub

QueryDosDevice

QueryDosDeviceで、MS-DOSバイス名を取得する。

QueryDosDeviceW 関数 (fileapi.h) - Win32 apps | Microsoft Learn

QueryDosDevice.cppで、

こう書く。

各ドライブレターのMS-DOSデバイス名
各ドライブレターのMS-DOSバイス

各ドライブレターのMS-DOSバイス名。
よくみたら、"\Device~"で始まってる。
そう、MS-DOSバイス名とは、SPDRP_PHYSICAL_DEVICE_OBJECT_NAMEで得られる物理デバイスオブジェクト名のことでもある。

Sample/winapi/QueryDosDevice/QueryDosDevice/src/QueryDosDevice at master · bg1bgst333/Sample · GitHub

GetDriveType

GetDriveTypeで、ドライブタイプを判定する。

GetDriveTypeW 関数 (fileapi.h) - Win32 apps | Microsoft Learn
GetDriveType

GetDriveType.cppで、

コマンドライン引数のドライブレターに、":\\"(表示時は":\".)を連結し、GetDriveTypeに渡すと、ドライブタイプが返ってくる。

Cはメインドライブ、DはUSBメモリを挿して実行した後、取り外してUSBHDDを挿して実行、最後のGはGoogle Drive。
Cはメインドライブ、DはUSBメモリを挿して実行した後、取り外してUSBHDDを挿して実行、最後のGはGoogle Drive

Cはメインドライブ、DはUSBメモリを挿して実行した後、取り外してUSBHDDを挿して実行、最後のGはGoogle Drive
USBHDDは固定になるのか。
そしてGoogle Driveも固定扱い。

Sample/winapi/GetDriveType/GetDriveType/src/GetDriveType at master · bg1bgst333/Sample · GitHub