Subversion Repositories recyclebinunit

Compare Revisions

Regard whitespace Rev 98 → Rev 99

/trunk/Recycle Bin Unit v2/Changelog.txt
1,6 → 1,10
 
=== Changelog RecBinUnit v2 ===
 
2022-07-02
+ Improved recognition of Vista+ index files which are not named with file name $I...
+ Fixed issues in FORMAT.md
 
2022-06-30
+ Added support for $Ixxx "Vista" format 2 files (added somewhere in a Windows 10 build)
The difference towards $Ixxx "Vista" format 1 files is that the filename is not limited to MAX_PATH anymore. (New limit 0xFFFFFFFD)
/trunk/Recycle Bin Unit v2/RecBinUnit2.pas
5,7 → 5,7
// E-MAIL: info@daniel-marschall.de //
// Web: www.daniel-marschall.de & www.viathinksoft.de //
////////////////////////////////////////////////////////////////////////////////////
// Revision: 30 JUN 2022 //
// Revision: 02 JUL 2022 //
// This unit is freeware, but please link to my website if you are using it! //
////////////////////////////////////////////////////////////////////////////////////
// Successfully tested with: //
55,7 → 55,7
Windows, SysUtils, Classes, ContNrs, ShellAPI, Registry, Messages, Math;
 
const
RECBINUNIT_VERSION = '2022-06-30';
RECBINUNIT_VERSION = '2022-07-02';
 
RECYCLER_CLSID: TGUID = '{645FF040-5081-101B-9F08-00AA002F954E}';
NULL_GUID: TGUID = '{00000000-0000-0000-0000-000000000000}';
816,21 → 816,42
var
fs: TFileStream;
infoHdr: TRbInfoHeader;
testID: string;
vistaId: string;
wTest: TRbInfoWItem;
bakPosition: int64;
testVistaItem: TRbVistaItem;
begin
fs := TFileStream.Create(AFile, fmOpenRead);
try
fs.Seek(0, soFromBeginning);
 
{$REGION 'First try if it is a Vista index file'}
testVistaItem := nil;
if SameText(copy(ExtractFileName(AFile), 1, 2), '$I') then
begin
testID := copy(testID, 3, Length(testID)-2);
list.Add(TRbVistaItem.Create(fs, AFile, testID));
vistaId := copy(AFile, 3, Length(AFile)-2);
testVistaItem := TRbVistaItem.Create(fs, AFile, vistaId);
end
else
begin
vistaId := ''; // manual file that was not named $I..., so we cannot get $R... ID and therefore no physical file!
try
testVistaItem := TRbVistaItem.Create(fs, AFile, vistaId);
if Copy(testVistaItem.Source,2,2) <> ':\' then
FreeAndNil(testVistaItem);
except
testVistaItem := nil;
end;
end;
{$ENDREGION}
 
if Assigned(testVistaItem) then
begin
list.Add(testVistaItem);
end
else
begin
fs.Seek(0, soFromBeginning);
if TolerantReading then
begin
// This is a special treatment how to recover data from an INFO/INFO2 file
850,7 → 871,7
// a ':' at the Unicode string.
bakPosition := fs.Position;
wTest := TRbInfoWItem.Create(fs, AFile);
if Copy(wTest.SourceUnicode, 2, 1) = ':' then
if Copy(wTest.SourceUnicode, 2, 2) = ':\' then
begin
// Yes, it is a valid Unicode record.
list.Add(wTest);
863,11 → 884,16
list.Add(TRbInfoAItem.Create(fs, AFile));
end;
end
else
else if fs.Position + SizeOf(TRbInfoRecordA) <= fs.Size then
begin
// No, there is not enough space left for an Unicode record.
// So we assume that the following record will be a valid ANSI record.
list.Add(TRbInfoAItem.Create(fs, AFile));
end
else
begin
// Not enough space to read a Ansi record!
// Ignore it
end;
end;
end
1266,8 → 1292,8
var
Dummy2: DWORD;
Dummy3: DWORD;
FileSystem: array[0..MAX_PATH] of char;
VolumeName: array[0..MAX_PATH] of char;
FileSystem: array[0..MAX_PATH-1] of char;
VolumeName: array[0..MAX_PATH-1] of char;
s: string;
begin
s := FDriveLetter + DriveDelim + PathDelim; // ohne die Auslagerung in einen String kommt es zu einer AV in ntdll
1536,7 → 1562,7
r1: TRbVistaRecord1;
r2: TRbVistaRecord2Head;
r2SourceUnicode: array of WideChar;
version: DWORD;
version: int64;
i: Integer;
resourcestring
LNG_VISTA_WRONG_FORMAT = 'Invalid Vista index format version %d';
1614,6 → 1640,9
function TRbVistaItem.GetPhysicalFile: string;
begin
result := FIndexFile;
if Pos('$I', Result) = 0 then
result := ''
else
result := StringReplace(Result, '$I', '$R', [rfIgnoreCase]);
end;
 
