ハードウェア情報の取得
OS からハードウェア情報を取得する方法は OS 毎に様々です。
例えば Windows だったら Win32 API を呼んで取ったり WMI (Windows Management Instrumentation) を使って取ったりできます。
同様に Linux もいくつか取り方があるようですが一番簡単なのが proc ディレクトリから情報を読み出す、という方法です。
UNIX 系 OS はデバイスなどもファイルシステムにマッピングされるため、ハードウェアの情報もファイルとして取り出せます。それらの情報があるのが proc ディレクトリです。
ためしに Ubuntu (Windows Store 版) で、proc ディレクトリを見てみるとこんな感じです。
xxx@XXX:/$ ls -alF /proc/
total 0
dr-xr-xr-x 9 root root 0 Sep 29 17:24 ./
drwxr-xr-x 1 root root 4096 Sep 29 17:23 ../
dr-xr-xr-x 7 root root 0 Sep 29 17:24 1/
dr-xr-xr-x 7 root root 0 Sep 29 18:05 3/
dr-xr-xr-x 7 jun xxx 0 Sep 29 18:05 4/
dr-xr-xr-x 7 jun xxx 0 Sep 29 18:05 6298/
dr-xr-xr-x 2 root root 0 Sep 29 17:24 bus/
-r--r--r-- 1 root root 0 Sep 29 17:24 cgroups
-r--r--r-- 1 root root 0 Sep 29 17:24 cmdline
-r--r--r-- 1 root root 0 Sep 29 17:24 cpuinfo
-r--r--r-- 1 root root 0 Sep 29 17:24 filesystems
-r--r--r-- 1 root root 0 Sep 29 17:24 interrupts
-r--r--r-- 1 root root 0 Sep 29 17:24 loadavg
-r--r--r-- 1 root root 0 Sep 29 17:24 meminfo
lrwxrwxrwx 1 root root 0 Sep 29 17:24 mounts -> self/mounts
lrwxrwxrwx 1 root root 0 Sep 29 17:24 net -> self/net/
lrwxrwxrwx 1 root root 0 Sep 29 17:24 self -> 6298/
-r--r--r-- 1 root root 0 Sep 29 17:24 stat
dr-xr-xr-x 6 root root 0 Sep 29 17:24 sys/
dr-xr-xr-x 2 root root 0 Sep 29 17:24 tty/
-r--r--r-- 1 root root 0 Sep 29 17:24 uptime
-r--r--r-- 1 root root 0 Sep 29 17:24 version
-r--r--r-- 1 root root 0 Sep 29 17:24 version_signature
色々なファイル・ディレクトリがあります。
それらが、どのような意味を持つのかは検索してみて下さい。
ここでは、この中の1つ "cpuinfo" を見てみます。
cat で中身を表示すると
xxx@XXX:/$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 94
model name : Intel(R) Core(TM) i7-6700T CPU @ 2.80GHz
stepping : 3
(以下略)
こんな風に CPU の情報が取得できました。
つまり cpuinfo はその名の通り CPU の情報を返すファイル、ということです。
さて、では proc ディレクトリ配下のファイルを プログラムから、しかも Delphi から取得するには、どのようにするのでしょうか?
Delphi で読み出す
罠
proc 配下のファイルも当然ファイルなので、普通にファイルとして扱えます。
…しかし!そこに罠が!
例えば、次のように…
procedure ReadCPUInfo;
var
SL: TStringList;
begin
SL := TStringList.Create;
try
SL.LoadFromFile('/proc/cpuinfo');
Writeln('----------');
Writeln(SL.Text);
Writeln('----------');
finally
SL.DisposeOf;
end;
end;
とすると、表示されるのは…
----------
----------
上記のようになり、SL.Text の部分は空行になっています。
何故空行になるかは、先ほどの ls proc の内容を見ると判ります。
-r--r--r-- 1 root root 0 Sep 29 17:24 cpuinfo
となっていて、ファイルサイズが 0 になっているのです!
そして、TStringList.LoadFromFile の内部から呼ばれる [TStrings.LoadFromStream] (http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.Classes.TStrings.LoadFromStream) は、ファイルサイズを見てバッファを確保するため 0 バイトを返すファイルは読み込めません!
すごく当たり前の実装なのですが、今回のような場合は使えません。
罠の回避
では、どのように読み込むかというと、
- File Stream を 1 バイト読み込む。
- 読み出したバイト数が 1 の場合 1 に戻り、それ以外の場合は終了。
とするだけです。
それを実装すると下記の様になります。
function ReadCPUInfo: String;
var
Ch: Char;
FS: TFileStream;
begin
Result := '';
FS := TFileStream.Create('/proc/cpuinfo', fmOpenRead);
try
while FS.Read(Ch, 1) = 1 do // 1バイト読み込んだ戻値が 1 の間処理を継続
Write(Ch);
Writeln;
finally
FS.DisposeOf;
end;
end;
これを実行すると下記の出力が得られ、先に実行した cat /proc/cpuinfo と同じ結果になりました。
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 94
model name : Intel(R) Core(TM) i7-6700T CPU @ 2.80GHz
stepping : 3
(以下略)
ここでは cpuinfo だけ読むようにしてみましたが、他にも色々取得できます。
ですので、汎用化してみます。
汎用化
汎用化したソースが以下です。
短いので全文載せて起きます。
(*
* Hardware Information reader from proc dicrectory.
*
* PLATFORMS
* Linux
*
* LICENSE
* Copyright (c) 2018 HOSOKAWA Jun
* Released under the MIT license
* http://opensource.org/licenses/mit-license.php
*
* USAGE
* Call ReadHardInfo with information filename.
*
* SAMPLE
* Info := TProcDirectory.ReadInfo('cpuinfo');
*
* 2018/09/28 Version 1.0.0
* Programmed by HOSOKAWA Jun (twitter: @pik)
*)
unit PK.HardInfo.ProcDirectory.Linux;
interface
type
TProcDirectory = record
private const
PROC_PATH = '/proc';
public
class function ReadInfo(const iFilename: String): String; static;
end;
implementation
uses
System.Classes,
System.SysUtils,
System.IOUtils;
class function TProcDirectory.ReadInfo(const iFilename: String): String;
var
Ch: Char;
Path: String;
FS: TFileStream;
SB: TStringBuilder;
begin
Path := TPath.Combine(PROC_PATH, iFilename);
if not TFile.Exists(Path) then
Exit('');
FS := TFileStream.Create(Path, fmOpenRead);
try
SB := TStringBuilder.Create;
try
// The file in the proc directory, it's size return -1,
// Therefore, only one byte can be read.
while FS.Read(Ch, 1) = 1 do
SB.Append(Ch);
Result := SB.ToString;
finally
SB.DisposeOf;
end;
finally
FS.DisposeOf;
end;
end;
end.
使う時は、こんな風にします。
Writeln(TProcDirectory.ReadInfo('cpuinfo'));
まとめ
なお、この罠は当然 Delphi に限ったことではないです。
やはり、各 OS について詳しく知っていないと、マルチプラットフォームアプリケーションの開発は難しいですね!