2010年12月9日木曜日

プロセスの再起動

仕事で、プロセスを外部から強制的に再起動を
する必要があったのでとりあえずつくってみた。

停止させるプロセスは、仕様上一意性が保障されている
ので、コマンドライン引数に起動するプロセスの絶対パスを
与えて、そこからプロセスIDを求めています。

プロセスに停止メッセージ(メインウインドウのクローズ)を
ポストし、5秒まっても終了していなかったら
強制終了しています。

プロセスの起動には、JvCreateProcessコンポーネントを
使っています。
このコンポーネントは、非常に便利ですね。




program RestartProcess;

{$APPTYPE CONSOLE}

uses
  SysUtils,Windows,TLHELP32,Messages,JvCreateProcess;


function GetProcessFromName(ProcessName :String) : Cardinal;
var
   ProcEntry : TProcessEntry32;
   SanpshotHandle : THandle;
   ListProcName : String;

begin
   //Toolhelp32を使用する例
  Result := 0;
   SanpshotHandle := TlHelp32.CreateToolhelp32Snapshot(TlHelp32.TH32CS_SNAPPROCESS,0);
   if (SanpshotHandle <> -1) then
      begin
         ProcEntry.dwSize := Sizeof(TProcessEntry32W);
         if (TlHelp32.Process32First(SanpshotHandle,ProcEntry)) Then
         begin
            repeat
              ListProcName := ProcEntry.szExeFile;
              if CompareText(ListProcName,ProcessName) = 0 then
              begin
                 Result := ProcEntry.th32ProcessID;
              end;
              //WriteLn(ListProcName);
          until (TlHelp32.Process32Next(SanpshotHandle,ProcEntry) = false);
       end;
    end;
    CloseHandle(SanpshotHandle);

end;


function EnumWindowsProc(hwindow :HWnd; lparam :LPARAM):BOOL; stdcall;
var
  ProcessID : Cardinal;
  ThreadID : Cardinal;
begin

   Result := True;

   ThreadID := GetWindowThreadProcessId(hwindow, ProcessID);

   If (ProcessID = lParam) Then
   begin
      PostMessage(hwindow, WM_CLOSE, 0, 0);
     Result := true;
   End;
End;


function SendClose(ProcID : Cardinal) : Boolean;
begin
   Result := EnumWindows(@EnumWindowsProc, ProcID)
End;

function StopProcess(ProcessName : String; Force : Boolean = false) : Integer;
var
   ProcessID : Cardinal;
  hProcess : THandle;
begin
   ProcessID := GetProcessFromName(ProcessName);
  if ProcessID = 0 then
  begin
     Result := -1;
  end
  else
  begin
     if (ProcessID > 0) Then
     begin
        if Force then
        begin
           hProcess := OpenProcess(PROCESS_TERMINATE, False, ProcessID);
           TerminateProcess(hProcess , 0 );
           CloseHandle(hProcess);
           Result := 0;
        end
        else
        begin
           Result := 1;
           if SendClose(ProcessID) then
           begin
              Result := 0;
           end;
        end;
     end;
  end;
end;

var
   StopResult : Integer;
   JvCreateProcess: TJvCreateProcess;
  ExeName : String;
  ProcessID : Cardinal;

begin
  try
  { TODO -oUser -cConsole Main : ここにコードを記述してください }

     if ParamCount > 0 then
     begin
        ExeName := ExtractFileName(ParamStr(1));
         StopResult := StopProcess(ExeName);

        //五秒まって停止イしたかどうかを確認する
        Sleep(5000);

        ProcessID := GetProcessFromName(ExeName);

        //プロセスが正常に停止できなかったので' +
        //強制終了する
        if ProcessID > 0 Then
        begin
           StopResult := StopProcess(ExeName,true);
           Sleep(10000);
        end;


        if StopResult <> 1 then
        begin
           JvCreateProcess := TJvCreateProcess.Create(nil);
           try
              JvCreateProcess.CommandLine := ParamStr(1);
              JvCreateProcess.WaitForTerminate := false;
              JvCreateProcess.Run;
           finally
              JvCreateProcess.Free;
            end;
        end;
     end;
     //ReadLn;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

2010年11月5日金曜日

StartsStr関数,EndsStr関数

プログラムの作成中にHelpを眺めていてたまたま目についた関数

StartsStrは、ある文字列が指定したサブ文字列で始まるかをチェックする関数
EndsStrは、ある文字列が指定したサブ文字列で終わるるかをチェックする関数
ともに大文字小文字は別物として扱います。

上記のようなチェックを大文字小文字区別しないで行う関数もあり、
それぞれ、StartsText,EndsTexstです。

以下、サンプル

WriteLn('大文字小文字区別して比較');
      if StartsStr('so','So What ?') then
      begin
       WriteLn('はじまるよ')
      end
      else
      begin
       WriteLn('はじまらない')
      end;

      WriteLn('大文字小文字区別なしに比較');
      if StartsText('so','So what ?') then
      begin
       WriteLn('はじまるよ')
      end
      else
      begin
       WriteLn('はじまらない')
      end;


でも、これらの関数っていつからあったのだろうか?
手元の環境で調べたところ少なくともDelphi2007には
あったようだけど

2010年11月3日水曜日

Purgeもどき

仕事で使っているアプリケーションがExeName_YYYYMMDDHHNNSS.Logのような
ログファイルを一杯作るので、消してくれという依頼があった。

ただし、このフォルダには、上記のログを作るファイルがいくつかああって
3世代程度は残してくれという依頼があった。

昔、仕事で触ってたOpenVmsのファイルシステムにはバージョンというのが
あって、古いバージョンのファイルを消すPurgeコマンドがあり
これとにたようなものが欲しいなということで、とりあえず作ってみた。
(本家には似ても似つかぬ、あくまでももどきですが。)

使い方は、

PurgeMock "パージファイルマスク" /D:パージファイルがあるフォルダ /G:残す世代数

で、/R: を指定することで、パージファイルマスクに正規表現が使用できます。

また、ファイルの更新日時を基準に/G:で指定した数だけ残るよう古い日付のものを
削除しますが、 /T:を指定することでファイルの作成日時を基準にPurgeすることが
できます。

