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ドライブは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だとわかる。
ドライブレターからボリュームの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。
さて、ここまでやってきたのは、指定のドライブレターの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になった。
いろいろいじってみたら、どうもSetupDiGetClassDevsを使う関数を2回以上呼ぶとこうなってしまう。
そこで、
SetupDiGetClassDevsを1回しか使わない方法でやったら、

親のDevInstが3になった。
"3"はボリュームではないのか?
親のデバイスは何なのか?
謎は深まるばかり・・・。
Sample/winapi/CM_Get_Parent/CM_Get_Parent/src/CM_Get_Parent at master · bg1bgst333/Sample · GitHub