C++ で CPU情報を取得

(1/1)
C++でCPU情報を取得
PHP で CPU 情報を取得(Windows アプリ版)」で作った CPU 取得プログラムを C++に移植する。ご利用の CPU が脆弱性が公表されているものかどうか確認するのに役立つだろう。
CPU 情報は GetSystemInfo 関数によって取得するが、ベンダ名やアーキテクチャは外部ファイル "processorList.xml" にあるテキストから取り出す。そこで、XML解釈を行うために、ライブラリ Libxml2 を導入する。

サンプル・プログラム

圧縮ファイルの内容
cpuid.exeサンプル・プログラム本体
processorList.xmlCPU情報ファイル。
libxml2.dllLibxml2 DLL
source/cpuid.phpソース・プログラム
source/resource.hリソース・ヘッダ
source/resource.rcリソース・ファイル
source/application.icoアプリケーション・アイコン

Libxml2 の導入

Libxml2 はバイナリ配布されていない。そこで、ソースをダウンロードし、MinGW でコンパイルしてやる必要がある。

libxml - The XML C parser and toolkit of Gnome から最新のパッケージをダウンロードする。(2020/08/01現在:libxml2-2.9.7.tar.gz)

解凍したら、make ファイルを configure し、コンパイルする。

configure.js compiler=mingw prefix=[MinGW のパス] include=[MinGW のパス]\include lib=[MinGW のパス]\lib debug=yes iconv=no
mkdir bin.mingw
mkdir int.mingw
mkdir int.a.mingw
mkdir int.utils.mingw
cd win32
make -f Makefile.mingw



"bin.mingw" ディレクトリ内にコンパイルされた "libxml2.a", "libxml2.lib" を [MinGW のパス]\lib へ、libxml2-2.7.5 ディレクトリにある "include\libxml" を丸ごと "[MinGW のパス]\include" へコピーする。
また、実行時に動的DLL "libxml2.dll" が必要になるので、System32 ディレクトリにコピーしておく。

リソースの準備

Eclipse を起動し、新規プロジェクト cpuid を用意する。
ResEdit を起動し、resource.rc を用意する。

Eclipse に戻り、ソース・プログラム "cpuid.cpp" を追加する。
今回は Libxml2 を使用する関係で、リンカー・フラグを -mwindows -static -lstdc++ -lgcc -lwinpthread -lxml2 -static "libxml2.dll のパス\ibxml2.dll" に設定する。

解説:ヘッダファイル等

0010: // 初期化処理 ======================================================
0011: #include <iostream>
0012: #include <cstdlib>
0013: #include <memory>
0014: #include <string>
0015: #include <libxml/xpath.h>
0016: #include <libxml/xmlreader.h>
0017: #include <libxml/tree.h>
0018: 
0019: #include <windows.h>
0020: #include <stdio.h>
0021: #include <stdlib.h>
0022: 
0023: #include "resource.h"
0024: 
0025: using namespace std;
0026: 
0027: //char*バッファサイズ
0028: #define SIZE_BUFF     512
0029: 
0030: //現在のインターフェイス
0031: static HINSTANCE hInst;
0032: 
0033: //エラー・メッセージ格納用
0034: string ErrorMessage;

Libxml2 を利用するために、いくつかのヘッダフィルを incluide する。
[cstdlibblue] と memory は、後述するスマートポインタを扱うために必要。

解説:クラスとスマートポインタ

0036: //ProcessorListファイル名
0037: #define FNAME "processorList.xml"
0038: 
0039: //ProcessorInfoクラス
0040: #define SIZE_PROCESSORINFO 500        //格納上限
0041: class ProcessorInfo {
0042: public:
0043:     unsigned int level;
0044:     unsigned int type;
0045:     unsigned int number;
0046:     unsigned int model;
0047:     unsigned int family;
0048:     unsigned int stepping;
0049:     char vendor[SIZE_BUFF + 1];
0050:     char architecture[SIZE_BUFF + 1];
0051: };
0052: 
0053: //検索結果格納用
0054: unique_ptr<ProcessorInfoProcessorList[SIZE_PROCESSORINFO] = {};

CPU 情報ファイル(XML ファイル)から読み込む情報を格納するために、クラス ProcessorInfo を定義している。
ProcessorInfo クラスはポインタ配列として管理するが、オブジェクト生成時にポインタへ格納できるようにスマートポインタを利用する。
クラスやスマートポインタがあるおかげで、C++ではオブジェクト指向プログラミングを可能にしている。

解説:CPU情報リストを取得

