デバイスマネージャーで表示できる項目なら、プログラム上からでも取得できるはずと思い、やってみました。最終的に、2.5 Gbpsか5 Gbpsかどうかと、レーン数(x1, …, x16)をWindows APIで取得し、表示するプログラムを書きました。

まずはデバイスマネージャーを開き、目的の情報が取得できることを調べます。

デバイスマネージャの「デバイス (接続別)」表示で、PCI Express Root Portやその下位の項目のプロパティを見てきます。そして、それらしきものを見つけました。

デバイスマネージャーからNVIDIA GeForce GT 610のプロパティを表示し、「詳細」タブのプロパティで「PCIの現在のリンク幅」を選択した状態のウィンドウ

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

おそらく、デバイスマネージャーでの「最大リンク速度」と「最大リンク幅」にあたるものでしょう。

というわけで、デバイスマネージャーに表示される情報はプログラムからも取得できるという話でした。私は、こんな風に、SetupDiGetClassDevsSetupDiEnumDeviceInfoで目的のデバイスを探し始め、あとは適当にSetupDi系とCM系 (PnP Configuration Manager)の関数を使うという感じでやっています。これがベストなのかどうか、少し自信はないのですが。


スポンサード リンク

この記事のカテゴリ