なお、パージファイルマスクにより、複数の異なるファイル名のファイルが削除対象
となりえますが、このツールでは、このような場合のことを想定しておりません。
(/G:で指定した数になるようにばっさり消します。)

以下、ソース(delphi Xeがあればコンパイル可能です。)を置いておきます。
ライセンスは、MITライセンスとします。
ソース使用は自由ですが、無保証とします。

まずはメイン。

program PurgeMock;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  PurgeMockImp in 'PurgeMockImp.pas',
  MidasLib
  ;

function CreatePurgeMockParams : TPurgeMockParams;
var
  PurgeMockParams : TPurgeMockParams;
 i : Integer;
begin

  PurgeMockParams := TPurgeMockParams.Create;
  try
    try
   PurgeMockParams.Init(Paramstr(0));
   for i := 1 to ParamCount -1 do
      begin
       PurgeMockParams.SetPurgeParam(Paramstr(i));
      end;
    except
      PurgeMockParams.Free;
      PurgeMockParams := nil;
    end;

  finally
    Result := PurgeMockParams;
  end;

end;

var
  PurgeMockParams : TPurgeMockParams;
  DoPurgeMock     : TPuregMock;
  Ret : Integer;

begin
  try
    { TODO -oUser -cConsole メイン : ここにコードを記述してください }
    PurgeMockParams := CreatePurgeMockParams;
    if Assigned(PurgeMockParams) then
    begin
      DoPurgeMock := TPuregMock.Create(PurgeMockParams);
      try
        Ret := DoPurgeMock.Purge;
      finally
        DoPurgeMock.Free;
      end;
      PurgeMockParams.Free;
    end;


  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


次に、パージコマンドの本体

unit PurgeMockImp;

interface

uses
  SysUtils
 ,DB
  ,DBClient
  ;

type
  TPurgeTimeStamp = (ptCreate, ptUpdate);

  TPurgeMockParams = class
  private
    FTargetDir: String;
    FUseRegularExpression: Boolean;
    FFileMask: String;
    FLeavingGenerations: Integer;
    FPurgeTimeStamp: TPurgeTimeStamp;
    procedure SetTargetDir(val: String);
    procedure SetUseRegularExpression(val: Boolean);
    procedure SetFileMask(val: String);
    procedure SetLeavingGenerations(val: Integer);
    procedure SetPurgeTimeStamp(val: TPurgeTimeStamp);
  public
    property TargetDir: String read FTargetDir write SetTargetDir;
    property UseRegularExpression: Boolean read FUseRegularExpression
      write SetUseRegularExpression;
    property FileMask: String read FFileMask write SetFileMask;
    property LeavingGenerations: Integer read FLeavingGenerations
      write SetLeavingGenerations;
    property PurgeTimeStamp: TPurgeTimeStamp read FPurgeTimeStamp
      write SetPurgeTimeStamp;
    procedure Init(ExePath: String);
  procedure SetPurgeParam(PurgeParam : String);
  end;

  TPuregMock = class
  public
    constructor Create(Params: TPurgeMockParams);
    function Purge: Integer;
    destructor Destroy; override;
  strict private
    FPurgeMockParams: TPurgeMockParams;
    FPurgeMockDataSet : TClientDataSet;
    function MakePurgeFileList: Integer;
    function ExecPurge: Integer;
    function CreatePurgeDataSet : TClientDataSet;
  end;

  function TryParseCmdLineSw(ParameterString : String; Sw : Array of String; var ParamValue : String; IgnoreCase: Boolean = true) : boolean;

implementation

Uses
   StrUtils
  ,IOUtils
  ,Types
  ,RegularExpressionsCore
  ;

procedure TPurgeMockParams.SetTargetDir(val: String);
begin
  FTargetDir := val;
end;

procedure TPurgeMockParams.SetUseRegularExpression(val: Boolean);
begin
  FUseRegularExpression := val;
end;

procedure TPurgeMockParams.Init(ExePath: String);
begin

 FTargetDir := ExtractFileDir(ExePath);
 FUseRegularExpression := false;
 FileMask := '*.*';
 FLeavingGenerations := 1;
 FPurgeTimeStamp := ptUpdate;

end;

procedure TPurgeMockParams.SetFileMask(val: String);
begin
  FFileMask := val
end;

procedure TPurgeMockParams.SetLeavingGenerations(val: Integer);
begin
 if Val >= 0 then
  begin
   FLeavingGenerations := val;
  end
  else
  begin
   FLeavingGenerations := 0;
  end;
end;

procedure TPurgeMockParams.SetPurgeParam(PurgeParam: String);
var
  ValueStr : String;
begin

 if TryParseCmdLineSw(PurgeParam,['/D:','-D:'],ValueStr) then
  begin
   FTargetDir := ValueStr;
    Exit;
  end;

 if TryParseCmdLineSw(PurgeParam,['/U:','-U:'],ValueStr) then
  begin
   FUseRegularExpression := true;
    Exit;
  end;

 if TryParseCmdLineSw(PurgeParam,['/G:','-G:'],ValueStr) then
  begin
   LeavingGenerations := StrToIntDef(ValueStr,1);
    Exit;
  end;

 if TryParseCmdLineSw(PurgeParam,['/T:','-T:'],ValueStr) then
  begin
   FPurgeTimeStamp := ptCreate;
    Exit;
  end;

 if TryParseCmdLineSw(PurgeParam,[],ValueStr) then
  begin
    FFileMask := ValueStr;
    Exit;
  end;

end;

procedure TPurgeMockParams.SetPurgeTimeStamp(val: TPurgeTimeStamp);
begin
  FPurgeTimeStamp := val;
end;

constructor TPuregMock.Create(Params: TPurgeMockParams);
begin
  inherited Create;
  FPurgeMockParams := Params;
end;

function TPuregMock.Purge: Integer;
begin

  Result := -1;

 FPurgeMockDataSet := CreatePurgeDataSet;
  if Assigned(FPurgeMockDataSet) then
  begin
    if MakePurgeFileList > 0 then
    begin
      Result := ExecPurge;
    end;
    FPurgeMockDataSet.EmptyDataSet;
    FPurgeMockDataSet.Active := false;
    FPurgeMockDataSet.Free;
  end;

