ADD: Feature [0001762] Show the processes, which lock the file from deletion #2

This commit is contained in:
Alexander Koblov 2018-10-26 16:58:54 +00:00
commit 3516f8d117
6 changed files with 251 additions and 5 deletions

View file

@ -284,7 +284,7 @@ end;"/>
<MinVersion Minor="3" Valid="True"/>
</Item12>
</RequiredPackages>
<Units Count="238">
<Units Count="239">
<Unit0>
<Filename Value="doublecmd.lpr"/>
<IsPartOfProject Value="True"/>
@ -1716,6 +1716,12 @@ end;"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="fViewOperations"/>
</Unit237>
<Unit238>
<Filename Value="ffileunlock.pas"/>
<IsPartOfProject Value="True"/>
<HasResources Value="True"/>
<UnitName Value="fFileUnlock"/>
</Unit238>
</Units>
</ProjectOptions>
<CompilerOptions>

97
src/ffileunlock.lfm Normal file
View file

@ -0,0 +1,97 @@
object frmFileUnlock: TfrmFileUnlock
Left = 326
Height = 342
Top = 203
Width = 638
BorderIcons = [biSystemMenu, biMaximize]
Caption = 'Unlock'
ChildSizing.LeftRightSpacing = 12
ChildSizing.TopBottomSpacing = 12
ClientHeight = 342
ClientWidth = 638
Position = poOwnerFormCenter
LCLVersion = '1.8.4.0'
object stgFileHandles: TStringGrid
AnchorSideLeft.Control = Owner
AnchorSideTop.Control = Owner
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = btnClose
Left = 12
Height = 281
Top = 12
Width = 614
Anchors = [akTop, akLeft, akRight, akBottom]
AutoFillColumns = True
BorderSpacing.Top = 12
BorderSpacing.Bottom = 12
ColCount = 3
Columns = <
item
SizePriority = 0
Title.Caption = 'File Handle'
Width = 100
end
item
SizePriority = 0
Title.Caption = 'Process ID'
Width = 100
end
item
Title.Caption = 'Executable Path'
Width = 413
end>
FixedCols = 0
Flat = True
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRowSelect, goSmoothScroll]
TabOrder = 0
OnSelection = stgFileHandlesSelection
ColWidths = (
100
100
413
)
end
object btnUnlockAll: TButton
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = btnClose
AnchorSideBottom.Control = btnClose
AnchorSideBottom.Side = asrBottom
Left = 464
Height = 25
Top = 305
Width = 75
Anchors = [akRight, akBottom]
BorderSpacing.Right = 12
Caption = 'Unlock All'
OnClick = btnUnlockAllClick
TabOrder = 1
end
object btnUnlock: TButton
AnchorSideTop.Control = btnUnlockAll
AnchorSideRight.Control = btnUnlockAll
Left = 377
Height = 25
Top = 305
Width = 75
Anchors = [akTop, akRight]
BorderSpacing.Right = 12
Caption = 'Unlock'
OnClick = btnUnlockClick
TabOrder = 2
end
object btnClose: TButton
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = Owner
AnchorSideBottom.Side = asrBottom
Left = 551
Height = 25
Top = 305
Width = 75
Anchors = [akRight, akBottom]
Caption = 'Close'
ModalResult = 11
TabOrder = 3
end
end

9
src/ffileunlock.lrj Normal file
View file

@ -0,0 +1,9 @@
{"version":1,"strings":[
{"hash":96810395,"name":"tfrmfileunlock.caption","sourcebytes":[85,110,108,111,99,107],"value":"Unlock"},
{"hash":78334293,"name":"tfrmfileunlock.stgfilehandles.columns[0].title.caption","sourcebytes":[70,105,108,101,32,72,97,110,100,108,101],"value":"File Handle"},
{"hash":164572548,"name":"tfrmfileunlock.stgfilehandles.columns[1].title.caption","sourcebytes":[80,114,111,99,101,115,115,32,73,68],"value":"Process ID"},
{"hash":165250008,"name":"tfrmfileunlock.stgfilehandles.columns[2].title.caption","sourcebytes":[69,120,101,99,117,116,97,98,108,101,32,80,97,116,104],"value":"Executable Path"},
{"hash":93881116,"name":"tfrmfileunlock.btnunlockall.caption","sourcebytes":[85,110,108,111,99,107,32,65,108,108],"value":"Unlock All"},
{"hash":96810395,"name":"tfrmfileunlock.btnunlock.caption","sourcebytes":[85,110,108,111,99,107],"value":"Unlock"},
{"hash":4863637,"name":"tfrmfileunlock.btnclose.caption","sourcebytes":[67,108,111,115,101],"value":"Close"}
]}

116
src/ffileunlock.pas Normal file
View file