/trunk/Recycle Bin Unit v2/RecBinUnitLowLvl.pas
1,7 → 1,6
unit RecBinUnitLowLvl;
 
// TODO: Gain more information about drive '@' / Homedrive / Netdrive? Win2000 source
// + überall verwenden
// TODO: Gain more information about drive '@' / Homedrive / Netdrive?
 
interface
 
10,18 → 9,18
 
type
PRbInfoHeader = ^TRbInfoHeader;
TRbInfoHeader = record
format: DWORD; // Unsure if this is just a version field or some unknown flags...!
TRbInfoHeader = packed record
format: DWORD; // Version of the info file
// Win95 (without IE4): 00 00 00 00
// Win95 (with IE4): 04 00 00 00
// Win NT4: 02 00 00 00
// Win Me, 2000, XP: 05 00 00 00
totalEntries: DWORD; // Only Win95 (without IE4) and Win NT4, unknown purpose for other OS versions
nextPossibleID: DWORD; // Only Win95 (without IE4) and Win NT4, unknown purpose for other OS versions
recordLength: DWORD; // 0x181 = INFO structure (without Unicode)
// 0x320 = INFO2 structure (with Unicode)
// Win NT4: 02 00 00 00 (Win96/Cairo?)
// Win95 (with IE4), 98: 04 00 00 00
// Win Me, 2000, XP: 05 00 00 00 (NT4+IE4, NT5?)
totalEntries: DWORD; // Only Win95 (without IE4) and Win NT4, other OS versions might use the registry instead
nextPossibleID: DWORD; // Only Win95 (without IE4) and Win NT4, other OS versions might use the registry instead
recordLength: DWORD; // 0x181 = ANSI records
// 0x320 = Unicode records
totalSize: DWORD; // sum of all "originalSize" values;
// Only Win95 (without IE4) and Win NT4, unknown purpose for other OS versions
// Only Win95 (without IE4) and Win NT4, other OS versions might use the registry instead
end;
 
type
28,10 → 27,10
// Windows 95: INFO file with TRbInfoRecordA; Folder deletion NOT possible
// Windows 95 +IE4: INFO2 file with TRbInfoRecordA; Folder deletion possible
PRbInfoRecordA = ^TRbInfoRecordA;
TRbInfoRecordA = record
sourceAnsi: array[0..MAX_PATH-3] of AnsiChar; // 258 elements
TRbInfoRecordA = packed record
sourceAnsi: array[0..MAX_PATH-1] of AnsiChar; // 260 characters (including NUL terminator)
recordNumber: DWORD;
sourceDrive: DWORD;
sourceDrive: DWORD; // 0=A, 1=B, 2=C, ...
deletionTime: FILETIME;
originalSize: DWORD; // Size occupied on disk. Not the actual file size.
// INFO2, for folders: The whole folder size with contents
41,20 → 40,19
// Windows NT4: INFO file with TRbInfoRecordW; Folder deletion possible
// Windows 2000+: INFO2 file with TRbInfoRecordW; Folder deletion possible
PRbInfoRecordW = ^TRbInfoRecordW;
TRbInfoRecordW = record
sourceAnsi: array[0..MAX_PATH-3] of AnsiChar; // 258 elements
TRbInfoRecordW = packed record
sourceAnsi: array[0..MAX_PATH-1] of AnsiChar; // 260 characters (including NUL terminator)
recordNumber: DWORD;
sourceDrive: DWORD;
sourceDrive: DWORD; // 0=A, 1=B, 2=C, ...
deletionTime: FILETIME;
originalSize: DWORD;
sourceUnicode: array[0..MAX_PATH-3] of WideChar; // 258 elements
unknown1: DWORD; // Dummy?
sourceUnicode: array[0..MAX_PATH-1] of WideChar; // 260 characters (including NUL terminator)
end;
 