end;

function TPuregMock.CreatePurgeDataSet: TClientDataSet;
var
 PurgeDataSet : TClientDataSet;
 FD : TFieldDef;
begin
 PurgeDataSet := TClientDataSet.Create(nil);
  try
    try
   FD := PurgeDataSet.FieldDefs.AddFieldDef;
      FD.Name := 'FileName';
      FD.DataType := ftString;
      FD.Size := 1024;

   FD := PurgeDataSet.FieldDefs.AddFieldDef;
      FD.Name := 'TimeStamp';
      FD.DataType := ftDatetime;

      PurgeDataSet.CreateDataSet;
      PurgeDataSet.AddIndex('Purge_Idx','TimeStamp;FileName',[]);
      PurgeDataSet.IndexName := 'Purge_Idx';

    Except
     if Assigned(PurgeDataSet) then PurgeDataSet.Free;
      PurgeDataSet := nil;
    end;
  finally
   Result := PurgeDataSet;
  end;
end;

destructor TPuregMock.Destroy;
begin
  if Assigned(FPurgeMockParams) then
  begin
    FPurgeMockParams := nil;
  end;

  inherited Destroy;
end;

function TPuregMock.MakePurgeFileList: Integer;
var
 SearchOption: TSearchOption;
 SearchDir : TDirectory;
  SearchFileMask : String;
  LList: TStringDynArray;
  FileFullName,FileName : String;
  CanAddList : Boolean;
  PerlRegEx: TPerlRegEx;
  PurgeFile : TFile;

begin

  Result := 0;

  //使っても使わなくても正規表現用のクラスをオープンしておく。
  PerlRegEx:= TPerlRegEx.Create;
  try
    SearchOption := TSearchOption.soTopDirectoryOnly;
    SearchFileMask := FPurgeMockParams.FileMask;


    if FPurgeMockParams.UseRegularExpression then
    begin
      PerlRegEx.RegEx := SearchFileMask;
      SearchFileMask := '*.*';
    end;

    try
      LList := SearchDir.GetFiles(FPurgeMockParams.FTargetDir,SearchFileMask, SearchOption);
      FPurgeMockDataSet.Active := true;
      for FileFullName in LList do
      begin
        CanAddList := true;
        if FPurgeMockParams.UseRegularExpression then
        begin
          FileName := ExtractFileName(FileFullName);
          PerlRegEx.Subject := FileName;
          CanAddList := PerlRegEx.Match;
        end;

        if CanAddList then
        begin
          //パージ対象リストに追加
          FPurgeMockDataSet.Append;
          FPurgeMockDataSet.FieldByName('FileName').AsString := FileFullName;
          if FPurgeMockParams.FPurgeTimeStamp = ptCreate then
          begin
           FPurgeMockDataSet.FieldByName('TimeStamp').AsDateTime := PurgeFile.GetCreationTime(FileFullName);
          end
          else
          begin
           FPurgeMockDataSet.FieldByName('TimeStamp').AsDateTime := PurgeFile.GetLastAccessTime(FileFullName);
          end;
          FPurgeMockDataSet.Post;
        end;
      end;
      Result := FPurgeMockDataSet.RecordCount;

    except
      Result := -1;
    end;
  finally
  if Assigned(PerlRegEx) then PerlRegEx.Free;

  end;
end;

function TPuregMock.ExecPurge: Integer;
var
 DeleteCount : integer;
  DeleteFileName : string;
  PurgeFile : TFile;
begin
  DeleteCount := 0;

  try
    FPurgeMockDataSet.Active := true;
    FPurgeMockDataSet.First;

    while not(FPurgeMockDataSet.Eof) do
    begin
      DeleteFileName := FPurgeMockDataSet.FieldByName('FileName').AsString;
      PurgeFile.Delete(DeleteFileName);
      inc(DeleteCount);

      //レコードをDeleteした場合は、Next不要なので注意
      FPurgeMockDataSet.Delete;
      if FPurgeMockDataSet.RecordCount <= FPurgeMockParams.LeavingGenerations then Break;
    end;
   Result := DeleteCount;

  Except
   Result := -1;
  end;
end;

function TryParseCmdLineSw(ParameterString : String;
                           Sw : Array of String;
                           var ParamValue : String;
                           IgnoreCase: Boolean) : boolean;
var
 i : integer;
begin

 Result := false;

  if Length(Sw) = 0 then
  begin
   //SW指定がない場合はそのままの文字列を返す
   ParamValue := ParameterString;
    Result := true;
  end
  else
  begin
   for i := low(Sw) to high(Sw) do
    begin
      if IgnoreCase then
      begin
        if StartsText(Sw[i],ParameterString) then
        begin
          Result := true;
          ParamValue := ReplaceText(ParameterString,Sw[i],'');
          Break;
        end;
      end
      else
      begin
        if StartsStr(Sw[i],ParameterString) then
        begin
          Result := true;
          ParamValue := ReplaceStr(ParameterString,Sw[i],'');
          Break;
        end;
      end;
    end;
  end;

end;

end.

サービスの起動と停止

現在、進行中の案件で、サービスの再起動を定期的に行う必要が
あるので、Delphiでできるかどうか調べてみた。

Delphi PrismではdotNetFreameworkに標準で用意されたクラスが使えるので
簡単だが、Delphi(Win32)では用意されていないみたいだ。

さらに調べたところ、Jedi Code Library(Jcl)には、サービスを扱うクラス
(TJclSCManager,TJclNTService)が用意されていることを知ったので、
実際に検証してみた。

以下、検証用に作ったサンプル(サービスの列挙と指定したサービスの
停止と起動)です。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    LabeledEdit1: TLabeledEdit;
    Button2: TButton;
    Button3: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
    JclSvcCtrl
  , TypInfo
  ;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  SvcMgr : TJclSCManager;
  i : Integer;
begin
//
  SvcMgr := TJclSCManager.Create();

  try
    SvcMgr.Refresh(true);
    ListBox1.Clear;
    for i := 0 to SvcMgr.ServiceCount -1 do
    begin
      ListBox1.Items.Add(SvcMgr.Services[i].ServiceName);
    end;


  finally
    SvcMgr.Free;
  end;

