storme Posted August 11, 2009 Posted August 11, 2009 (edited) G'day AllI've been fighting with this script for a while now and I thought it was time to bring it out and have a few people laugh at it, tell me there is an easier way and for me to go beat my head into a brick wall for being so stupid. Anyway on to the first step in the plan..... The script came out of another script I'm writing to cleanup my computer. (The usual thing temp directory, IE cache, etc.) But with FileDelete kept missing files, so I went on a journey to discover how to KILL THEM ALL >_< and this is the point I’ve reached.To delete a file/directory the script tries the following in order:(If the file/directory is deleted on any step it skips the following steps.1. Standard - FileDelete/FileRecycle2. Runs Unlocker.exe then FileDelete/FileRecycle3. Runs takeown.exe then FileDelete/FileRecycle4. If the deletetion is a file then it kills the process then FileDelete/FileRecycle5. IF all else fails it adds it to the PendingFileRenameOperations to be deleted on the next restart.Please feel free to look though the code and make any suggestions or improvements you think appropriate.If there is something that should be added then please let me know.There is still a LOT of debug lines in the code that are staying there until I have it finalised, just ignore them. BEWARE if this script does its job properly it will DELETE FILES (I know it sounds silly but I don’t want anyone to post here and say that my script has deleted their important files.)expandcollapse popup#include <File.au3> #include <Misc.au3> Opt("MustDeclareVars", 1) ;Stop script from running more than once _Singleton(@ScriptName) If Not @Compiled Then Opt("TrayIconDebug", 1) EndIf FileInstall("takeown.exe", @TempDir & "\takeown.exe", 1) FileInstall("Unlocker.exe", @TempDir & "\Unlocker.exe", 1) FileInstall("UnlockerDriver5.sys", @TempDir & "\UnlockerDriver5.sys", 1) ; TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST Global $RL = 0 Run(@ScriptDir & "\MAKE_directories.bat") Sleep(1000) DirectoryCleanup("tst_dir", True) Exit ; TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ; Delete the contents of specified directory ; DON'T delete directory Func DirectoryCleanup($Directory, $removeSubDir = True, $Recycle = True) Local $searchHandle ; Handle for search string Local $fileName ; Name of current file Local $result ; Store result of various actions for later compare If FileExists($Directory) Then ;Try standard delete If $Recycle Then $result = FileRecycle($Directory & "\*.*") ConsoleWrite("DC " & $RL & "FileRecycle(" & $Directory & " & '\*.*') result = " & $result & @CR) Else $result = FileDelete($Directory & "\*.*") ConsoleWrite("DC " & $RL & "FileDelete(" & $Directory & " & '\*.*') result = " & $result & @CR) EndIf ;Check and delete any files/directories left $searchHandle = FileFindFirstFile($Directory & "\*") If $searchHandle <> -1 Then While 1 $fileName = FileFindNextFile($searchHandle) If @error = 1 Then ExitLoop ; no more files ConsoleWrite(@CR & "DC " & $RL & "FileFindNextFile = " & $Directory & "\" & $fileName & @CR) ;Check if Directory If StringInStr(FileGetAttrib($Directory & "\" & $fileName), "D") <> 0 Then ConsoleWrite("DC " & $RL & "DIRECTORY" & @CR) DirectoryRemove($Directory, $removeSubDir, $Recycle) Else ConsoleWrite("DC " & $RL & "FILE" & @CR) FileRemove($Directory & "\" & $fileName) EndIf WEnd FileClose($searchHandle) EndIf EndIf EndFunc ;==>DirectoryCleanup ;Remove directory & it's contents Func DirectoryRemove($Directory, $removeSubDir = True, $Recycle = True) Local $searchHandle ; Handle for search string Local $fileName ; Name of current file Local $result = True; Store result of various actions for later compare If Not FileExists($Directory) Then Return $result $RL = $RL + 1 ;Try a quick deletion - If it works then no need for fancy stuff If $removeSubDir And StringInStr(FileGetAttrib($Directory & "\" & $fileName), "D") Then ; If subdirectories aren't to be removed then can't just delete If $Recycle Then $result = FileRecycle($Directory) ConsoleWrite("DR " & $RL & "FileRecycle(" & $Directory & ") result = " & $result & @CR) Else $result = FileDelete($Directory) ConsoleWrite("DR " & $RL & "FileDelete(" & $Directory & ") result = " & $result & @CR) EndIf EndIf If FileExists($Directory) Then If $Recycle Then $result = FileRecycle($Directory & "\*.*") ConsoleWrite("DR " & $RL & "FileRecycle(" & $Directory & " & '\*.*') result = " & $result & @CR) Else $result = FileDelete($Directory & "\*.*") ConsoleWrite("DR " & $RL & "FileDelete(" & $Directory & " & '\*.*') result = " & $result & @CR) EndIf ;Check and delete any files/directories left $searchHandle = FileFindFirstFile($Directory & "\*") If $searchHandle <> -1 Then While 1 $fileName = FileFindNextFile($searchHandle) If @error = 1 Then ExitLoop ; no more files ConsoleWrite(@CR & "DR " & $RL & "FileFindNextFile = " & $Directory & "\" & $fileName & @CR) ;Check if Directory If StringInStr(FileGetAttrib($Directory & "\" & $fileName), "D") <> 0 Then ConsoleWrite("DR " & $RL & "DIRECTORY" & @CR) If $removeSubDir Then ;**TODO** Use TAKEOWN to take ownership of the Directory before deleting. ;"takeown /f " & $fileName & "/r /dY" ;Remove Directory FileRemove($Directory & "\" & $fileName) ; Try to remove directory If FileExists($Directory & "\" & $fileName) Then ;Removal Failed (must be a "stuck" file) recursivly check sub directory ConsoleWrite("DR " & $RL & "DIRECTORY - Recurse" & @CR) $result = DirectoryRemove($Directory & "\" & $fileName) FileRemove($Directory & "\" & $fileName) ; Try to remove directory again (should be empty) $RL = $RL - 1 EndIf EndIf Else ConsoleWrite("DR " & $RL & "FILE" & @CR) $result = FileRemove($Directory & "\" & $fileName) EndIf WEnd FileClose($searchHandle) EndIf EndIf Return $result EndFunc ;==>DirectoryRemove ;Remove file even if it's locked/inuse/etc Func FileRemove($fileName, $Recycle = True) Local $result = True If FileExists($fileName) = 0 Then Return ;Try basic delete/recycle If $Recycle Then $result = FileRecycle($fileName) ConsoleWrite("FR FileRecycle(" & $fileName & ") result = " & $result & @CR) Else $result = FileDelete($fileName) ConsoleWrite("RF FileDelete(" & $fileName & ") result = " & $result & @CR) EndIf ;Test if Bsic delete worked If Not $result Then ;Basic delete failed ;Reset attributes so file can be deleted (untested) FileSetAttrib($fileName, "-RSH+N") ;try methods of unlocking files For $Attempt = 1 To 3 Select Case $Attempt = 1 ConsoleWrite("FR UNLOCKER " & $fileName & @CR) ;Remove open handles to files ConsoleWrite('\Unlocker.exe "' & _PathFull($fileName) & '" -S -D') RunWait(@TempDir & '\Unlocker.exe "' & _PathFull($fileName) & '" -S -D');, @SW_HIDE) If @error Then MsgBox(0, "Unlocker.exe -S Error", @error & @CR & $fileName) EndIf Case $Attempt = 2 ;**TODO** Use TAKEOWN to take ownership of the file before deleting. ConsoleWrite("FR Takeown " & $fileName & @CR) RunWait(@TempDir & "\takeown.exe " & $fileName, "", @SW_HIDE) If @error Then MsgBox(0, "takeown.exe Error", @error) EndIf Case $Attempt = 3 ConsoleWrite("FR Close process " & $fileName & @CR) ;CLOSE running programs ;extract only filename to feed to processkill Local $szDrive, $szDir, $szFName, $szExt _PathSplit($fileName, $szDrive, $szDir, $szFName, $szExt) ProcessKill($szFName & $szExt) EndSelect $result = True ; reset for new clean If FileExists($fileName) Then If $Recycle Then $result = FileRecycle($fileName) ConsoleWrite("FR FileRecycle(" & $fileName & ") result = " & $result & @CR) Else $result = FileDelete($fileName) ConsoleWrite("RF FileDelete(" & $fileName & ") result = " & $result & @CR) EndIf Else ExitLoop EndIf Next If Not $result Then ; Only Files can be deleted like this (Check if it's a file) If StringInStr(FileGetAttrib($fileName), "D") = 0 Then ConsoleWrite(" MOVE FILE on restart " & $fileName & ") result = " & $result & @CR) ;Delete File on system restart Local $pe = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager" Local $pv = "PendingFileRenameOperations" RegWrite($pe, $pv, "REG_MULTI_SZ", "\??\" & FileGetShortName($fileName) & @LF & @LF & RegRead($pe, $pv)) Else ;ConsoleWrite(" WILL NOT MOVE DIRECTORY on restart " & $fileName & @CR) EndIf ;**TODO** What about sending to "recycling bin" ?? how can I do that on restart ?? EndIf EndIf ConsoleWrite("FR Result = " & $result & @CR) Return $result EndFunc ;==>FileRemove Func ProcessKill($ProcessKill_in);PID or the name of the process ;dexto http://www.autoitscript.com/forum/index.php?showtopic=95999&view=findpost&p=690149 Local $ProcessKill_PID, $ProcessKill_all, $ProcessKill_trackEach, $ProcessKill_each, $ProcessKill_PIDtoKill Local $ProcessKill_objWMIService, $ProcessKill_procToKill, $ProcessKill_track If IsNumber($ProcessKill_in) Then $ProcessKill_PID = $ProcessKill_in Else $ProcessKill_all = ProcessList($ProcessKill_in) If @error Then Return 0 $ProcessKill_trackEach = 0 For $ProcessKill_each = 1 To $ProcessKill_all[0][0] $ProcessKill_trackEach += ProcessKill($ProcessKill_all[$ProcessKill_each][1]) Next If $ProcessKill_trackEach == $ProcessKill_all[0][0] Then Return 1 Else Return 0 EndIf EndIf $ProcessKill_PIDtoKill = ProcessExists($ProcessKill_PID) If $ProcessKill_PIDtoKill == 0 Then Return 0 EndIf ProcessClose($ProcessKill_PIDtoKill) Sleep(300) If ProcessExists($ProcessKill_PIDtoKill) == 0 Then Return 1 EndIf $ProcessKill_objWMIService = ObjGet("winmgmts:\root\cimv2") $ProcessKill_procToKill = $ProcessKill_objWMIService.ExecQuery("Select * from win32_process where processid = " & $ProcessKill_PIDtoKill) $ProcessKill_track = 0 For $ProcessKill_procToKillThread In $ProcessKill_procToKill $ProcessKill_track += ConsoleWrite($ProcessKill_procToKillThread.Terminate & @CRLF) Next If $ProcessKill_track > 0 Then Return 0 EndIf Return 1 EndFunc ;==>ProcessKill Func SetStatus($text) ;TODO Use to set status line or label or .... EndFunc ;==>SetStatusThe attached zip file contains the script, unlocker, takeown and my test batch file.EDIT: Forgot to click "Attach this file" DOH!Thanks in advance for ALL constructive comments.directorycleanV0.07.zip Edited August 11, 2009 by storme Some of my small contributions to AutoIt Browse for Folder Dialog - Automation SysTreeView32 | FileHippo Download and/or retrieve program information | Get installedpath from uninstall key in registry | RoboCopy function John Morrison aka Storm-E
storme Posted August 12, 2009 Author Posted August 12, 2009 Well no interest...sigh...but not to worry I wrote it for my little project and though others mught find it usefull. Anyway I've adding some new options to the functions (all optional) that allow you to decide which of the optionals (unlock, takeown, etc) you want to use. I HAVN'T undated the zip file in the first post yet. expandcollapse popup#include <File.au3> #include <Misc.au3> Opt("MustDeclareVars", 1) ;Stop script from running more than once _Singleton(@ScriptName) If Not @Compiled Then Opt("TrayIconDebug", 1) EndIf FileInstall("takeown.exe", @TempDir & "\takeown.exe", 1) FileInstall("Unlocker.exe", @TempDir & "\Unlocker.exe", 1) FileInstall("UnlockerDriver5.sys", @TempDir & "\UnlockerDriver5.sys", 1) ; TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST Global $RL = 0 Run(@ScriptDir & "\MAKE_directories.bat") Sleep(1000) DirectoryCleanup("tst_dir", True, True, True, True, True, True) Exit ; TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ; Delete the contents of specified directory ; DON'T delete directory Func DirectoryCleanup($Directory, $removeSubDir = True, $Recycle = True, $Unlock = False, $Takeown = False, $KillProcess = False, $DelOnReboot = False) Local $searchHandle ; Handle for search string Local $fileName ; Name of current file Local $result ; Store result of various actions for later compare If FileExists($Directory) Then ;Try standard delete If $Recycle Then $result = FileRecycle($Directory & "\*.*") ConsoleWrite("DC " & $RL & "FileRecycle(" & $Directory & " & '\*.*') result = " & $result & @CR) Else $result = FileDelete($Directory & "\*.*") ConsoleWrite("DC " & $RL & "FileDelete(" & $Directory & " & '\*.*') result = " & $result & @CR) EndIf ;Check and delete any files/directories left $searchHandle = FileFindFirstFile($Directory & "\*") If $searchHandle <> -1 Then While 1 $fileName = FileFindNextFile($searchHandle) If @error = 1 Then ExitLoop ; no more files ConsoleWrite(@CR & "DC " & $RL & "FileFindNextFile = " & $Directory & "\" & $fileName & @CR) ;Check if Directory If StringInStr(FileGetAttrib($Directory & "\" & $fileName), "D") <> 0 Then ConsoleWrite("DC " & $RL & "DIRECTORY" & @CR) DirectoryRemove($Directory, $removeSubDir, $Recycle, $Unlock, $Takeown, $KillProcess, $DelOnReboot) Else ConsoleWrite("DC " & $RL & "FILE" & @CR) FileRemove($Directory & "\" & $fileName, $Unlock, $Takeown, $KillProcess, $DelOnReboot) EndIf WEnd FileClose($searchHandle) EndIf EndIf EndFunc ;==>DirectoryCleanup ;Remove directory & it's contents Func DirectoryRemove($Directory, $removeSubDir = True, $Recycle = True, $Unlock = False, $Takeown = False, $KillProcess = False, $DelOnReboot = False) Local $searchHandle ; Handle for search string Local $fileName ; Name of current file Local $result = True; Store result of various actions for later compare If Not FileExists($Directory) Then Return $result $RL = $RL + 1 ;Try a quick deletion - If it works then no need for fancy stuff If $removeSubDir And StringInStr(FileGetAttrib($Directory & "\" & $fileName), "D") Then ; If subdirectories aren't to be removed then can't just delete If $Recycle Then $result = FileRecycle($Directory) ConsoleWrite("DR " & $RL & "FileRecycle(" & $Directory & ") result = " & $result & @CR) Else $result = FileDelete($Directory) ConsoleWrite("DR " & $RL & "FileDelete(" & $Directory & ") result = " & $result & @CR) EndIf EndIf If FileExists($Directory) Then If $Recycle Then $result = FileRecycle($Directory & "\*.*") ConsoleWrite("DR " & $RL & "FileRecycle(" & $Directory & " & '\*.*') result = " & $result & @CR) Else $result = FileDelete($Directory & "\*.*") ConsoleWrite("DR " & $RL & "FileDelete(" & $Directory & " & '\*.*') result = " & $result & @CR) EndIf ;Check and delete any files/directories left $searchHandle = FileFindFirstFile($Directory & "\*") If $searchHandle <> -1 Then While 1 $fileName = FileFindNextFile($searchHandle) If @error = 1 Then ExitLoop ; no more files ConsoleWrite(@CR & "DR " & $RL & "FileFindNextFile = " & $Directory & "\" & $fileName & @CR) ;Check if Directory If StringInStr(FileGetAttrib($Directory & "\" & $fileName), "D") <> 0 Then ConsoleWrite("DR " & $RL & "DIRECTORY" & @CR) If $removeSubDir Then ;**TODO** Use TAKEOWN to take ownership of the Directory before deleting. ;"takeown /f " & $fileName & "/r /dY" ;Remove Directory FileRemove($Directory & "\" & $fileName) ; Try to remove directory If FileExists($Directory & "\" & $fileName) Then ;Removal Failed (must be a "stuck" file) recursivly check sub directory ConsoleWrite("DR " & $RL & "DIRECTORY - Recurse" & @CR) $result = DirectoryRemove($Directory & "\" & $fileName, $Unlock, $Takeown, $KillProcess, $DelOnReboot) FileRemove($Directory & "\" & $fileName, $Unlock, $Takeown, $KillProcess, $DelOnReboot) ; Try to remove directory again (should be empty) $RL = $RL - 1 EndIf EndIf Else ConsoleWrite("DR " & $RL & "FILE" & @CR) $result = FileRemove($Directory & "\" & $fileName, $Unlock, $Takeown, $KillProcess, $DelOnReboot) EndIf WEnd FileClose($searchHandle) EndIf EndIf Return $result EndFunc ;==>DirectoryRemove ;Remove file even if it's locked/inuse/etc Func FileRemove($fileName, $Recycle = True, $Unlock = False, $Takeown = False, $KillProcess = False, $DelOnReboot = False) Local $result = True If FileExists($fileName) = 0 Then Return ;Try basic delete/recycle If $Recycle Then $result = FileRecycle($fileName) ConsoleWrite("FR FileRecycle(" & $fileName & ") result = " & $result & @CR) Else $result = FileDelete($fileName) ConsoleWrite("RF FileDelete(" & $fileName & ") result = " & $result & @CR) EndIf ;Test if Bsic delete worked If Not $result Then ;Basic delete failed ;Reset attributes so file can be deleted (untested) FileSetAttrib($fileName, "-RSH+N") ;try methods of unlocking files For $Attempt = 1 To 3 Select Case $Attempt = 1 And $Unlock ConsoleWrite("FR UNLOCKER " & $fileName & @CR) ;Remove open handles to files ConsoleWrite('\Unlocker.exe "' & _PathFull($fileName) & '" -S -D') RunWait(@TempDir & '\Unlocker.exe "' & _PathFull($fileName) & '" -S -D');, @SW_HIDE) If @error Then MsgBox(0, "Unlocker.exe -S Error", @error & @CR & $fileName) EndIf Case $Attempt = 2 And $Takeown ;**TODO** Use TAKEOWN to take ownership of the file before deleting. ConsoleWrite("FR Takeown " & $fileName & @CR) RunWait(@TempDir & "\takeown.exe " & $fileName, "", @SW_HIDE) If @error Then MsgBox(0, "takeown.exe Error", @error) EndIf Case $Attempt = 3 And $KillProcess ConsoleWrite("FR Close process " & $fileName & @CR) ;CLOSE running programs ;extract only filename to feed to processkill Local $szDrive, $szDir, $szFName, $szExt _PathSplit($fileName, $szDrive, $szDir, $szFName, $szExt) ProcessKill($szFName & $szExt) EndSelect $result = True ; reset for new clean If FileExists($fileName) Then If $Recycle Then $result = FileRecycle($fileName) ConsoleWrite("FR FileRecycle(" & $fileName & ") result = " & $result & @CR) Else $result = FileDelete($fileName) ConsoleWrite("RF FileDelete(" & $fileName & ") result = " & $result & @CR) EndIf Else ExitLoop EndIf Next If (Not $result) And $DelOnReboot Then ; Only Files can be deleted like this (Check if it's a file) If StringInStr(FileGetAttrib($fileName), "D") = 0 Then ConsoleWrite(" MOVE FILE on restart " & $fileName & ") result = " & $result & @CR) ;Delete File on system restart Local $pe = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager" Local $pv = "PendingFileRenameOperations" RegWrite($pe, $pv, "REG_MULTI_SZ", "\??\" & FileGetShortName($fileName) & @LF & @LF & RegRead($pe, $pv)) Else ;ConsoleWrite(" WILL NOT MOVE DIRECTORY on restart " & $fileName & @CR) EndIf ;**TODO** What about sending to "recycling bin" ?? how can I do that on restart ?? EndIf EndIf ConsoleWrite("FR Result = " & $result & @CR) Return $result EndFunc ;==>FileRemove Func ProcessKill($ProcessKill_in);PID or the name of the process ;dexto http://www.autoitscript.com/forum/index.php?showtopic=95999&view=findpost&p=690149 Local $ProcessKill_PID, $ProcessKill_all, $ProcessKill_trackEach, $ProcessKill_each, $ProcessKill_PIDtoKill Local $ProcessKill_objWMIService, $ProcessKill_procToKill, $ProcessKill_track If IsNumber($ProcessKill_in) Then $ProcessKill_PID = $ProcessKill_in Else $ProcessKill_all = ProcessList($ProcessKill_in) If @error Then Return 0 $ProcessKill_trackEach = 0 For $ProcessKill_each = 1 To $ProcessKill_all[0][0] $ProcessKill_trackEach += ProcessKill($ProcessKill_all[$ProcessKill_each][1]) Next If $ProcessKill_trackEach == $ProcessKill_all[0][0] Then Return 1 Else Return 0 EndIf EndIf $ProcessKill_PIDtoKill = ProcessExists($ProcessKill_PID) If $ProcessKill_PIDtoKill == 0 Then Return 0 EndIf ProcessClose($ProcessKill_PIDtoKill) Sleep(300) If ProcessExists($ProcessKill_PIDtoKill) == 0 Then Return 1 EndIf $ProcessKill_objWMIService = ObjGet("winmgmts:\root\cimv2") $ProcessKill_procToKill = $ProcessKill_objWMIService.ExecQuery("Select * from win32_process where processid = " & $ProcessKill_PIDtoKill) $ProcessKill_track = 0 For $ProcessKill_procToKillThread In $ProcessKill_procToKill $ProcessKill_track += ConsoleWrite($ProcessKill_procToKillThread.Terminate & @CRLF) Next If $ProcessKill_track > 0 Then Return 0 EndIf Return 1 EndFunc ;==>ProcessKill Func SetStatus($text) ;TODO Use to set status line or label or .... EndFunc ;==>SetStatus Feedback is most welcome. Some of my small contributions to AutoIt Browse for Folder Dialog - Automation SysTreeView32 | FileHippo Download and/or retrieve program information | Get installedpath from uninstall key in registry | RoboCopy function John Morrison aka Storm-E
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now