Delphi Programming / Object Pascal
[掲載 2008年01月13日] [更新 2018年10月15日] Delphi サンプルプログラム集
000_コマンドラインコンパイラの実行
動作確認等
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32, VCL-64
000_CommandLineCompile.zip [3,133 KB] 2018年10月15日版 (EXE 同梱)
2018年10月15日 (これより前の履歴は省略)
インストールされている Delphi のバージョンを自動で検出するようにした
このサンプルプログラムは,当サイトで提供しているサンプルプログラムの動作確認用に作成したものです.Delphiの IDE を起動しないでコンパイルが可能かを確認することができます.
以下の様な仕様となっています.
起動すると,現在のシステムにインストールされている Delphi のバージョンを検出
各 Delphi のバージョンのライブラリパスも自動検出してライブラリパスを設定
指定したフォルダ内のサブフォルダ内も含めてプロジェクト全てをコンパイルする
フォルダまたはプロジェクトファイルのドラッグ・アンド・ドロップにも対応
リソースファイル (.res ファイル等) がないプロジェクトはコンパイルできない
インクルードファイルは,それが必要なファイルと同じフォルダにないと機能しない
Delphi XE2 以降では,ユニットスコープ名がないとコンパイル不可の場合がある
コンパイルができても,作成した EXE が正常に動作するとは限りません.プロジェクトを作成したバージョンによっては,他のバージョンではコンパイルできないこともあります.
[備考]
本ページのサンプルでは,コマンドラインコンパイラとして,dcc32.exe または dcc64.exe を使用しています.dcc32.exe も dcc64.exe も .dpr, dproj どちらのプロジェクトファイルでもコンパイルできますが,本ページのサンプルでは .dpr だけを使用する仕様になっています.
MSBuild を使用してコンパイルする場合は,プロジェクトファイルとして .dproj
を使用します..dpr ではコンパイルできません.
[フォルダ指定] ボタンをクリックして,フォルダを指定すると,そのフォルダ内のプロジェクト全てをコンパイルします.手順は以下の図の説明を参考にしてください.プロジェクトファイルのドラッグ・アンド・ドロップにも対応しています
このプログラムを,Windows Vista 以降で実行する場合は,管理者権限 [管理者として実行] する必要があります.そこで,アプリケーションマニフェストを使用しています.EXE 起動時に,下図のよう管理者への昇格確認のダイアログが表示されます.
図1
管理者権限で実行
図3
指定したフォルダ内 (サブフォルダ内も全て含む) を Delphi XE でコンパイル中
タブを切り替えると,別バージョンの Delphi でのコンパイルとなる
リソースファイルがないプロジェクトは,コンパイル不可
図4
必要であればライブラリパスを追加
Delphi の IDE で設定したパスも追加可能
プロジェクトフォルダの 1 つ上のフォルダは,実行時に自動で検索パスに追加される
この設定は本アプリのみで有効で,Delphi のバージョンごとに設定を保存
実際にパスが存在するかのチェック機能はない
リスト1
指定フォルダ内の全プロジェクトをコンパイル
リソースファイルがないプロジェクトは,コンパイル不可
copy code
unit CommandLineCompileUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, ExtCtrls, AppEvnts, Generics.Collections,
Generics.Defaults;
type
TDelphiVerInfo = record
ProductVer : Double;
AppFullPath : string ;
RegKeyBase : string ;
VersionStr : string ;
Enable64bit : Boolean;
end ;
TForm1 = class (TForm)
TabControl1: TTabControl;
Panel1: TPanel;
btnFolder: TButton;
btnCancel: TButton;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
btnEditLibPath: TButton;
CheckBox3: TCheckBox;
CheckBox4: TCheckBox;
Memo1: TMemo;
Panel3: TPanel;
Label1: TLabel;
ProgressBar1: TProgressBar;
OpenDialog1: TOpenDialog;
ApplicationEvents1: TApplicationEvents;
btnClearConfig: TButton;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure btnEditLibPathClick(Sender: TObject);
procedure btnFolderClick(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure TabControl1Change(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
procedure btnClearConfigClick(Sender: TObject);
private
{ Private 宣言 }
FCancelFlag : Boolean;
procedure GetdprFilesListAll(APath: string ; FileList:TStringList);
function CreateCommandLineStr(dccPath: string ; ProjName: string ): string ;
procedure ControlsEnabled(WC: TWinControl; Flag : Boolean);
function ComplieProject(dccPath: string ; FileList: TStringList): Integer;
public
{ Public 宣言 }
FIniFileName : string ;
procedure SetDefaultLibPath(TabName, ConfigFileName: string ; SL: TStringList);
end ;
procedure GetDelphiVerList(AList: TList<TDelphiVerInfo>);
function Is64bitWindows: Boolean;
var
Form1: TForm1;
implementation
uses
IniFiles,
Registry,
FileCtrl,
Types,
IOUtils,
ShlObj,
ShellAPI,
StrUtils,
EditLibraryPath,
plCommandRedirect;
// ChangeWindowMessageFilter関数用の定数
const
MSGFLT_ADD = 1 ;
MSGFLT_REMOVE = 2 ;
WM_COPYGLOBALDATA = $0049 ;
{$R *.dfm}
// =============================================================================
// 初期化処理
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
LFuncFilter : function (msg: Cardinal; dwFlag: DWORD): BOOL; stdcall ;
LDLLHandle : THandle;
LIniFile : TIniFile;
LFormLeft : Integer;
LDelphiList : TList<TDelphiVerInfo>;
LDelphiInfo : TDelphiVerInfo;
LIndex : Integer;
LAppKeyLib : string ;
LdccexePath : string ;
LIniTabName : string ;
LTabName : string ;
begin
// ---------------------------------------------------------------------------
// ユーザーインターフェイス特権の分離 (UIPI)対策
// 低い権限で実行したアプリ(エクスプローラ等)からのドラッグ&ドロップを受付
// Windows Vista以降用
// 関数ChangeWindowMessageFilterは,Windows XP以前にはないのでGetProcAddress
// で確認する必要がある
// ---------------------------------------------------------------------------
if CheckWin32Version(6 , 0 ) then begin
LDLLHandle := loadLibrary('user32.dll' );
try
if LDLLHandle > 0 then begin
@LFuncFilter := GetProcAddress(LDLLHandle, 'ChangeWindowMessageFilter' );
if @LFuncFilter <> nil then begin
LFuncFilter(WM_DROPFILES, MSGFLT_ADD);
LFuncFilter(WM_COPYDATA, MSGFLT_ADD);
LFuncFilter(WM_COPYGLOBALDATA, MSGFLT_ADD);
end ;
end ;
finally
FreeLibrary(LDLLHandle);
end ;
end ;
btnCancel.Enabled := False;
Label1.Caption := '' ;
ProgressBar1.Visible := False;
Memo1.ReadOnly := True;
Memo1.Lines.Clear;
FIniFileName := ChangeFileExt(Application.ExeName, '.ini' );
LIniFile := TIniFile.Create(FIniFileName);
try
LFormLeft := 0 ;
LTabName := '' ;
if FileExists(FIniFileName) then begin
LIniTabName := LIniFile.ReadString ('Common' , 'TabName' , '' );
LFormLeft := LIniFile.ReadInteger('Common' , 'FormLeft' , 0 );
Self.Top := LIniFile.ReadInteger('Common' , 'FormTop' , Top);
Self.Width := LIniFile.ReadInteger('Common' , 'FormWidth' , Width);
Self.Height := LIniFile.ReadInteger('Common' , 'FormHeight' , Height);
CheckBox1.Checked := LIniFile.ReadBool('Common' , 'Checkbox1' , False);
CheckBox2.Checked := LIniFile.ReadBool('Common' , 'Checkbox2' , False);
CheckBox3.Checked := LIniFile.ReadBool('Common' , 'Checkbox3' , True);
CheckBox4.Checked := LIniFile.ReadBool('Common' , 'Checkbox4' , True);
end ;
finally
FreeAndNil(LIniFile);
end ;
if LFormLeft = 0 then begin
Self.Position := poMainFormCenter;
end else begin
Self.Position := poDesigned;
Self.Left := LFormLeft;
end ;
if TabControl1.Tabs.Count = 0 then begin
// Delphiの各バージョンの情報を取得
// 取得した情報は内部で確保してもいいのだが参考のためにIniファイルに保存
// この設定をIniファイルから読み込んで使用
LDelphiList := TList<TDelphiVerInfo>.Create;
GetDelphiVerList(LDelphiList);
LIniFile := TIniFile.Create(FIniFileName);
try
TabControl1.TabHeight := 23 ;
TabControl1.TabWidth := 120 ;
for LDelphiInfo in LDelphiList do begin
LdccexePath := ExtractFilePath(LDelphiInfo.AppFullPath);
LAppKeyLib := LDelphiInfo.RegKeyBase + '\Library' ;
if LDelphiInfo.Enable64bit then begin
LTabName := LDelphiInfo.VersionStr + ' (32)' ;
TabControl1.Tabs.Add(LTabName);
LIniFile.WriteString(LTabName, 'dcc32exePath' , LdccexePath + 'dcc32.exe' );
LIniFile.WriteString(LTabName, 'AppKeyLib' , LAppKeyLib + '\Win32' );
LIniFile.WriteFloat(LTabName, 'ProductVer' , LDelphiInfo.ProductVer);
if Is64bitWindows then begin
LTabName := LDelphiInfo.VersionStr + ' (64)' ;
TabControl1.Tabs.Add(LTabName);
LIniFile.WriteString(LTabName, 'dcc32exePath' , LdccexePath + 'dcc64.exe' );
LIniFile.WriteString(LTabName, 'AppKeyLib' , LAppKeyLib + '\Win64' );
LIniFile.WriteFloat(LTabName, 'ProductVer' , LDelphiInfo.ProductVer);
end ;
end else begin
LTabName := LDelphiInfo.VersionStr;
TabControl1.Tabs.Add(LTabName);
LIniFile.WriteString(LTabName, 'dcc32exePath' , LdccexePath + 'dcc32.exe' );
LIniFile.WriteString(LTabName, 'AppKeyLib' , LAppKeyLib);
LIniFile.WriteFloat(LTabName, 'ProductVer' , LDelphiInfo.ProductVer);
end ;
end ;
finally
FreeAndNil(LIniFile);
FreeAndNil(LDelphiList);
end ;
end ;
if TabControl1.Tabs.Count = 0 then begin
ControlsEnabled(Panel1, False);
end else begin
LIndex := TabControl1.Tabs.IndexOf(LIniTabName);
if LIndex < 0 then LIndex := 0 ;
TabControl1.TabIndex := LIndex;
TabControl1Change(nil );
// ドラッグアンドドロップを許可
// usesにShellAPIが必要
DragAcceptFiles(Memo1.Handle, True);
DragAcceptFiles(Panel1.Handle, True);
end ;
end ;
// =============================================================================
// フォームを閉じる時は,フォームの位置とサイズをiniファイルに保存
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
LIniFile : TIniFile;
LTabName : string ;
LStrList : TStringList;
LIndex : Integer;
begin
if TabControl1.Tabs.Count = 0 then begin
LTabName := '' ;
end else begin
LTabName := TabControl1.Tabs[TabControl1.TabIndex];
end ;
LIniFile := TIniFile.Create(FIniFileName);
LStrList := TStringList.Create;
try
// IniファイルのCommon以外のセクションを消去
LIniFile.ReadSections(LStrList);
for LIndex := LStrList.Count - 1 downto 0 do begin
if LStrList[LIndex] = 'Common' then continue;
LIniFile.EraseSection(LStrList[LIndex]);
end ;
LIniFile.WriteString ('Common' , 'TabName' , LTabName);
LIniFile.WriteInteger('Common' , 'FormTop' , Self.Top);
LIniFile.WriteInteger('Common' , 'FormLeft' , Self.Left);
LIniFile.WriteInteger('Common' , 'FormWidth' , Self.Width);
LIniFile.WriteInteger('Common' , 'FormHeight' , Self.Height);
finally
FreeAndNil(LIniFile);
FreeAndNil(LStrList);
end ;
end ;
// =============================================================================
// [パス編集]
// コンパイルに使用するライブラリのパスの編集
// ここで設定しない場合は,$(DELPHI)Libまたは$(BDS)のdcc32.cfgの設定のみを使
// 用することになる
// =============================================================================
procedure TForm1.btnEditLibPathClick(Sender: TObject);
begin
Form2 := TForm2.Create(Self);
try
Form2.ShowModal;
Update;
finally
FreeAndNil(Form2);
end ;
end ;
// =============================================================================
// [フォルダ指定]
// フォルダを選択してコンパイル開始
// コマンドラインの実行には,サンプルプログラム集の[552_コマンドラインのリダイ
// レクト]で作成したユニットCommandRedirectを使用
//
// 選択したフォルダ内のdprファイルを検索してコンパイルする
// コンパイルのオプションはCreateCommandLineStrメソッドで作成
// リソース(.res)がないプロジェクトはコンパイルできない
// =============================================================================
procedure TForm1.btnFolderClick(Sender: TObject);
var
LFileDlg : TFileOpenDialog;
LIniFile : TIniFile;
LTabName : string ;
LBasePath : string ;
LdccexePath : string ;
LFileList : TStringList;
begin
LIniFile := TIniFile.Create(FIniFileName);
LTabName := TabControl1.Tabs[TabControl1.TabIndex];
// 前回終了時のフォルダを初期フォルダに
LBasePath := '' ;
LdccexePath := '' ;
try
if FileExists(FIniFileName) then begin
LBasePath := LIniFile.ReadString('Common' , 'BasePath' , '' );
if LIniFile.SectionExists(LTabName) then begin
LdccexePath := LIniFile.ReadString(LTabName, 'dcc32exePath' , '' );
end ;
end ;
if LBasePath = '' then begin
LBasePath := ExtractFileDir(Application.ExeName);
end ;
if not FileExists(LdccexePath) then begin
ShowMessage('コマンドラインコンパイラが見つかりません.' );
end else begin
// Windows Vista以上の場合
if CheckWin32Version(6 , 0 ) then begin
LFileDlg := TFileOpenDialog.Create(Self);
try
LFileDlg.Options := [fdoPickFolders, fdoForceFileSystem];
LFileDlg.DefaultFolder := LBasePath;
LFileDlg.FileName := LBasePath;
if LFileDlg.Execute then begin
Update;
LBasePath := LFileDlg.FileName;
LFileList := TStringList.Create;
try
GetdprFilesListAll(IncludeTrailingPathDelimiter(LBasePath), LFileList);
ComplieProject(LdccexePath, LFileList)
finally
FreeAndNil(LFileList);
end ;
end ;
finally
FreeAndNil(LFileDlg);
end ;
end else begin
if FileCtrl.SelectDirectory('対象フォルダ指定' , '' , LBasePath) then begin
LIniFile.WriteString('Common' , 'BasePath' , LBasePath);
LFileList := TStringList.Create;
try
GetdprFilesListAll(IncludeTrailingPathDelimiter(LBasePath), LFileList);
ComplieProject(LdccexePath, LFileList)
finally
FreeAndNil(LFileList);
end ;
end ;
end ;
end ;
LIniFile.WriteString('Common' , 'BasePath' , LBasePath);
finally
FreeAndNil(LIniFile);
end ;
end ;
// =============================================================================
// 各Delphiのバージョンのライブラリハスを格納したファイルを削除
// =============================================================================
procedure TForm1.btnClearConfigClick(Sender: TObject);
var
LDir : string ;
LFileList : TStringDynArray;
LCFGFile : string ;
LTabName : string ;
LIndex : Integer;
begin
LTabName := TabControl1.Tabs[TabControl1.TabIndex];
LDir := ExtractFileDir(Application.ExeName);
LFileList := TDirectory.GetFiles(LDir, '*.CFG' , TSearchOption.soTopDirectoryOnly);
for LIndex := 0 to Length(LFileList) - 1 do begin
LCFGFile := LFileList[LIndex];
if ContainsText(LCFGFile, LTabName) then Continue;
if FileExists(LCFGFile) then begin
DeleteFile(LCFGFile);
end ;
end ;
end ;
// =============================================================================
// エクスプローラからのファイルのドラッグ・ドロップ処理
// =============================================================================
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
var
LCount : Integer;
LBuffer : array [0 ..MAX_PATH - 1 ] of Char;
LPath : string ;
LIniFile : TIniFile;
LTabName : string ;
LdccexePath : string ;
LFileList : TStringList;
LCmdExeName : WideString;
LCmdLine : TplCommandRedirect;
LFileCount : Integer;
LCheckFlag : Boolean;
LIndex : Integer;
begin
LCmdExeName := '' ;
if Msg.message =WM_DROPFILES then begin
LCheckFlag := CheckBox1.Checked;
if (FindDragTarget(Msg.pt, False) = Memo1) or
(FindDragTarget(Msg.pt, False) = Panel1) then begin
LFileList := TStringList.Create;
LFileList.Sorted := True;
Memo1.Clear;
Memo1.Update;
try
try
LCount := DragQueryFile(Msg.wParam, High(DWORD), nil , 0 );
LFileCount := 0 ;
for LIndex := 0 to LCount - 1 do begin
DragQueryFile(Msg.WParam, LIndex, LBuffer, MAX_PATH);
LPath := LBuffer;
if UpperCase(ExtractFileExt(LPath)) = '.DPR' then begin
// dprのドラッグアンドドロップの時はEXEがあってもコンパイルする
CheckBox1.Checked := False;
LFileList.Add(LPath);
end else
if UpperCase(ExtractFileExt(LPath)) = '.EXE' then begin
// CreataeProcessのStartupInfoのdwFlagsにSTARTF_USESHOWWINDOWを指定
// しているのでcmd.exeが必要
LCmdExeName := 'cmd.exe /c ' + LPath;
inc(LFileCount);
end else
if SysUtils.DirectoryExists(LPath) then begin
GetdprFilesListAll(IncludeTrailingPathDelimiter(LPath), LFileList);
end ;
end ;
finally
// ドロップが完了したことを伝える
DragFinish(msg.WParam);
end ;
if LCmdExeName <> '' then begin
if LFileCount > 1 then begin
ShowMessage('EXEファイルは1つしか実行できません.' );
end else begin
Screen.Cursor := crHourGlass;
LCmdLine := TplCommandRedirect.Create;
try
Memo1.Text := LCmdLine.GrabStdOutText(LCmdExeName);
finally
LCmdLine.Free;
Screen.Cursor := crDefault;
end ;
end ;
end else begin ;
LIniFile := TIniFile.Create(FIniFileName);
LTabName := TabControl1.Tabs[TabControl1.TabIndex];
LdccexePath := '' ;
try
if FileExists(FIniFileName) then begin
if LIniFile.SectionExists(LTabName) then begin
LdccexePath := LIniFile.ReadString(LTabName,'dcc32exePath' ,'' );
end ;
end ;
if not FileExists(LdccexePath) then begin
ShowMessage('コマンドラインコンパイラが見つかりません.' );
end else begin
ComplieProject(LdccexePath, LFileList)
end ;
finally
FreeAndNil(LIniFile);
end ;
end ;
finally
FreeAndNil(LFileList);
end ;
end ;
CheckBox1.Checked := LCheckFlag;
end ;
end ;
// -----------------------------------------------------------------------------
// APath内のdprファイルを,サブフォルダ内も含めて再帰的に検索
// フルパスで取得し引数のFileListに格納
// CheckBox1をチェックしている場合はexe作成済みのプロジェクトを除外
// -----------------------------------------------------------------------------
procedure TForm1.GetdprFilesListAll(APath: string ; FileList: TStringList);
var
LFileList : TStringDynArray;
LexeName : string ;
LLoopCnt : Integer;
begin
LFileList := TDirectory.GetFiles(APath, '*.dpr' , TSearchOption.soAllDirectories);
for LLoopCnt := 0 to Length(LFileList) - 1 do begin
if CheckBox1.Checked then begin
LexeName := ChangeFileExt(LFileList[LLoopCnt], '.exe' );
if not FileExists(LexeName) then begin
FileList.Add(LFileList[LLoopCnt]);
end ;
end else begin
FileList.Add(LFileList[LLoopCnt]);
end ;
end ;
end ;
// -----------------------------------------------------------------------------
// コンパイル実行のメソッド
//
// パイプを使用してコンパイル実行メッセージを読み出すようにしている
//
// [引数]
// dccPath コマンドコンパイラのフルパス
// FlieList コンパイル対象のdprファイルのリスト
// -----------------------------------------------------------------------------
function TForm1.ComplieProject(dccPath: string ; FileList: TStringList): Integer;
var
LProjName : string ;
LOptionStr : string ;
LCmdLine : TplCommandRedirect;
LCmdStr : string ;
LLoopCnt : Integer;
LStrList : TStringList;
begin
Screen.Cursor := crHourGlass;
Result := 0 ;
ControlsEnabled(Panel1, False);
btnCancel.Enabled := True;
Memo1.Clear;
Update;
FCancelFlag := False;
Label1.Caption := IntToStr(0 ) + '/' + IntToStr(FileList.Count);
ProgressBar1.Position := 0 ;
ProgressBar1.Visible := True;
LCmdLine := TplCommandRedirect.Create;
LStrList := TStringList.Create;
try
ProgressBar1.Min := 0 ;
ProgressBar1.Max := FileList.Count;
for LLoopCnt := 0 to FileList.Count - 1 do begin
LOptionStr := CreateCommandLineStr(dccPath, FileList[LLoopCnt]);
LProjName := AnsiQuotedStr(FileList[LLoopCnt], '"' );
// -Uオプションで対象プロジェクトのフォルダを指定しているがうまく働か
// ないプロジェクトがあるので,ここでカレントディレクトリを変更してみた
// 現在のところOKのようである
SetCurrentDir(ExtractFileDir(FileList[LLoopCnt]));
// エラーが発生しないとファイル名を表示しないので追加
Memo1.Lines.Add(FileList[LLoopCnt]);
// プロジェクトをコマンドラインコンパイラでコンパイル
LCmdStr := dccPath + ' ' + LProjName + '' + LOptionStr;
LStrList.Text := LCmdLine.GrabStdOutText(LCmdStr);
if Trim(LStrList.Text) = '' then LStrList.Text := 'コンパイルに失敗しました' ;
Memo1.Lines.Add(LStrList.Text);
Memo1.Lines.Add(sLineBreak);
ProgressBar1.Position := LLoopCnt + 1 ;
Label1.Caption := IntToStr(LLoopCnt + 1 ) + '/' + IntToStr(FileList.Count);
ProgressBar1.Update;
Label1.Update;
Application.ProcessMessages;
if FCancelFlag then break;
end ;
finally
FreeAndNil(LCmdLine);
FreeAndNil(LStrList);
// カレントディレクトリをこのフォルダに戻す
SetCurrentDir(ExtractFileDir(Application.ExeName));
ControlsEnabled(Panel1,True);
btnCancel.Enabled := False;
end ;
ProgressBar1.Visible := False;
Screen.Cursor := crDefault;
end ;
// -----------------------------------------------------------------------------
// コンパイルオプションの文字列を作成する関数
// コマンドコンパイラ(dcc32.exe)は以下の順番にコンパイルオプションの設定を読む
// 設定はオーバライドして使用される
// このメソッドで作成しているのは(4)
// 引数のProjNameがプロジェクトのフルパス(dprファイル)
// 検索(ライブラリ)パスとして,コンパイル対象のファイルがある1つ上の階層のフォ
// ルダも追加している
//
// (1) $(DELPHI)Binまたは$(BDS)Binのdcc32.cfg
// (2) カレントディレクトリのdcc32.cfg
// (3) コンパイル対象プロジェクトのディレクトリのdcc32.cfg
// (4) コマンドラインで指定したオプション文字列
//
// コンパイルオプション(Pipeによる出力にも影響する)
// -A ユニットのエイリアス設定.
// -B 再構築.DLLやリソースファイルの修正をプロジェクトのEXEに反映するにため
// には再構築が必要.
// -U ライブラリパスの指定.複数の時は;(セミコロン)で区切る.
// IDEの[ツール][環境オプション]の[ライブラリ]タブに相当するが,コマンドラ
// インコンパイラの場合は,複数のユニットを使用する場合,そのプロジェクト
// のフォルダも指定する必要がある.
// -E 作成したEXEファイルの置き場所.これを指定しなくても.dprと同じフォルダに
// EXEファイルができるが,このオプションを指定しないと,DLLがカレントディ
// レクトリ(このComplieAllProjet)にできてしまう.
// -R リソースファイル検索パス.
// -H ヒントメッセージの表示(Pipeに出力する).
// -W 警告メッセージの出力(Pipeに出力する)
// -Q Quietモード.コンパイル中のファイル名と行番号の表示をしない.起動時の著
// 作権メッセージと終了時のコンパイル結果の情報だけを表示.エラーが生じた場
// 合も表示(ここで表示とはPipeに出力するという意味).
// -----------------------------------------------------------------------------
function TForm1.CreateCommandLineStr(dccPath: string ; ProjName: string ): string ;
var
LTabName : string ;
LConfilFileName : string ;
LIniFile : TIniFile;
LProductVer : Double;
LStrList : TStringList;
LStr : string ;
LProjDir : string ;
LLoopCnt : Integer;
begin
LProjDir := ExtractFileDir(ProjName);
LStrList := TStringList.Create;
LTabName := TabControl1.Tabs[TabControl1.TabIndex];
LConfilFileName := ExtractFilePath(Application.ExeName)
+ 'LibPath' + LTabName + '.CFG' ;
try
LStrList.LoadFromFile(LConfilFileName);
// ヒントと警告は対象ユニットで表示オプションがある時は無効
// ChekBox2は再構築の指定
if CheckBox2.Checked then LStr := LStr + ' -B' ;
// ChekBox3はヒント表示の指定
if CheckBox3.Checked then LStr := LStr + ' -H+' else LStr := LStr + ' -H-' ;
// ChekBox4は警告表示の指定
if CheckBox4.Checked then LStr := LStr + ' -W+' else LStr := LStr + ' -W-' ;
LStr := LStr
+ ' -Q'
+ ' -E' + AnsiQuotedStr(LProjDir, '"' )
+ ' -R' + AnsiQuotedStr(LProjDir, '"' );
// Delphi XE2以降の場合
// ユニットスコープ名(Delphi XE2のプロジェクトオプションからコピーしたリスト)
// これだけでは不足しているかも知れない
LIniFile := TIniFile.Create(FIniFileName);
try
LProductVer := LIniFile.ReadFloat(LTabName, 'ProductVer' , 0 );
if LProductVer >= 15.99 then begin
LStr := LStr + ' -NS'
+ 'Winapi'
+ ';System.Win'
+ ';Data.Win'
+ ';Datasnap.Win'
+ ';Web.Win;'
+ ';Soap.Win'
+ ';Xml.Win'
+ ';Bde'
+ ';System'
+ ';Xml'
+ ';Data'
+ ';Datasnap'
+ ';Web'
+ ';Soap'
+ ';Vcl'
+ ';Vcl.Imaging'
+ ';Vcl.Touch'
+ ';Vcl.Samples'
+ ';Vcl.Shell' ;
end ;
finally
FreeAndNil(LIniFile);
end ;
// ライブラリパスの追加
LStr := LStr + ' -U' ;
for LLoopCnt := 0 to LStrList.Count - 1 do begin
if LLoopCnt = 0 then begin
LStr := LStr + AnsiQuotedStr(LStrList[LLoopCnt], '"' );
end else begin
LStr := LStr + ';' + AnsiQuotedStr(LStrList[LLoopCnt], '"' );
end ;
end ;
// 対象プロジェクトとその1つ上の階層のフォルダを検索対象として追加
Result := LStr
+ ';' + AnsiQuotedStr(LProjDir, '"' )
+ ';' + AnsiQuotedStr(ExtractFileDir(LProjDir), '"' );
finally
FreeAndNil(LStrList);
end ;
end ;
// =============================================================================
// [キャンセル]
// =============================================================================
procedure TForm1.btnCancelClick(Sender: TObject);
begin
FCancelFlag := True;
end ;
// -----------------------------------------------------------------------------
// 引数のWCで指定するコントロール上のコントロールのEnabledプロパティを設定
// TPanelの上に載っているコントロールはTPanelをDisalbeにすれば操作不可になる
// が視覚的効果がないので,ここで設定
// -----------------------------------------------------------------------------
procedure TForm1.ControlsEnabled(WC: TWinControl; Flag: Boolean);
var
LControl : TControl;
LIndex : Integer;
begin
if WC.ControlCount = 0 then exit;
for LIndex := 0 to WC.ControlCount - 1 do begin
LControl := WC.Controls[LIndex];
LControl.Enabled := Flag;
if LControl is TWinControl then ControlsEnabled(TWinControl(LControl),Flag);
end ;
end ;
// =============================================================================
// タブコントロールのタブを切り替えたら
// =============================================================================
procedure TForm1.TabControl1Change(Sender: TObject);
var
LIniFile : TIniFile;
LConfigFile : string ;
LStrList : TStringList;
LTabName : string ;
begin
LIniFile := TIniFile.Create(FIniFileName);
LTabName := TabControl1.Tabs[TabControl1.TabIndex];
LConfigFile := 'LibPath' + LTabName + '.CFG' ;
try
LIniFile.WriteString('Common' , 'TabName' , LTabName);
finally
FreeAndNil(LIniFile);
end ;
// ライブラリパスの設定ファイルがなければ調査して保存
// 初めてタブを切り換えた場合のみ実行する
if not FileExists(LConfigFile) then begin
LStrList := TStringList.Create;
try
// ライブラリパスの検出
SetDefaultLibPath(LTabName, LConfigFile, LStrList);
LStrList.SaveToFile(LConfigFile);
finally
FreeAndNil(LStrList);
end ;
end ;
end ;
// =============================================================================
// コンパイルの条件設定用のチェックボックスの状態を変更したら
// それをIniファイルに保存
// =============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
var
LIniFile : TIniFile;
begin
LIniFile := TIniFile.Create(FIniFileName);
try
LIniFile.WriteBool('Common' , 'Checkbox1' , CheckBox1.Checked);
LIniFile.WriteBool('Common' , 'Checkbox2' , CheckBox2.Checked);
LIniFile.WriteBool('Common' , 'Checkbox3' , CheckBox3.Checked);
LIniFile.WriteBool('Common' , 'Checkbox4' , CheckBox4.Checked);
finally
FreeAndNil(LIniFile);
end ;
end ;
// -----------------------------------------------------------------------------
// DelphiのIDEで設定してあるライブラリパスを追加
// ライブラリパスの取得方法がわからないのでレジストリから取得
// 重複するパスの文字列は追加しない
// ただし$(DELPHI)や$(BDS)等は別文字列として扱っている
//
// 例えばDelphi7の場合,$(DELPHI)とC:\Program Files\Borland\Delphi7\binは実際
// には同じパスを意味しているが,ここでは重複してしまう
// -----------------------------------------------------------------------------
procedure TForm1.SetDefaultLibPath(TabName, ConfigFileName: string ;
SL: TStringList);
var
LIniFile : TIniFile;
LReg : TRegistry;
LAppKey : string ;
LAppItem : string ;
begin
// IniファイルからKEY値を読み出す
// ルートキーがHKEY_CURRENT_USERと仮定して,EXE起動時にKEY項目を取得すみ
LIniFile := TIniFile.Create(FIniFileName);
try
LAppKey := LIniFile.ReadString(TabName, 'AppKeyLib' , '' );
finally
FreeAndNil(LIniFile);
end ;
LReg := TRegistry.Create;
try
LReg.Access := KEY_READ;
LReg.RootKey := HKEY_CURRENT_USER;
LAppItem := 'Search Path' ;
SL.Sorted := True;
SL.Duplicates := dupIgnore;
SL.CaseSensitive := False;
if LReg.OpenKey(LAppKey, False) then begin
try
SL.Delimiter := ';' ;
SL.StrictDelimiter := True;
SL.DelimitedText := LReg.ReadString(LAppItem);
finally
LReg.CloseKey;
end ;
end ;
finally
FreeAndNil(LReg);
end ;
end ;
// -----------------------------------------------------------------------------
// インストール済みDelphiの各バージョンの情報をジェネリックスのリストに格納
// リストに格納するのはDephiの各バージョンの情報を格納するレコード型
// -----------------------------------------------------------------------------
procedure GetDelphiVerList(AList: TList<TDelphiVerInfo>);
procedure InitializeRecord(var AInfo: TDelphiVerInfo);
begin
AInfo.ProductVer := 0.0 ;
AInfo.AppFullPath := 'Empty' ;
AInfo.RegKeyBase := 'Empty' ;
AInfo.VersionStr := 'Empty' ;
AInfo.Enable64bit := False;
end ;
const
LArrayBDS : array [0 ..2 ] of string
= ('\Borland\BDS' , '\CodeGear\BDS' , '\Embarcadero\BDS' );
var
LRegistry : TRegistry;
LKeyBDS : string ;
LKeyEeachVer : string ;
LKeyNameList : TStringList;
LKeyItemList : TStringList;
LAppInfo : TDelphiVerInfo;
LTempValue : string ;
LLoopCnt : Integer;
LLoopBDS : Integer;
begin
AList.Clear;
LRegistry := TRegistry.Create;
LKeyNameList := TStringList.Create;
LKeyItemList := TStringList.Create;
try
LRegistry.Access := KEY_READ;
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
// -------------------------------------------------------------------------
// Delphi 2 - 7
// Delphi 4 までは最初の項目がDelphi のバージョン文字列
// Delphi 5 からは項目名の App がEXEのフルパス
// -------------------------------------------------------------------------
LKeyBDS := 'Software\Borland\Delphi' ;
if LRegistry.OpenKey(LKeyBDS, False) then begin
// 現在のキーの階層下のキー名の一覧を取得
// このキーがDelphiの各バージョンごとのキー
LRegistry.GetKeyNames(LKeyNameList);
for LLoopCnt := 0 to LKeyNameList.Count - 1 do begin
LKeyEeachVer := LKeyBDS + PathDelim + LKeyNameList[LLoopCnt];
LRegistry.CloseKey;
InitializeRecord(LAppInfo);
if LRegistry.OpenKey(LKeyEeachVer, False) then begin
LAppInfo.ProductVer := StrToFloat(LKeyNameList[LLoopCnt]);
if LAppInfo.ProductVer < 5.0 then begin
// 現在のDelphiのキー名の各項目名(名前:レジストリエントリ)一覧を取得
LRegistry.GetValueNames(LKeyItemList);
if LKeyItemList.Count < 1 then Continue;
LAppInfo.AppFullPath := LRegistry.ReadString(LKeyItemList[0 ]);
end else begin
if not LRegistry.ValueExists('App' ) then Continue;
LAppInfo.AppFullPath := LRegistry.ReadString('App' );
end ;
// Delphi のバージョン文字列の作成
// 小数点以下が 0 の時は整数部だけとする
LTempValue := Trim(LKeyNameList[LLoopCnt]);
if RightStr(LTempValue, 2 ) = '.0' then begin
LTempValue := LeftStr(LTempValue, 1 );
end ;
LAppInfo.VersionStr := 'Delphi ' + LTempValue;
LAppInfo.RegKeyBase := LKeyEeachVer;
// ジェネリックスのリストにレコード型のデータを追加
AList.Add(LAppInfo);
end ;
end ;
end ;
LRegistry.CloseKey;
// -------------------------------------------------------------------------
// Delphi 2005 以降
// -------------------------------------------------------------------------
for LLoopBDS := 0 to Length(LArrayBDS) - 1 do begin
LKeyBDS := 'Software' + LArrayBDS[LLoopBDS];
if LRegistry.OpenKey(LKeyBDS, False) then begin
// 現在のキーの階層下のキー名の一覧を取得
// このキーがDelphiの各バージョンごとのキー
LRegistry.GetKeyNames(LKeyNameList);
for LLoopCnt := 0 to LKeyNameList.Count - 1 do begin
LKeyEeachVer := LKeyBDS + PathDelim + LKeyNameList[LLoopCnt];
LRegistry.CloseKey;
InitializeRecord(LAppInfo);
if LRegistry.OpenKey(LKeyEeachVer, False) then begin
if not LRegistry.ValueExists('App' ) then Continue;
LAppInfo.AppFullPath := LRegistry.ReadString('App' );
if LRegistry.ValueExists('ProductVersion' ) then begin
LAppInfo.ProductVer := StrToFloat(LRegistry.ReadString('ProductVersion' ));
end ;
LRegistry.CloseKey;
// Delphi XE,Delphi XE5,..等の文字列を取得
if LRegistry.KeyExists(LKeyEeachVer + '\Personalities' ) then begin
if LRegistry.OpenKey(LKeyEeachVer + '\Personalities' , False) then begin
if LRegistry.ValueExists('Delphi.Win32' ) then begin
LAppInfo.VersionStr := LRegistry.ReadString('Delphi.Win32' );
end ;
end ;
end ;
LAppInfo.RegKeyBase := LKeyEeachVer;
// 64 ビットの開発が可能かを調査
if ContainsText(LKeyBDS, 'Embarcadero\BDS' ) then begin
LRegistry.CloseKey;
if LRegistry.KeyExists(LKeyEeachVer + '\Library\Win64' ) then begin
// 64ビット用のWindowsアプリが開発可能な場合はTrue
LAppInfo.Enable64bit := True;
end else begin
LAppInfo.Enable64bit := False;
end ;
end ;
AList.Add(LAppInfo);
end ;
end ;
end ;
LRegistry.CloseKey;
end ;
// ---------------------------------------------------------
// プロダクトバージョンの値でリストをソート
// TComparer関係の使用にはusesにGenerics.Defaultsが必要
// ---------------------------------------------------------
AList.Sort(TComparer<TDelphiVerInfo>.Construct(
function (const Left, Right: TDelphiVerInfo): Integer
begin
Result := Trunc((Left.ProductVer - Right.ProductVer) * 10 );
end )
);
finally
FreeAndNil(LRegistry);
FreeAndNil(LKeyNameList);
FreeAndNil(LKeyItemList);
end ;
end ;
// -----------------------------------------------------------------------------
// 32ビットのWindowsか64ビットのWindowsかを調べる関数
// 64ビット版のWindowsの場合はTrueを返す
// -----------------------------------------------------------------------------
function Is64bitWindows: Boolean;
var
Wow64Proc : function (hProcess: THandle; var Wow64: BOOL): BOOL stdcall ;
RetFlag : LongBool;
begin
@Wow64Proc := GetProcAddress(GetModuleHandle('Kernel32.dll' ), 'IsWow64Process' );
if @Wow64Proc <> nil then begin
Wow64Proc(GetCurrentProcess, RetFlag);
if SizeOf(THandle) = 4 then begin
Result := RetFlag;
end else
if SizeOf(THandle) = 8 then begin
Result := True;
end ;
end else begin
// Windows XPの64ビット版か
Result := CheckWin32Version(5 , 2 );
end ;
end ;
end .
copy code
unit EditLibraryPath;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls, AppEvnts;
type
TForm2 = class (TForm)
Panel1: TPanel;
btnAdd: TButton;
btnDelete: TButton;
Panel2: TPanel;
btnAddDelphiPath: TButton;
ListView1: TListView;
ApplicationEvents1: TApplicationEvents;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure btnAddClick(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
procedure btnAddDelphiPathClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
FIniFilename : string ;
FConfigFile : string ;
FTabName : string ;
procedure AddOriginLibPath(APath: string );
public
{ Public 宣言 }
end ;
var
Form2: TForm2;
implementation
uses
FileCtrl,
IniFiles,
ShellAPI,
CommandLineCompileUnit;
{$R *.dfm}
// =============================================================================
// フォーム生成時の処理
// iniファイルとライブラリバスを保存しているファイルから読み出して表示
// =============================================================================
procedure TForm2.FormCreate(Sender: TObject);
var
LIniFile : TIniFile;
LFormLeft : Integer;
LStrList : TStringList;
LLoopCnt : Integer;
begin
btnDelete.Enabled := False;
FIniFilename := Form1.FIniFileName;
FTabName := Form1.TabControl1.Tabs[Form1.TabControl1.TabIndex];
FConfigFile := 'LibPath' + FTabName + '.CFG' ;
Self.Caption := Self.Caption + ' ' + FTabName;
LFormLeft := 0 ;
LIniFile := TIniFile.Create(FIniFileName);
try
if FileExists(FIniFileName) then begin
LFormLeft := LIniFile.ReadInteger('Common' , 'EditFormLeft' , 0 );
Self.Top := LIniFile.ReadInteger('Common' , 'EditFormTop' , Top);
Self.Width := LIniFile.ReadInteger('Common' , 'EditFormWidth' , Width);
Self.Height := LIniFile.ReadInteger('Common' , 'EditFormHeight' , Height);
end ;
finally
FreeAndNil(LIniFile);
end ;
if LFormLeft = 0 then begin
Self.Position := poMainFormCenter;
end else begin
Self.Position := poDesigned;
Self.Left := LFormLeft;
end ;
ListView1.Items.Clear;
LStrList := TStringList.Create;
LStrList.Sorted := True;
LStrList.Duplicates := dupIgnore;
try
if FileExists(FConfigFile) then begin
LStrList.LoadFromFile(FConfigFile);
for LLoopCnt := 0 to LStrList.Count - 1 do begin
if Trim(LStrList[LLoopCnt]) <> '' then begin
ListView1.Items.Add.Caption := Trim(LStrList[LLoopCnt]);
end ;
end ;
end else begin
// Delphiパス取込
btnAddDelphiPathClick(nil );
end ;
finally
FreeAndNil(LStrList);
end ;
// ドラッグアンドドロップを許可
// usesにShellAPIが必要
DragAcceptFiles(ListView1.Handle, True);
end ;
// =============================================================================
// フォームを閉じる時は現在の設定値を保存
// 保存するのは,ライブラリパス名とこのフォームの表示位置とサイズ
// =============================================================================
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
var
LStrList : TStringList;
LIniFile : TIniFile;
LStr : string ;
LIndex : Integer;
begin
LStrList := TStringList.Create;
try
for LIndex := 0 to ListView1.Items.Count - 1 do begin
LStr := Trim(ListView1.Items[LIndex].Caption);
if LStr <> '' then LStrList.Add(LStr);
end ;
LStrList.SaveToFile(FConfigFile);
finally
FreeAndNil(LStrList);
end ;
LIniFile := TIniFile.Create(FIniFileName);
try
LIniFile.WriteInteger('Common' , 'EditFormTop' , Self.Top);
LIniFile.WriteInteger('Common' , 'EditFormLeft' , Self.Left);
LIniFile.WriteInteger('Common' , 'EditFormWidth' , Self.Width);
LIniFile.WriteInteger('Common' , 'EditFormHeight' , Self.Height);
finally
FreeAndNil(LIniFile);
end ;
end ;
// =============================================================================
// フォームをリサイズしたらListViewの表示幅も変更
// =============================================================================
procedure TForm2.FormResize(Sender: TObject);
begin
ListView1.Column[0 ].Width := ListView1.ClientWidth - GetSystemMetrics(SM_CXHSCROLL);
end ;
// =============================================================================
// [追加]
// フォルダ選択ダイアログを表示して選択
// 初期フォルダは,Iniファイルに保存してある値を使用
// それがなければこのプログラムがあるフォルダ
// フォルダを選択したらそのフォルダ名をIniファイルに記録しておく
// =============================================================================
procedure TForm2.btnAddClick(Sender: TObject);
var
LIniFile : TIniFile;
LBasePath : string ;
begin
LIniFile := TIniFile.Create(FIniFileName);
try
if FileExists(FIniFileName)then begin
LBasePath := LIniFile.ReadString('Common' , 'EditBasePath' , '' );
end else begin
LBasePath := ExtractFileDir(Application.ExeName);
end ;
if SelectDirectory('ライブラリパス追加' , '' , LBasePath) then begin
AddOriginLibPath(ExcludeTrailingPathDelimiter(LBasePath));
LIniFile.WriteString('Common' , 'EditBasePath' , LBasePath);
end ;
finally
FreeAndNil(LIniFile);
end ;
btnDelete.Enabled := False;
end ;
// =============================================================================
// [削除]
// 現在選択中のパスの設定を削除
// =============================================================================
procedure TForm2.btnDeleteClick(Sender: TObject);
begin
ListView1.DeleteSelected;
end ;
// =============================================================================
// [Delphiパス取込]
// DelphiのIDEで設定してあるライブラリパスを追加
// =============================================================================
procedure TForm2.btnAddDelphiPathClick(Sender: TObject);
var
LStrList : TStringList;
LStrListAdd : TStringList;
LStr : string ;
LIndex : Integer;
begin
ListView1.Items.BeginUpdate;
ListView1.Items.Clear;
LStrList := TStringList.Create;
LStrListAdd := TStringList.Create;
LStrList.Sorted := True;
LStrList.Duplicates := dupIgnore;
for LIndex := 0 to ListView1.Items.Count -1 do begin
LStr := Trim(ListView1.Items[LIndex].Caption);
if LStr <> '' then LStrList.Add(LStr);
end ;
try
Form1.SetDefaultLibPath(FTabName, FConfigFile, LStrListAdd);
for LIndex := 0 to LStrListAdd.Count - 1 do begin
LStr := Trim(LStrListAdd[LIndex]);
if LStr <> '' then LStrList.Add(LStrListAdd[LIndex]);
end ;
for LIndex := 0 to LStrList.Count - 1 do begin
ListView1.Items.Add.Caption := LStrList[LIndex];
end ;
finally
FreeAndNil(LStrList);
FreeAndNil(LStrListAdd);
end ;
ListView1.Items.EndUpdate;
btnDelete.Enabled := False;
end ;
// =============================================================================
// リストビューをクリックしたら,[削除]ボタンのEnabledを設定
// =============================================================================
procedure TForm2.ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
begin
btnDelete.Enabled := ListView1.SelCount > 0 ;
end ;
// =============================================================================
// エクスプローラからのファイルのドラッグ・ドロップ処理
// ただし1つだけの場合のみ有効
// =============================================================================
procedure TForm2.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
var
LCount : Integer;
LBuffer : array [0 ..MAX_PATH] of Char;
LDragPath : string ;
begin
if Msg.message = WM_DROPFILES then begin
if (FindDragTarget(Msg.pt, False) = ListView1) then begin
LCount := DragQueryFile(Msg.wParam, High(DWORD), nil , 0 );
try
if LCount = 1 then begin
LDragPath := '' ;
DragQueryFile(Msg.WParam, 0 , LBuffer, MAX_PATH);
LDragPath := LBuffer;
AddOriginLibPath(LDragPath);
end ;
finally
// ドロップが完了したことを伝える
DragFinish(msg.WParam);
end ;
end ;
end ;
end ;
// -----------------------------------------------------------------------------
// ライブラリパスに,引数のAPathを追加
// 重複しないための処理用関数
// -----------------------------------------------------------------------------
procedure TForm2.AddOriginLibPath(APath: string );
var
LStrList : TStringList;
LStr : string ;
LIndex : Integer;
begin
// 重複を避けるためにパスのリストを一度TStringListに 退避
LStrList := TStringList.Create;
LStrList.Sorted := True;
LStrList.Duplicates := dupIgnore;
for LIndex := 0 to ListView1.Items.Count -1 do begin
LStr := Trim(ListView1.Items[LIndex].Caption);
if LStr <> '' then LStrList.Add(LStr);
end ;
// TStringListに退避したのでTListViewはクリア
ListView1.Items.BeginUpdate;
ListView1.Items.Clear;
try
if DirectoryExists(APath) then begin
LStrList.Add(APath);
end ;
// 作成したパスリストをTListViewに表示
for LIndex := 0 to LStrList.Count - 1 do begin
ListView1.Items.Add.Caption := LStrList[LIndex];
end ;
finally
FreeAndNil(LStrList);
end ;
ListView1.Items.EndUpdate;
btnDelete.Enabled := False;
end ;
end .
02_ コンポーネントをインストールしないで動作確認
コマンドラインコンパイラでコンパイルすると,コンパイル対象のプロジェクトで使用しているコンポーネントをインストールしなくてもコンパイルできます.
コンポーネントのファイルは,パスの通ったフォルダに必要ですが,これは [パスの編集] で追加しておきます.添付のテスト用のプロジェクトでは 1 つ上の階層のフォルダにあります.この場合はパスを通す必要はありません.プロジェクトのフォルトの 1 つ上のフォルダも,実行時に自動でパスを通すようにしています.
ネット上で配布されているコンポーネント類のデモ,サンプルのプログラムには,リソースファイルがないものがあります.そのようなプロジェクトはコンパイルできません.
添付のテスト用プロジェクトは,[140_ オブジェクトリスト ( クラス型 ) の保存 - TLabel] で使用している,画像表示機能付き縦横両用ラベルコンポーネントをフォームに配置しています.
図5
プロジェクトファイル (dpr) のドラッグ・アンド・ドロップでコンパイル開始
図6
添付のテスト用プロジェクト
図7
コンパイルした結果
図8
EXE を実行
リスト3
テスト用のプロジェクトのフォームのユニット
TplVertLabel というビジュアルコンポーネントを3つフォームに配置している
Delphi XE2 でもコンパイルできるように,uses にユニットスコープ名を付けている
copy code
unit CompoTestUnit;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, plVertLabel;
{$ELSE}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plVertLabel;
{$IFEND}
type
TForm1 = class (TForm)
plVertLabel1: TplVertLabel;
plVertLabel2: TplVertLabel;
plVertLabel3: TplVertLabel;
private
{ Private declarations }
public
{ Public declarations }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
end .
03_ uses 部にユニットスコープ名を追加 ( Delphi XE2 以上用 )
uses 部のユニット名に,ユニットスコープ名を付けるプログラムを作成してみました.
Delphi XE2 よりも前のバージョンのコードを変換するのに利用できます.Embarcadero のサイトのデータを使用しますので.以下の手順で操作してください.
まず,Delphi のオンラインヘルプの [ユニット名とユニット スコープ名のアルファベット順一覧] のページを表示します
(1) そのページのリファレンス一覧をコピーします (=のある行を全て)
(2) 添付の UnitScopeList.txt を開きます.このファイルの中身は空です
(3) この UnitScopeList.txt にコピーしたテキストを貼り付けて,保存します
(4) AddUnitScopeUses.exe を起動します
(5) 拡張子が .pas または .dpr のファイルを,エクスプローラからドラッグ・ドロップします
(6) 変換結果が表示されます.これで OK なら [保存] ボタンで保存します (上書き保存です)
プロジェクトのソース (.dpr) は,Forms という部分だけにユニットスコープ名を付加します.逆にユニットスコープ名付きだけの uses 部の場合は,ユニットスコープ名なしのユニット名を,コンパイラのバージョン分岐を挿入して追加します.
なお,このプログラムのソースコードは添付していません.整備中です.
このプログラムは,特に管理者権限 [管理者として実行] する必要はありません.
図9
ユニット名にユニットスコープ名を付加するプログラム
[保存] ボタンで上書き保存
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class (TForm)
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
end .
04_ ドラッグ・アンド・ドロップ機能の実装 ( Vista 以降の UIPI
対応 )
エクスプローラ等からのドラッグ・アンド・ドロップは,WM_DROPFILES メッセージを受信した後,関数 DragQueryFile で処理が可能です.
しかし,Windows Vista 以降では,ユーザーインタフェイス特権の分離 (UIPI) の機能のため,エクスプローラからのファイル名のドラッグ・アンド・ドロップができないことがあります.
コマンドラインコンパイラは,管理者権限で実行しないとコンパイルできません.一方,エクスプローラは通常,少し低い権限での起動となっています.高い権限で起動しているアプリケーションは,自分より低い権限で起動しているアプリケーションからのメッセージの受信に制限があります.
つまり,下からの要求には応じないというわけです.
逆の場合は可能です.つまり,同レベルと上からの指示は受け取ることになっています.
これが「ユーザインターフェイス特権の分離 (UIPI)」機能によって発生する現象の一部です.
したがって,Delphi の IDE から実行したプログラムは,エクスプローラからのドラッグ・アンド・ドロップができない場合があります.作成した EXE を一般ユーザで起動した場合は可能です.
そこで,ChangeWindowMessageFilter という関数で,ドラッグ・アンド・ドロップに関係するメッセージの受信を許可するために,以下のようなコードを追加しました.
ドラッグ・アンド・ドロップのメッセージは,WM_DROPFILES メッセージですが,実際には,関係する他のメッセージも同時に許可する必要があります.これは他の Windows のメッセージでも同じです.
ChangeWindowMessageFilter 関数は,Windows XP 以前にはありません.GetProcAddress 関数で,該当する DLL 内にこの関数があるかを確認してから処理しています.そうしないと,Windows XP でコンパイルできないコードとなってしまいます.これを,dll の動的リンクと言っています.
[備考 1]
ChangeWindowMessageFilter 関数,MSGFLT_ADD, MSGFLT_REMOVE, WM_COPYGLOBALDATA も Delphi XE5 には実装されています.関数と定数値の定義文を書く必要はありません.
[備考 2]
UIPI は,メッセージの送受信に関する機能です.影響を受けるのは,ドラッグ・ドロップ関係だけではありません.例えば,管理者権限で実行しているアプリの表示位置を SetWindowPos 関数を使用して変更しようとしても,一般ユーザで起動したアプリからは変更できません.
リスト5
Windows Vista 以降でファイル名のドラッグ・アンド・ドロップを実現するめに追加したコード
ChangeWindowMessageFilter 関数で,メッセージの受信を許可する
copy code
// ChangeWindowMessageFilter関数用の定数
const
MSGFLT_ADD = 1 ;
MSGFLT_REMOVE = 2 ;
WM_COPYGLOBALDATA = $0049 ;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
LFuncFilter : function (msg: Cardinal; dwFlag: DWORD): BOOL; stdcall ;
LDLLHandle : THandle;
begin
if CheckWin32Version(6 , 0 ) then begin
LDLLHandle := loadLibrary('user32.dll' );
try
if LDLLHandle > 0 then begin
@LFuncFilter := GetProcAddress(LDLLHandle, 'ChangeWindowMessageFilter' );
if @LFuncFilter <> nil then begin
LFuncFilter(WM_DROPFILES, MSGFLT_ADD);
LFuncFilter(WM_COPYDATA, MSGFLT_ADD);
LFuncFilter(WM_COPYGLOBALDATA, MSGFLT_ADD);
end ;
end ;
finally
FreeLibrary(LDLLHandle);
end ;
end ;
アプリケーション間のメッセージ通信の問題に対応する別の方法があります.そのためには,
アプリケーション マニフェストに通信を許可するコードがある
デジタル署名 ( コードサイニング証明書 ) を取得している
アプリケーションの実行ファイルが %ProgramFiles% フォルダにある
という条件が必要です.詳しい内容は,参考リンクのマイクロソフトの記事にあります.
メッセージの受信許可の設定には注意が必要です.間違って他のユーザのファイルを操作するような設定をしてしまう恐れもあります.
[備考 3]
エクスプローラの操作権限は,その環境にもよります.例えば,ネットワーク上のドライブのフォルダを開く際,管理者権限が必要であれば,管理者権限で起動することになります.
この場合も,どの操作が可能であるかは,セキュリティの設定も関係してきます.
この仕様は,Windows XP の時代にはすでに実装されていました.Windows Vista では,UAC の実装に伴って,確実に機能するようになったとも言えます.
05_ Window 7 64 ビット版のレジストリの構成
このコマンドラインコンパイラの実行では,レジストリからコンパイラの情報を取得しています.
もし,64 ビットの EXE を作製した場合,レジストリからの読み出しと,コマンドコンパイラの実行に注意が必要です.そこで,レジストリへのアクセスの簡単なテストをしてみました.
このテストは,64 ビット版の Windows のみで意味のある内容となっています.
64 ビット版の Windows 7 には,32 ビットバージョンのレジストリエディタと 64 ビットバージョンのレジストリエディタがあります.
64 ビットのアプリケーション関係はもちろん 64 ビットバージョンのレジストリに書き込まれます.32 ビットのアプリケーションの [HKEY_LOCAL_MACHINE] [SOFTWARE] 関係の情報は,32 ビットバージョンのレジストリに書き込まれますが,64 ビットバージョンのレジストリにも書き込まれます.そこは,下図のように,
[HKEY_LOCAL_MACHINE] [SOFTWARE] [Wow6432Node]
という場所です.
通常はリダイレクト機能が働くので,特にこのキー項目を意識する必要はありません.
問題となるのは,32 ビットのアプリから 64 ビットバージョンのレジストリを操作するとか,64 ビットのアプリから 32 ビットバージョンのレジストリを操作する時です.
この後のサンプルは,この動作を確認するためのテストプログラムです.
64 ビット版の Windows の 32 ビット版レジストリエディタは,次のコマンドで起動できます.
[ファイル名を指定して実行] のダイアログで,以下をコピペしても起動できます.
32 ビット版の Windows には,このレジストリエディタは存在しません.
32 ビット版の Windows のレジストリエディタは,RegEdit.exe または RegEdit コマンドだけで起動できます.
C:\Windows\SysWOW64\Regedit
図10
64 ビットバージョンのレジストリの内容
[Wow6432Node] に 32 ビット版アプリの情報がある
図11
32 ビットバージョンのレジストリの内容
64 ビットバージョンのレジストリの [Wow6432Node] と同じ内容が格納されている (全てではない)
32 ビット版のレジストリエディタは [ファイル名を指定して実行] から C:\Windows\SysWOW64\Regedit で起動できる
06_ レジストリへのアクセスとアプリの起動 - 32 ビットアプリから
実際のレジストリの操作として,レジストリの読み出しだけをテストしてみます.
読み出しだけであれは,特に管理者としての実行は必要ありません.起動するアプリが管理者権限実行されるかどうかは,このテストプログラムを管理者権限で実行するかどうかで決まります.
Delphi 自身は 32 ビットのアプリケーションです.そこで,ここでは,Windows 7 に標準で実装されている PowerShell を起動します.PowerShell は 32 ビット版と 64 ビット版があります.
PowerShell の実行ファイル名は,レジストリの下図の Path 項目に記録されています.
このテストは,64 ビット版の Windows のみで意味のある内容となっています.
Windows が 32 ビット版か 64 ビット版かの判定はしていません.
図12
PowwrShell
テストは以下の手順で行います.
(1) PowerShell の実行ファイル EXE のフルパスをレジストリから読み取る
(2) ShellExecuteEx 関数を使用して,PowerShell を起動する
(3) 起動したプロセスハンドルから実際に 32 bit 版か 64 bit 版なのかを判定する
このテストプログラムは,32 ビットのアプリケーションから,32 ビットと 64 ビットのレジストリから PowserShell の EXE のフルパスを取得します.
添付のサンプルプロジェクトは,ターゲットプラットフォームを 32 ビットとしています.
この方法は,システム関係内のフォルダにあるアプリケーションでは利用できるとは限りません.システム関係のアプリケーションは,基本的に,ファイルシステムのリダイレクト機能により,System32 フォルダを指定しても SysWOW64 フォルダ内をアクセスしてしまいます.
実行可能かの基準ついては筆者には分かりません.実行してみて確認するしかないようです.
図13
設計時画面
図14
32 ビット版を起動
[SysWOW64] 内の PowerShell が起動
図15
起動した 32 ビット版の PowerShell
カレントディレクトリは,このプロジェクトのフォルダ
図16
64 ビット版を起動
図17
64 ビット版のフォルダ
リスト6
32 ビットアプリから 32 ビット版または 64 ビット版の PowerShell を起動
copy code
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FProcessHandle : Cardinal;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
SHellAPI,
Registry;
{$R *.dfm}
// ----------------------------------------------------------------------------
// ShellExecuteExでEXE実行
// ShellExecuteExはファイル(拡張子)の関連付けでアプリを起動できる
// SEE_MASK_NOCLOSEPROCESSは,ShellExecuteEx終了後もプロセスを実行したままにする
// この指定がないとTShellExecuteInfo.Processでプロセスのハンドルが取得できない
//
// ShellExcuteExの使用にはusesにShellAPIが必要
// ----------------------------------------------------------------------------
function RunExeFile(hWnd: HWND; AFile: string ; AParameters: string ;
Verb: string ): Cardinal;
var
LShellInfo : TShellExecuteInfo;
LResult : DWORD;
begin
FillChar(LShellInfo, SizeOf(LShellInfo), 0 );
LShellInfo.cbSize := SizeOf(LShellInfo);
LShellInfo.Wnd := hWnd;
LShellInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI;
LShellInfo.lpVerb := PChar(Verb);
LShellInfo.lpFile := PChar(AFile);
LShellInfo.lpParameters := PChar(AParameters);
LShellInfo.nShow := SW_SHOW;
ShellExecuteEx(@LShellInfo);
// ShellExecuteExは成功するとhInstAppに33以上の値を返す
if LShellInfo.hInstApp >= 33 then begin
// 起動終了まで待つための処理
repeat
LResult := WaitForInputIdle(LShellInfo.hProcess, 50 );
Application.ProcessMessages;
Sleep(100 );
until LResult <> WAIT_TIMEOUT;
end ;
// 戻値は起動プロセスのハンドルとする
Result := LShellInfo.hProcess;
end ;
// =============================================================================
// 32bitアプリで,32bit版のアプリのフルパスをレジストリから取得
// ここでは,32bit版のPowerShellのフルパスを取得
//
// 64ビット版Windows専用のコードなので注意
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRegisry : TRegistry;
LAppKey : string ;
LAppItem : string ;
LAppFullPath : string ;
LIsWOW64 : BOOL;
LStrText : string ;
begin
LRegisry := TRegistry.Create;
try
// KEY_READ or KEY_WOW64_32KEY;のように32bitであることを明示してもよい
LRegisry.Access := KEY_READ;
LRegisry.RootKey := HKEY_LOCAL_MACHINE;
LAppKey := 'SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' ;
LAppItem := 'Path' ;
if LRegisry.OpenKey(LAppKey, False) then begin
try
LAppFullPath := LRegisry.ReadString(LAppItem);
finally
LRegisry.CloseKey;
end ;
end ;
finally
FreeAndNil(LRegisry);
end ;
Edit1.Text := LAppFullPath;
// 起動し,プロセスのハンドルを取得
// 独立起動とするので,第1引数を0とする
FProcessHandle := RunExeFile(0 , LAppFullPath, '' , '' );
if FProcessHandle <> 0 then begin
// 起動したプロセスのビットバージョンを調査
IsWow64Process(FProcessHandle, LIsWOW64);
if LIsWOW64 then begin
MessageBox(Handle, '32 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '64 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end else begin
LStrText := 'PowerShell の起動に失敗しました' ;
MessageBox(Handle, PChar(LStrText), 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end ;
// =============================================================================
// 32bitアプリで,64bit版のアプリのフルパスをレジストリから取得
// ここでは,64bit版のPowerShellのフルパスを取得
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LRegistry : TRegistry;
LAppKey : string ;
LAppItem : string ;
LAppFullPath : string ;
LIsWOW64 : BOOL;
LStrText : string ;
begin
LRegistry := TRegistry.Create;
try
// 64bitのレジストリにアクセスするには,アクセスフラグにKEY_WOW64_64KEY指定する
LRegistry.Access := KEY_READ or KEY_WOW64_64KEY;
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
LAppKey := 'SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' ;
LAppItem := 'Path' ;
if LRegistry.OpenKey(LAppKey, False) then begin
try
LAppFullPath := LRegistry.ReadString(LAppItem);
finally
LRegistry.CloseKey;
end ;
end ;
finally
FreeAndNil(LRegistry);
end ;
Edit1.Text := LAppFullPath;
// System32はSysnativeに置き換えないとリダイレクトしてしまう
// Sysnativeは,32bitアプリから64bitのシステムフォルダへのアクセス用の仮想フォルダ
LAppFullPath := StringReplace(LAppFullPath, 'System32' , 'Sysnative' , []);
// 起動し,プロセスのハンドルを取得
// 独立起動とするので,第1引数を0とする
FProcessHandle := RunExeFile(0 , LAppFullPath, '' , '' );
if FProcessHandle <> 0 then begin
// 起動したプロセスのビットバージョンを調査
IsWow64Process(FProcessHandle, LIsWOW64);
if LIsWOW64 then begin
MessageBox(Handle, '32ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '64ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end else begin
LStrText := 'PowerShell の起動に失敗しました' ;
MessageBox(Handle, PChar(LStrText), 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end ;
// =============================================================================
// 32bitアプリで,64bit版のアプリのフルパスをレジストリから取得
// ここでは,64bit版のPowerShellのフルパスを取得
// TRegistry.Createでレジストリへのアクセスフラグを指定する場合
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LRegistry : TRegistry;
LAppKey : string ;
LAppItem : string ;
LAppFullPath : string ;
LIsWOW64 : BOOL;
LStrText : string ;
begin
// TRegistryのCreate時にアクセスフラグを指定する方法
// 64bitのレジストリにアクセスするには,アクセスフラグにKEY_WOW64_64KEY指定する
LRegistry := TRegistry.Create(KEY_READ or KEY_WOW64_64KEY);
try
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
LAppKey := 'SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' ;
LAppItem := 'Path' ;
if LRegistry.OpenKey(LAppKey, False) then begin
try
LAppFullPath := LRegistry.ReadString(LAppItem);
finally
LRegistry.CloseKey;
end ;
end ;
finally
FreeAndNil(LRegistry);
end ;
Edit1.Text := LAppFullPath;
// System32はSysnativeに置き換えないとリダイレクトしてしまう
// Sysnativeは,32bitアプリから64bitのシステムフォルダへのアクセス用の仮想フォルダ
LAppFullPath := StringReplace(LAppFullPath, 'System32' , 'Sysnative' , []);
// 起動し,プロセスのハンドルを取得
// 独立起動とするので,第1引数を0とする
FProcessHandle := RunExeFile(0 , LAppFullPath, '' , '' );
if FProcessHandle <> 0 then begin
// 起動したプロセスのビットバージョンを調査
IsWow64Process(FProcessHandle, LIsWOW64);
if LIsWOW64 then begin
MessageBox(Handle, '32 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '64 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end else begin
LStrText := 'PowerShell の起動に失敗しました' ;
MessageBox(Handle, PChar(LStrText), 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end ;
end .
07_ レジストリへのアクセスとアプリの起動 - 64 ビットアプリから
今度は,64 ビットのアプリから 32 ビットまたは 64 ビット版の PowerShell を起動します.
手順は前のサンプルと同じです.
添付のサンプルプロジェクトは,ターゲットプラットフォームを 64 ビットとしています.
図18
設計時画面
図19
32 ビット版を起動
図20
32 ビット版のフォルダ
図21
64 ビット版を起動
図22
起動した 64 ビット版の PowerShell
カレントディレクトリは,プロジェクトのフォルダとなっている
リスト7
64 ビットアプリから 32 ビット版または 64 ビット版の PowerShell を起動
copy code
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
ProcessHandle : Cardinal;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
SHellAPI,
Registry;
{$R *.dfm}
// ----------------------------------------------------------------------------
// ShellExecuteExでEXE実行
// ShellExecuteExはファイル(拡張子)の関連付けでアプリを起動できる
// SEE_MASK_NOCLOSEPROCESSは,ShellExecuteEx終了後もプロセスを実行したままにする
// この指定がないとTShellExecuteInfo.Processでプロセスのハンドルが取得できない
//
// ShellExcuteExの使用にはusesにShellAPIが必要
// ----------------------------------------------------------------------------
function RunExeFile(hWnd: HWND; AFile: string ; AParameters: string ;
Verb: string ): Cardinal;
var
LShellInfo : TShellExecuteInfo;
LResult : DWORD;
begin
FillChar(LShellInfo, SizeOf(LShellInfo), 0 );
LShellInfo.cbSize := SizeOf(LShellInfo);
LShellInfo.Wnd := hWnd;
LShellInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI;
LShellInfo.lpVerb := PChar(Verb);
LShellInfo.lpFile := PChar(AFile);
LShellInfo.lpParameters := PChar(AParameters);
LShellInfo.nShow := SW_SHOW;
ShellExecuteEx(@LShellInfo);
// ShellExecuteExは成功するとhInstAppに33以上の値を返す
if LShellInfo.hInstApp >= 33 then begin
// 起動終了まで待つための処理
repeat
LResult := WaitForInputIdle(LShellInfo.hProcess, 50 );
Application.ProcessMessages;
Sleep(100 );
until LResult <> WAIT_TIMEOUT;
end ;
// 戻値は起動プロセスのハンドルとする
Result := LShellInfo.hProcess;
end ;
// =============================================================================
// 64bitアプリで,32bit版のアプリのフルパスをレジストリから取得
// ここでは,32bit版のPowerShellのフルパスを取得
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRegistry : TRegistry;
LAppKey : string ;
LAppItem : string ;
LAppFullPath : string ;
LIsWOW64 : BOOL;
LStrText : string ;
begin
LRegistry := TRegistry.Create;
try
// 32bitのレジストリにアクセスするには,アクセスフラグにKEY_WOW64_32KEY指定する
LRegistry.Access := KEY_READ or KEY_WOW64_32KEY;
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
LAppKey := 'SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' ;
LAppItem := 'Path' ;
if LRegistry.OpenKey(LAppKey, False) then begin
try
LAppFullPath := LRegistry.ReadString(LAppItem);
finally
LRegistry.CloseKey;
end ;
end ;
finally
FreeAndNil(LRegistry);
end ;
Edit1.Text := LAppFullPath;
// 起動し,プロセスのハンドルを取得
// 独立起動とするので,第1引数を0とする
ProcessHandle := RunExeFile(0 , LAppFullPath, '' , '' );
if ProcessHandle <> 0 then begin
// 起動したプロセスのビットバージョンを調査
IsWow64Process(ProcessHandle, LIsWOW64);
if LIsWOW64 then begin
MessageBox(Handle, '32 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '64 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end else begin
LStrText := 'PowerShell の起動に失敗しました' ;
MessageBox(Handle, PChar(LStrText), 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end ;
// =============================================================================
// 64bitアプリで,64bit版のアプリのフルパスをレジストリから取得
// ここでは,64bit版のPowerShellのフルパスを取得
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LRegistry : TRegistry;
LAppKey : string ;
LAppItem : string ;
LAppFullPath : string ;
LIsWOW64 : BOOL;
LStrText : string ;
begin
LRegistry := TRegistry.Create;
try
// KEY_WOW64_64KEYがなくても,64bitアプリでは64bitのレジストリにアクセスする
LRegistry.Access := KEY_READ or KEY_WOW64_64KEY;
LRegistry.RootKey := HKEY_LOCAL_MACHINE;
LAppKey := 'SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' ;
LAppItem := 'Path' ;
if LRegistry.OpenKey(LAppKey, False) then begin
try
LAppFullPath := LRegistry.ReadString(LAppItem);
finally
LRegistry.CloseKey;
end ;
end ;
finally
FreeAndNil(LRegistry);
end ;
Edit1.Text := LAppFullPath;
// 起動し,プロセスのハンドルを取得
// 独立起動とするので,第1引数を0とする
ProcessHandle := RunExeFile(0 , LAppFullPath, '' , '' );
if ProcessHandle <> 0 then begin
// 起動したプロセスのビットバージョンを調査
IsWow64Process(ProcessHandle, LIsWOW64);
if LIsWOW64 then begin
MessageBox(Handle, '32 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '64 ビット版です' , 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end else begin
LStrText := 'PowerShell の起動に失敗しました' ;
MessageBox(Handle, PChar(LStrText), 'PowerShell' , MB_OK or MB_ICONASTERISK);
end ;
end ;
end .