end;

procedure TForm1.Button3Click(Sender: TObject);
var
  SvcMgr : TJclSCManager;
  Svc : TJclNTService;
begin
//
  //if True then
  SvcMgr := TJclSCManager.Create();
  try
    SvcMgr.Refresh(true);
    if (SvcMgr.FindService(LabeledEdit1.Text,Svc)) Then
    begin
      if TComponent(Sender).Tag = 1 then
      begin
        Svc.Start;
      end
      else
      begin
        Svc.Stop;
      end;
      Label1.Caption := GetEnumName(TypeInfo(TJclServiceState),Ord(Svc.ServiceState)) ;
    end;
  finally
    SvcMgr.Free;
  end;


end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  SvcMgr : TJclSCManager;
  Svc : TJclNTService;
begin
  if ListBox1.ItemIndex >=0 then
  begin
    LabeledEdit1.Text := ListBox1.Items.Strings[ListBox1.ItemIndex];
    SvcMgr := TJclSCManager.Create();
    try
      SvcMgr.Refresh(true);
      if (SvcMgr.FindService(LabeledEdit1.Text,Svc)) Then
      begin
        Label1.Caption := GetEnumName(TypeInfo(TJclServiceState),Ord(Svc.ServiceState));
      end;
    finally
      SvcMgr.Free;
    end;

  end;

end;

end.




なお、jclには上記クラスを使用したサンプルプログラムがありますので詳細については
そちらを参照して下さい。

2010年11月2日火曜日

JvScheduledEventsを試してみる。

仕事で、とあるプロセスを定刻起動する必要があった。

OS標準のタスクスケジューラを使用してもよっかったが
起動できるのがバッチファイルか単独のEXEになるので
もううちょっと処理を柔軟にしたいと思いJVCLの
JvScheduledEventsを試してみた。

TJvScheduledEventsは、画面でスケジューリングの
設定が可能であるが、今回は、スケジュールを外部
ファイルに持たせたかったので、プラグラム中で
設定することにした。

以下、サンプルで試したソース。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, JvScheduledEvents, StdCtrls, ExtCtrls, ComCtrls, JvComponentBase,
  JvCreateProcess;

type
  TForm1 = class(TForm)
    Button1: TButton;
    DateTimePicker1: TDateTimePicker;
    Label1: TLabel;
    LabeledEdit1: TLabeledEdit;

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    FJvScheduledEvents : TJvScheduledEvents;
    procedure JvScheduledEventsExecute(Sender: TJvEventCollectionItem;
              const IsSnoozeEvent: Boolean);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  JclSchedule;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  EventItem : TJvEventCollectionItem;
  IJclSched : JclSchedule.IJclSchedule;
  IDaySched : IJclDailySchedule;
  IDyaFreq : IJclScheduleDayFrequency;

begin
  EventItem := FJvScheduledEvents.Events.Add;
  IJclSched := EventItem.Schedule;

   //イベントアイテムのスケジュール自体は、
   //IJclScheduleで受けますが、実態はTJclScheduleで
   //IJclScheduleのほか
   //IJclScheduleDayFrequency,
   //IJclDailySchedule,
    //IJclWeeklySchedule,
   //IJclMonthlySchedule,
   //IJclYearlySchedule
   //を継承しています。

   IJclSched.RecurringType := srkDaily;

   IDaySched := (IJclSched as IJclDailySchedule);
   if Assigned(IDaySched) then
   begin
      //毎日実行する場合は、EveryWeekDayをFalseにして
      //間隔を1(日)にします。
      IDaySched.EveryWeekDay := false;
      IDaySched.Interval := 1;
   end;

   IDyaFreq := (IJclSched as IJclScheduleDayFrequency);
   if Assigned(IDyaFreq) then
   begin
      IDyaFreq.StartTime := DateTimeToTimeStamp(Self.DateTimePicker1.Time).Time;
      IDyaFreq.EndTime   := IDyaFreq.StartTime;
      IDyaFreq.Interval := 1;
   end;

   EventItem.Name := LabeledEdit1.Text;
   EventItem.OnExecute := JvScheduledEventsExecute;
   EventItem.Start;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FJvScheduledEvents := TJvScheduledEvents.Create(Self);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FJvScheduledEvents.Events.Clear;
  FJvScheduledEvents.Free;
end;

procedure TForm1.JvScheduledEventsExecute(Sender: TJvEventCollectionItem;
  const IsSnoozeEvent: Boolean);
var
  JvCreateProcess: TJvCreateProcess;
begin

  JvCreateProcess := TJvCreateProcess.Create(Self);
  try
    JvCreateProcess.CommandLine := Sender.Name;
    JvCreateProcess.WaitForTerminate := true;
    JvCreateProcess.Run;

  finally
    JvCreateProcess.Free;
  end;


end;

end.


ポイントは、以下の2つかと思います。


  1. JclScheduleをUsesに加えることと
  2. TJvEventCollectionItem.Scheduleの戻り値がIJclSchedule型であるが実態はTJclSchedule型でIJclScheduleのほかIJclScheduleDayFrequency,IJclDailySchedule,IJclWeeklySchedule,IJclMonthlySchedule,
    IJclYearlyScheduleを継承していて設定したいスケジュールにあわせて適切にキャストする必要があること

今回のサンプルは、『ボタンを押したら指定した時刻にメモ帳を起動する』というタイマーで処理しても
十分なものですが、リフレクション、パッケージの動的ロードなどを使えば、もっと面白いことが
できそうな気がします。

2010年9月23日木曜日

SQL Server のデータベースのテーブルとフィールド名を表示する。(その2)

Sql Server 2005以上であれば、システムカタログに対してクエリを発行することで
テーブル名とフィールド名のリストの取得ができます。
(詳細は、システムカタログのクエリのQandAページを参照)

クエリを発行できるということは、





程度の画面であればマスターリンクを使ってノンコーディング
(クエリーは組む必要はありますが)でテーブルとフィールドのリストを表示できます。

このへんがDelphiのすごいところですね。

以下、フォーム表示をテキスト表示したもの