type
// Introduced in Windows Vista
PRbVistaRecord1 = ^TRbVistaRecord1;
TRbVistaRecord1 = record
TRbVistaRecord1 = packed record
version: int64; // Always 01 00 00 00 00 00 00 00
originalSize: int64;
deletionTime: FILETIME;
64,7 → 62,7
type
// Introduced somewhere in a Win10 release
PRbVistaRecord2Head = ^TRbVistaRecord2Head;
TRbVistaRecord2Head = record
TRbVistaRecord2Head = packed record
version: int64; // Always 02 00 00 00 00 00 00 00
originalSize: int64;
deletionTime: FILETIME;
77,7 → 75,7
// Windows 95 + Windows NT 4
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket: PurgeInfo (Binary)
PRbWin95PurgeInfo = ^TRbWin95PurgeInfo;
TRbWin95PurgeInfo = record
TRbWin95PurgeInfo = packed record
cbSize: DWORD;
bGlobalSettings: BOOL;
percentDrive: array['A'..'Z'] of WORD; // 0x00..0x64 = 0%..100%
/trunk/Recycle Bin Unit v2/RecyclerListing.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Recycle Bin Unit v2/RecyclerListingMain.dfm
54,7 → 54,7
TabOrder = 1
end
object Button2: TButton
Left = 599
Left = 615
Top = 48
Width = 106
Height = 33
77,14 → 77,21
object CheckBox2: TCheckBox
Left = 183
Top = 6
Width = 146
Width = 138
Height = 17
Caption = 'Hide orphan Index files'
Checked = True
State = cbChecked
Caption = 'Hide non-existing files'
TabOrder = 2
end
object Button3: TButton
Left = 592
Top = 56
Width = 17
Height = 21
Caption = '...'
TabOrder = 5
OnClick = Button3Click
end
end
object OpenDialog1: TOpenDialog
Left = 928
Top = 16
93,7 → 100,7
Left = 928
Top = 48
Bitmap = {
494C01010E001000140010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
494C01010E001000040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
0000000000003600000028000000400000004000000001002000000000000040
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
/trunk/Recycle Bin Unit v2/RecyclerListingMain.pas
17,10 → 17,12
LabeledEdit1: TLabeledEdit;
ImageList1: TImageList;
CheckBox2: TCheckBox;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure TreeView1DblClick(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
localRecyclersNode: TTreeNode;
individualRecyclersNode: TTreeNode;
40,6 → 42,14
// TODO: zu jedem element mehr informationen anzeigen, nicht nur den ursprungsnamen
// TODO: Einstellungen usw anzeigen, so wie im alten Demo
 
const
ICON_FILE = 0;
ICON_TREEVIEW = 2;
ICON_BIN = 4;
ICON_DRIVE = 6;
ICON_UNKNOWN = 8;
ICON_FOLDER = 10;
 
procedure TRecyclerListingMainForm.Button1Click(Sender: TObject);
var
drives: TObjectList{TRbDrive};
77,7 → 87,7
nDrive := TreeView1.Items.AddChildObject(localRecyclersNode, Format(S_DRIVE, [drive.DriveLetter])+': ' + GUIDToString(drive.VolumeGUID), drive)
else
nDrive := TreeView1.Items.AddChildObject(localRecyclersNode, Format(S_DRIVE, [drive.DriveLetter])+':', drive);
nDrive.ImageIndex := 6;
nDrive.ImageIndex := ICON_DRIVE;
nDrive.SelectedIndex := nDrive.ImageIndex;
 
bins.Clear;
90,7 → 100,7
bin := bins.Items[iBin] as TRbRecycleBin;
 
nBin := TreeView1.Items.AddChildObject(nDrive, bin.FileOrDirectory, bin);
nBin.ImageIndex := 4;
nBin.ImageIndex := ICON_BIN;
nBin.SelectedIndex := nBin.ImageIndex;
 
items.Clear;
108,11 → 118,11
nItem := TreeView1.Items.AddChildObject(nBin, sCaption, item);
 
if FileExists(item.PhysicalFile) then
nItem.ImageIndex := 0
nItem.ImageIndex := ICON_FILE
else if DirectoryExists(item.PhysicalFile) then
nItem.ImageIndex := 10 // TODO: Feature: Read folder contents and display them in this treeview. (Also change icon to "open folder")
nItem.ImageIndex := ICON_FOLDER // TODO: Feature: Read folder contents and display them in this treeview. (Also change icon to "open folder")
else
nItem.ImageIndex := 8;
nItem.ImageIndex := ICON_UNKNOWN;
nItem.SelectedIndex := nItem.ImageIndex;
end;
end;
141,6 → 151,8
bin := TRbRecycleBin.Create(LabeledEdit1.Text);
 
nBin := TreeView1.Items.AddChildObject(individualRecyclersNode, bin.FileOrDirectory, bin);
nBin.ImageIndex := ICON_BIN;
nBin.SelectedIndex := nBin.ImageIndex;
individualRecyclersNode.Expand(false);
 
items := TObjectList.Create(false);
160,11 → 172,11
nItem := TreeView1.Items.AddChildObject(nBin, sCaption, item);
 
if FileExists(item.PhysicalFile) then
nItem.ImageIndex := 0
nItem.ImageIndex := ICON_FILE
else if DirectoryExists(item.PhysicalFile) then
nItem.ImageIndex := 10 // TODO: Feature: Read folder contents and display them in this treeview. (Also change icon to "open folder")
nItem.ImageIndex := ICON_FOLDER // TODO: Feature: Read folder contents and display them in this treeview. (Also change icon to "open folder")
else
nItem.ImageIndex := 8;
nItem.ImageIndex := ICON_UNKNOWN;
nItem.SelectedIndex := nItem.ImageIndex;
end;
finally
174,6 → 186,12
nBin.Expand(false);
end;
 
procedure TRecyclerListingMainForm.Button3Click(Sender: TObject);
begin
if OpenDialog1.Execute then
LabeledEdit1.Text := OpenDialog1.FileName;
end;
 
procedure TRecyclerListingMainForm.FormShow(Sender: TObject);
resourcestring
S_LOCAL_RECYCLE_BINS = 'Local recycle bins';
180,11 → 198,11
S_MANUAL_RECYCLE_BINS ='Manually added recycle bins';
begin
localRecyclersNode := TreeView1.Items.Add(nil, S_LOCAL_RECYCLE_BINS);
localRecyclersNode.ImageIndex := 2;
localRecyclersNode.ImageIndex := ICON_TREEVIEW;
localRecyclersNode.SelectedIndex := localRecyclersNode.ImageIndex;
 
individualRecyclersNode := TreeView1.Items.Add(nil, S_MANUAL_RECYCLE_BINS);
individualRecyclersNode.ImageIndex := 2;
individualRecyclersNode.ImageIndex := ICON_TREEVIEW;
individualRecyclersNode.SelectedIndex := individualRecyclersNode.ImageIndex;
end;
 
192,7 → 210,7
var
item: TRbRecycleBinItem;
begin
if TreeView1.Selected.ImageIndex = 0 then
if TreeView1.Selected.ImageIndex = ICON_FILE then
begin
// File
item := TRbRecycleBinItem(TreeView1.Selected.Data);
200,7 → 218,7
// TODO: Maybe we should add a feature to drag'n'drop a file/folder out of RecycleBinUnit into the explorer (With options copy or move, depending on the ShiftState)
ShellExecute(Handle, 'open', PChar(item.PhysicalFile), '', '', SW_NORMAL);
end;
if TreeView1.Selected.ImageIndex = 10 then
if TreeView1.Selected.ImageIndex = ICON_FOLDER then
begin
// Folder
item := TRbRecycleBinItem(TreeView1.Selected.Data);