Delphi Programming / Object Pascal
[掲載 2009年04月25日] [更新 2016年08月03日] Delphi サンプルプログラム集 |
462_環境変数とそのパス名の取得 |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro |
|
462_EnvironmentList.zip [2,741 KB] 2016年08月03日版 (EXE 同梱) |
Windows の環境変数の値を取得して表示するサンプルです.
Windows の環境変数の値は,環境変数の設定ダイアログで確認することができます.この設定ダイアログは以下のコマンドで表示されたダイアログで,[環境変数] をクリックすると表示できます. |
systempropertiesadvanced.exe |
|
図1
環境変数の編集ダイアログ |
環境変数は,以下のように,コマンドプロセッサの set コマンドを使用して取得できます.また,設定には setx コマンドが使用できます.設定には管理者権限が必要です. |
//環境変数とその値をリスト
set
//環境変数PATHEXTの一覧(省略可能な拡張子の一覧)
set pathext
//環境変数PATHの一覧(パス名の一覧)
set path
//環境変数PATHにパスを追加
setx /M path "%path%;D:\000"
[備考]
特殊フォルダを定義した環境変数を,エクスプローラのアドレスバーに入力すると,その特殊フォルダを表示できます.例えば %APPDATA% を入力すると,C:\Users\<ユーザー名>\AppData\Roaming のフォルダを表示します.
環境変数は [ファイル名を指定して実行] のダイアログのコマンドとしても実行できます.[ファイル名を指定して実行] のダイアログで表示すると,別のエクスプローラで表示されます. |
01_環境変数とパス名の取得 - GetEnvironmentVariable 関数 |
上で説明した Windows の環境変数のリストと,その値を表示するテストプログラムです.現在のプロセスに対応する環境変数のリストを取得して表示します.
このサンプルでは ShellExecuteEx 関数でバッチファイルを実行していますが,バッチファイルの処理終了を待ってから次の処理を行うようになっています.これは,ShellExecuteEx 関数で起動したアプリケーション,プログラムの終了まで待つ,ということです.
この動作にするためには,TShellExecuteInfo の fMask メンバーに SEE_MASK_NOCLOSEPROCESS を含めて,起動したプロセスのハンドルの値を取得するようにします.このハンドルを使用して,オブジェクトの状態を検出します. |
[備考 1]
SET コマンドではなく,Windows API の GetEnvironmentStrings 関数を使用したサンプルコードが以下の記事にあります.
|
[備考 2]
本ページのサンプルは,環境変数の取得ですが,環境変数の設定には SetEnvironmentVariable 関数を使用します.setx コマンドによる設定とは違い,SetEnvironmentVariable 関数で設定した環境変数は,自プロセスだけで有効な値となります.
例えば,PATH の値に新しいパスを追加すると,自アプリで,ディレクトリ名を省略しての操作が可能になります.
|
|
図2
設計時画面 |
|
図3
環境変数のリスト
- IDE 上の [F9] で実行した場合
- BDS, DELPHI, EMBARCA 等が含まれている
|
上の図は IDE 上で実行した場合ですが,デバッグ時に有効な Delphi 専用の環境変数である BDS 等も含まれています.
下図は EXE を直接実行した場合です.BDS 等は除かれています. |
|
図4
環境変数のリスト
- EXE を直接実行した場合
- BDS, DELPHI, EMBARCA 等は含まれていない
|
リスト1
環境変数のリストとそのフルパス等を表示
GetEnvironmentVariable 関数を使用する例 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
ListBox1: TListBox;
procedure FormShow(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
ShellAPI,
StrUtils;
{$R *.dfm}
//=============================================================================
// 現在のユーザの環境変数のリストをリストボックスにリストする
// SETコマンドを実行してリストを一度ファイルに保存している
// このSETコマンドでは,環境変数とそのフルパスが取得できるが,ここでは,テスト
// のため,環境変数だけを取出し,プログラムコード内で使用する形式(左右の端に%
// を付加したもの)を表示する
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
LShellInfo : TShellExecuteInfo;
LBatFullPath : String;
LOutFullPath : String;
LStrList : TStringList;
LEnvString : String;
LiPos : Integer;
i : Integer;
begin
LStrList := TStringList.Create;
LBatFullPath := ExtractFilePath(Application.ExeName) + 'EnvDirList.BAT';
LOutFullPath := ExtractFilePath(Application.ExeName) + 'EnvDirList.TXT';
DeleteFile(LOutFullPath);
//バッチファイルを作成してこのアプリのフォルダに保存
try
LStrList.Add('SET>' + LOutFullPath);
LStrList.Add('DEL %0');
LStrList.Add('EXIT');
LStrList.SaveToFile(LBatFullPath);
finally
FreeAndNil(LStrList);
end;
//バッチファイルを実行
//SEE_MASK_NOCLOSEPROCESSがないと終了待ちはできない
with LShellInfo do begin
cbSize := Sizeof(LShellInfo);
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := Application.Handle;
lpVerb := 'open';
lpFile := PChar(LBatFullPath);
lpParameters := nil;
lpDirectory := nil;
nShow := SW_HIDE;
end;
ShellExecuteEx(@LShellInfo);
//バッチファイルの実行終了を(ファイルが出来るまで)待つ
while WaitForSingleObject(LShellInfo.hProcess, 0) = WAIT_TIMEOUT do begin
Application.ProcessMessages;
Sleep(100);
end;
//リストボックスに環境変数のみを取出して両端に%を付加
ListBox1.Items.Clear;
LStrList := TStringList.Create;
try
LStrList.LoadFromFile(LOutFullPath);
for i := 0 to LStrList.Count - 1 do begin
LEnvString := LStrList[i];
LiPos := Pos('=', LEnvString);
LEnvString := StrUtils.AnsiLeftStr(Trim(LEnvString), LiPos - 1);
ListBox1.Items.Add(SysUtils.AnsiQuotedStr(LEnvString, '%'));
end;
finally
LStrList.Free;
end;
end;
//=============================================================================
// リストボックスで選択した環境変数のフルパスをEdit1に表示
// GetEnvironmentVariable関数を使用
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
LListItem : string;
LPathStr : string;
begin
if ListBox1.ItemIndex >= 0 then begin
LListItem := ListBox1.Items[ListBox1.ItemIndex];
//文字列中の%を取除く
LPathStr := StringReplace(LListItem, '%', '', [rfReplaceAll]);
Edit1.Text := GetEnvironmentVariable(LPathStr);
end else begin
Edit1.Text := '';
end;
end;
end.
02_環境変数とパス名の取得 - ExpandEnvironmentStrings 関数 |
上サンプルでは GetEnvironmentVariable 関数を使用しました.
環境変数の値をフルパスに変換するには,ExpandEnvironmentStrings 関数も使用できます.この関数はサンプルプログラム集の他のサンプルでも使用していますが,それらは文字列の長さを MAX_PATH に固定しています.それは,単一のファイル名を取得するだけだからです.
本ページの場合,環境変数によっては,複数のパス名が含まれていて,MAX_PATH の長さを超える場合があります.そこで,まず,パス名を受け取るのに必要なバッファのサイズを取得して,文字列の長さを設定しています.それから再度 ExpandEnvironmentStrings 関数を実行して,実際のパス文字列を取得するようにしています.
|
リスト2
環境変数のリストとそのフルパス等を表示
ExpandEnvironmentStrings 関数を使用する例 |
//=============================================================================
// リストボックスで選択した環境変数のフルパスをEdit1に表示
// ExpandEnvironmentStrings関数を使用
// 対象の環境変数は%で囲まれた文字列である必要がある
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
LEnvText : String;
LEnvPath : String;
LLen : DWORD;
begin
if ListBox1.ItemIndex >= 0 then begin
LEnvText := ListBox1.Items[ListBox1.ItemIndex];
LLen := ExpandEnvironmentStrings(PChar(LEnvText), nil, 0);
if LLen > 0 then begin
SetLength(LEnvPath, LLen);
ExpandEnvironmentStrings(PChar(LEnvText), PChar(LEnvPath), LLen);
// Trim しないと Null 終端文字が含まれたままとなる
Edit1.Text := Trim(LEnvPath);
end;
end else begin
Edit1.Text := '';
end;
end;
環境変数の %PATH% の実際の値は,複数あります.それらは ; (セミコロン) で区切られています.
これらを個別に取得するサンプルです.
これらのパス (ディレクトリ) 内にあるファイルは,ディレクトリ名を指定しなくても実行したり,開いたり利用できます.つまり「システム的にパスが通っている」場所にあるファイルということになります. |
[備考]
Windows の Update で Delphi 関係のパスが削除されていまったという報告があります.筆者も経験しました.マイクロソフトは Delphi に恨みがあるのかも知れません. |
|
図5
環境変数 %PATH% のリスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
StrUtils;
{$R *.DFM}
//=============================================================================
// 環境変数%PATH%のパスのリスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LEnvStr : string;
LPathList : TStringList;
begin
LEnvStr := 'PATH';
LPathList := TStringList.Create;
try
LPathList.Sorted := True;
//デリミタの設定
LPathList.Delimiter := ';';
LPathList.StrictDelimiter := True;
//環境変数のパスをセット
LPathList.DelimitedText := GetEnvironmentVariable(LEnvStr);
Memo1.Lines := LPathList;
Label1.Caption := IntToStr(LPathList.Count);
finally
FreeAndNil(LPathList);
end;
end;
end.
04_環境変数 %PATH% のパスの長さ制限と回避方法 |
環境変数の PATH の文字列の長さには制限があります.
下記のリンクの記事は,新規に環境変数を追加して,既にあるパス名の一部を新しく追加した変数の文字列で置き換えることにより,PATH 全体の文字列の長さを短くする方法です.
|
05_省略可能な拡張子のリスト - 環境変数 %PATHEXT% |
%PATH% に似た環境変数 %PATHEXT% があります.EXT,つまり拡張子関係の環境変数です.
筆者の Windows 7 の環境では以下のようになっています.コマンドを実行する際,拡張子を省略すると,コマンドにこの拡張子の付加した実行ファイル名を順番に検索して,見つかれば実行する仕組みになっています.なければ実行エラーとなります.
拡張子はそのファイルの種類を表しています.基本的には省略する必要はないでしょう.ネット上に掲載されているコード類でも,ほとんど省略していないようです.
|
|
図6
環境変数 %PATHEXT% のリスト (省略可能な拡張子)
- 優先順位は図のリストの順
- AAA を実行すると,AAA.COM と AAA.EXE の両方が存在する場合,AAA.COM が実行される
|
//=============================================================================
// 環境変数%PATHEXT%のリスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LEnvStr : string;
LPathList : TStringList;
begin
LEnvStr := 'PATHEXT';
LPathList := TStringList.Create;
try
//デリミタの設定
LPathList.Delimiter := ';';
LPathList.StrictDelimiter := True;
//環境変数のパスをセット
LPathList.DelimitedText := GetEnvironmentVariable(LEnvStr);
Memo1.Lines := LPathList;
Label1.Caption := IntToStr(LPathList.Count);
finally
FreeAndNil(LPathList);
end;
end;
06_ディレクトリの指定なしで実行可能なプログラム |
環境変数の %PATH% で設定されているディレクトリ内のファイルは,そのディレクトリ名を省略しても実行可能です.それらは例えば,System32 や SysWOW64 フォルダ内のファイルです.
それ以外にもディレクトリ名を省略して実行できるプログラムがあります.それらは,レジストリの以下の階層下に登録されているファイル名のものです.本サンプルはこれらを列挙します. |
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths |
選択中のファイルをダブルクリックすると起動するようにしてありますが,必ずしも正常に起動できるとは限りません.プログラムやファイルの中には,他のプログラムから使用するような仕様になっていたり,パラメータが必要な場合があります.
また,64 ビットの Windows で 32 ビットのプログラムから実行する場合,リダイレクトの機能が働くため 64 ビットのアプリは起動できません.リダイレクトの処理については以下の最初のリンクの記事を参考にしてください.
|
|
図7
ディレクトリ名を省略しても実行可能なプログラムのリスト |
リスト5
ディレクトリ名を省略しても実行可能なプログラムのリスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
Label1: TLabel;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Registry,
ShellAPI;
{$R *.DFM}
//=============================================================================
// レジストリからパスが通っていなくても実行可能なファイルの一覧を取得
// usesにRegistryが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRegistry : TRegistry;
LRegKey : string;
LItemList : TStringList;
i : Integer;
begin
Edit1.Text := '';
LItemList := TStringList.Create;
LRegistry := TRegistry.Create;
LRegistry.Access := KEY_READ;
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
try
LRegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths';
//FolderDescriptions階層下のキー名(GUID)を列挙
if LRegistry.OpenKey(LRegKey, False) then begin
LRegistry.GetKeyNames(LItemList);
end;
LRegistry.CloseKey;
ListBox1.Sorted := True;
ListBox1.Items.Clear;
ListBox1.Items.BeginUpdate;
for i := 0 to LItemList.Count - 1 do begin
if LowerCase(ExtractFileExt(LItemList[i])) = '.exe' then begin
if LRegistry.OpenKey(LRegKey + PathDelim + LItemList[i], False) then begin
if LRegistry.ReadString('') <> '' then begin
ListBox1.Items.Add(LItemList[i]);
end;
LRegistry.CloseKey;
end;
end;
end;
ListBox1.Items.EndUpdate;
Label1.Caption := IntToStr(ListBox1.Items.Count);
finally
FreeAndNil(LRegistry);
FreeAndNil(LItemList);
end;
end;
//=============================================================================
// ListBox1のOnDblClickイベント処理
// TListBoxの選択中のEXEを実行
//
// usesにShellAPIが必要
//=============================================================================
procedure TForm1.ListBox1DblClick(Sender: TObject);
var
LShellCmd : string;
LRetCode : Cardinal;
begin
if ListBox1.ItemIndex < 0 then exit;
if Edit1.Text <> '' then begin
LShellCmd := ListBox1.Items[ListBox1.ItemIndex];
LRetCode := ShellExecute(Handle, '', PChar(LShellCmd), nil, nil, SW_SHOW);
if LRetCode <=32 then begin
MessageBox(Handle, PChar(SysErrorMessage(LRetCode)), '情報', MB_OK);
end;
end;
end;
//-----------------------------------------------------------------------------
// 短いパス名を長いパス名に変換
//-----------------------------------------------------------------------------
function ChangeShortToLongPath(ShortName: String): String;
var
Buff : array[0..MAX_PATH-1] of Char;
begin
if (GetLongPathName(PChar(ShortName), Buff, Length(Buff))) > 0 then begin
Result := Buff;
end else begin
Result := ShortName;
end;
end;
//=============================================================================
// ListBox1のOnClickイベント処理
// 選択中のApps PathsキーのEXEファイルのフルパスを取得してEdit1に表示
//
// usesにRegistryが必要
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
LRegistry : TRegistry;
LRegKey : string;
LRegItem : string;
begin
Edit1.Text := '';
if ListBox1.ItemIndex < 0 then exit;
LRegistry := TRegistry.Create;
LRegistry.Access := KEY_READ;
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
try
LRegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths'
+ PathDelim + ListBox1.Items[ListBox1.ItemIndex];
if LRegistry.OpenKey(LRegKey, False) then begin
LRegItem := LRegistry.ReadString('');
if LRegItem <> '' then begin
Edit1.Text := ChangeShortToLongPath(LRegItem);
end;
end;
finally
LRegistry.CloseKey;
FreeAndNil(LRegistry);
end;
end;
//=============================================================================
// ListBox1のOnKeyDownイベント処理
//=============================================================================
procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if ListBox1.ItemIndex < 0 then exit;
if Key = VK_RETURN then begin
ListBox1DblClick(nil);
Key := 0;
end;
end;
end.
07_環境変数 $(DELPHI) と $(BDS) の取得 - デバッグ時のみ |
上のサンプルの実行結果のリストに %BDS% があります.これはデバッグ時に実行した場合にのみ取得できる値です.上のサンプルの EXE を直接実行した場合はリストに表示されません.
古い Delphi のバージョンでは %DELPHI% となります.
これらの値の取得コードについては,サンプルプログラム [827_デバッグ中の実行かEXE実行か] に掲載しています.参考にしてください. |
|