object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 535
  ClientWidth = 727
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object PageControl1: TPageControl
    Left = 0
    Top = 0
    Width = 727
    Height = 535
    ActivePage = TabSheet2
    Align = alClient
    TabOrder = 0
    object TabSheet1: TTabSheet
      Caption = 'TabSheet1'
      object Label1: TLabel
        Left = 192
        Top = 88
        Width = 56
        Height = 13
        Caption = #12501#12451#12540#12523#12489#21517
      end
      object Label2: TLabel
        Left = 16
        Top = 88
        Width = 50
        Height = 13
        Caption = #12486#12540#12502#12523#21517
      end
      object Label3: TLabel
        Left = 552
        Top = 88
        Width = 56
        Height = 13
        Caption = #12501#12451#12540#12523#12489#21517
      end
      object Label4: TLabel
        Left = 376
        Top = 88
        Width = 50
        Height = 13
        Caption = #12486#12540#12502#12523#21517
      end
      object Button1: TButton
        Left = 96
        Top = 16
        Width = 169
        Height = 33
        Caption = 'dbGo'#12398#12513#12477#12483#12489#12434#20351#29992
        TabOrder = 0
        OnClick = Button1Click
      end
      object ListBox1: TListBox
        Left = 16
        Top = 106
        Width = 169
        Height = 401
        ItemHeight = 13
        TabOrder = 1
        OnClick = ListBox1Click
      end
      object ListBox2: TListBox
        Left = 191
        Top = 106
        Width = 169
        Height = 401
        ItemHeight = 13
        TabOrder = 2
      end
      object ListBox3: TListBox
        Left = 376
        Top = 106
        Width = 169
        Height = 401
        ItemHeight = 13
        TabOrder = 3
        OnClick = ListBox3Click
      end
      object ListBox4: TListBox
        Left = 550
        Top = 106
        Width = 169
        Height = 401
        ItemHeight = 13
        TabOrder = 4
      end
      object Button2: TButton
        Left = 472
        Top = 16
        Width = 169
        Height = 33
        Caption = 'DbExpress'#12398#12513#12477#12483#12489#12434#20351#29992
        TabOrder = 5
        OnClick = Button2Click
      end
    end
    object TabSheet2: TTabSheet
      Caption = 'TabSheet2'
      ImageIndex = 1
      object DBGrid1: TDBGrid
        Left = 0
        Top = 0
        Width = 145
        Height = 507
        Align = alLeft
        DataSource = DataSource1
        TabOrder = 0
        TitleFont.Charset = DEFAULT_CHARSET
        TitleFont.Color = clWindowText
        TitleFont.Height = -11
        TitleFont.Name = 'Tahoma'
        TitleFont.Style = []
        Columns = <
          item
            Expanded = False
            FieldName = 'Name'
            Visible = True
          end>
      end
      object DBGrid2: TDBGrid
        Left = 145
        Top = 0
        Width = 160
        Height = 507
        Align = alLeft
        DataSource = DataSource2
        TabOrder = 1
        TitleFont.Charset = DEFAULT_CHARSET
        TitleFont.Color = clWindowText
        TitleFont.Height = -11
        TitleFont.Name = 'Tahoma'
        TitleFont.Style = []
        Columns = <
          item
            Expanded = False
            FieldName = 'NAME'
            Visible = True
          end>
      end
    end
    object TabSheet3: TTabSheet
      Caption = 'TabSheet3'
      ImageIndex = 2
      object DBGrid3: TDBGrid
        Left = 0
        Top = 0
        Width = 177
        Height = 507
        Align = alLeft
        DataSource = DataSource3
        TabOrder = 0
        TitleFont.Charset = DEFAULT_CHARSET
        TitleFont.Color = clWindowText
        TitleFont.Height = -11
        TitleFont.Name = 'Tahoma'
        TitleFont.Style = []
        Columns = <
          item
            Expanded = False
            FieldName = 'Name'
            Visible = True
          end>
      end
      object DBGrid4: TDBGrid
        Left = 177
        Top = 0
        Width = 184
        Height = 507
        Align = alLeft
        DataSource = DataSource4
        TabOrder = 1
        TitleFont.Charset = DEFAULT_CHARSET
        TitleFont.Color = clWindowText
        TitleFont.Height = -11
        TitleFont.Name = 'Tahoma'
        TitleFont.Style = []
        Columns = <
          item
            Expanded = False
            FieldName = 'NAME'
            Visible = True
          end>
      end
    end
  end
  object ADOConnection1: TADOConnection
    Connected = True
    ConnectionString =
      'Provider=SQLNCLI10.1;Integrated Security="";Persist Security Inf' +
      'o=False;User ID=sa;Initial Catalog=AdventureWorks;Data Source=SA' +
      'KANOTE-PC\SqlExpress;Initial File Name="";Server SPN=""'
    LoginPrompt = False
    Provider = 'SQLNCLI10.1'
    Left = 16
    Top = 456
  end
  object SQLConnection1: TSQLConnection
    ConnectionName = 'MSSQLConnection'
    DriverName = 'MSSQL'
    GetDriverFunc = 'getSQLDriverMSSQL'
    LibraryName = 'dbxmss.dll'
    LoginPrompt = False
    Params.Strings = (
      'SchemaOverride=%.dbo'
      'DriverUnit=DBXMSSQL'
    
        'DriverPackageLoader=TDBXDynalinkDriverLoader,DBXCommonDriver150.' +
        'bpl'
    
        'DriverAssemblyLoader=Borland.Data.TDBXDynalinkDriverLoader,Borla' +
        'nd.Data.DbxCommonDriver,Version=15.0.0.0,Culture=neutral,PublicK' +
        'eyToken=91d62ebb5b0d1b1b'
    
        'MetaDataPackageLoader=TDBXMsSqlMetaDataCommandFactory,DbxMSSQLDr' +
        'iver150.bpl'
    
        'MetaDataAssemblyLoader=Borland.Data.TDBXMsSqlMetaDataCommandFact' +
        'ory,Borland.Data.DbxMSSQLDriver,Version=15.0.0.0,Culture=neutral' +
        ',PublicKeyToken=91d62ebb5b0d1b1b'
      'GetDriverFunc=getSQLDriverMSSQL'
      'LibraryName=dbxmss.dll'
      'VendorLib=sqlncli10.dll'
      'MaxBlobSize=-1'
      'OSAuthentication=False'
      'PrepareSQL=True'
      'DriverName=MSSQL'
      'HostName=SAKANOTE-PC\SQLEXPRESS'
      'Database=AdventureWorks'
      'User_Name=sa'
      'Password=sysdba'
      'BlobSize=-1'
      'ErrorResourceFile='
      'LocaleCode=0000'
      'IsolationLevel=ReadCommitted'
      'OS Authentication=False'
      'Prepare SQL=False'
      'ConnectTimeout=60'
      'Mars_Connection=False')
    VendorLib = 'sqlncli10.dll'
    Connected = True
    Left = 464
    Top = 56
  end
  object ADOQuery1: TADOQuery
    Connection = ADOConnection1
    CursorType = ctStatic
    Parameters = <>
    SQL.Strings = (
      'SELECT Object_ID, Name FROM SYS.TABLES')
    Left = 16
    Top = 488
  end
  object DataSetProvider1: TDataSetProvider
    DataSet = ADOQuery1
    Left = 56
    Top = 504
  end
  object ClientDataSet1: TClientDataSet
    Active = True
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider1'
    Left = 88
    Top = 504
  end
  object DataSource1: TDataSource
    DataSet = ClientDataSet1
    Left = 24
    Top = 184
  end
  object ADOQuery2: TADOQuery
    Connection = ADOConnection1
    CursorType = ctStatic
    Parameters = <
      item
        Name = 'Object_ID'
        DataType = ftInteger
        Value = 14623095
      end>
    SQL.Strings = (
      'SELECT Object_ID , NAME FROM sys.columns')
    Left = 144
    Top = 504
  end
  object ClientDataSet2: TClientDataSet
    Active = True
    Aggregates = <>
    IndexFieldNames = 'Object_ID'
    MasterFields = 'Object_ID'
    MasterSource = DataSource1
    PacketRecords = 0
    Params = <>
    ProviderName = 'DataSetProvider2'
    Left = 208
    Top = 504
  end
  object DataSetProvider2: TDataSetProvider
    DataSet = ADOQuery2
    Left = 176
    Top = 504
  end
  object DataSource2: TDataSource
    DataSet = ClientDataSet2
    Left = 96
    Top = 176
  end
  object ClientDataSet3: TClientDataSet
    Active = True
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider3'
    Left = 464
    Top = 112
  end
  object DataSetProvider3: TDataSetProvider
    DataSet = SQLQuery1
    Left = 464
    Top = 168
  end
  object SQLQuery1: TSQLQuery
    MaxBlobSize = -1
    Params = <>
    SQL.Strings = (
      'SELECT Object_ID, Name FROM SYS.TABLES')
    SQLConnection = SQLConnection1
    Left = 464
    Top = 216
  end
  object DataSource3: TDataSource
    DataSet = ClientDataSet3
    Left = 456
    Top = 280
  end
  object DataSetProvider4: TDataSetProvider
    DataSet = SQLQuery2
    Left = 600
    Top = 176
  end
  object ClientDataSet4: TClientDataSet
    Active = True
    Aggregates = <>
    IndexFieldNames = 'Object_ID'
    MasterFields = 'Object_ID'
    MasterSource = DataSource3
    PacketRecords = 0
    Params = <>
    ProviderName = 'DataSetProvider4'
    Left = 592
    Top = 104
  end
  object DataSource4: TDataSource
    DataSet = ClientDataSet4
    Left = 576
    Top = 280
  end
  object SQLQuery2: TSQLQuery
    MaxBlobSize = -1
    Params = <
      item
        DataType = ftInteger
        Name = 'Object_ID'
        ParamType = ptInput
        Value = 14623095
      end>
    SQL.Strings = (
      'SELECT Object_ID , NAME FROM sys.columns')
    SQLConnection = SQLConnection1
    Left = 584
    Top = 240
  end
