Delphi Programming / Object Pascal Delphi Programming / Object Pascal
[掲載 2008年04月26日] [更新 2013年10月17日] Delphi サンプルプログラム集
898_WMI イントロダクション
動作確認等
Windows 7 U64(SP1) + Delphi XE Pro
[12] Windows 7 U64(SP1) + Delphi XE3 Pro VCL-32
898_WMI_Introduction.zip [7,758 KB] 2013年09月21日版 (EXE 同梱)
2013年08月27日
WmiSet Components の記事を削除
WMI Delphi Code Creator と SMBIOS 情報取得の記事を追加
解説的な記事を大幅に追加し、サンプルコードも追加
リンクの修正 (マイクロソフトのページは本当によく変わる)
2013年09月08日
VBS (VBScript) のコードからの移植の記事を追加
WMI (Windows Management Instrumentation) は、主に、Windows のシステム管理に必要な情報を取得するために用意されたインターフェイスです。システム管理を目的として考案された WBEM(業界団体の DMTF が策定)を、マイクロソフトが拡張したものを WMI といっています。WMI を使用すると、
システム関係の情報の取得と設定や操作
ハードウェアに関する情報の取得 (BIOS やドライブ等)
ソフトウェアに関する情報や操作 (OS の情報やプロセスの管理等)
等を行うことができます。特にシステム関係では管理者権限が必要な操作も可能です。ただし、ミッションクリティカルな、リアルタイム性を必要とする処理には向いないようです。
WMI ではこれらの情報を主に WQL というクエリーを実行します。他の方法についてはリンクの記事参考参考にして下さい。
スクリプトセンターのサンプルは、WMI だけではなく、VBS, WSH 等のコードもありますが、WMI のコードも多くあります。もしこのページが英文で表示されたら、ページの右上のコンボボックスで [日本語] を選択してください。しばらくすると日本語に切り替わります。
図1
スクリプトセンターのサンプル
WMI で扱えると言うか、定義されている数は膨大な量です。目的のクラスを探し出すのは容易ではないでしょう。残念ながら、MSDN オンラインには、全ての情報があるわけではありません。
以下の WUtils.com のサイトに WMI 関係のリファレンス記事があります。当サイトの WMI 関係のサンプルのほとんどは CIMV2 という名前空間を扱っていますが、この名前空間だけでも 1,592 のクラスがあります。
システムで使用可能なこれらの名前空間の一覧と、各々の名前空間で使用可能なクラス名の一覧は、後述の WMI Delphi Code Creator や WMI Code Creator で確認できます。これらのツールでは現在の環境で操作可能なプロパティ等も確認できまから、リファレンスとしても利用できます。
01_ タイプライブラリの取り込み - WbemScripting_TLB の作成
Delphi で WMI を使用するには、WMI Script という COM オブジェクトを生成して使用します。
本ページのサンプルは、1 部を除いて、[タイプライブラリの取り込み] で
[Microsoft WMI Scripting
V1.2 Library (Version 1.2)]
を取り込んで作成した WbemScripting_TLB というユニットを使用しています。
WMI 関係のオブジェクトを全て OleVariant 型で扱う場合は、この作業は必要ありませんが、ユニットは作成しておいた方が便利です。
このユニットには、WMI の COM API や Script API を使用するためのコード、定数値等の定義関係が含まれています。必要な時に、これらを参照することができます。
図2
タイプライブラリの取り込み
02_ プロパティのセット - プロパティ名とプロパティの型
最初のサンプルでは、論理ディスクの情報を取得します。各ディスクオブジェクトの名前、ディスクと容量とか空き容量等は、このディスクオブジェクトのプロパティということになります。
WMI で扱うプロパティの型には、下表のようなものがあります。
アプリケーションでは、これらの型に応じて値を取得し、処理に使用することになります。
これらは バリアント型のルーチンの TVariantArg や TVarData でキャストしても、判定できません。コード中で使用しているように、ISWbemProperty の CIMType プロパティで判定します。
定数名
(CIMType の値)
値
説明
Delphi の型
wbemCimtypeSint16
2
16 ビット整数値
SmallInt
wbemCimtypeSint32
3
32 ビット整数値
Integer, LongInt
wbemCimtypeReal32
4
32 ビット実数値
Single
wbemCimtypeReal64
5
64 ビット実数値
Double
wbemCimtypeString
8
文字列
String
wbemCimtypeBoolean
11
論理値
Boolean
wbemCimtypeObject
13
CIM オブジェクト
Get でオブジェクトを取得
wbemCimtypeSint8
16
8 ビット整数値
ShortInt
wbemCimtypeUint8
17
8 ビット符号なし整数値
Byte
wbemCimtypeUint16
18
16 ビット符号なし整数値
WORD
wbemCimtypeUint32
19
32 ビット符号なし整数値
Cardinal, LongWord, DWORD
wbemCimtypeSint64
20
64 ビット整数値
Int64
wbemCimtypeUint64
21
64 ビット符号なし整数値
UInt64
wbemCimtypeDatetime
101
日付時刻 (CIM_DATETIME 型)
ISWbemDateTime を使用して変換
wbemCimtypeReference
102
CIM オブジェクトへの参照
文字列として取得可能
[非同期イベントシンク] 参照
wbemCimtypeChar16
103
16 ビットの文字
-
システムの論理ディスクの全てのプロパティを取得するサンプルです。
プロパティの型判定の参考用のサンプルですが、符号なし 64 ビット値だけしか判定していません。後の全てのプロパティは文字列として取得して TMemo に表示します。
このサンプルでは、符号なし 64 ビットのプロパティは、容量と空き容量です。プロパティ名は各々 Size と FreeSpace です。
まず、WMI Script オブジェクトを生成し、WMI サービスに接続します。WMI サービスが起動していないと接続に失敗しますが、WMI サービスは、意図的に操作しない限り、Windows の起動と同時に実行されています。次に、このサービスに対してクエリーを実行して、その結果を取得します。
これはデータベースの選択クエリーと同じですが、* (アスタリスク) で全てのプロパティを取得するようにしています。
以下のリンクは、このサンプルで使用しているインターフェイス関係の、MSDN オンライン記事へのリンクです。
図3
設計時画面
図4
実行結果例
リスト1
全ての論理ディスクの全てのプロパティを列挙するサンプル
列挙の順番は、プロパティ名のアルファベット順となる
プロパティの型を判定して処理
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, WbemScripting_TLB;
type
TForm1 = class (TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
protected
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj, ActiveX;
{$R *.dfm}
{ TForm1 }
// =============================================================================
// 全論理ディスクの全てのプロパティを取得して表示
// プロパティの型を判定して処理する例(ここではUInt64型のみ処理)
//
// usesにComObj,ActiveX,WbemScripting_TLBが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : ISWbemLocator;
objWMIService : ISWbemServices;
objPropSet : ISWbemObjectSet;
objPropSetEnum : IEnumVariant;
objTemp : OleVariant;
iValue : LongWord;
wmiObject : ISWbemObject;
wmiPropSet : ISWbemPropertySet;
wmiPropSetEnum : IEnumVariant;
wmiProperty : ISWbemProperty;
ValueUInt64 : UInt64;
fmtStrStr : String ;
fmtStrInteger : String ;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
// Wbemのオブジェクトを生成
objWMILocator := CoSWbemLocator.Create;
// WMIサービスに接続。引数は必要に応じて設定
objWMIService := objWMILocator.ConnectServer(
'Localhost' , // コンピュータ名。ローカルの場合は空白でも可
'root\cimv2' , // 名前空間
'' , // ユーザ名。ローカルの場合は空文字にする
'' , // パスワード。ローカルの場合は空文字にする
'' , // ロケール識別子
'' , // ドメイン名
0 , // 応答フラグ
nil );
// クエリーを実行して、プロパティのセットのオブジェクトを取得
objPropSet := objWMIService.ExecQuery(
'SELECT * FROM Win32_LogicalDisk' ,
'WQL' ,
wbemFlagReturnImmediately,
nil );
// プロパティのセットのコレクションを取得
objPropSetEnum := objPropSet._NewEnum as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// 各オブジェクト(ここでは1つの論理ディスク)のISWbemObjectオブジェクトを取得
wmiObject := IUnknown(objTemp) as ISWBemObject;
// プロパティの列挙を取得
wmiPropSet := wmiObject.Properties_;
wmiPropSetEnum := (wmiPropSet._NewEnum) as IEnumVariant;
while wmiPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティを取得
wmiProperty := IUnknown(objTemp) as ISWBemProperty;
// プロパティが符号なし64ビット整数の場合
if wmiProperty.CIMType = wbemCimtypeUint64 then begin
// 値がnullでない場合に書式を整えて表示
if not VarIsNull(wmiProperty.Get_Value) then begin
// プロパティの名前
fmtStrStr := Format('%-30s' , [wmiProperty.Name]);
// プロパティの値
ValueUInt64 := wmiProperty.Get_Value;
fmtStrInteger:= FormatFloat('###,##0' , ValueUInt64);
Memo1.Lines.Add(fmtStrStr + fmtStrInteger);;
end ;
end else begin
if VarIsNull(wmiProperty.Get_Value) then begin
Memo1.Lines.Add(VarToStr(wmiProperty.Name));
end else begin
// プロパティの名前
fmtStrStr := Format('%-30s' , [wmiProperty.Name]);
// プロパティの名前と実際の値を文字列にして表示
Memo1.Lines.Add(fmtStrStr + VarToStr(wmiProperty.Get_Value));
end ;
end ;
end ;
Memo1.Lines.Add('-----------------------------------------------' );
end ;
Memo1.Lines.EndUpdate;
end ;
end .
上のコードでは、プロパティの列挙オブジェクトを取得するのに、3 行を使用しています。
もし、ISWbemObject, ISWbemPropertySet のプロパティやメソッドを使用する予定がなければ、この部分は省略できます。
copy code
wmiObject := IUnknown(objTemp) as ISWBemObject;
wmiPropSet := wmiObject.Properties_;
wmiPropSetEnum := (wmiPropSet._NewEnum) as IEnumVariant;
↓
wmiPropSetEnum := IUnknown(objTemp.Properties_._NewEnum) as IEnumVariant;
[WMI を使用して Windows のバージョンだけを取得] の記事にあるように、プロパティセットのコレクションを取得する部分を別のメソッドにすると使い勝手がいいかも知れません。
上のサンプルは全てのプロパティを取得する例でした。今度は、特定のプロパティだけを取得するサンプルです。
上のサンプルは、2 つの while ループを使用しています。最初の while ループは、実装されている論理ディスクの数だけループします。次の while では、各論理ディスクのプロパティの数だけループします。
特定のプロパティだけが必要な場合、プロパティの数分もループさせる必要はありません。1 つの論理ディスクでは 1 回だけで十分です。したがって、2 回目のループは必要ありません。
そこで、次のコードでは論理ディスクごとに ISWbemPropertySet の Item メソッドで目的のプロパティのオブジェクト、ISWbemProperty を取得します。
後はこの ISWbemProperty のプロパティやメソッドで、論理ディスクのプロパティ名や値を取得して表示します。この時、プロパティの型の判定は特に必要ありません。プロパティの種類によって型は決まっています。それにしたがって処理すればいいことになります。
図5
実行結果例
特定のプロパティのみ取得
ここではドライブ名、容量、空き容量
リスト3
論理ディスクの特定のプロパティだけを取得する例
copy code
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティのセットを取得
wmiPropSet := (IUnknown(objTemp) as ISWBemObject).Properties_;
// DeviceIDプロパティを取得
wmiProperty := wmiPropSet.Item('DeviceID' , 0 );
if VarIsNull(wmiProperty.Get_Value) then begin
Memo1.Lines.Add(wmiProperty.Name);
end else begin
// プロパティの名前
fmtStrStr := Format('%-20s' , [wmiProperty.Name]);
// プロパティの名前と実際の値を文字列にして表示
Memo1.Lines.Add(fmtStrStr + VarToStr(wmiProperty.Get_Value));
end ;
// Sizeプロパティを取得
wmiProperty := wmiPropSet.Item('Size' , 0 );
if VarIsNull(wmiProperty.Get_Value) then begin
Memo1.Lines.Add(wmiProperty.Name);
end else begin
// プロパティの名前
fmtStrStr := Format('%-20s' , [wmiProperty.Name]);
// プロパティの値
ValueUInt64 := wmiProperty.Get_Value;
fmtStrInteger:= FormatFloat('###,##0' , ValueUInt64);
Memo1.Lines.Add(fmtStrStr + fmtStrInteger);;
end ;
// FreeSpaceプロパティを取得
// 処理内容は上のSizeプロパティの時と同じ
wmiProperty := wmiPropSet.Item('FreeSpace' , 0 );
if VarIsNull(wmiProperty.Get_Value) then begin
Memo1.Lines.Add(wmiProperty.Name);
end else begin
// プロパティの名前
fmtStrStr := Format('%-20s' , [wmiProperty.Name]);
// プロパティの値
ValueUInt64 := wmiProperty.Get_Value;
fmtStrInteger:= FormatFloat('###,##0' , ValueUInt64);
Memo1.Lines.Add(fmtStrStr + fmtStrInteger);;
end ;
Memo1.Lines.Add('---------------------------------------' );
end ;
04_ プロパティを OleVariant 型で取得
プロパティは、ISWbemProperty を取得しなくても、直接 OleVariant 型で操作できます。下のコードはその例です。
リスト4
プロパティの一部だけを取得するコード例
プロパティのオブジェクト (ISWbemProperty) を使用しない場合
copy code
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
strTemp := VarToStrDef(objTemp.DeviceID, '' );
Memo1.Lines.Add('DeviceID ' + strTemp);
if VarIsNull(objTemp.Size) then begin
Memo1.Lines.Add('Size ' );
end else begin
ValueUInt64 := objTemp.Size;
Memo1.Lines.Add('Size ' + FormatFloat('###,##0' , ValueUInt64));
end ;
if VarIsNull(objTemp.FreeSpace) then begin
Memo1.Lines.Add('Size ' );
end else begin
ValueUInt64 := objTemp.FreeSpace;
Memo1.Lines.Add('Size ' + FormatFloat('###,##0' , ValueUInt64));
end ;
Memo1.Lines.Add('---------------------------------------' );
end ;
プロパティの型に応じて取得するのではなく、単に文字列として表示するだけであれば、以下のようにバリアントの値を文字列に変換する、VarToStrDef 関数や VarToStr 関数で文字列に変換できます。
プロパティの型に応じた変数に値を代入して処理する必要がなければ、これでも十分です。ただし、プロパティの値を文字列に変換する際には注意が必要です。これについては次項で説明します。
図6
実行結果例
特定のプロパティのみ取得
ここではドライブ名、容量、空き容量
リスト5
書式なしで、プロパティを文字列として取得して表示する例
ネット上にある VB 関係のサンプルは、この形式のコードが多い
copy code
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
Memo1.Lines.Add('DeviceID ' + VarToStrDef(objTemp.DeviceID, '' ));
Memo1.Lines.Add('Size ' + VarToStrDef(objTemp.Size, '' ));
Memo1.Lines.Add('FreeSapce ' + VarToStrDef(objTemp.FreeSpace, '' ));
Memo1.Lines.Add('---------------------------------------' );
end ;
06_ プロパティを文字列として取得する場合 - 配列型のプロパティ
WMI で扱うプロパティの型は、本ページの最初に表で示しました。この他、配列型のプロパティがあります。この配列型のプロパティは再帰的に格納されている場合もあります。
配列型のプロパティの場合、各要素の型が、表の型のどれかになります。
次のコードは、BIOS の全ての情報を取得する例ですが、VarToStr 関数や VarToStrDef 関数だけではバリアントの型変換ができないというエラーが発生して、文字列として取得できない場合があります。
そこで、このサンプルでは、OleVariant 型の値を String 型の文字列として取得する関数を作成して処理しています。
図7
実行結果例
リスト6
BIOS の情報を取得
配列型の OleVariant の値を文字列で取得する例
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, WbemScripting_TLB;
type
TForm1 = class (TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
protected
{ Private 宣言 }
public
{ Public 宣言 }
function VarToString(const OleVar: OleVariant): String ;
end ;
var
Form1: TForm1;
implementation
uses ComObj, ActiveX;
{$R *.dfm}
{ TForm1 }
// =============================================================================
// BIOSの情報の取得
// プロパティの値を文字列として取得する場合の処理例
//
// usesにComObj,ActiveX,WbemScripting_TLBが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : ISWbemLocator;
objWMIService : ISWbemServices;
objPropSet : ISWbemObjectSet;
objPropSetEnum : IEnumVariant;
objTemp : OleVariant;
iValue : LongWord;
wmiPropSetEnum : IEnumVariant;
wmiProperty : ISWbemProperty;
fmtStrStr : String ;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
// Wbemのオブジェクトを生成
objWMILocator := CoSWbemLocator.Create;
// WMIサービスに接続。引数は必要に応じて設定
objWMIService := objWMILocator.ConnectServer(
'' , // コンピュータ名。ローカルの場合は空白で可
'root\cimv2' , // 名前空間
'' , // ユーザ名。ローカルの場合は空文字にする
'' , // パスワード。ローカルの場合は空文字にする
'' , // ロケール識別子
'' , // ドメイン名
0 , // 応答フラグ
nil );
// クエリーを実行して、プロパティのセットのオブジェクトを取得
objPropSet := objWMIService.ExecQuery(
'SELECT * FROM Win32_BIOS' ,
'WQL' ,
wbemFlagReturnImmediately,
nil );
// プロパティのセットのコレクションを取得
objPropSetEnum := objPropSet._NewEnum as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// 各オブジェクトのプロパティの列挙を取得
wmiPropSetEnum := IUnknown(objTemp.Properties_._NewEnum) as IEnumVariant;
while wmiPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティを取得
wmiProperty := IUnknown(objTemp) as ISWBemProperty;
// プロパティの名前と実際の値を文字列にして表示
fmtStrStr := Format('%-25s' , [wmiProperty.Name]);
Memo1.Lines.Add(fmtStrStr + VarToString(wmiProperty.Get_Value));
end ;
Memo1.Lines.Add('---------------------------------------' );
end ;
Memo1.Lines.EndUpdate;
end ;
// =============================================================================
// OleVariantの値をString型に変換(再帰処理)
// 複数の値を持つプロパティがある。そこでこれを[]で括った結果を返すことにする
// そのためVarArrayも扱えるようにしたVarToStr関数
// =============================================================================
function TForm1.VarToString(const OleVar: OleVariant): String ;
var
LowBound : Integer;
HighBound : Integer;
i : Integer;
begin
Result := '' ;
// 配列の場合
if VarIsArray(OleVar) then begin
LowBound := VarArrayLowBound(OleVar, 1 );
HighBound := VarArrayHighBound(OleVar, 1 );
for i := LowBound to HighBound do begin
if i = HighBound then begin
Result := Result + VarToString(OleVar[i]);
end else begin
Result := Result + VarToString(OleVar[i]) + ',' ;
end ;
end ;
Result:='[' + Result + ']' ;
// 単一の値を持つプロパティの場合
end else begin
Result := VarToStr(OleVar);
end ;
if Result = '' then Result := 'NULL' ;
end ;
end .
後述の THE ROAD TO DELPHI
のサイトにバリアント配列型のプロパティを処理するコードがあります。それを使用したサンプルです。
VarStrNull と VarArrayToStr という名前の関数ですが、要素の型によって処理を分岐させています。こちらの方が使いやすいかも知れません。
図8
実行結果例
リスト7
BIOS の情報の取得
配列型の OleVariantの値を文字列で取得する関数を使用した例
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, WbemScripting_TLB;
type
TForm1 = class (TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
protected
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj, ActiveX;
{$R *.dfm}
{ TForm1 }
// -----------------------------------------------------------------------------
// 配列型のプロパティを文字列で取得する関数の本体
// WMI Delphi Code Creator の記事のサイトのコード
// -----------------------------------------------------------------------------
function VarArrayToStr(const vArray: variant): string ;
function _VarToStr(const V: variant): string ;
var
Vt: integer;
begin
Vt := VarType(V);
case Vt of
varSmallint,
varInteger : Result := IntToStr(integer(V));
varSingle,
varDouble,
varCurrency : Result := FloatToStr(Double(V));
varDate : Result := VarToStr(V);
varOleStr : Result := WideString(V);
varBoolean : Result := VarToStr(V);
varVariant : Result := VarToStr(Variant(V));
varByte : Result := char(byte(V));
varString : Result := String (V);
varArray : Result := VarArrayToStr(Variant(V));
end ;
end ;
var
i : integer;
begin
Result := '[' ;
if (VarType(vArray) and VarArray)=0 then
Result := _VarToStr(vArray)
else
for i := VarArrayLowBound(vArray, 1 ) to VarArrayHighBound(vArray, 1 ) do
if i=VarArrayLowBound(vArray, 1 ) then
Result := Result + _VarToStr(vArray[i])
else
Result := Result + '|' + _VarToStr(vArray[i]);
Result:=Result + ']' ;
end ;
// -----------------------------------------------------------------------------
// 配列型のプロパティを文字列で取得する関数
// nullの時は空白文字を返す
// -----------------------------------------------------------------------------
function VarStrNull(const V:OleVariant):string ; // avoid problems with null strings
begin
Result := '' ;
if not VarIsNull(V) then
begin
if VarIsArray(V) then
Result := VarArrayToStr(V)
else
Result := VarToStr(V);
end ;
end ;
// =============================================================================
// BIOSの情報の取得
// プロパティの値を文字列として取得する場合の処理例
//
// usesにComObj,ActiveX,WbemScripting_TLBが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : ISWbemLocator;
objWMIService : ISWbemServices;
objPropSet : ISWbemObjectSet;
objPropSetEnum : IEnumVariant;
objTemp : OleVariant;
iValue : LongWord;
wmiPropSetEnum : IEnumVariant;
wmiProperty : ISWbemProperty;
fmtStrStr : String ;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
// Wbemのオブジェクトを生成
objWMILocator := CoSWbemLocator.Create;
// WMIサービスに接続。引数は必要に応じて設定
objWMIService := objWMILocator.ConnectServer(
'' , // コンピュータ名。ローカルの場合は空白で可
'root\cimv2' , // 名前空間
'' , // ユーザ名。ローカルの場合は空文字にする
'' , // パスワード。ローカルの場合は空文字にする
'' , // ロケール識別子
'' , // ドメイン名
0 , // 応答フラグ
nil );
// クエリーを実行して、プロパティのセットのオブジェクトを取得
objPropSet := objWMIService.ExecQuery(
'SELECT * FROM Win32_BIOS' ,
'WQL' ,
wbemFlagReturnImmediately,
nil );
// プロパティのセットのコレクションを取得
objPropSetEnum := objPropSet._NewEnum as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// 各オブジェクトのプロパティの列挙を取得
wmiPropSetEnum := IUnknown(objTemp.Properties_._NewEnum) as IEnumVariant;
while wmiPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティを取得
wmiProperty := IUnknown(objTemp) as ISWBemProperty;
// プロパティの名前と実際の値を文字列にして表示
fmtStrStr := Format('%-25s' , [wmiProperty.Name]);
Memo1.Lines.Add(fmtStrStr + VarStrNull(wmiProperty.Get_Value));
end ;
Memo1.Lines.Add('---------------------------------------' );
end ;
Memo1.Lines.EndUpdate;
end ;
end .
プロパティ名とプロパティの値を取得する部分を、OleVariant 型で処理する場合は、次のようになります。プロパティの値は、Get_Value ではなく、Value を使用します。
copy code
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// 各オブジェクトのプロパティの列挙を取得
wmiPropSetEnum := IUnknown(objTemp.Properties_._NewEnum) as IEnumVariant;
while wmiPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティの名前と実際の値を文字列にして表示
fmtStrStr := Format('%-25s' , [VarToStr(objTemp.Name)]);
Memo1.Lines.Add(fmtStrStr + VarToString(objTemp.Value));
end ;
Memo1.Lines.Add('---------------------------------------' );
end ;
07_ 日付時刻型プロパティの処理例 - TDateTime 型に変換
WMI で扱う日付時刻型は CIM_DATETIME という型です。
インターフェイスオブジェクトは ISWbemDateTime です。Script オブジェクトは SWbemDateTime ですが、Delphi ではどちらでも同じです。
次のサンプルは、特定のファイルのファイル名、ファイルタイプ、作成日時、更新日時を取得するサンプルです。CIM_DATETIME 型のデータを Delphi の TDateTime 型に変換する関数を作成し、それを使用して、書式を設定して表示しています。
WMI では、この他に、32 ビットまたは 64 ビットの符号なし整数値で日付時間の値が格納されているプロパティがあります。この符号なし整数値を扱う例が [903_ WMI 非同期イベントシンク] にいくつかあります。実行結果の図に、日付と時刻が表示されているサンプルで確認してください。
図9
ファイル構成
リスト8
ファイルのプロパティの一部を取得
CIM_DATETIME のデータを TDateTime 型に変換する例
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, WbemScripting_TLB;
type
TForm1 = class (TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
protected
{ Private 宣言 }
function WbemToTDateTime(V : OleVariant): TDateTime;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj, ActiveX;
{$R *.dfm}
{ TForm1 }
// =============================================================================
// ファイルの作成日時と更新日時を取得して表示
//
// usesにComObj,ActiveX,WbemScripting_TLBが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : ISWbemLocator;
objWMIService : ISWbemServices;
FilePath : String ;
strWMISQL : String ;
objPropSet : ISWbemObjectSet;
objPropSetEnum : IEnumVariant;
objTemp : OleVariant;
iValue : LongWord;
wmiPropSet : ISWbemPropertySet;
wmiProperty : ISWbemProperty;
fmtStrStr : String ;
fmtStrDateTime : String ;
ADateTime : TDateTime;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
// Wbemのオブジェクトを生成
objWMILocator := CoSWbemLocator.Create;
// WMIサービスに接続。引数は必要に応じて設定
objWMIService := objWMILocator.ConnectServer(
'' , // コンピュータ名。ローカルの場合は空白で可
'root\cimv2' , // 名前空間
'' , // ユーザ名。ローカルの場合は空文字にする
'' , // パスワード。ローカルの場合は空文字にする
'' , // ロケール識別子
'' , // ドメイン名
0 , // 応答フラグ
nil );
// クエリーの実行
FilePath := Application.ExeName;
FilePath := StringReplace(FilePath, '\' , '\\' , [rfReplaceAll]);
strWMISQL := 'Select * From CIM_DataFile Where Name ="' + FilePath + '"' ;
// クエリーを実行して、プロパティのセットのオブジェクトを取得
objPropSet := objWMIService.ExecQuery(
strWMISQL,
'WQL' ,
wbemFlagReturnImmediately,
nil );
// プロパティのセットのコレクションを取得
objPropSetEnum := objPropSet._NewEnum as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティのセットを取得
wmiPropSet := (IUnknown(objTemp) as ISWBemObject).Properties_;
// Nameプロパティを取得
wmiProperty := wmiPropSet.Item('Name' , 0 );
fmtStrStr := Format('%-15s' , [wmiProperty.Name]);
Memo1.Lines.Add(fmtStrStr + VarToStrDef(wmiProperty.Get_Value, '' ));
// FileTypeプロパティを取得
wmiProperty := wmiPropSet.Item('FileType' , 0 );
fmtStrStr := Format('%-15s' , [wmiProperty.Name]);
Memo1.Lines.Add(fmtStrStr + VarToStrDef(wmiProperty.Get_Value, '' ));
// InstallDate(作成日時)プロパティを取得
wmiProperty := wmiPropSet.Item('InstallDate' , 0 );
fmtStrStr := Format('%-15s' , [wmiProperty.Name]);
ADateTime := WbemToTDateTime(wmiProperty.Get_Value);
if ADateTime <> 0 then begin
fmtStrDateTime := FormatDateTime('yyyy-mm-dd : hh:nn:ss' , ADateTime);
Memo1.Lines.Add(fmtStrStr + fmtStrDateTime);
end else begin
Memo1.Lines.Add(fmtStrStr);
end ;
// LastModified(更新日時)プロパティを取得
wmiProperty := wmiPropSet.Item('LastModified' , 0 );
fmtStrStr := Format('%-15s' , [wmiProperty.Name]);
ADateTime := WbemToTDateTime(wmiProperty.Get_Value);
if ADateTime <> 0 then begin
fmtStrDateTime := FormatDateTime('yyyy-mm-dd : hh:nn:ss' , ADateTime);
Memo1.Lines.Add(fmtStrStr + fmtStrDateTime);
end else begin
Memo1.Lines.Add(fmtStrStr);
end ;
end ;
Memo1.Lines.EndUpdate;
end ;
// =============================================================================
// CIM_DATETIMEの日付時刻型をDelphiのTDateTime型に変換する関数
// OleVariantで返して、呼び出し側でTDateTime型に変換した方がいいかも知れない
// =============================================================================
function TForm1.WbemToTDateTime(V: OleVariant): TDateTime;
var
WMIDateTime : ISWbemDateTime;
begin
Result := 0 ;
if VarIsNull(V) then exit;
WMIDateTime := CoSWbemDateTime.Create;
WMIDateTime.Value := V;
Result := WMIDateTime.GetVarDate(True);
end ;
end .
参考までに、指定したディレクトリ内の指定拡張子のファイルを取得するには、クエリーの構文を変更することで取得できます。
下のコードはその例です。D:\000 内の拡張子が pas のファイルの一覧を表示します。
copy code
// =============================================================================
// 指定ディレクトリ内の指定拡張子のファイルの一覧
//
// usesにComObj,ActiveX,WbemScripting_TLBが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : ISWbemLocator;
objWMIService : ISWbemServices;
strWMISQL : String ;
objPropSet : ISWbemObjectSet;
objPropSetEnum : IEnumVariant;
objTemp : OleVariant;
iValue : LongWord;
wmiPropSet : ISWbemPropertySet;
wmiProperty : ISWbemProperty;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
// Wbemのオブジェクトを生成
objWMILocator := CoSWbemLocator.Create;
// WMIサービスに接続。引数は必要に応じて設定
objWMIService := objWMILocator.ConnectServer(
'' , // コンピュータ名。ローカルの場合は空白で可
'root\cimv2' , // 名前空間
'' , // ユーザ名。ローカルの場合は空文字にする
'' , // パスワード。ローカルの場合は空文字にする
'' , // ロケール識別子
'' , // ドメイン名
0 , // 応答フラグ
nil );
// クエリーの実行
strWMISQL := 'Select * From CIM_DataFile '
+ 'Where Drive="d:" and Path = "\\000\\" and Extension = "pas"' ;
// クエリーを実行して、プロパティのセットのオブジェクトを取得
objPropSet := objWMIService.ExecQuery(
strWMISQL,
'WQL' ,
wbemFlagReturnImmediately,
nil );
// プロパティのセットのコレクションを取得
objPropSetEnum := objPropSet._NewEnum as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティのセットを取得
wmiPropSet := (IUnknown(objTemp) as ISWBemObject).Properties_;
// Nameプロパティを取得
wmiProperty := wmiPropSet.Item('Name' , 0 );
Memo1.Lines.Add(VarToStrDef(wmiProperty.Get_Value, '' ));
end ;
Memo1.Lines.EndUpdate;
end ;
WMI とは直接関係ありませんが、ファイルのタイムスタンプ (更新日時) を取得するもっとも簡単な方法は、以下の FileAge 関数を使用した方法ではないかと思います。
ただし、ファイルの更新日時が、どんなアプリケーションで、どんな状況で取得する必要があるかにもよります。したがって、この FileAge 関数の使用が常に最良というわけではありません。
// =============================================================================
// ファイルのタイムスタンプ(更新日時)の取得
// System.SysUtils.FileAgeを使用した例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
FilePath : String ;
FileDate : TDateTime;
begin
FilePath := ExtractFilePath(Application.ExeName) + 'AAA.txt' ;
if FileAge(FilePath, FileDate, True) then begin
ShowMessage(DateTimeToStr(FileDate));
end ;
end ;
08_ 日付時刻型プロパティの処理例 - ミリ秒まで取得
同じく CIM_DATETIME を扱う例です。
OS が最後に起動した日時を取得して、現在までの経過日数および経過時間を時間、分、秒、ミリ秒まで計算して表示します。つまり、システム稼働時間の確認ということになります。
経過日時は、TDateTime 型の値から直接は算出できません。そこで、TDateTime 型の値を TTimeStamp 型に変換してから、日数とミリ秒に分解します。ここから計算で求めています。
[備考]
ログインの日時等は、Win32_NetworkLoginProfile クラスを使用すると取得できます。
図10
実行結果例
OS の起動日時と経過日時
ミリ秒単位まで計算して表示
リスト9
CIM_DATETIME のデータを扱う例
OS が最後に起動した日時と、起動後の経過日時を表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, WbemScripting_TLB;
type
TForm1 = class (TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
protected
{ Private 宣言 }
function WbemToTDateTime(V : OleVariant): TDateTime;
function DateTimeString(V : OleVariant): String ;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj, ActiveX;
{$R *.dfm}
{ TForm1 }
// =============================================================================
// OSの起動日時と経過時間をミリ秒まで取得
//
// usesにComObj,ActiveX,WbemScripting_TLBが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : ISWbemLocator;
objWMIService : ISWbemServices;
objPropSet : ISWbemObjectSet;
objPropSetEnum : IEnumVariant;
objTemp : OleVariant;
iValue : LongWord;
wmiPropSet : ISWbemPropertySet;
wmiProperty : ISWbemProperty;
fmtStrStr : String ;
fmtStrDateTime : String ;
ADateTime : TDateTime;
TimeDay : Integer;
TimeMili : Integer;
IntTemp : Integer;
Hour : Integer;
Minute : Integer;
Second : Integer;
MiliSecond : Integer;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
// Wbemのオブジェクトを生成
objWMILocator := CoSWbemLocator.Create;
// WMIサービスに接続。引数は必要に応じて設定
objWMIService := objWMILocator.ConnectServer(
'' , // コンピュータ名。ローカルの場合は空白で可
'root\cimv2' , // 名前空間
'' , // ユーザ名。ローカルの場合は空文字にする
'' , // パスワード。ローカルの場合は空文字にする
'' , // ロケール識別子
'' , // ドメイン名
0 , // 応答フラグ
nil );
// クエリーを実行して、プロパティのセットのオブジェクトを取得
objPropSet := objWMIService.ExecQuery(
'Select * From Win32_OperatingSystem' ,
'WQL' ,
wbemFlagReturnImmediately,
nil );
// プロパティのセットのコレクションを取得
objPropSetEnum := objPropSet._NewEnum as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// 順番に取り出す
while objPropSetEnum.Next(1 , objTemp, iValue) = 0 do begin
// プロパティのセットを取得
wmiPropSet := (IUnknown(objTemp) as ISWBemObject).Properties_;
// Caption(OS名)プロパティを取得
wmiProperty := wmiPropSet.Item('Caption' , 0 );
fmtStrStr := Format('%-17s' , [wmiProperty.Name]);
Memo1.Lines.Add(fmtStrStr + VarToStrDef(wmiProperty.Get_Value, '' ));
// 最後に起動した日時(LastBootUpTime)を取得
wmiProperty := wmiPropSet.Item('LastBootUpTime' , 0 );
fmtStrStr := Format('%-17s' , [wmiProperty.Name]);
fmtStrDateTime := DateTimeString(wmiProperty.Get_Value);
Memo1.Lines.Add(fmtStrStr + fmtStrDateTime);
// 最後に起動した日時をTDateTime型の値に変換
ADateTime := WbemToTDateTime(wmiProperty.Get_Value);
// その日時と現在の日時をTTimeStamp型に変換して、日とミリ秒の差を計算
TimeDay := DateTimeToTimeStamp(Now).Date - DateTimeToTimeStamp(ADateTime).Date;
TimeMili := DateTimeToTimeStamp(Now).Time - DateTimeToTimeStamp(ADateTime).Time;
if TimeMili < 0 then begin
TimeDay := TimeDay - 1 ;
TimeMili := TimeMili + 3600 * 24 * 1000 ;
end ;
// 時,分,秒,ミリセコンドを計算
Hour := TimeMili div (3600 * 1000 );
IntTemp := TimeMili mod (3600 * 1000 );
Minute := IntTemp div (60 * 1000 );
IntTemp := IntTemp mod (60 * 1000 );
Second := IntTemp div 1000 ;
MiliSecond := IntTemp mod 1000 ;
Memo1.Lines.Add('経過日時 '
+ IntToStr(TimeDay) + '日'
+ IntToStr(Hour) + '時間'
+ IntToStr(Minute) + '分'
+ IntToStr(Second) + '秒'
+ IntToStr(MiliSecond) + 'ミリ秒' );
end ;
Memo1.Lines.EndUpdate;
end ;
// =============================================================================
// CIM_DATETIMEの日付時刻型の日時を書式付き文字列に変換
// =============================================================================
function TForm1.DateTimeString(V: OleVariant): String ;
var
WMIDateTime : ISWbemDateTime;
begin
Result := '' ;
if VarIsNull(V) then exit;
WMIDateTime := CoSWbemDateTime.Create;
WMIDateTime.Value := V;
Result := Format('%.4d/%.2d/%.2d %.2d:%.2d:%.2d.%.3d' ,
[WMIDateTime.Year,
WMIDateTime.Month,
WMIDateTime.Day,
WMIDateTime.Hours,
WMIDateTime.Minutes,
WMIDateTime.Seconds,
WMIDateTime.MicroSeconds * 1000 ]);
end ;
// =============================================================================
// CIM_DATETIMEの日付時刻型をDelphiのTDateTime型に変換する関数
// OleVariantで返して、呼び出し側でTDateTime型に変換した方がいいかも知れない
// =============================================================================
function TForm1.WbemToTDateTime(V: OleVariant): TDateTime;
var
WMIDateTime : ISWbemDateTime;
begin
Result := 0 ;
if VarIsNull(V) then exit;
WMIDateTime := CoSWbemDateTime.Create;
WMIDateTime.Value := V;
Result := WMIDateTime.GetVarDate(True);
end ;
end .
ISWbemDateTime を OleVariant 型で処理する場合は、例えば以下のようになります。
オブジェクトを OleVariant 型で扱う場合は、コード補完機能のように、WMIDateTime の Year 等のメンバーの正確なスペルを知ることができません。したがって、一度 ISWbemDateTime 型で動作することを確認してから、このような OleVariant 型のコードに変換しています。
copy code
// =============================================================================
// CIM_DATETIMEの日付時刻型の日時を書式付き文字列に変換
// =============================================================================
function TForm1.DateTimeString(V: OleVariant): String ;
var
WMIDateTime : OleVariant;
begin
Result := '' ;
if VarIsNull(V) then exit;
WMIDateTime := CreateOleObject('WbemScripting.SWbemDateTime' );
WMIDateTime.Value := V;
Result := Format('%.4d/%.2d/%.2d %.2d:%.2d:%.2d.%.3d' ,
[Integer(WMIDateTime.Year),
Integer(WMIDateTime.Month),
Integer(WMIDateTime.Day),
Integer(WMIDateTime.Hours),
Integer(WMIDateTime.Minutes),
Integer(WMIDateTime.Seconds),
Integer(WMIDateTime.MicroSeconds) * 1000 ]);
end ;
// =============================================================================
// CIM_DATETIMEの日付時刻型をDelphiのTDateTime型に変換する関数
// OleVariantで返して、呼び出し側でTDateTime型に変換した方がいいかも知れない
// =============================================================================
function TForm1.WbemToTDateTime(V: OleVariant): TDateTime;
var
WMIDateTime : OleVariant;
begin
Result := 0 ;
if VarIsNull(V) then exit;
WMIDateTime := CreateOleObject('WbemScripting.SWbemDateTime' );
WMIDateTime.Value := V;
Result := WMIDateTime.GetVarDate(True);
end ;
09_ CreateOleObject による WMI オブジェクトの生成
COM オブジェクトは CreateOleObject でそのインスタンスを生成することができます。
したがって、WMI Script オブジェクトも、ワードやエクセルと同じように、CreateOleObject でそのインスタンスを生成できます。その例です。
このサンプルでは、ISWbemServices、ISWbemObjectSet を OleVariant 型で扱っています。COM オブジェクトを OleVariant 型で扱う場合、デフォルトの値で使用するメソッドの引数を省略できます。
これまでのサンプルと同じ様に、必要に応じて ISWbemProperty や ISWbemDateTime オブジェクト等も利用することができますが、これらも OleVariant 型で扱えます。このサンプルでは使用していませんので、uses には WbemScripting_TLB を追加していません。
他のサンプルで使用している CoSWbemLocator は、下コードのように、CreateComObject で生成したオブジェクトを ISWbemLocator でキャスト(型変換) しているだけです。この CreateComObject の部分は CreateOleObject でも生成できます。
Script オブジェクトを CreateOleObject で生成して OleVariant 型で扱ったり、他の COM オブジェクトも OleVariant 型で扱うかは、プログラマが決めることです。
copy code
class function CoSWbemLocator.Create: ISWbemLocator;
begin
Result := CreateComObject(CLASS_SWbemLocator) as ISWbemLocator;
end ;
[備考]
たとえ、CreateOleObject で WMI のインスタンスを生成するにしても、WbemScripting_TLB を作成しておくと便利です。このファイルには、定数等の定義があります。
また、非同期イベントのサンプルがありますが、非同期のイベント部分を OleVariant で扱うには無理があるでしょう。非同期イベント処理では、WbemScripting_TLB を使用した方が無難です。
図11
設計時画面
図12
実行結果例
リスト10
CreateOleObject の使用例
論理ディスクの特定のプロパティを取得して表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
ListBox1: TListBox;
procedure Button1Click(Sender: TObject);
protected
{ Private 宣言 }
function VarToString(const OleVar: OleVariant): String ;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj, ActiveX;
{$R *.dfm}
{ TForm1 }
// =============================================================================
// 論理ディスクの一覧を取得して、いくつかのプロパティを取得して表示
// usesにComObj,ActiveXが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : OleVariant;
objWMIService : OleVariant;
colItems : OleVariant;
colItem : OleVariant;
colEnum : IEnumvariant;
iValue : LongWord;
begin
ListBox1.Items.Clear;
// WMIオブジェクトを生成
ObjWMILocator := CreateOleObject('WbemScripting.SWbemLocator' );
// WMIサービスに接続
// 第1引数はコンピュータ名(空白なら自分のマシン)
ObjWMIService := ObjWMILocator.ConnectServer('' , 'root\CIMV2' );
// クエリーの実行
// デフォルトの引数は省略できるので、以下でも可
// colItems := objWMIService.ExecQuery('SELECT * FROM Win32_LogicalDisk');
colItems := objWMIService.ExecQuery('SELECT * FROM Win32_LogicalDisk' , 'WQL' , 0 );
// プロパティのセットのコレクションを取得
colEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// それを順番に各々のプロパティを取得
while colEnum.Next(1 , colItem, iValue) = 0 do begin
ListBox1.Items.Add(Format('Description %s' , [VarToString(colItem.Description)]));
ListBox1.Items.Add(Format('DeviceID %s' , [VarToString(colItem.DeviceID)]));
ListBox1.Items.Add(Format('Size %s' , [VarToString(colItem.Size)]));
ListBox1.Items.Add('' );
end ;
end ;
// =============================================================================
// OleVariantの値をString型に変換(再帰処理)
// 複数の値を持つプロパティがある。そこでこれを[]で括った結果を返すことにする
// そのためVarArrayも扱えるようにしたVarToStr関数
// =============================================================================
function TForm1.VarToString(const OleVar: OleVariant): String ;
var
LowBound : Integer;
HighBound : Integer;
i : Integer;
begin
Result := '' ;
// 配列の場合
if VarIsArray(OleVar) then begin
LowBound := VarArrayLowBound(OleVar, 1 );
HighBound := VarArrayHighBound(OleVar, 1 );
for i := LowBound to HighBound do begin
if i = HighBound then begin
Result := Result + VarToString(OleVar[i]);
end else begin
Result := Result + VarToString(OleVar[i]) + ',' ;
end ;
end ;
Result:='[' + Result + ']' ;
// 単一の値を持つプロパティの場合
end else begin
Result := VarToStr(OleVar);
end ;
if Result = '' then Result := 'NULL' ;
end ;
end .
WMI オブジェクトのインスタンスを CreateOleObject で生成し、さらにプロパティも OleVariant 型で処理する場合、Button1 のイベントのコードは、例えば以下のようになります。このコードは、全てのプロパティを列挙するようにしています。
全てを OleVariant 型で扱えば、タイプライブラリの取り込みで作成した WbemScripting_TLB は必要ありません。
このコードは、OleVariant 型で処理する際のひな型として利用できます。後は、プロパティの型に応じた処理や、上のサンプルのように特定のプロパティだけを取り出す処理にすることなります。
VarToString は上のコードでも使用している OleVariantの値をString型に変換する関数です。
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
objWMILocator : OleVariant;
objWMIService : OleVariant;
colItems : OleVariant;
colItem : OleVariant;
colEnum : IEnumvariant;
iValue : LongWord;
wmiPropSetEnum : IEnumVariant;
fmtStrStr : String ;
begin
ListBox1.Items.Clear;
ObjWMILocator := CreateOleObject('WbemScripting.SWbemLocator' );
ObjWMIService := ObjWMILocator.ConnectServer('' , 'root\CIMV2' );
colItems := objWMIService.ExecQuery('SELECT * FROM Win32_LogicalDisk' );
colEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
while colEnum.Next(1 , colItem, iValue) = 0 do begin
wmiPropSetEnum := IUnknown(colItem.Properties_._NewEnum) as IEnumVariant;
while wmiPropSetEnum.Next(1 , colItem, iValue) = 0 do begin
fmtStrStr := Format('%-32s' , [VarToStr(colItem.Name)]);
ListBox1.Items.Add(fmtStrStr + VarToString(colItem.Value));
end ;
ListBox1.Items.Add('---------------------------------------' );
end ;
end ;
10_ VBS ( VBScript ) の WMI コードを移植
ネット上で、次のような VB や VBS (VBScript) を使用したサンプルを見かけることがあります。
winmgmts: は WMI オブジェクトのライブラリを意味する文字列です。WMI が Windows Management Instrumentation ですから、おそらく Windows Managements ぐらいの意味でしょう。つまり、このコードは WMI オブジェクトを取得して、Win32_LogicalDisk クラスのクエリーを実行します。
結果は、上のコードの colItems に相当することになります。
これは、このままでは利用できません。Delphi では GetObject という関数も InstancesOf という修飾子も使用できません。この VBS のコードは、WMI の Win32_LogicalDisk クラスを使用しています。したがって、上のコードのように書き換えれば動作します。
GetObject("winmgmts:").InstancesOf ("Win32_LogicalDisk")
このような VBS のコードは、[950_Delphi で VBScript を操作] の説明の方法でも移植できます。
Delphi で VBScript のオブジェクトのインスタンスを生成したら、Eval 関数の引数に、上の VBS のコードをそのまま文字列として渡します。Eval 関数の戻り値は OleVariant 型です。
下がそのコードです。実行結果は、CreateOleObject のサンプルと全く同じになります。
VBS のコードの中に & (アンパサンド) がある場合、これは文字列の結合の意味で、結合する文字列は変数を意味していることがほとんどです。したがって、この & を含めた文字列を Eval 関数の引数にしても動作しません。その場合は、文字列変数を別に用意して処理します。
ですが、書き直してしまった方が早い気もします。Delphi では 3 行となりますが、各々が機能別になっています。
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
VBSpt : TScriptControl;
colItems : OleVariant;
colItem : OleVariant;
colEnum : IEnumvariant;
iValue : LongWord;
begin
ListBox1.Items.Clear;
// VBScriptオブジェクトを生成
// VBScriptの使用にはusesにMSScriptControl_TLBが必要
VBSpt := TScriptControl.Create( Self );
VBSpt.Language := 'VBScript' ;
try
// VBSの GetObject("winmgmts:").InstancesOf ("Win32_LogicalDisk") 相当のコード
colItems := VBSpt.Eval('GetObject("winmgmts:").InstancesOf("Win32_LogicalDisk")' );
// プロパティのセットのコレクションを取得
colEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// それを順番に各々のプロパティを取得
while colEnum.Next(1 , colItem, iValue) = 0 do begin
ListBox1.Items.Add(Format('Description %s' , [VarToString(colItem.Description)]));
ListBox1.Items.Add(Format('DeviceID %s' , [VarToString(colItem.DeviceID)]));
ListBox1.Items.Add(Format('Size %s' , [VarToString(colItem.Size)]));
ListBox1.Items.Add('' );
end ;
finally
FreeAndNil(VBSpt);
end ;
end ;
次のように、"winmgmts:" だけのオブジェクトを OleVariant 型で受け取ることもできます。
この VBSpt.Eval で受け取ることができるのは、GetObject が生成するオブジェクトが IUnknown 型インターフェイスのオブジェクトの場合だけです。
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
VBSpt : TScriptControl;
objWMIService : OleVariant;
colItems : OleVariant;
colItem : OleVariant;
colEnum : IEnumvariant;
iValue : LongWord;
begin
ListBox1.Items.Clear;
// VBScriptオブジェクトを生成
// VBScriptの使用にはusesにMSScriptControl_TLBが必要
VBSpt := TScriptControl.Create( Self );
VBSpt.Language := 'VBScript' ;
try
// "winmgmts:" 部分だけのオブジェクトを取得
objWMIService := VBSpt.Eval('GetObject("winmgmts:")' );
colItems := objWMIService.ExecQuery('SELECT * FROM Win32_LogicalDisk' , 'WQL' , 0 );
// プロパティのセットのコレクションを取得
colEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// それを順番に各々のプロパティを取得
while colEnum.Next(1 , colItem, iValue) = 0 do begin
ListBox1.Items.Add(Format('Description %s' , [VarToString(colItem.Description)]));
ListBox1.Items.Add(Format('DeviceID %s' , [VarToString(colItem.DeviceID)]));
ListBox1.Items.Add(Format('Size %s' , [VarToString(colItem.Size)]));
ListBox1.Items.Add('' );
end ;
finally
FreeAndNil(VBSpt);
end ;
end ;
InstancesOf を次のように使用することもできます。ただし、InstancesOf の引数はこの場合、文字列です。したがって、" (ダブルクォーテーション) ではなく、' (シングルクォーテーション) を使用します。Eval の最初の使用例のコードでは、文字列の中の文字列だったので " を使用しています。
文字列の中の文字列の場合、''' とすることもできます。
対象となる VB や VBScript のコードがどのような動作をするかが分かれば、Delphi 本来のコードに移植しやすいのですが、VB, VBS も変数の型の指定なしで実行できるのが利点となっています。せめて実行時の型が分かれば移植しやすいのですが、中々難しいところです。
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
VBSpt : OleVariant;
objWMIService : OleVariant;
colItems : OleVariant;
colItem : OleVariant;
colEnum : IEnumvariant;
iValue : LongWord;
begin
ListBox1.Items.Clear;
// VBScriptオブジェクトを生成
VBSpt := CreateOleObject('MSScriptControl.ScriptControl' );
VBSpt.Language := 'VBScript' ;
try
// VBSの GetObject("winmgmts:") 相当のコード
objWMIService := VBSpt.Eval('GetObject("winmgmts:")' );
colItems := objWMIService.InstancesOf('Win32_LogicalDisk' );
// プロパティのセットのコレクションを取得
colEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// それを順番に各々のプロパティを取得
while colEnum.Next(1 , colItem, iValue) = 0 do begin
ListBox1.Items.Add(Format('Description %s' , [VarToString(colItem.Description)]));
ListBox1.Items.Add(Format('DeviceID %s' , [VarToString(colItem.DeviceID)]));
ListBox1.Items.Add(Format('Size %s' , [VarToString(colItem.Size)]));
ListBox1.Items.Add('' );
end ;
finally
FreeAndNil(VBSpt);
end ;
end ;
更に、VBScript も OleVariant 型で扱うのであれば、上のコードは次のように書けます。
VBScript を OleVariant 型で扱う場合は、MSScriptControl_TLB.pas は必要ありません。
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
VBSpt : OleVariant;
objWMIService : OleVariant;
colItems : OleVariant;
colItem : OleVariant;
colEnum : IEnumvariant;
iValue : LongWord;
begin
ListBox1.Items.Clear;
// VBScriptオブジェクトを生成
VBSpt := CreateOleObject('MSScriptControl.ScriptControl' );
VBSpt.Language := 'VBScript' ;
try
// VBSの GetObject("winmgmts:") 相当のコード
objWMIService := VBSpt.Eval('GetObject("winmgmts:")' );
colItems := objWMIService.InstancesOf('Win32_LogicalDisk' );
// プロパティのセットのコレクションを取得
colEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
// -------------------------------------------------------------------------
// 以下がプロパティの取得コード
// -------------------------------------------------------------------------
// それを順番に各々のプロパティを取得
while colEnum.Next(1 , colItem, iValue) = 0 do begin
ListBox1.Items.Add(Format('Description %s' , [VarToString(colItem.Description)]));
ListBox1.Items.Add(Format('DeviceID %s' , [VarToString(colItem.DeviceID)]));
ListBox1.Items.Add(Format('Size %s' , [VarToString(colItem.Size)]));
ListBox1.Items.Add('' );
end ;
finally
VBSpt := Unassigned;
end ;
end ;
11_ WMI Delphi Code Creator
THE ROAD TO DELPHI のサイトに WDCC.exe というユーティリティがあります。
これは WMI の Delphi の処理コードを生成するアプリケーションです。今回は [Binaries] をダウンロードして使用してみました。
このようなツールが手元にあれば、リファレンス用として活用できます。
図13
WDCC.exe を起動してクエリー等を設定
上の図で論理ディスクの全てのプロパティを取得する設定をして、任意のフォルダにプロジェクトを保存します。プロジェクトはコンソールアプリケーションとなっています。コンパイルは上の図の状態からも実行できます。
プロジェクトをコンパイルすると、
Null型から Integer 型へのバリアント型変換はできません
というエラーが発生して実行できません。
図14
エラー
そこで、下のコードのように、NullStrictConvert := False; としました。また符号なし 64 ビット整数値は、符号なし 64 ビットの変数に代入してから書式を設定します。
Format 関数の引数内でキャストすると実行時にエラーが発生しますが、変数であればエラーは発生しません。
その下の図が実行結果です。コンソールの画面バッファサイズの設定によっては、取得項目数が多いと、最初の部分が表示されない場合があります。
copy code
procedure GetWin32_LogicalDiskInfo;
const
WbemUser ='' ;
WbemPassword ='' ;
WbemComputer ='localhost' ;
wbemFlagForwardOnly = $00000020 ;
var
//
ValueInt64 : UInt64; // 追加
begin ;
//
//
ValueInt64 := FWbemObject.FreeSpace;
Writeln('FreeSpace ' + FormatFloat('####,##0' , ValueInt64));
//
ValueInt64 := FWbemObject.Size;
Writeln('Size ' + FormatFloat('####,##0' , ValueInt64));
//
//
end ;
end ;
begin
try
NullStrictConvert := False; // 追加
CoInitialize(nil );
try
GetWin32_LogicalDiskInfo;
finally
CoUninitialize;
NullStrictConvert := True; // 追加
end ;
except
on E:EOleException do
Writeln(Format('EOleException %s %x' , [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':' , E.Message);
end ;
Writeln('Press Enter to exit' );
Readln;
end .
図15
実行結果
12_ SMBIOS ( System Management BIOS ) 情報の取得
SMBIOS (システム管理 BIOS) は、マザーボード等の製品管理情報の規格のことです。
SMBIOS の情報は WMI からも取得できますが、上の WMI Delphi Code Creator を提供しているサイトで、SMBIOS 関係の情報を取得するためユニットが提供されています。
ユニット名は uSMBIOS です。これを uses に追加して利用します。ソースコードは code google site から SVN でダウンロードできます。
[Samples] フォルダには下図のようなサンプルが収められています。
図17
サンプルプログラムのリスト
サンプルプログラムはコンソールアプリケーションとなっています。ダウンロードしたファイルを解凍したままの状態でコンパイルすることができます。相対パスの設定で [Common] の uSMBIOS にパスが設定されています。
各サンプルには EXE ファイルも添付されていますが、下図は実際に Delphi XE Pro でコンパイルして実行した結果です。
図18
サンプルの実行結果
13_ コンポーネント WMI and SMART Component
WMI を 利用しやすいようにしたコンポーネントです。
2013年08月27日現在、Delphi XE4 でのテストも行っているようです。
このコンポーネントには WbemScripting_TLB が含まれています。下図のようなデモログラムも EXE ファイル形式で含まれています。これを使用するどんな WMI のクラス (クエリー) があり、どんなプロパティがあるか、またその結果も確認することができます。
このコンポーネントはインストール (登録) 作業はせず、uses に magwmi と magsubs1 を追加して利用します。
下図は、クエリー Win32_OperatingSystem を実行したところです。結果の左側がプロパティのリストで右がその値となっています。
Online-Admin.com というサイトに WmiSet Components というコンポーネントがありますが、Delphi 7 までの対応となっています。
図19
コンポーネント内のリスト
図20
附属のサンプルプログラム
14_ その他のユーティリティ類 - wbemtest.exe ( ウェブエムテスト )
Windows 2000 以上であれば、Windows GUI ツール の 1 つとして、WMI の機能をテストするためのプログラム、wbemtest.exe がインストールされています。[スタート] から [ファイル名を指定して実行] で起動できます。
説明と使用方法は、下のリンクの Microsoft TecNet のスクリプトセンターにあります。
図21
wbemtest.exe 起動
図22
起動して接続
15_ その他のユーティリティ類 - WMI Code Creator
マイクロソフトのサイトに WMI Code Creator というユーティリティがあり、これも WMI の各クエリーとそのプロパティ等を確認することができます。
これは、前述の WMI and SMART Component のデモプログラムと違い、全てのプロパティのリストではなく、取得したいプロパティを選択する方式となっています。また、プログラムのソースコードを生成する機能もありますが、Delphiのコードは生成できません。
WMI Delphi Code Creator と同じように、名前空間の一覧と、各々の名前空間で使用可能なクラスの一覧から選択してクエリーを実行することができます。
図23
WMI Code Creator
図24
使用可能な名前空間の一覧
図25
名前空間 CIMV2 で使用可能なクラス名の一覧
16_ Delphi XE3 の Winapi.Wbem ユニット
Delphi XE3 で WMI を扱うユニット、Winapi.Wbem が追加されています。このユニットを使用したテストプログラムです。このユニットには IEnumWbemClassObject, IWbemClassObject というインターフェイスが実装されていて、プロパティの処理が便利になっています。
ユニット内のコードは、WbemScripting_TLB とはだいぶ違います。
下のコードは、ローカルコンピュータの全論理ディスクの全てのプロパティを取得します。
図26
実行結果
リスト12
Delphi XE3 の Winapi.Wbem ユニット
全ディスクの全プロパティを取得
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
Winapi.ActiveX,
Winapi.Wbem;
{$R *.dfm}
const
// 偽装レベルの定数
RPC_C_AUTHN_LEVEL_DEFAULT = 0 ;
RPC_C_IMP_LEVEL_ANONYMOUS = 1 ;
RPC_C_IMP_LEVEL_IDENTIFY = 2 ;
RPC_C_IMP_LEVEL_IMPERSONATE = 3 ;
RPC_C_IMP_LEVEL_DELEGATE = 4 ;
// 認証サービスの定数
RPC_C_AUTHN_WINNT = 10 ;
RPC_C_AUTHN_LEVEL_CALL = 3 ;
RPC_C_AUTHN_DEFAULT = Longint($FFFFFFFF );
EOAC_NONE = 0 ;
// 承認定数
RPC_C_AUTHZ_NONE = 0 ;
RPC_C_AUTHZ_NAME = 1 ;
RPC_C_AUTHZ_DCE = 2 ;
RPC_C_AUTHZ_DEFAULT = Longint($FFFFFFFF );
// =============================================================================
// Winapi.Wbemユニット(Delphi XE3以降)を使用したWMIのサンプル
// ローカルコンピュータの論理ディスクの(ほぼ)全てのプロパティを取得して表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
WbemUser = '' ;
WbemPassword = '' ;
WbemComputer = 'localhost' ;
WbemLocale = '' ;
var
LWbemLocator : IWbemLocator;
LWbemServices : IWbemServices;
ppEnum : IEnumWbemClassObject;
apObjects : IWbemClassObject;
puReturned : ULONG;
OpResult : HRESULT;
strName : WideString;
PropValue : OleVariant;
fmtStrStr : String ;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
OpResult := CoCreateInstance(
CLSID_WbemLocator,
nil ,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
LWbemLocator);
if Failed(OpResult) then exit;
// WMIサービスへの接続
OpResult := LWbemLocator.ConnectServer(
Format('\\%s\root\CIMV2' , [WbemComputer]),
WbemUser,
WbemPassword,
WbemLocale,
WBEM_FLAG_CONNECT_USE_MAX_WAIT,
'' ,
nil ,
LWbemServices);
if Succeeded(OpResult) then begin
// セキュリティレベルの設定
if Failed(CoSetProxyBlanket(
LWbemServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
nil ,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
nil ,
EOAC_NONE)) then Exit;
// クエリーの実行
LWbemServices.ExecQuery(
'WQL' ,
'SELECT * FROM Win32_LogicalDisk' ,
WBEM_FLAG_FORWARD_ONLY,
nil ,
ppEnum);
// -----------------------------------------------------------------------
// 以下がプロパティの取得コード
// -----------------------------------------------------------------------
// プロパティセットの列挙
while ppEnum.Next(Integer(WBEM_INFINITE), 1 , apObjects, puReturned)=0 do begin
// プロパティの列挙開始
if (apObjects.BeginEnumeration(
WBEM_FLAG_NONSYSTEM_ONLY) = WBEM_S_NO_ERROR) then begin
// 各プロパティの取得
while apObjects.Next(
0 , @strName, @PropValue, nil , nil ) = WBEM_S_NO_ERROR do begin
fmtStrStr := Format('%-32s' , [strName]);
Memo1.Lines.Add(fmtStrStr + VarToStrDef(PropValue, '' ));
VarClear(PropValue);
end ;
end ;
Memo1.Lines.Add('--------------------------------------' );
end ;
end ;
Memo1.Lines.EndUpdate;
end ;
end .
特定のプロパティだけを取得する場合は、例えば次のようになります。
pVal は Variant 型、pType は CIMTYPE 型、plFlavor は PInteger 型の変数です。
copy code
// -----------------------------------------------------------------------
// 以下がプロパティの取得コード
// -----------------------------------------------------------------------
// プロパティセットの列挙
while ppEnum.Next(Integer(WBEM_INFINITE), 1 , apObjects, puReturned)=0 do begin
pType := 0 ;
plFlavor := nil ;
apObjects.Get('Name' , 0 , pVal, @pType, plFlavor);
Memo1.Lines.Add(Format('%-32s' , ['Name' ]) + VarToStr(pVal));
apObjects.Get('Size' , 0 , pVal, @pType, plFlavor);
Memo1.Lines.Add(Format('%-32s' , ['Size' ]) + VarToStr(pVal));
apObjects.Get('FreeSpace' , 0 , pVal, @pType, plFlavor);
Memo1.Lines.Add(Format('%-32s' , ['FreeSpace' ]) + VarToStr(pVal));
Memo1.Lines.Add('--------------------------------------' );
VarClear(pVal);
end ;