0090: /**
0091:  * CPU情報リストを取得する
0092:  * @param  char *fname CPU情報リストファイル名
0093:  * @return int 取得した情報数/(-1):取得失敗
0094: */
0095: int getProcessorList(const char *fnamechar *date) {
0096:     xmlTextReaderPtr reader;
0097:     xmlChar *name, *value;
0098:     int nodeTypestate = 0, retcnt = -1;
0099: 
0100:     #define STATE_NONE         0
0101:     #define STATE_VENDOR     1
0102:     #define STATE_FAMILY     2
0103:     #define STATE_MODEL         3
0104:     #define STATE_ARCHITECTURE 4
0105:     #define STATE_DATE         5
0106: 
0107:     //XMLファイル読み込み
0108:     reader = xmlNewTextReaderFilename(fname);
0109:     if (reader == 0) {
0110:         ErrorMessage = "XML readerエラー";
0111:         return cnt;
0112:     }
0113: 
0114:     //XML解釈
0115:     ret = xmlTextReaderRead(reader);
0116:     while (ret == 1) {
0117:         nodeType = (int)xmlTextReaderNodeType(reader);
0118:         name = xmlTextReaderName(reader);
0119:         //ノード
0120:         if (nodeType == (int)XML_READER_TYPE_ELEMENT) {
0121:             //データ追加
0122:             if (xmlStrcmp(nameBAD_CAST "processor") == 0) {
0123:                 state = STATE_NONE;
0124:                 cnt++;
0125:                 ProcessorList[cnt] = make_unique<ProcessorInfo>();
0126:             //vendor
0127:             } else if (xmlStrcmp(nameBAD_CAST "vendor") == 0) {
0128:                 state = STATE_VENDOR;
0129:             //family
0130:             } else if (xmlStrcmp(nameBAD_CAST "family") == 0) {
0131:                 state = STATE_FAMILY;
0132:             //model
0133:             } else if (xmlStrcmp(nameBAD_CAST "model") == 0) {
0134:                 state = STATE_MODEL;
0135:             //architecture
0136:             } else if (xmlStrcmp(nameBAD_CAST "architecture") == 0) {
0137:                 state = STATE_ARCHITECTURE;
0138:             //作成日
0139:             } else if (xmlStrcmp(nameBAD_CAST "date") == 0) {
0140:                 state = STATE_DATE;
0141:             } else {
0142:                 state = STATE_NONE;
0143:             }
0144:         //テキスト
0145:         } else if (nodeType == (int)XML_READER_TYPE_TEXT) {
0146:             switch (state) {
0147:                 //vendor
0148:                 case STATE_VENDOR:
0149:                     value = xmlTextReaderValue(reader);
0150:                     strncpy(ProcessorList[cnt]->vendor, (char *)valueSIZE_BUFF);
0151:                     break;
0152:                 //family
0153:                 case STATE_FAMILY:
0154:                     value = xmlTextReaderValue(reader);
0155:                     ProcessorList[cnt]->family = strtol((char *)valueNULL, 16);
0156:                     break;
0157:                 //model
0158:                 case STATE_MODEL:
0159:                     value = xmlTextReaderValue(reader);
0160:                     ProcessorList[cnt]->model = strtol((char *)valueNULL, 16);
0161:                     break;
0162:                 //architecture
0163:                 case STATE_ARCHITECTURE:
0164:                     value = xmlTextReaderValue(reader);
0165:                     strncpy(ProcessorList[cnt]->architecture, (char *)valueSIZE_BUFF);
0166:                     break;
0167:                 //作成日
0168:                 case STATE_DATE:
0169:                     value = xmlTextReaderValue(reader);
0170:                     strncpy(date, (char *)valueSIZE_BUFF);
0171:                     break;
0172:                 default:
0173:                     break;
0174:             }
0175:         } else if (nodeType == (int)XML_READER_TYPE_END_ELEMENT) {
0176:             state = STATE_NONE;
0177:         }
0178:         ret = xmlTextReaderRead(reader);
0179:     }
0180: 
0181:     return cnt;
0182: }

CPU 情報ファイル(XML ファイル)から配列 ProcessorList へ情報を読み込むのがユーザー関数 getProcessorList である。Libxml2スマートポインタ を利用している。

Libxml2 による XML パーシングにはいくつかの方法があるが、今回は、xmlTextReaderNodeType 関数を使って、ノードの種類を識別子ながらデータを読み込んでいくようにした。

解説:CPU情報を取得

0184: /**
0185:  * SystemInfoの結果とCPU情報リストをマッチングしCPU情報を取得
0186:  * @param   ProcessorInfo cpuinfo 結果を格納
0187:  * @return  bool TRUE:取得成功/FALSE:取得失敗
0188: */
0189: bool getProcessorInfo(ProcessorInfo &cpuinfo) {
0190:     //CPU情報取得
0191:     SYSTEM_INFO systemInfo;
0192:     GetSystemInfo(&systemInfo);
0193:     cpuinfo.level  = (unsigned int)systemInfo.wProcessorLevel;
0194:     cpuinfo.type   = (unsigned int)systemInfo.dwProcessorType;
0195:     cpuinfo.number = (unsigned int)systemInfo.dwNumberOfProcessors;
0196:     cpuinfo.model  = (unsigned int)(systemInfo.wProcessorRevision >> 8);
0197:     cpuinfo.stepping = (unsigned int)(systemInfo.wProcessorRevision & 0x00FF);
0198:     strcpy(cpuinfo.vendor, "unknown");
0199:     strcpy(cpuinfo.architecture, "unknown");
0200: 
0201:     //CPU情報リストとマッチング
0202:     bool res = false;
0203:     for (int i = 0; i < SIZE_PROCESSORINFOi++) {
0204:         if (ProcessorList[i] == nullptr)  break;
0205:         if ((ProcessorList[i]->family == cpuinfo.level) && (ProcessorList[i]->model == cpuinfo.model)) {
0206:             strcpy(cpuinfo.vendorProcessorList[i]->vendor);
0207:             strcpy(cpuinfo.architectureProcessorList[i]->architecture);
0208:             res = true;
0209:             break;
0210:         }
0211:     }
0212:     return res;
0213: }

GetSystemInfo を使って取得した CPU 情報と、前述の getProcessorList を使って取得した CPU 情報リストをマッチングし、CPU 情報をテキストとして取り出すのがユーザー関数 getProcessorInfo である。

参考サイト

(この項おわり)
header