end

SQL Server のデータベースのテーブルとフィールド名を表示する。(その1)

仕事でSql Serverのテーブルリストを表示する必要があったので・・・

DelphiでSQl Srerverのテーブルリストを表示する方法をいくつか


1. dbGOのConnectionのGetTableNamesメソッドとGetFieldNamesメソッドを利用する。


dbGoのGetTableNamesを利用すればInitial_Catalogで指定したデータベースの
テーブルリストを取得できます。

また、GetFieldNamesを利用すれば、指定したテーブルのフィールドのリストを表示できます。

ソースは、こんな感じ・・・
(ボタンをクリックするとテーブルのリストを表示し、リストの中のテーブルをクリックすると
クリックしたテーブルのリストを表示します。)

procedure TForm1.Button1Click(Sender: TObject);
begin
  ADOConnection1.Connected := true;
  ADOConnection1.GetTableNames(ListBox1.Items,false);
  ADOConnection1.Connected := false;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  TableName : String;
begin
   if ListBox1.Items.Count > 0 then
   begin
      TableName := ListBox1.Items[ListBox1.ItemIndex];
      if Length(TableName) > 0 then
      begin
        ADOConnection1.Connected := true;
        ADOConnection1.GetFieldNames(TableName,ListBox2.Items);
        ADOConnection1.Connected := false;
      end;
   end;
end;

2. DbExpressを利用する。

DbExpressのSQLConnectionにもGetTableNamesメソッドとGetFieldNamesがあるので
dbGOと同様に処理できます。

ただし、試した中では、DbExpressでは、スキーマを指定しないとdboスキーマのテーブル
しか取得しないようなので、GetSchemaNamesでスキーマ名のリストを取得したうえで
スキーマ毎にテーブルを取得する必要がありました。

でソースはこんな感じ。スキーマ名を取得してる関係でちょっと複雑です。

procedure TForm1.Button1Click(Sender: TObject);

procedure TForm1.Button2Click(Sender: TObject);
var
  GetSchemaNames : TStringList;
  TableNames : TStringList;
  i,j : Integer;
