Delphi Programming / Object Pascal
[掲載 2007年09月15日] [更新 2015年09月28日] Delphi サンプルプログラム集 |
860_自分自身を含むフォルダを削除するプログラム |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
アプリケーションマニフェストファイル添付 |
|
860_DeadSelfFolder.zip [1,080 KB] 2016年08月16日版 (EXE 同梱) |
- 2015年09月28日
- ファイル削除のアニメーションの表示を廃止
- plUnInstallUnit.pas に Delphi 2009 以降対応のコードを追加
- Windows 7 U64(SP1) + Delphi XE(UP1) Pro で動作確認
|
Delphi の利点の一つは,余分なランタイムライブラリ等を必要としないで,EXE 単体で動作するアプリケーションを開発できることです.これはそのアプリが不要になった時にも簡単に削除できることも意味しています.
本ページのサンプルプログラムは,開発したアプリケーションが単一のフォルダ (サブフォルダも含めて) で構成されているような場合に,そのフォルダごと削除するものです.つまりそのアプリケーションの「アンインストール」ということになります.もちろん,手動でフォルダを削除しても構いませんが,ここでは [アンインストール] 等のメニューから実行する状況を想定してみました.
このサンプルは,
の各コード同様,バッチファイルを使用しています.
実際にフォルダを削除するのは,次項の削除用のプログラムを起動して行っています.フォルダを削除するプログラムは,フォルダを削除した後,自分自身を削除するバッチファイルを作成して,自分自身を削除して,作成したバッチファイルも削除するようになっています.
削除用のプログラムは,ユーザがアクセス (読書き) 可能なフォルダに置く必要があります.本サンプルでは,CSIDL_PERSONAL のフォルダにコピーして,そこから起動するようにしています.
|
|
図1
設計時画面
- [アンインストール] ボタンをクリックすると,削除用のプログラムを起動
|
|
図2
[アンインストール] 開始
|
|
図3
フォルダの削除終了
- この後,このダイアログを表示しているプログラムを,そのプログラム自身で削除する
|
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
function GetFolderPath(nFolder: Integer): string;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
ShellApi,
ShlObj,
ActiveX;
{$R *.dfm}
//=============================================================================
// 手順
// (1)このEXEのフォルダ内にあらかじめplUnInstall.dprをコンパイルして
// plUnInstall.exeを作成しておく
// plUnInstall.exeが実際の削除用プログラム
// (2)plUnInatall.exeを別のフォルダにコピー
// (3)移動したplUnIntall.exeに引数を指定して実行
// (4)本アプリを終了
//
// ShellExecuteの使用にはusesにShellApiが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LMsgText : String;
LSourcePath : String;
LDestPath : String;
LAppPath : String;
LCmdParam : String;
begin
if DebugHook <> 0 then begin
LMsgText := 'デバッグ中です' + sLineBreak + 'アンインストールできません';
MessageBox(Handle, PChar(LMsgText), '情報', MB_ICONINFORMATION);
exit;
end;
LMsgText := 'アンインストールします' + sLineBreak + 'よろしいですか';
if MessageBox(Handle,
PChar(LMsgText),
'確認',
MB_YESNO or MB_ICONINFORMATION) = IDNO then begin
exit;
end;
LSourcePath := ExtractFilePath(Application.ExeName) + 'plUnInstall.exe';
LDestPath := GetFolderPath(CSIDL_PERSONAL);
LDestPath := IncludeTrailingPathDelimiter(LDestPath) + 'plUnInstall.exe';
LAppPath := Application.ExeName;
LAppPath := AnsiQuotedStr(LAppPath, '"');
//plUnInstall.exeをLDestPathにコピーして実行
//このLDestPathはユーザがアクセス(読書き)可能なフォルダであること
if FileExists(LSourcePath) then begin
LMsgText := 'テストアプリ削除';
CopyFile(PChar(LSourcePath), PChar(LDestPath), False);
Application.ProcessMessages;
//plUnInstall.exeを起動
LCmdParam := LAppPath + ' ' + IntToStr(Application.Handle) + ' ' + LMsgText;
ShellExecute(Handle, '', PChar(LDestPath), PChar(LCmdParam), nil, SW_SHOW);
//このアプリは終了
//後は起動したplUnInstall.exeがいろいろ実行
Close;
end else begin
LMsgText := 'アンインストールできません';
MessageBox(Handle, PChar(LMsgText), '情報', MB_ICONINFORMATION);
end;
end;
//-----------------------------------------------------------------------------
// 特殊フォルダのフルパスをCSIDL値から取得する関数
//
// nFolder 特殊フォルダのCSIDL値の例
// CSIDL_APPDATA アプリケーションデータ
// CSIDL_PERSONAL マイドキュメント
// その他いろいろありShlObj.pas参照
// usesにShlObj(SH...)とActiveX(IMalloc)が必要
//-----------------------------------------------------------------------------
function TForm1.GetFolderPath(nFolder: Integer): string;
var
Lpidl : PItemIDList;
Lbufffer : array [0..MAX_PATH] of Char;
LMalloc : IMalloc;
begin
CoGetMalloc(1, LMalloc);
SHGetSpecialFolderLocation(0, nFolder, Lpidl);
try
SHGetPathFromIDList(Lpidl, Lbufffer);
Result := Lbufffer;
finally
LMalloc.Free(Lpidl);
end;
end;
end.
上のコードでは,以下の plUninstall.exe を起動しています.実際のフォルダの削除はこのプログラムが実行します.できるだけ汎用的になるようにしてみました.
もし,デスクトップと [スタート] [プログラム] に削除するアプリケーションのショートカットがあればそれも削除します.
以前のサンプルでは,TAnimate を使用して,ファイル削除のアニメーションダイアログを表示していましたが,2015年09月28日の更新で中止しました.元々,簡単なアプリのアンインストール用を想定しているため,実際には,ほとんどダイアログを表示している時間はありません.この変更でフォームは必要なくなりましたが,そのままフォームを使用するコードとなっています.
|
|
図4
設計時画面
|
リスト2
実際に指定したフォルダの削除を行うプログラム
指定したフォルダを削除した後,自分自身を削除する |
unit plUnInstallUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
FApplicationFullPath : String;
FDelFolder : String;
FDelAppHandle: THandle;
FExeFullPath : String;
FBatFullPath : String;
function DeleteFolders(AFolderPath: string): Integer;
procedure BeforeApplicationExit;
procedure DeleteShorCut(AFindPath, ASearcFolder: String);
function FindFileShortCut(AFindPath, ASearchFolder: String): String;
function GetFolderPath(AnFolder: Integer): string;
function GetLinkPathName(ALinkFilePath: String): String;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
ShellAPI,
ShlObj,
ActiveX,
ComObj;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//
// 実行手順
// (1)このEXEを使用するアプリで、このEXEを別のフォルダにコピーする
// (2)このEXEを使用するアプリ側で、コピー先のこのEXEを実行する
// この時コマンドラインに以下の引数を指定する
//
// ParamStr(1) アンインストール対象のアプリのフルパス
// 削除するフォルダ名はこの値から取得
// ParamStr(2) アンインストール対象のアプリのハンドルを文字列に変換した値
// ParamStr(3) このフォームのCaptionで本プログラムのタイトル
//
// プロジェクトのソースコードに以下を追加して,このフォームを非表示で実行
// Application.ShowMainForm:=False;
// このプログラムは単独で実行した場合,何もしないで終了する
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
if ParamCount >= 2 then begin
FDelFolder := '';
FExeFullPath := Application.ExeName;
FApplicationFullPath := ParamStr(1);
FDelFolder := ExtractFileDir(FApplicationFullPath);
FDelAppHandle := StrToIntDef(ParamStr(2), 0);
if ParamStr(3) = '' then begin
Application.Title := 'アンインストール';
Self.Caption := Application.Title;
end else begin
Application.Title := ParamStr(3);
Self.Caption := Application.Title;
end;
//削除対象のフォルダの指定がなければこのEXEも削除不可なので失敗
if FDelFolder <> '' then begin
BeforeApplicationExit;
end else begin
MessageBox(Handle,
'アンインストールまたはキャンセルできません',
'情報',
MB_ICONINFORMATION);
end;
end;
//このEXEを強制終了
Application.Terminate;
end;
//-----------------------------------------------------------------------------
// 削除の実行手順
//
// (1)このEXEを呼出したEXEが終了するまで待つ
// (2)削除対象のフォルダを削除
// (3)このEXEを削除するためのバッチファイルを作成
// このバッチファイルには自分自身を削除するコードがある
//-----------------------------------------------------------------------------
procedure TForm1.BeforeApplicationExit;
var
LBatCmdList : TStringList;
begin
//[デスクトップ][プログラム][スタートメニュー]にショートカットがあれば削除
DeleteShorCut(FApplicationFullPath, GetFolderPath(CSIDL_DESKTOPDIRECTORY));
DeleteShorCut(FApplicationFullPath, GetFolderPath(CSIDL_STARTMENU));
DeleteShorCut(FApplicationFullPath, GetFolderPath(CSIDL_PROGRAMS));
//削除場所によっては表示の更新に以下が必要
//SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
//このプログラムを呼出したアプリが終了するまでループ
while True do begin
Application.ProcessMessages;
if GetClassLong(FDelAppHandle, GCL_HMODULE) = 0 then break;
end;
//フォルダ(FDelFolder)を削除
//フォルダが存在しないという値である0を検出するまでループ
try
while True do begin
if DirectoryExists(FDelFolder) = False then break;
DeleteFolders(FDelFolder);
Application.ProcessMessages;
end;
except
end;
LBatCmdList:=TStringList.Create;
//BATファイルは"(ダブルクォーテーション)で囲まなくてOK
FBatFullPath := ChangeFileExt(FExeFullPath, '.BAT');
FExeFullPath := AnsiQuotedStr(FExeFullPath, '"');
//このEXEファイルを削除するバッチファイル
//削除が確認できるまでループする
//削除後はバッチファイル自身も削除する(DEL %0)
try
LBatCmdList.Add(':Lbl0');
LBatCmdList.Add('DEL ' + FExeFullPath);
LBatCmdList.Add('IF EXIST ' + FExeFullPath + ' GOTO Lbl0');
LBatCmdList.Add('DEL %0');
LBatCmdList.Add('EXIT');
LBatCmdList.SaveToFile(FBatFullPath);
finally
LBatCmdList.Free;
end;
Hide;
MessageBox(Handle, 'アンインストールが終了しました', '情報', MB_ICONINFORMATION);
try
//作成したバッチファイルを実行
ShellExecute(Handle, '', PChar(FBatFullPath), nil, nil, SW_HIDE);
except
end;
end;
//-----------------------------------------------------------------------------
// 引数のAFolderPathというフォルダ(サブフォルダも含めて)を削除する関数
// SHFileOperation本来の進行ダイアログは非表示
// fFlagsメンバの値にFOF_ALLOWUNDOを追加すればゴミ箱への移動
//
// Delphi 2009以降の場合は,System.IOUtils.TDirectory.Deleteの方がいいだろう
//-----------------------------------------------------------------------------
function TForm1.DeleteFolders(AFolderPath: string): Integer;
var
LfoStruct : TSHFileOpStruct;
begin
LfoStruct.Wnd := Application.Handle;
LfoStruct.wFunc := FO_DELETE;
LfoStruct.pFrom := PChar(AFolderPath+#0);
LfoStruct.pTo := #0;
LfoStruct.fFlags:= FOF_SILENT or FOF_NOCONFIRMATION;
LfoStruct.fAnyOperationsAborted := False;
LfoStruct.hNameMappings := nil;
LfoStruct.lpszProgressTitle := nil;
Result := SHFileOperation(LfoStruct);
end;
//-----------------------------------------------------------------------------
// 指定場所のショートカットを削除
//
// AFindPath ショートカットの元ファイル(実行形式など)
// ASearcFolder ショートカットの有無を検索するフォルダ
//-----------------------------------------------------------------------------
procedure TForm1.DeleteShorCut(AFindPath, ASearcFolder: String);
var
ALinkPath : String;
begin
ALinkPath := FindFileShortCut(AFindPath, ASearcFolder);
if ALinkPath <> '' then begin
DeleteFile(ALinkPath);
end;
end;
//-----------------------------------------------------------------------------
// 指定したファイルが指定フォルダ内にあるか調査
//
// SearchLinkFolderで指定するフォルダの中に,FindPathのファイルのショートカッ
// トリンクが作成されているかを検出する.
// ショートカットの名前が変更されている場合も検索可能
// ショートカットがない場合は空文字を返す
// 既にに作成済みならそのフルパス名(拡張子は.lnk)を返す
//
// 名前の比較は念のため,全て小文字に変換してから比較する.
//-----------------------------------------------------------------------------
function TForm1.FindFileShortCut(AFindPath, ASearchFolder: String): String;
var
LSearchRec : TSearchRec;
LGetFileName : String;
LFindPath : String;
LPath : String;
LAttr : Integer;
LSearchFolder : String;
begin
Result := '';
LAttr := FaArchive;
//大文字と小文字を区別しないで検索するために小文字に統一
LFindPath := AnsiLowerCase(ExtractFileName(AFindPath));
//フォルダ名の最後尾に\がなければ追加
LSearchFolder := IncludeTrailingBackslash(ASearchFolder);
try
FindFirst(LSearchFolder + '*.*', LAttr, LSearchRec);
//ASearcFolder内のファイルを全て調べる
repeat
LGetFileName := AnsiLowerCase(LSearchRec.Name);
if (LGetFileName <> '.') and (LGetFileName <> '..') then begin
LPath := AnsiLowerCase(GetLinkPathName(LSearchFolder + LGetFileName));
if ExtractFileName(LPath) = LFindPath then begin
Result := LSearchFolder + LGetFileName;
break;
end;
end;
until FindNext(LSearchRec) <> 0;
finally
FindClose(LSearchRec);
end;
end;
//-----------------------------------------------------------------------------
// CSIDL値から特殊フォルダのフルパスを取得する関数
//
// AnFolder 特殊フォルダのCSIDL値の例
// CSIDL_DESKTOPDIRECTORY ディスクトップ
// CSIDL_PROGRAMS スタートメニューのプログラム
// CSIDL_STARTMENU スタートメニュー
// CSIDL_STARTUP スタートアップ
// CSIDL_SENDTO コンテキストの「送る」
// その他いろいろありShlObj.pas参照
// usesにShlObj(SH...)とActiveX(IMalloc)が必要
//-----------------------------------------------------------------------------
function TForm1.GetFolderPath(AnFolder: Integer): string;
var
Lpidl : PItemIDList;
LMalloc : IMalloc;
Lbuffer : array [0..MAX_PATH-1] of Char;
begin
CoGetMalloc(1, LMalloc);
SHGetSpecialFolderLocation(0, AnFolder, Lpidl);
try
SHGetPathFromIDList(Lpidl, Lbuffer);
Result := Lbuffer;
finally
LMalloc.Free(Lpidl);
end;
end;
//-----------------------------------------------------------------------------
// 引数のALinkFilePath(フルパス)が,あるファイルのショートカットリンクの時に
// そのリンク元,つまり実体を返す関数.成功しない時は空文字を返す
//
// LinkPath ショートカットファイル名(拡張子はlnk)
// TShellLinkにはusesにShlObjが必要
// TPersisFileにはusesにActiveXが必要
// CreateComObjectにはusesにComObjが必要
//-----------------------------------------------------------------------------
function TForm1.GetLinkPathName(ALinkFilePath: String): String;
var
LShellLink : IShellLink;
LPersistFile : IPersistFile;
LFileLink : String;
LiFlag : Cardinal;
LFindData : TWin32FindData;
LFilePath : array[0..MAX_PATH-1] of char;
Lwsz : array[0..MAX_PATH-1] of WideChar;
begin
Result := '';
LFileLink := ALinkFilePath;
LShellLink := CreateComObject(CLSID_SHELLLINK) as IShellLink;
LPersistFile := LShellLink as IPersistFile;
LiFlag := SLGP_RAWPATH or SLGP_UNCPRIORITY;
//---------------------------------------------------------------------------
// Delphi 2009以上用のコード
//---------------------------------------------------------------------------
{$IFDEF Unicode}
if LPersistFile.Load(PChar(LFileLink), STGM_READ) = S_OK then begin
try
//初期化
FillChar(LFilePath, SizeOf(LFilePath), #0);
//ショートカットファイルの参照先を取得
LShellLink.GetPath(@LFilePath, MAX_PATH, LFindData, LiFlag);
//関数の戻り値にセット
Result := LFilePath;
except
end;
end;
{$ELSE}
//---------------------------------------------------------------------------
// Delphi 2007以前用のコード
//---------------------------------------------------------------------------
MultiByteToWideChar(CP_ACP, 0, PChar(LFileLink), -1, Lwsz, MAX_PATH);
if LPersistFile.Load(Lwsz, STGM_READ) = S_OK then begin
try
//初期化
FillChar(LFilePath, SizeOf(LFilePath), #0);
//ショートカットファイルの参照先を取得
LShellLink.GetPath(@LFilePath, MAX_PATH, LFindData, LiFlag);
//関数の戻り値にセット
Result := LFilePath;
except
end;
end;
{$ENDIF}
end;
end.
本ページのサンプルは,自分自身を含むディレクトリを削除するものです.
一般的なディレクトリの作成,削除等の操作については,以下の記事を参考にしてください. |
|