デバイスマネージャーで表示できる項目なら、プログラム上からでも取得できるはずと思い、やってみました。最終的に、2.5 Gbpsか5 Gbpsかどうかと、レーン数(x1, …, x16)をWindows APIで取得し、表示するプログラムを書きました。
まずはデバイスマネージャーを開き、目的の情報が取得できることを調べます。
デバイスマネージャの「デバイス (接続別)」表示で、PCI Express Root Portやその下位の項目のプロパティを見てきます。そして、それらしきものを見つけました。
00000010と書いてあり、十六進法と考えると16です。x16のスロットにx16のカードを挿しているので、求めている項目でありそうです。
次に、Windows SDKのincludeフォルダ内をPCIでgrepしたところ、pciprop.hといういかにもなファイルを見つけました。この中に、以下のような箇所を見つけました。
// // This property is applicable to an express device with an express link. It // describes the current link speed for the device. The encodings are defined // as follows. // #define DevProp_PciExpressDevice_LinkSpeed_TwoAndHalf_Gbps 1 #define DevProp_PciExpressDevice_LinkSpeed_Five_Gbps 2 DEFINE_PCI_DEVICE_DEVPKEY(DEVPKEY_PciDevice_CurrentLinkSpeed, 9); // // This property is applicable to an express device with an express link. It // describes the current link width whose encoding is as follows. // #define DevProp_PciExpressDevice_LinkWidth_By_1 1 #define DevProp_PciExpressDevice_LinkWidth_By_2 2 #define DevProp_PciExpressDevice_LinkWidth_By_4 4 #define DevProp_PciExpressDevice_LinkWidth_By_8 8 #define DevProp_PciExpressDevice_LinkWidth_By_12 12 #define DevProp_PciExpressDevice_LinkWidth_By_16 16 #define DevProp_PciExpressDevice_LinkWidth_By_32 32 DEFINE_PCI_DEVICE_DEVPKEY(DEVPKEY_PciDevice_CurrentLinkWidth, 10); |
まずDEVPKEY_PciDevice_CurrentLinkSpeedを見つけて、「そういえば『PCIの現在のリンク速度』という項目あったな」と思いました。次に、DEVPKEY_PciDevice_CurrentLinkWidthが出てきて「やっぱりこれだな」と思った次第です。
というわけで、こんなプログラムを書いてみました。
#define UNICODE #define INITGUID #include <iostream> #include <windows.h> #include <setupapi.h> #include <cfgmgr32.h> #include <devpkey.h> #include <pciprop.h> #pragma comment(lib, "setupapi.lib") #pragma comment(lib, "cfgmgr32.lib") bool GetUInt32Property( DEVINST devInst, const DEVPROPKEY& propKey, UINT32& result) { DEVPROPTYPE propertyType; ULONG bufferSize = sizeof result; auto cr = CM_Get_DevNode_Property( devInst, &propKey, &propertyType, reinterpret_cast<BYTE*>(&result), &bufferSize, 0); return cr == CR_SUCCESS && propertyType == DEVPROP_TYPE_UINT32; } int main() { std::wcout.imbue(std::locale("")); auto hdi = SetupDiGetClassDevs( nullptr, L"PCI", nullptr, DIGCF_ALLCLASSES | DIGCF_PRESENT); for (DWORD i = 0; ; ++i) { SP_DEVINFO_DATA data{ sizeof data }; if (!SetupDiEnumDeviceInfo(hdi, i, &data)) { // 厳密にやるなら、GetLastError()を見て、 // ERROR_NO_MORE_ITEMSなら正常終了、それ以外なら異常と判断する。 break; } DEVPROPTYPE propertyType; WCHAR buffer[1024]; // 決め打ち。本来は動的に決めるほうが良いだろう。 ULONG bufferSize = sizeof buffer; auto cr = CM_Get_DevNode_Property( data.DevInst, &DEVPKEY_NAME, &propertyType, reinterpret_cast<BYTE*>(buffer), &bufferSize, 0); if (cr != CR_SUCCESS || propertyType != DEVPROP_TYPE_STRING) { continue; } UINT32 currentLinkSpeed; UINT32 currentLinkWidth; if (!GetUInt32Property( data.DevInst, DEVPKEY_PciDevice_CurrentLinkSpeed, currentLinkSpeed) || !GetUInt32Property( data.DevInst, DEVPKEY_PciDevice_CurrentLinkWidth, currentLinkWidth)) { continue; } std::wcout << "--------\n"; std::wcout << buffer << std::endl; switch (currentLinkSpeed) { case DevProp_PciExpressDevice_LinkSpeed_TwoAndHalf_Gbps: std::wcout << L"2.5 Gbps" << std::endl; break; case DevProp_PciExpressDevice_LinkSpeed_Five_Gbps: std::wcout << L"5 Gbps" << std::endl; break; default: std::wcout << L"Unknown speed" << std::endl; break; } std::wcout << L"Link width: x" << currentLinkWidth << std::endl; } SetupDiDestroyDeviceInfoList(hdi); } |
余談ですが、CM_Get_DevNode_Propertyの戻り値の型CONFIGRETの値は、そのまま使うほか、関数CM_MapCrToWin32ErrでWin32エラーコードに変換することも可能です。
さて、自分のパソコンでの実行結果はこんな感じです。このうち、実際にはイーサネットとオーディオはオンボードです。
-------- Realtek PCIe GBE Family Controller 2.5 Gbps Link width: x1 -------- High Definition Audio コントローラー 2.5 Gbps Link width: x16 -------- NVIDIA GeForce GT 610 2.5 Gbps Link width: x16
このプログラムには含めませんでしたが、ほかにもこんな定数がpciprop.hにありました。
- DEVPKEY_PciDevice_MaxLinkSpeed
- DEVPKEY_PciDevice_MaxLinkWidth
おそらく、デバイスマネージャーでの「最大リンク速度」と「最大リンク幅」にあたるものでしょう。
というわけで、デバイスマネージャーに表示される情報はプログラムからも取得できるという話でした。私は、こんな風に、SetupDiGetClassDevsとSetupDiEnumDeviceInfoで目的のデバイスを探し始め、あとは適当にSetupDi系とCM系 (PnP Configuration Manager)の関数を使うという感じでやっています。これがベストなのかどうか、少し自信はないのですが。
スポンサード リンク |
この記事のカテゴリ
- C++ ⇒ Windows APIでPCI Express関係の情報を得る
- Windows ⇒ WinAPI ⇒ デバイス関係のAPI ⇒ Windows APIでPCI Express関係の情報を得る