NimでWindowsのCPU使用率を求める

nim
windows

Linuxだと/proc/stat見ればわかるのですが、
Windowsだとなんか色々面倒みたいなので、ウッキウキで作りました。

作ったのですが、構造体とか変数の型とかをWin32APIに合わせるの面倒だったので、
雑に作ってみたところ見事に動いて、半笑いになりました。

結論

コードはGitHubにあげてます。
精度は知らん。
https://github.com/nnahito/nim_windows_total_cpu

コード解説

必要な構造体

とりあえず全部パブリクアクセスで。
LONGとかDWORDはimport winim/leanをすれば使えるようになります。

type PDH_COUNTER_INFO* = object
    dwLength*: DWord
    dwType*: DWord
    CVersion*: DWord
    CStatus*: DWord
    lScale*: LONG
    lDefaultScale*: LONG
    dwUserData*: DWORD_PTR
    dwQueryUserData*: DWORD_PTR
    szFullPath*: LPSTR

# CPU使用情報が入る構造体
type PPDH_FMT_COUNTERVALUE* = object
    CStatus*: DWORD
    longValue*: LONG
    doubleValue*: DOUBLE
    largeValue*: LONGLONG
    AnsiStringValue*: LPCSTR
    WideStringValue*: LPCWSTR

Win32APIをDLLからロードする

# dllから必要関数のロード
proc PdhOpenQuery*(szDataSource: LPVOID, dwUserData: PDH_COUNTER_INFO, phQuery: ptr int): WINBOOL {.stdcall, dynlib: "Pdh", importc: "PdhOpenQueryA".}
proc PdhAddCounter*(hQuery: int, szFullCounterPath: LPCSTR, dwUserData: DWORD_PTR, phCounter: ptr int): WINBOOL {.stdcall, dynlib: "Pdh", importc: "PdhAddCounterA".}
proc PdhCollectQueryData*(hQuery: int): LONG {.stdcall, dynlib: "Pdh", importc: "PdhCollectQueryData".}
proc PdhGetFormattedCounterValue*(hCounter: int, dwFormat: DWORD, lpdwType: LPDWORD, pValue: ptr PPDH_FMT_COUNTERVALUE) {.stdcall, dynlib: "Pdh", importc: "PdhGetFormattedCounterValue".}

https://docs.microsoft.com/en-us/windows/win32/api/pdh/nf-pdh-pdhopenquerya
https://docs.microsoft.com/en-us/windows/win32/api/pdh/nf-pdh-pdhaddcountera
https://docs.microsoft.com/en-us/windows/win32/api/pdh/nf-pdh-pdhcollectquerydata
https://docs.microsoft.com/en-us/windows/win32/api/pdh/nf-pdh-pdhgetformattedcountervalue

あたりを参考に。

CPU使用率の取得

まずはプロセス取得のためにハンドルを取得

discard PdhOpenQuery(nil, dwUserData, &hQuery)

取得したいプロセスのCPU使用率を指定。
今回は全体のCPU使用率なので、_Totalを指定。

var lpcstrStr: LPCSTR
lpcstrStr = "\\Processor(_Total)\\% Processor Time"
discard PdhAddCounter(hQuery, lpcstrStr, 0, &hCounter)

CPU使用率の取得。
PdhCollectQueryDataを2回以上叩く。
前回のPdhCollectQueryData実行時からのCPU使用率が取得されるっぽい。

discard PdhCollectQueryData(hQuery)

取得したCPU使用率を人間様がわかるようにフォーマット

var fmtValue: PPDH_FMT_COUNTERVALUE
PdhGetFormattedCounterValue(hCounter, 512, nil, &fmtValue)

echo fmtValue.doubleValue

なんかこんな感じで取得できる。