@ -0,0 +1,116 @@
unit fFileUnlock;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
Grids, uFileUnlock;
type
{ TfrmFileUnlock }
TfrmFileUnlock = class(TForm)
btnUnlockAll: TButton;
btnUnlock: TButton;
btnClose: TButton;
stgFileHandles: TStringGrid;
procedure btnUnlockAllClick(Sender: TObject);
procedure btnUnlockClick(Sender: TObject);
procedure stgFileHandlesSelection(Sender: TObject; aCol, aRow: Integer);
private
procedure ShowThread;
procedure UnlockRow(Index: Integer);
public
end;
function ShowUnlockForm(ProcessInfo: TProcessInfoArray): Boolean;
implementation
uses
Windows, Math, fMain;
function ShowUnlockForm(ProcessInfo: TProcessInfoArray): Boolean;
var
Index: Integer;
UnlockEnabled: Boolean = False;
begin
with TfrmFileUnlock.Create(frmMain) do
try
stgFileHandles.RowCount:= Length(ProcessInfo) + 1;
for Index:= 1 to stgFileHandles.RowCount - 1 do
begin
if (ProcessInfo[Index - 1].FileHandle <> 0) then begin
UnlockEnabled:= True;
stgFileHandles.Cells[0, Index]:= IntToStr(ProcessInfo[Index - 1].FileHandle);
end;
stgFileHandles.Cells[1, Index]:= IntToStr(ProcessInfo[Index - 1].ProcessId);
stgFileHandles.Cells[2, Index]:= ProcessInfo[Index - 1].ExecutablePath;
end;
btnUnlockAll.Enabled:= UnlockEnabled;
stgFileHandles.Row:= IfThen(UnlockEnabled, 1, 0);
TThread.Synchronize(nil, @ShowThread);
Result:= (ModalResult = mrOK);
finally
Free;
end;
end;
{$R *.lfm}
{ TfrmFileUnlock }
procedure TfrmFileUnlock.stgFileHandlesSelection(Sender: TObject; aCol, aRow: Integer);
begin
btnUnlock.Enabled:= Length(stgFileHandles.Cells[0, aRow]) > 0;
end;
procedure TfrmFileUnlock.btnUnlockClick(Sender: TObject);
begin
UnlockRow(stgFileHandles.Row);
if (stgFileHandles.RowCount = 1) then
begin
Close;
ModalResult:= mrOK;
end;
end;
procedure TfrmFileUnlock.btnUnlockAllClick(Sender: TObject);
var
Index: Integer;
begin
for Index:= stgFileHandles.RowCount - 1 downto 1 do
begin
UnlockRow(Index);
end;
if (stgFileHandles.RowCount = 1) then
begin
Close;
ModalResult:= mrOK;
end;
end;
procedure TfrmFileUnlock.ShowThread;
begin
ShowModal;
end;
procedure TfrmFileUnlock.UnlockRow(Index: Integer);
var
ProcessId: DWORD;
FileHandle: HANDLE;
begin
ProcessId:= StrToDWord(stgFileHandles.Cells[1, Index]);
FileHandle:= StrToQWord(stgFileHandles.Cells[0, Index]);
if FileUnlock(ProcessId, FileHandle) then stgFileHandles.DeleteRow(Index);
end;
end.

View file

@ -61,7 +61,7 @@ implementation
uses
DCOSUtils, uLng, uFileSystemUtil, uTrash, uAdministrator, uOSUtils
{$IF DEFINED(MSWINDOWS)}
, uFileUnlock
, Windows, uFileUnlock, fFileUnlock
{$ENDIF}
;
@ -343,12 +343,22 @@ begin
end;
if AdministratorPrivileges then
PossibleResponses:= ResponsesError
begin
SetLength(PossibleResponses, Length(ResponsesError));
Move(ResponsesError[0], PossibleResponses[0], SizeOf(ResponsesError));
end
else begin
SetLength(PossibleResponses, Length(ResponsesError) + 1);
Move(ResponsesError[0], PossibleResponses[0], SizeOf(ResponsesError));
PossibleResponses[High(PossibleResponses)]:= fsourRetryAdmin;
end;
{$IF DEFINED(MSWINDOWS)}
if (Length(ProcessInfo) > 0) or (LastError = ERROR_ACCESS_DENIED) or (LastError = ERROR_SHARING_VIOLATION) then
begin
SetLength(PossibleResponses, Length(PossibleResponses) + 1);
PossibleResponses[High(PossibleResponses)]:= fsourUnlock;
end;
{$ENDIF}
case AskQuestion(sQuestion, '',
PossibleResponses,
fsourRetry, fsourAbort) of
@ -360,9 +370,17 @@ begin
RaiseAbortOperation;
fsourRetryAdmin:
Exit(False);
{$IF DEFINED(MSWINDOWS)}
fsourUnlock:
begin
bRetry:= True;
GetFileInUseProcessSlow(FileName, LastError, ProcessInfo);
ShowUnlockForm(ProcessInfo);
end;
{$ENDIF}
end;
end;
end;
until bRetry = False;
end;

View file

@ -316,8 +316,8 @@ resourcestring
rsNoEquivalentInternalCommand = 'No internal equivalent command';
// Locked by another process
rsMsgProcessId = 'Process: %d';
rsMsgApplicationName = 'Application: %s';
rsMsgProcessId = 'PID: %d';
rsMsgApplicationName = 'Description: %s';
rsMsgExecutablePath = 'Executable: %s';
rsMsgOpenInAnotherProgram = 'The action cannot be completed because the file is open in another program:';