Page 1 of 1

Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Sun Sep 11, 2016 9:52 pm
by Lunasole
Hi. I was playing a bit with a code which can to delete files from Recycle basing on date when they were deleted to it.

There are two ways to do this: use special interface to control recycle, or use direct access. Surely I've choose 2nd ^^

The recycle in XP is organized following way:
- in a root of every drive is folder called "RECYCLER"
- inside of this folder are subfolders named using user SID
- at last, inside of those subfolders files placed to recycle are located

You need to use external file manager (like Total Commander) to access those folders, or do it programatically.

The files and folders placed to recycle are named like "Dd123.ext" - where "Dd" is like a prefix, 123 is file index, and .ext is extension.

And there is another file placed near - it's named "INFO2" and is system file where all metadata is stored about deleted files.
Windows uses such files to not enumerate all recycled files everytime and to allow files with exact names be placed to recycle without conflicts.

To do something with recycle content or just to examine it, you need to read or write this file manually.
It's however simple enough, the following code reads it and can be easily changed to write too.

For me that stuff interesting to make some daemon for my ElDiablo which will automatically delete oldest files from recycle (basing on deletion date) - as I don't like to purge whole recycle, but also don't like when it contains too much files, but still not reached size limit.

For windows Vista and newer the recycle index format is different (for now I didn't played with it, but maybe soon will).

Code: Select all

EnableExplicit

;{ Common/declares }
	
	Structure TOKEN_USER
		User.SID_AND_ATTRIBUTES
	EndStructure
	
	; Returns string representation of user SID (security identifier)
	; Use ConvertStringSidToSid() API to convert string back to binary
	; RETURN:		string view of SID on success
	Procedure$ GetUserSID()
		Protected hToken, dwBufferSize, hLib
		Protected SID$, *SID.String
		Protected *User.TOKEN_USER
		
		; get process token
		OpenProcessToken_(GetCurrentProcess_(), #TOKEN_QUERY, @hToken)
		; get user token with SID 
		GetTokenInformation_(hToken, #TokenUser, 0, 0, @dwBufferSize)
		*User = AllocateMemory(dwBufferSize)
		GetTokenInformation_(hToken, #TokenUser, *User, dwBufferSize, @dwBufferSize)
		
		; convert SID to a string
		hLib = OpenLibrary(#PB_Any, "Advapi32.dll")
		If IsLibrary(hLib)
			CallFunction(hLib, "ConvertSidToStringSidW", *User\User\Sid, @*SID)
			SID$ = PeekS(*SID)
			LocalFree_(*SID)
			CloseLibrary(hLib)
		EndIf
		
		
		; cleanup
		CloseHandle_(hToken)
		FreeMemory(*User)
		
		; return
		ProcedureReturn SID$
	EndProcedure
	
	
	; enumerates files within given folder (or it's subfolders too)
	; Filter$			a mask to filter files by extensions, separated using "|". example: "txt|jpg", "bmp". empty string used to find all files
	; IgnoredPaths$ 	a list of folders to skip on scan. full paths expected without trailing "\", separated using "|"
	; ScanDeepth 		"-1" = unlimited, "0" = scan only specified folder, "1+" = custom
	; GetFolders:		if true, includes also found folders to results. folder paths ends with "\", unlike file paths
	; RETURN: number of files found; Files() array contains full paths (starting from element 1)
	Procedure GetFiles (Path$, Array Files$ (1), Filter$ = "", IgnoredPaths$ = "", ScanDeepth = 0, GetFolders = #False)
		Static CStep = 10240           	; step to resize Files () array, has affect on performance
		Static Count, Counter, Deepth	; Files() size and count. Deepth is current recursion deepth
		If Deepth = 0
			Count = 0 :   Counter = 0 ; reset static stuff
			If Filter$ : Filter$ + "|" : EndIf
			If IgnoredPaths$ : IgnoredPaths$ + "|" : EndIf
			ReplaceString (Path$, "/", "\", #PB_String_InPlace) ; windows can handle both \ and / (at least explorer.exe can), but it is better to ensure
			Dim Files$ (0)
		EndIf
		If Not (Right(Path$, 1)) = "\" : Path$ + "\" : EndIf
		Protected HDir = ExamineDirectory(#PB_Any, Path$, "*.*")
		Protected Current$
		If HDir
			Deepth + 1
			While NextDirectoryEntry(HDir)         ; iterate all files within directory
				Current$ = DirectoryEntryName(HDir); store current file/folder name for future
				If DirectoryEntryType(HDir) = #PB_DirectoryEntry_File ; enumerate files only
					If PeekC(@Filter$) = 0 Or FindString(Filter$, GetExtensionPart(Current$) + "|", 1, #PB_String_NoCase)
						Counter + 1
						If Counter >= Count
							Count + CStep
							ReDim Files$ (Count)
						EndIf
						Files$(Counter) = Path$ + Current$ ; store full path
					EndIf
				ElseIf DirectoryEntryType(HDir) = #PB_DirectoryEntry_Directory 
					If ScanDeepth = -1 Or Deepth <= ScanDeepth; check recursion deepth
						If Not Current$ = "." And Not Current$ = ".."
							Current$ = Path$ + Current$
							If PeekC(@IgnoredPaths$) And FindString(IgnoredPaths$, Current$ + "|", 1, #PB_String_NoCase)
								; skip this subfolder   
							Else
								If GetFolders ; add folders to results too
									Counter + 1
									If Counter >= Count
										Count + CStep
										ReDim Files$ (Count)
									EndIf
									Files$(Counter) = Current$ + "\"  ; store full path
								EndIf
								GetFiles (Current$, Files$(), Filter$, IgnoredPaths$, ScanDeepth, GetFolders) ; go on with subfolders
							EndIf
						EndIf
					EndIf
				EndIf
			Wend
			FinishDirectory(HDir)
			Deepth - 1
			If Deepth = 0 And Counter   ; proceed return only from first call and only if is something to return
				ReDim Files$ (Counter)
				ProcedureReturn Counter
			EndIf
		EndIf
	EndProcedure
	
	
	; just a quickly written function to format FileTime
	Procedure$ FileTimeToString (FileTime.q)
		Protected STime.SYSTEMTIME
		FileTimeToSystemTime_(@FileTime, @STime)
		
		ProcedureReturn RSet(Str(STime\wYear), 4, "0") + "." +
			 RSet(Str(STime\wMonth), 2, "0") + "." + 
			 RSet(Str(STime\wDay), 2, "0") + " " + 
			 RSet(Str(STime\wHour), 2, "0") + ":" +
			 RSet(Str(STime\wMinute), 2, "0") + ":" + 
			 RSet(Str(STime\wSecond), 2, "0")
	EndProcedure
	
;}

;{ Recycle index file format: Win XP and 98 }
	
	; Following declarations ported from 
	; 	https://github.com/abelcheung/rifiuti2/releases/tag/0.6.1
	
	; These offsets are relative To file start 
	#RECORD_COUNT_OFFSET     = 4
	#RECORD_MAXINDEX_OFFSET  = 8
	#RECORD_SIZE_OFFSET      = 12
	#RECORD_START_OFFSET     = 20
	
	; Following offsets are relative To start of each record
	#LEGACY_FILENAME_OFFSET  = $0
	#RECORD_INDEX_OFFSET     = #MAX_PATH
	#DRIVE_LETTER_OFFSET     = ((#MAX_PATH) + 4)
	#FILETIME_OFFSET         = ((#MAX_PATH) + 8)
	#FILESIZE_OFFSET         = ((#MAX_PATH) + 16)
	#UNICODE_FILENAME_OFFSET = ((#MAX_PATH) + 20)
	
	#VERSION4_RECORD_SIZE    = ((#MAX_PATH) + 20)        ; /* 280 bytes */
	#VERSION5_RECORD_SIZE    = ((#MAX_PATH) * 3 + 20)	 ; /* 800 bytes */
	
	
	; Index file header
	Structure HEADER
		SIGNATURE.l			; should be $00000005, but maybe that only for Version5 format
		RECORD_COUNT.l		; total number of records inside of file
		RECENT_INDEX.l		; the last index used to enumerate files. windows adds + 1 to this when adding new record, but don't decrement when deleting
		RECORD_SIZE.l		; the size of every record. valid values are 280 or 800 bytes
		UNKNOWN1.l
	EndStructure
	
	; Windows 98 index record format = 280 bytes
	Structure V4RECORD
		LegacyFilename.a [#MAX_PATH]	; old filename (win98), ascii
		Index.l							; record index, corresponding to index of some deleted file (recycle filenames are like "Dd123.ext", where 123 is index)
		DriveLetter.l					; index of drive letter. 1 = "A", 2 = "B" and so on
		FileTime.q						; timestamp showing when file was deleted
		FileSize.l						; the size of deleted file
	EndStructure
	
	; Win2k, XP (unicode filenames) = 800 bytes
	Structure V5RECORD Extends V4RECORD
		FileName.w [#MAX_PATH]			; actual filename, unicode
	EndStructure
	
;}

;{ INFO2 read/write }
	
	; Read INFO2 index file to array of records
	; this function didn't tested with V4 records (Windows 98 format), but should be easy to adapt it
	; Out()			output array
	; FileName$		path to a INFO2 file
	; RETURN:		number of records read, and records placed to Out() array starting from index 1
	Procedure INFO2_ReadRecords (Array Out.V5RECORD(1), FileName$)
		Protected hFile = OpenFile(#PB_Any, FileName$, #PB_File_SharedRead)
		Protected Header.HEADER
		Protected *Buffer.V5RECORD
		Protected RealCount
		
		If IsFile(hFile)
			; check if file header is valid
			If ReadData(hFile, Header, SizeOf(Header)) = SizeOf(HEADER) And HEADER\SIGNATURE = $00000005
				; check if record size is one of valid
				If (Header\RECORD_SIZE = #VERSION4_RECORD_SIZE) Or (Header\RECORD_SIZE = #VERSION5_RECORD_SIZE)
					; 				Debug "Record count is " + Str(Header\RECORD_COUNT)
					; 				Debug "Record max index is " + Str(Header\RECENT_INDEX)
					
					; allocate buffer to read records
					*Buffer = AllocateMemory(Header\RECORD_SIZE)
					
					; jump to records section
					FileSeek(hFile, #RECORD_START_OFFSET, #PB_Absolute)
					
					; read all records until the end of file reached
					While Eof(hFile) = 0
						; try to read next record
						If ReadData(hFile, *Buffer, Header\RECORD_SIZE) = Header\RECORD_SIZE
							RealCount + 1
							If RealCount > ArraySize(Out())
								ReDim Out(RealCount + 2048)
							EndIf
							; add readed record to results
							CopyMemory(*Buffer, @Out(RealCount), Header\RECORD_SIZE)
						EndIf
					Wend
					FreeMemory(*Buffer)
				EndIf
			EndIf
			
			CloseFile(hFile)
		EndIf
		
		; return
		ReDim Out(RealCount)
		ProcedureReturn RealCount
	EndProcedure
	
	; Write records to a INFO2 file
	; this function didn't tested with V4 records (Windows 98 format), but should be easy to adapt it
	; Out()			records to write
	; FileName$		path to an output INFO2 file
	; RETURN:		true on success
	Procedure INFO2_WriteRecords (Array Records.V5RECORD(1), FileName$)
		Protected hFile = CreateFile(#PB_Any, FileName$)
		Protected Header.HEADER
		Protected RealCount
		
		If IsFile(hFile)
			; sort records by index
			SortStructuredArray(Records(), #PB_Sort_Ascending, OffsetOf(V5RECORD\Index), #PB_Long, 1, ArraySize(Records()))
			
			; write file header
			Header\SIGNATURE = $00000005
			Header\RECORD_SIZE = SizeOf(V5RECORD)
			Header\RECORD_COUNT = ArraySize(Records())
			Header\RECENT_INDEX = Records(ArraySize(Records()))\Index
			WriteData(hFile, Header, SizeOf(Header))
			
			; write all records
			For RealCount = 1 To ArraySize(Records())
				WriteData(hFile, Records(RealCount), SizeOf(V5RECORD))
			Next RealCount
			
			CloseFile(hFile)
		EndIf
		
		; return
		ProcedureReturn Bool(hFile)
	EndProcedure
	
;}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; test

; to examine drives and RECYCLER paths
Define Drive, Path$
Define UserSID$ = GetUserSID()

; to examine files inside of RECYCLER
Dim R$(0): Define RCount

; to store content of loaded INFO2 file
Dim Records.V5RECORD (0): Define RecNum

; count all records in all INFO2 files
Define TotalDel



For Drive = 'A' To 'Z'
	; current user recycled files on current drive
	Path$ = Chr(Drive) + ":\RECYCLER\" + UserSID$
	
	; if path is valid
	If FileSize(Path$) = -2
		; get all files of user recycler
		RCount = GetFiles(Path$, R$(), "", "", -1, #False)
		
		; iterate all collected files
		; ; only INFO2 files are interesting
		While RCount > 0
			If GetFilePart(R$(RCount)) = "INFO2"
				RecNum = INFO2_ReadRecords(Records(), R$(RCount))
				TotalDel + RecNum
			EndIf
			RCount - 1
		Wend
	EndIf
Next Drive

Debug "Records found in INFO2 files: " + Str(TotalDel)

Re: Accessing content of Recycle Bin (Windows XP)

Posted: Mon Sep 12, 2016 5:56 pm
by Lunasole
Greatly extended attached code.
Now it can analyze whole Recycle (exactly, only files related to a current user) and rewrite every INFO2 file (as well as manage deleted files). However I didn't tested this yet ^^

The INFO2 format is almost fully clear now, I just still not sure about 4 bytes inside of INFO2 header (UNKNOWN1 field). They are looking like crc32... maybe.

Re: Accessing content of Recycle Bin (Windows XP)

Posted: Tue Sep 13, 2016 9:49 am
by Kwai chang caine
Unfortunately, i have W7, so i can't test your code (Return zero)
But thanks when even for sharing 8)

Re: Accessing content of Recycle Bin (Windows XP)

Posted: Tue Sep 13, 2016 6:07 pm
by Lunasole
Kwai chang caine wrote:Unfortunately, i have W7, so i can't test your code (Return zero)
But thanks when even for sharing 8)
Yes, W7 has different recycler structure and I didn't made code for it yet.

Btw, you can use Oracle VirtualBox (as for me that is greatest VM soft, and it is open-source) to set up XP on it and test stuff. If your CPU has nice virtualization support like AMD-V, there will be almost zero overhead by CPU usage and memory consumption for running virtual OS. That's also a way to see how incredibly fast is XP on modern hardware, comparing to W7 or W10 :3

Re: Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Tue Sep 13, 2016 9:22 pm
by Lunasole
FOR WINDOWS VISTA AND HIGHER

Here is some code for Vista and Seven (didn't tested with 8 and 10, because damn those stupid OS ^^)

The recycle structure used in Vista is different and generally very primitive and less optimized that one in XP:
- every index record is stored inside separated file, which has name beginning with $I and size of 544 bytes
- every deleted file or folder has name starting with $R
The path is based on user SID, same as in XP.

So, very simple. 1 index file = 1 structure, and removing file from recycle requires just to delete $I and $R files ($R can be folder with subfolders also).
Following example enumerates whole recycle content for current user and can be easily changed to delete or add some files to recycle.

Code: Select all

EnableExplicit

;{ Common/declares }
	
	; Extended console output
	; If Newline is 0, then string is printed without newline sequence.
	; Later it can be used to complete string (operation result):
	;	if text$ = "+", then OK will be printed
	;	if text$ = "-", then FAILED will be printed
	Procedure xLog (Text$, Newline = 0, TextColor = 0)
		If Text$ = "+"
			ConsoleColor(10, 0)
			PrintN(" Ok")
		ElseIf Text$ = "-"
			ConsoleColor(12, 0)
			PrintN(" FAILED")
		Else
			If Not TextColor
				ConsoleColor(7, 0)
			Else
				ConsoleColor(TextColor, 0)
			EndIf
			If Newline
				PrintN(Text$)
			Else
				Print(Text$)
			EndIf
		EndIf
	EndProcedure
	
	
	
	Structure TOKEN_USER
		User.SID_AND_ATTRIBUTES
	EndStructure
	
	; Returns string representation of user SID (security identifier)
	; Use ConvertStringSidToSid() API to convert string back to binary
	; RETURN:		string view of SID on success
	Procedure$ GetUserSID()
		Protected hToken, dwBufferSize, hLib
		Protected SID$, *SID.String
		Protected *User.TOKEN_USER
		
		; get process token
		OpenProcessToken_(GetCurrentProcess_(), #TOKEN_QUERY, @hToken)
		; get user token with SID 
		GetTokenInformation_(hToken, #TokenUser, 0, 0, @dwBufferSize)
		*User = AllocateMemory(dwBufferSize)
		GetTokenInformation_(hToken, #TokenUser, *User, dwBufferSize, @dwBufferSize)
		
		; convert SID to a string
		hLib = OpenLibrary(#PB_Any, "Advapi32.dll")
		If IsLibrary(hLib)
			CallFunction(hLib, "ConvertSidToStringSidW", *User\User\Sid, @*SID)
			SID$ = PeekS(*SID)
			LocalFree_(*SID)
			CloseLibrary(hLib)
		EndIf
		
		
		; cleanup
		CloseHandle_(hToken)
		FreeMemory(*User)
		
		; return
		ProcedureReturn SID$
	EndProcedure
	
	
	; enumerates files within given folder (or it's subfolders too)
	; Filter$			a mask to filter files by extensions, separated using "|". example: "txt|jpg", "bmp". empty string used to find all files
	; IgnoredPaths$ 	a list of folders to skip on scan. full paths expected without trailing "\", separated using "|"
	; ScanDeepth 		"-1" = unlimited, "0" = scan only specified folder, "1+" = custom
	; GetFolders:		if true, includes also found folders to results. folder paths ends with "\", unlike file paths
	; RETURN: number of files found; Files() array contains full paths (starting from element 1)
	Procedure GetFiles (Path$, Array Files$ (1), Filter$ = "", IgnoredPaths$ = "", ScanDeepth = 0, GetFolders = #False)
		Static CStep = 10240           	; step to resize Files () array, has affect on performance
		Static Count, Counter, Deepth	; Files() size and count. Deepth is current recursion deepth
		If Deepth = 0
			Count = 0 :   Counter = 0 ; reset static stuff
			If Filter$ : Filter$ + "|" : EndIf
			If IgnoredPaths$ : IgnoredPaths$ + "|" : EndIf
			ReplaceString (Path$, "/", "\", #PB_String_InPlace) ; windows can handle both \ and / (at least explorer.exe can), but it is better to ensure
			Dim Files$ (0)
		EndIf
		If Not (Right(Path$, 1)) = "\" : Path$ + "\" : EndIf
		Protected HDir = ExamineDirectory(#PB_Any, Path$, "*.*")
		Protected Current$
		If HDir
			Deepth + 1
			While NextDirectoryEntry(HDir)         ; iterate all files within directory
				Current$ = DirectoryEntryName(HDir); store current file/folder name for future
				If DirectoryEntryType(HDir) = #PB_DirectoryEntry_File ; enumerate files only
					If PeekC(@Filter$) = 0 Or FindString(Filter$, GetExtensionPart(Current$) + "|", 1, #PB_String_NoCase)
						Counter + 1
						If Counter >= Count
							Count + CStep
							ReDim Files$ (Count)
						EndIf
						Files$(Counter) = Path$ + Current$ ; store full path
					EndIf
				ElseIf DirectoryEntryType(HDir) = #PB_DirectoryEntry_Directory 
					If ScanDeepth = -1 Or Deepth <= ScanDeepth; check recursion deepth
						If Not Current$ = "." And Not Current$ = ".."
							Current$ = Path$ + Current$
							If PeekC(@IgnoredPaths$) And FindString(IgnoredPaths$, Current$ + "|", 1, #PB_String_NoCase)
								; skip this subfolder   
							Else
								If GetFolders ; add folders to results too
									Counter + 1
									If Counter >= Count
										Count + CStep
										ReDim Files$ (Count)
									EndIf
									Files$(Counter) = Current$ + "\"  ; store full path
								EndIf
								GetFiles (Current$, Files$(), Filter$, IgnoredPaths$, ScanDeepth, GetFolders) ; go on with subfolders
							EndIf
						EndIf
					EndIf
				EndIf
			Wend
			FinishDirectory(HDir)
			Deepth - 1
			If Deepth = 0 And Counter   ; proceed return only from first call and only if is something to return
				ReDim Files$ (Counter)
				ProcedureReturn Counter
			EndIf
		EndIf
	EndProcedure
	
	
	; just a quickly written function to format FileTime
	Procedure$ FileTimeToString (FileTime.q)
		Protected STime.SYSTEMTIME
		FileTimeToSystemTime_(@FileTime, @STime)
		
		ProcedureReturn RSet(Str(STime\wYear), 4, "0") + "." +
		                                                 RSet(Str(STime\wMonth), 2, "0") + "." + 
		                                                 RSet(Str(STime\wDay), 2, "0") + " " + 
		                                                 RSet(Str(STime\wHour), 2, "0") + ":" +
		                                                 RSet(Str(STime\wMinute), 2, "0") + ":" + 
		                                                 RSet(Str(STime\wSecond), 2, "0")
	EndProcedure
	
	; just a quickly written function to format FileTime
	Procedure FileTimeToDate (FileTime.q)
		Protected STime.SYSTEMTIME
		FileTimeToSystemTime_(@FileTime, @STime)
		
		ProcedureReturn Date(STime\wYear, STime\wMonth, STime\wDay, STime\wHour, STime\wMinute, STime\wSecond)
	EndProcedure
	
	
;}

;{ Recycle index file format: Windows Vista and 7 }
	
	; Windows Vista and 7 index record format = 544 bytes
	Structure VISTARECORD
		SIGNATURE.q					; should be $0000000000000001
		FileSize.q					; the size of deleted file
		FileTime.q					; timestamp showing when file was deleted
		FileName.w [#MAX_PATH]		; original filename, unicode
	EndStructure
	
	
	; Read Vista index file
	; *Out			output record
	; FileName$		path to a index file
	; RETURN:		true on success and data placed to *Out
	Procedure I_ReadRecord (*Out.VISTARECORD, Filename$)
		Protected hFile = OpenFile(#PB_Any, FileName$, #PB_File_SharedRead)
		Protected Success 
		If IsFile(hFile)
			; check if file header is valid
			If ReadData(hFile, *Out, SizeOf(VISTARECORD)) = SizeOf(VISTARECORD) And *Out\SIGNATURE = $0000000000000001
				; seems like valid record readed, return 
				Success = #True
			EndIf
			CloseFile(hFile)
		EndIf
		ProcedureReturn Success
	EndProcedure
	
;}



;;;;;;;;;;;;;;;;;;;;;;;;;;;
; some test

Procedure ProcessRecyclerVista ()
	; to examine drives and RECYCLER paths
	Protected 	Drive, Path$
	Protected  	UserSID$ = GetUserSID()
	
	; to examine files inside of RECYCLER
	Protected Dim 	RecyclerFiles$(0)
	Protected	 	RecyclerFilesCount, TmpFile
	
	; global statistic
	Protected TotalFiles, TotalRecords, TotalDel, OldDel, BrokenDel
	
	;INFO: DEBUG: spoof user SID for testing
	; 	UserSID$ = "S-1-5-21-3898355224-76657935-2475824961-1005"
	
	
	xLog("Analyzing recycle content for current user...", #True)
	For Drive = 'A' To 'Z'
		; current user recycled files on current drive
		Path$ = Chr(Drive) + ":\$Recycle.Bin\" + UserSID$
		
		; if path is valid
		If FileSize(Path$) = -2
			
			; get all files of user recycler
			RecyclerFilesCount = GetFiles(Path$, RecyclerFiles$(), "", "", 1, #True)
			TotalFiles + RecyclerFilesCount
			
			; iterate all collected files on this drive
			For TmpFile = 1 To RecyclerFilesCount
				
				Protected TmpName$ = GetFilePart(RecyclerFiles$(TmpFile))
				Protected TmpNameDel$ 
				Protected Index.VISTARECORD
				
				; if file name starts of $I, that is index file
				; if file name starts of $R, that is deleted file (or directory)
				If UCase(Left(TmpName$, 2)) = "$I"
					TmpNameDel$ = "$R" + Mid(TmpName$, 3)
					TotalRecords + 1
					
					; if there is index file, but deleted file for it missing
					If FileSize(GetPathPart(RecyclerFiles$(TmpFile)) + TmpNameDel$) = -1
						Debug "Broken index: " + TmpName$
						BrokenDel + 1
						; TODO: broken index file should be deleted
					Else
						; parse index file
						If I_ReadRecord (Index, RecyclerFiles$(TmpFile))
							; this is valid index file
							;TODO: date should be compared and both $I and $R files deleted if needed
							; 							Protected X = FileTimeToDate(Index\FileTime)
							; 							Debug PeekS(@Index\FileName[0], #MAX_PATH, #PB_Unicode)
							; 							Debug FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", X)
							; 							Debug "--"
							OldDel + 1
							
						Else
							; index doesn't look valid, probably should delete it
							Debug "Broken index: " + TmpName$
							BrokenDel + 1
							; TODO:
							; index should be deleted
							; also attached $R file should be deleted
						EndIf
					EndIf
				EndIf
			Next TmpFile
		EndIf
	Next Drive
	
	xLog("Files and folders found ........... " + Str(TotalFiles), #True)
	xLog("Indexes total ..................... " + Str(TotalRecords), #True)
	xLog("Indexes removed ................... " + Str(TotalDel), #True)
	xLog("   Broken records ................. " + Str(BrokenDel), #True, 8)
	xLog("   Old files + their records ...... " + Str(OldDel), #True, 8)
	xLog("PS. Nothing was removed, it's just a simulation ^^", #True)
	xLog("", #True)
	xLog("Press Enter key to quit...", #True)
	
EndProcedure



If OpenConsole("RecycleFlow (Vista, 7)")
	ProcessRecyclerVista()
	Input()
EndIf

Re: Accessing content of Recycle Bin (Windows XP)

Posted: Tue Sep 13, 2016 9:23 pm
by Lunasole
PS. The PB forum still corrupts code where tabs used instead spaces :)
Nothing to do

Re: Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Fri Sep 30, 2016 7:21 am
by Roger Hågensen
There is an mrthod for accessing the recycle bin https://blogs.msdn.microsoft.com/oldnew ... 00/?p=9773

Re: Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Sun Oct 02, 2016 1:09 am
by Lunasole
Roger Hågensen wrote:There is an mrthod for accessing the recycle bin https://blogs.msdn.microsoft.com/oldnew ... 00/?p=9773
Yes, I've mentioned such interface already. Raw access provides more abilities than wrapped API.

Re: Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Mon Oct 03, 2016 3:32 pm
by Kwai chang caine
Hello LUNASOLE
I have tested your code on W7 X86 8)
And just for your information, i have actually 1254 files in my recycle bin and your code found
Analyzing recycle content for current user...
Files and folders found ........... 33
Indexes total ..................... 27
Indexes removed ................... 0
Broken records ................. 25
Old files + their records ...... 2
PS. Nothing was removed, it's just a simulation ^^

Press Enter key to quit...

Re: Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Mon Oct 03, 2016 11:34 pm
by Lunasole
Kwai chang caine wrote:Hello LUNASOLE
I have tested your code on W7 X86 8)
And just for your information, i have actually 1254 files in my recycle bin and your code found
Sorry man, can't say anything about this for now, as currently I've joined both variants together and using them in my app and cannot use them as separated code yet. Probably there are some bugs in versions posted here, but also keep in mind that you can have 1254 files in recycle, but if they are inside one folder, only one item in recycler and recycler index file will be created for them, so this code counter will show 1.

Re: Accessing content of Recycle Bin (Windows XP, Vista, 7)

Posted: Tue Oct 04, 2016 11:46 am
by Kwai chang caine
Aaah !! Ok :D