Delphi Programming / Object Pascal
[掲載 2009年03月31日] [更新 2015年09月25日] Delphi サンプルプログラム集 |
332_NtQuerySystemInformation 関数を使用したプロセスリスト |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32 |
|
332_ProcessListNtQuery.zip [1,874 KB] 2015年09月25日版 (EXE 同梱) |
- 2010年08月10日
- [02_JEDIプロジェクトを利用したコード] を追加
|
01_NtQuerySystemInformation 関数を使用したプロセスリスト |
この関数は,MSDN の説明では,将来のバージョンでは廃止されるので,他の関数を使用するようにと書かれています.また,このサンプルで使用する情報用の構造体のメンバーの多くがシステムで使用するために予約されている.と書かれていますが,実際には Vista で使用可能です.たたし,一部の構造体 (レコード型) は,Vista 以降で変更があります.
このサンプルで使用している構造体のメンバーのリストはネット上で検索したものを利用しています.
このサンプルでは数項目しか表示しないようになっていますが,他の値は構造体のメンバから取得することになります.どのメンバーが何に相当するかは,[タスクマネージャー] を起動して,実際に確かめてみることで調査できます.Vista ではタスクマネージャーの表示可能項目は,30 項目もあります.これら全てが NtQuerySystemInformation 関数で求まるわけではありません.他の API 関数を用いて求めることになります.
目的にもよりますが,タスクマネージャーのようなアプリケーションを作成するのであれば,便利な関数ですが,単にプロセスの情報を取得するのであれば,MSDN の説明のように,他の関数を使用した方がいいのかも知れません. |
|
図1
設計時画面 |
|
図2
実行結果例 |
|
図3
Windwos 7 の [タスクマネージャー]
|
リスト1
NtQuerySystemInformation 関数を使用した起動プロセスのリスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
ListView1: TListView;
Panel1: TPanel;
Button1: TButton;
Label1: TLabel;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
procedure ListProcessName;
public
{ Public 宣言 }
end;
//-----------------------------------------------------------------------------
// SYSTEM_PROCESS_INFORMATION構造体の定義関係
// Delphi XEには定義がないので定義する必要あり
// 必要であれば(他のユニットからも利用)別ユニットにした方がいいかも知れない
//-----------------------------------------------------------------------------
_SYSTEM_PROCESS_INFORMATION = packed record
NextEntryOffset : DWORD;
dThreadCount : DWORD;
dReserved01 : DWORD;
dReserved02 : DWORD;
dReserved03 : DWORD;
dReserved04 : DWORD;
dReserved05 : DWORD;
dReserved06 : DWORD;
qCreateTime : LARGE_INTEGER;
qUserTime : LARGE_INTEGER;
qKernelTime : LARGE_INTEGER;
usLength : WORD;
usMaximumLength : WORD;
usName : Pointer;
BasePriority : DWORD;
dUniqueProcessId : DWORD;
dInheritedFromUniqueProcessId : DWORD;
dHandleCount : DWORD;
dReserved07 : DWORD;
dReserved08 : DWORD;
PeakVirtualSize : DWORD;
VirtualSize : DWORD;
PageFaultCount : DWORD;
PeakWorkingSetSize : DWORD;
WorkingSetSize : DWORD;
QuotaPeakPagedPoolUsage : DWORD;
QuotaPagedPoolUsage : DWORD;
QuotaPeakNonPagedPoolUsage : DWORD;
QuotaNonPagedPoolUsage : DWORD;
PagefileUsage : DWORD;
PeakPagefileUsage : DWORD;
dCommitCharge : DWORD;
end;
TSYSTEM_PROCESS_INFORMATION = _SYSTEM_PROCESS_INFORMATION;
PSYSTEM_PROCESS_INFORMATION = ^TSYSTEM_PROCESS_INFORMATION;
//Delphi XEにはこの関数の定義がないので作成
//これはWindowsのdll内関数を利用する1つの方法
function NtQuerySystemInformation(pSystemInformationClass: Cardinal;
pSystemInformation: PVOID;
pSystemInformationLength: ULONG;
pReturnLength: PULONG): Cardinal;
stdcall; external 'ntdll.dll';
const
//NtQuerySystemInformation関数の第1引数の値
//このサンプルでは5しか使用しないが,後の利用のために定義しておく
SystemBasicInformation = 0;
SystemPerformanceInformation = 2;
SystemTimeOfDayInformation = 3;
SystemProcessInformation = 5;
SystemProcessorPerformanceInformation = 8;
SystemInterruptInformation = 23;
SystemExceptionInformation = 33;
SystemRegistryQuotaInformation = 37;
SystemLookasideInformation = 45;
//-----------------------------------------------------------------------------
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// ListViewのカラムは設計時に作成しておく
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
ListProcessName;
end;
//=============================================================================
// [表示更新]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
ListProcessName;
end;
//=============================================================================
// NtQuerySystemInformationを使用して現在起動中のプロセスのリストを取得
// NtQuerySystemInformationの第1引数はSystemProcessInformation(=5)
//
// これ以外の値はLInfoレコード型の各メンバーから取得可能
// どのメンバーが何かは実際に表示して,[タスクマネージャ]と比較して調査可能
// ただし文字列はユニコードなのでDelphi2007以前ではPWideCharでキャストする
//=============================================================================
procedure TForm1.ListProcessName;
var
LInfo : PSYSTEM_PROCESS_INFORMATION;
LBuffer : array of Byte;
LSize : Uint64;
LCount : Integer;
LItem : TListItem;
LTime : Int64;
begin
ListView1.Items.BeginUpdate;
ListView1.Items.Clear;
//情報を格納するバッファサイズの取得
NtQuerySystemInformation(SystemProcessInformation, nil, 0, @LSize);
//情報格納用のバッファサイズを確保
SetLength(LBuffer, LSize);
if (NtQuerySystemInformation(SystemProcessInformation,
LBuffer,
LSize,
nil) = 0) then begin
//ListViewにプロセス名等を表示
LCount := 0;
while True do begin
//Cnt位置からのプロセスの情報をAInfoに取出す
LInfo := @LBuffer[LCount];
LItem := ListView1.Items.Add;
//プロセス名
LItem.Caption := PWideChar(LInfo.usName);
if LItem.Caption ='' then LItem.Caption := 'System Idel Process';
//PID
LItem.SubItems.Add(IntToStr(LInfo.dUniqueProcessId));
//メモリ使用量
LSize := LInfo.WorkingSetSize div 1024;
LItem.SubItems.Add(FormatFloat('###,##0 KB', LSize));
//スレッド
LItem.SubItems.Add(IntToStr(LInfo.dThreadCount));
//CPU時間
//LARGE_INTEGERの定義はWindows.pas参照
//100ns単位を秒に変換
LTime := (LInfo.qUserTime.QuadPart+LInfo.qKernelTime.QuadPart) div
10000000;
LItem.SubItems.Add(Format('%d:%.2d:%.2d',
[LTime div 3600,
(LTime mod 3600) div 60,
LTime mod 60]));
//NextEntryOffsetメンバが0を返せばそれ以上の情報はない
if (LInfo.NextEntryOffset = 0) then Break;
//次のプロセス情報の開始位置へ移動
Inc(LCount, LInfo.NextEntryOffset);
end;
end;
ListView1.Items.EndUpdate;
Label1.Caption := 'プロセス数 : ' + IntToStr(ListView1.Items.Count);
end;
end.
サンプルプログラム [475_パフォーマンスカウンタで CPU 使用率] で JEDI プロジェクトを利用しています.この JEDI プロジェクトの Win32API 関数類には,Delphi では不足している C++ のヘッダファイルに相当するファイルがほとんど含まれています.また,構造体の定義やメンバー名も MSDN オンラインの記述に準拠しています.
これを利用した場合のコードです.非常にスッキリしています. |
|
図4
実行結果例 |
リスト2
JEDI プロジェクトの NtQuerySystemInformation 関数を利用したコード |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
ListView1: TListView;
Panel1: TPanel;
Button1: TButton;
Label1: TLabel;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
procedure ListProcessName;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
JwaWindows;
{$R *.dfm}
//=============================================================================
// ListViewのカラムは設計時に作成しておく
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
ListProcessName;
end;
//=============================================================================
// [表示更新]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
ListProcessName;
end;
//=============================================================================
// NtQuerySystemInformationを使用して現在起動中のプロセスのリストを取得
// NtQuerySystemInformationの第1引数はSystemProcesses(=5)
//
// これ以外の値はLInfoレコード型の各メンバーから取得可能
// どのメンバーが何かは実際に表示して,[タスクマネージャ]と比較して調査可能
// ただし文字列はユニコードなのでDelphi2007以前ではPWideCharでキャストする
//=============================================================================
procedure TForm1.ListProcessName;
var
LInfo : PSystemProcesses;
LBuffer : array of Byte;
LSize : UInt64;
LCount : Integer;
LItem : TListItem;
LTime : Int64;
begin
ListView1.Items.BeginUpdate;
ListView1.Items.Clear;
//情報を格納するバッファサイズの取得
NtQuerySystemInformation(SystemProcessesAndThreadsInformation,
nil,
0,
@LSize);
//情報格納用のバッファサイズを確保
SetLength(LBuffer, LSize);
if (NtQuerySystemInformation(SystemProcessesAndThreadsInformation,
LBuffer,
LSize,
nil) = 0) then begin
//ListViewにプロセス名等を表示
LCount := 0;
while True do begin
//Cnt位置からのプロセスの情報をAInfoに取出す
LInfo := @LBuffer[LCount];
LItem := ListView1.Items.Add;
//プロセス名
LItem.Caption := LInfo.ProcessName.Buffer;
if LItem.Caption = '' then LItem.Caption := 'System Idel Process';
//PID
LItem.SubItems.Add(IntToStr(LInfo.ProcessId));
//メモリ使用量
LSize := LInfo.VmCounters.WorkingSetSize div 1024;
LItem.SubItems.Add(FormatFloat('###,##0 KB', LSize));
//スレッド
LItem.SubItems.Add(IntToStr(LInfo.ThreadCount));
//CPU時間
//LARGE_INTEGERの定義はWindows.pas参照
//100ns単位を秒に変換
LTime := (LInfo.UserTime.QuadPart + LInfo.KernelTime.QuadPart) div
10000000;
LItem.SubItems.Add(Format('%d:%.2d:%.2d',
[LTime div 3600,
(LTime mod 3600) div 60,
LTime mod 60]));
//NextEntryDeltaメンバが0を返せばそれ以上の情報はない
if (LInfo.NextEntryDelta = 0) then Break;
//次のプロセス情報の開始位置へ移動
Inc(LCount, LInfo.NextEntryDelta);
end;
end;
ListView1.Items.EndUpdate;
Label1.Caption := 'プロセス数 : ' + IntToStr(ListView1.Items.Count);
end;
end.
|