begin
  ListBox3.Items.Clear;
  SQLConnection1.Connected := true;
  GetSchemaNames := TStringList.Create;
  try
    SQLConnection1.GetSchemaNames(GetSchemaNames);
    for i := 0 to GetSchemaNames.Count -1 do
    begin
      TableNames := TStringList.Create;
      try
        SQLConnection1.GetTableNames(TableNames,GetSchemaNames.Strings[i],false);
        for j := 0 to TableNames.Count-1 do
        begin
          ListBox3.Items.Add(GetSchemaNames.Strings[i] + '.' + TableNames.Strings[j]);
        end;
      finally
        TableNames.Free;
      end;
    end;
  finally
    GetSchemaNames.Free;
  end;
  SQLConnection1.Connected := false;
end;

procedure TForm1.ListBox3Click(Sender: TObject);
var
  TableName : String;
begin
   if ListBox1.Items.Count > 0 then
   begin
      TableName := ListBox3.Items[ListBox3.ItemIndex];
      if Length(TableName) > 0 then
      begin
        SQLConnection1.Connected := true;
        SQLConnection1.GetFieldNames(TableName,ListBox4.Items);
        SQLConnection1.Connected := false;
      end;

   end;
end;

2010年9月12日日曜日

JoinStringはないの?(その2)

Delフサさんから指摘を受けましたので、オープン配列版のJoinString関数を
作成してみました。(Delフサ様ご指摘ありがとうございます。)

でついでに、文字列の最後にDelemeterを付加するかどうかを指定できる
オーバーロード関数をも作ってみました。

以下、ソースコード

unit VbLikeStringUtil;

interface

uses
  StrUtils,Types;

  function JoinString(AStrings : Array of String; Delemeter: String) : String overload;
  function JoinString(AStrings : Array of String; Delemeter: String; AddLastDelemeter : Boolean) : String overload;


implementation

uses
  SysUtils;


function JoinString(AStrings : Array of String; Delemeter: String) : String overload;
begin
  Result := JoinString(AStrings,Delemeter,false);
end;

function JoinString(AStrings : Array of String; Delemeter: String; AddLastDelemeter : Boolean) : String overload;
var
  i : Longint;
begin

  Result := '';
  if (Length(AStrings) > 0) then
  begin

    //最後の
    for i := low(AStrings) to high(AStrings)-1 do
    begin
      Result := Result + AStrings[i] + Delemeter;
    end;

    //文字配列の最後の要素
    Result := Result + AStrings[high(AStrings)];

    //最後にデリミターを付加する場合
    if AddLastDelemeter then
    begin
      Result := Result + Delemeter;
    end;

  end;
end;


でテストコードはこんな感じ。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  StrUtils,Types, VbLikeStringUtil;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  JoinItems : TStringDynArray;
begin


  SetLength(JoinItems,5);

  JoinItems[0] := 'イージス';
  JoinItems[1] := 'ジャスティス';
  JoinItems[2] := 'ザク';
  JoinItems[3] := 'セイバー';
  JoinItems[4] := 'グフ';

  Assert('イージス|ジャスティス|ザク|セイバー|グフ'=JoinString(JoinItems,'|'));

  Label1.Caption := JoinString(JoinItems,'|');



end;

procedure TForm1.Button2Click(Sender: TObject);
begin

  //空配列の場合は空文字を返す
  Assert(''=JoinString([],'|'));

  //通常の場合
  Assert('ミリアリア|キラ|メイリン|バルトフェルド'
     = JoinString(['ミリアリア','キラ','メイリン','バルトフェルド'],'|'));

  //オプションの第3引数にTrueを指定すると文字列の最後にデリミタを付加します。
  Assert('ミリアリア|キラ|メイリン|バルトフェルド|'
     = JoinString(['ミリアリア','キラ','メイリン','バルトフェルド'],'|',true));

  Label1.Caption := JoinString(['ミリアリア','キラ','メイリン','バルトフェルド'],'|',true);
end;

end.


ちなみに、今回の文字列は、Seedの自分の好きなキャラシリーズです。

2010年9月5日日曜日

JoinStringはないの?

VB6のJoin関数および.Net FrameworkのString.Joinメソッドは、
XEで追加されたほどSplitString使用頻度は多くないがたまに必要に
なることがある。

Help,ソースをざっくり見たところなさそうだったので実装してみた。
(といっても文字列を連結しただけですが・・・)

以下、ソースコード

unit VbLikeStringUtil;

interface

uses
  StrUtils,Types;

  function JoinString(AStrings: TStringDynArray; Delemeter : String) : String;

implementation

uses
  SysUtils;

function JoinString(AStrings: TStringDynArray; Delemeter : String) : String;
var
  i : Longint;
begin

  Result := '';
  for i := low(AStrings) to high(AStrings) do
  begin
    Result := Result + AStrings[i];
    if i < high(AStrings) then Result := Result + Delemeter;
  end;


end;

end.



実際の使用方法のサンプルは、こんな感じ

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  StrUtils,Types, VbLikeStringUtil;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  JoinItems : TStringDynArray;
begin


  SetLength(JoinItems,5);

  JoinItems[0] := 'イージス';
  JoinItems[1] := 'ジャスティス';
  JoinItems[2] := 'ザク';
  JoinItems[3] := 'セイバー';
  JoinItems[4] := 'グフ';


  Label1.Caption := JoinString(JoinItems,'|');


end;

end.

2010年9月3日金曜日

SplitStringを試してみる。

Delphi XEでSplitString関数が追加された。VBにあって、Delphiになかった関数で
個人的には気になってた関数です。(欲しかった関数です。)

で、ちょっとためしてみた。

以下、ソースコード

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  StrUtils,Types;

procedure TForm1.Button1Click(Sender: TObject);
var
  TestString : String;
  SplitResult:TStringDynArray;
begin
  TestString := 'フリーダム,ジャスティス,デストロイ,セイバー';
  SplitResult := SplitString(TestString,',');
  Label1.Caption := TestString;

  for TestString in SplitResult do begin
    ListBox1.Items.Add(TestString);
  end;

end;

end.


Visual Basicライクで非常に簡単に使えます。

内部的には、FindDelimiter関数を使ってDelemeterをみつけ
Copy関数で部分文字列を切り出してます。

これなら、下位バージョンでも実装できそうですね。

ところでFindDelimiter関数は、いつから存在したのでしょうか?
個人的には、Delphiにはまだまだ知らない便利な関数が一杯ありそうです。


2010.09.10 山本隆の開発日記で紹介して頂いたので
調子にのってC++ Builderで書いてみました。
(C++は自信がないので、役に立つかどうかわかりませんが・・・)

//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
 : TForm(Owner)
{

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

 UnicodeString TestString = "イージス|バスター|デュエル|ブリッツ|ストライク";

 LabeledEdit1->Text = TestString;

 TStringDynArray SplitResult = SplitString(TestString,"|");

 for (int i = 0; i < SplitResult.Length; i ++) {
  ListBox1->Items->Add(SplitResult[i]);
 }

 SplitResult.set_length(0);

}
//---------------------------------------------------------------------------

2010年5月3日月曜日

SameValue関数

StackOverFlowのTopicのトピックを見ていてDelphiのMathユニットに

SameValue関数なる関数が用意されていることを初めて知りました。

この関数は、指定した値が、2つの値がEpsilonで指定した値以内に
あれば、等しいと見なす関数です。

DelphiというかVCL(RTLも含むには)便利な比較関数が用意されています。

今まで、都度都度、自作していたかと思うとちょっと反省。

2010年4月17日土曜日

プロセスリストを表示する。

プロセスの一覧を表示するサンプルです。

Toolhelp32(DelphではTlHelp32)が使える環境では
比較的簡単ですが、

procedure TForm1.Button2Click(Sender: TObject);
var
 ProcEntry : TProcessEntry32W;
   SanpshotHandle : THandle;
begin
 //Toolhelp32を使用する例
   SanpshotHandle := TlHelp32.CreateToolhelp32Snapshot(TlHelp32.TH32CS_SNAPPROCESS,0);
   if (SanpshotHandle <> -1) then
   begin
    ListBox1.Items.Clear;
    ProcEntry.dwSize := Sizeof(TProcessEntry32W);
      if (TlHelp32.Process32First(SanpshotHandle, ProcEntry)) Then
      begin
         repeat
          ListBox1.Items.Add(ProcEntry.szExeFile);
         until (TlHelp32.Process32Next(SanpshotHandle,ProcEntry) = false);
      end;
   end;
   CloseHandle(SanpshotHandle);

end;



使えない環境(といいても、4.0以下のNTだけですが・・・)だと
大変です。



procedure TForm1.Button1Click(Sender: TObject);
var
    cb : Cardinal;
   elements : Cardinal;
   Needs : Cardinal;
   ProcIdArray : Array of DWORD;
   Win32Ret : LongBool;
   i : Cardinal;
   ProcHandle : THandle;
   OpenMode : THandle;
   ProcessName : String;

begin

    //プロセス数がいくつあるか解らないので大きめにとっておく
   elements := 128;
   Needs := elements * Sizeof(DWORD);
   cb := 0;
   while (cb <= Needs) do
   begin

        SetLength(ProcIdArray, elements);
       cb := Length(ProcIdArray) * Sizeof(DWORD);
       Needs := 0;
      Win32Ret := PsApi.EnumProcesses(PDWORD(ProcIdArray),cb,Needs);

      //APIが失敗したら抜ける
      if (not(Win32Ret)) then
      begin
         break;
      end;

      //領域が足りなかったときに備えて倍にする。
      elements := elements * 2;

   end;

   if (Win32Ret) then
   begin
       ListBox1.Items.Clear;
      OpenMode := Windows.PROCESS_QUERY_INFORMATION or Windows.PROCESS_VM_READ;
        elements := Needs div Sizeof(DWORD);
       for i := 0 to elements - 1 do
       begin
          //プロセスIDから情報をえる
         ProcHandle := Windows.OpenProcess(OpenMode,FALSE,ProcIdArray[i]);
         if (ProcHandle <> 0) then
         begin
             ProcessName := GetProcessName(ProcHandle);
            if (Length(ProcessName) > 0) then
            begin
                ListBox1.Items.Add(ProcessName);
            end;
         end;
         Windows.CloseHandle(ProcHandle);
      end;
   end;

end;

function TForm1.GetProcessName(ProcessHandle: THandle): String;
var
    cb : Cardinal;
   elements : Cardinal;
   Needs : Cardinal;
   ModuleHandleArray : Array of THandle;
   Win32Ret : LongBool;
   i : longint;
   ModuleHandle: THandle;
   ModuleName : WideString;
   ModeleNameLength : Integer;
   ProcessName : String;
   FileExt : String;
begin

    Result := '';
    //モジュール数数がいくつあるか解らないので大きめにとっておく
   elements := 128;
   Needs := elements * Sizeof(DWORD);
   cb := 0;
   while (cb <= Needs) do
   begin

        SetLength(ModuleHandleArray, elements);
       cb := Length(ModuleHandleArray) * Sizeof(DWORD);
       Needs := 0;
      Win32Ret := PsApi.EnumProcessModules(ProcessHandle,PDWORD(ModuleHandleArray),cb,Needs);

      //APIが失敗したら抜ける
      if (not(Win32Ret)) then
      begin
         break;
      end;
      //領域が足りなかったときに備えて倍にする。
      elements := elements * 2;
   end;

   if (Win32Ret) then
   begin
       ModeleNameLength := 255;
      SetLength(ModuleName,ModeleNameLength);
        elements := Needs div Sizeof(DWORD);
       for i := 0 to elements - 1 do
       begin
          ModuleHandle := ModuleHandleArray[i];
          ModeleNameLength := PsApi.GetModuleBaseName(
                                  ProcessHandle,
                                ModuleHandle,
                                PWideChar(ModuleName),
                                ModeleNameLength);
         if (ModeleNameLength > 0) then
         begin
             //モジュール名がExeファイルであればプロセスとみなす。
             SetLength(ModuleName,ModeleNameLength);
            ProcessName := ModuleName;
            FileExt := ExtractFileExt(ProcessName);
            if (CompareText(FileExt,'.EXE') = 0)  then
            begin
                Result :=  ProcessName;
               break;
            end;
         end;
      end;
   end;
end;