Webturtle 21 Posted January 4, 2018 (edited) Hallo, da auch andere Nutzer eine Warnung des DVBViewer Pro oder GE vermissen, wenn der Speicherplatz des Aufnahmelaufwerks knapp wird (vgl. http://www.DVBViewer.tv/forum/topic/60939-warnung-bei-tauben-aufnahmen), lade ich hier ein AutoHotkey-Script hoch, das ich als Behelfslösung geschrieben habe. Das Script wurde mit AutoHotkey Basic unter Windows XP geschrieben. Es ist quick und dirty und nicht ausführlich kommentiert. Es installiert sich im Systray und zeigt nach in einem festgelegten Turnus den freien Speicherplatz an. Beim Unterschreiten wird eine Message-Box angezeigt. Beim Rechtsklick auf das Symbol im Systray kann man sich u.a. die AutoHotkey-Hilfe anzeigen lassen, das Script neu starten (reload) oder editieren. Beim Doppelklick darauf wird das Script im augenblicklichen Stand angezeigt. Um es zu starten, muß man AutoHotkey herunterladen und installieren (https://autohotkey.com/download/). Sicherheitshalber sollten man auch AutoHotkey Basic 1.0.* herunterladen, falls das Script unter AutoHotkey_L 1.1.* oder 2.0-a* nicht korrekt läuft. Bei einigen Befehlen hat es Änderungen gegeben (Bei mir liefen einige Scripst nicht mehr korrekt). Mit der Installation wird auch eine englische Hilfe installiert. Man kann aber am angegebenen Ort auch eine deutsche Hilfe herunterladen. AutoHotkey bietet auch einen Macro-Recorder und die Möglichkeit, Scripte in Exe-Dateien zu kompilieren. Laufwerke, Speichergrößen und Zeiten müssen im Script den eigenen Bedürfnissen angepaßt werden! Falls Fragen auftauchen bitte melden. Wer das Script verbessert oder modifiziert, möge bitte seine Version auch hier zur Verfügung stellen. Hier das Script: DiskSpaceWarning.zip Viele Grüße Webturtle Edited March 16, 2018 by Webturtle Fehler in Script (Freespace statt Freespace_I) beseitigt Quote Share this post Link to post
nuts 65 Posted January 4, 2018 Vielleicht mal als Ansatz für eine etwas "schlankere" Lösung: Es gibt für den DVBViewer ja die vbs Scripte. Über die startrec.vbs könnte man bei jedem Aufnahmestart die Überprüfung laufen lassen und bei Bedarf eine Warnmeldung anzeigen lassen. Über den COM-Server des DVBViewers könnte man sich sogar das jeweilige Aufnahmeverzeichnis holen, sodass man bis auf den freien Speicherplatz nichts selbst definieren müsste. Quote Share this post Link to post
majstang 19 Posted January 4, 2018 (edited) Nice @Webturtle in these times around Christmas/New Year the abundance of "not-yet-had-time-to-watch-movie" recordings Im constantly on the edge of low HDD space, so something like your script is very needed With @nuts suggestions it will be awesome, but i would suggest fetching the stuff from DMS, since the COM calls wont work if DVBV is not running. Edited January 4, 2018 by majstang Quote Share this post Link to post
Webturtle 21 Posted January 5, 2018 Hallo, @nuts: Kennst Du eine gute Einführung oder Befehlsübersicht für vbs Scripte? Außer mit Batch-Dateien hatte ich bisher nichts mit Windows-Scripten zu tun. Viele Grüße Webturtle Quote Share this post Link to post
nuts 65 Posted January 5, 2018 Nee nicht wirklich, da ich VBS nicht unbedingt gerne verwende und leider im Moment auch keine Zeit habe da mitzuhelfen. Ist aber ähnlich wie VB und somit ähnlich zu Autohotkey. Du könntest auch den Hauptteil in der dir vertrauten Sprache belassen (als .exe zum download bereitstellen) und nur den Aufruf in die startrec.vbs verlegen Wie das geht ist hier beschrieben: http://de.DVBViewer.tv/wiki/Command.vbs Einstellbare Parameter würde ich dann als Kommandoparameter aus der startrec.vbs an das (Haupt-)Skript übergeben. Dann muss sich kein Anwender mit dem Programmierzeugs befassen, sondern nur die 2 Dateien (deine .exe und deine startrec.vbs) herunterladen. Quote Share this post Link to post
majstang 19 Posted January 13, 2018 Hallo Webturtle, Here is a version of your script which you may find interesting Regards majstang Spoiler #NoEnv #Persistent LowSpaceLimit := 10 ; Set the lowest allowed Gigabyte (GB) Disk Space limit. When breached triggers the Low Disk Space warning. DMSFolder := DMS_RECfolder(BringIt) ;Check for the DMS Recording folder "First one only" WatchFolder(DMSFolder, "ReportFunction", SubTree := 0, Watch := 19) ; No recursing ; ====================================================================================================================== ; Auxiliary functions ; ====================================================================================================================== loadXML(ByRef data) { o := ComObjCreate("MSXML2.DOMDocument.6.0") o.async := false ; o.preserveWhiteSpace := false ; true o.loadXML(data) return o } ; ---------------------------------------------------------------------------------------------------------------------- DisplayNode(Nodes, Indent := 0) { For Node In Nodes { If Node.nodeName != "#text" Text .= Spaces(Indent) . Node.nodeName ": " . Node.nodeValue . "`n" Else Text .= Spaces(Indent) . Node.nodeValue . "`n" If Node.hasChildNodes Text .= DisplayNode(Node.childNodes, Indent + 2) } Return Text } ; ---------------------------------------------------------------------------------------------------------------------- Spaces(N) { Loop, % N S .= " " Return S } ;--Asynchronous WebRequest URLToVar function---------------------------------------------------------------------- AsyncURLToVar(URL) { WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1") WebRequest.Open("GET", URL, true) WebRequest.Send() WebRequest.WaitForResponse() Return WebRequest.ResponseText } ;--------------------------------------------------------------------------------------------------------- ; Find DVBViewer Media Server recording folder - the first one only DMS_RECfolder(FindIt) { recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html") RS_Status := loadXML(recordingstatus) RS_RECfolder := RS_Status.selectSingleNode("/status/recfolders/folder").text Return RS_RECfolder } ;--------------------------------------------------------------------------------------------------------- ; Check Recordingstatus of the DVBViewer Media Server DMS_Status(CheckForStatus) { recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html") RS_Status := loadXML(recordingstatus) RS_StatusVar := RS_Status.selectSingleNode("/status/reccount").text Return RS_StatusVar } ;--------------------------------------------------------------------------------------------------------- ; Convert Megabytes to Gigabytes DisplaySize(FileSize) { Static GB := 1024 Return (FileSize >= GB) ? (Round(FileSize / GB)) : FileSize } ; ================================================================================================================================== ; Function: Notifies about changes within folders. ; This is a rewrite of HotKeyIt's WatchDirectory() released at ; http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/ ; Tested with: AHK 1.1.23.01 (A32/U32/U64) ; Tested on: Win 10 Pro x64 ; Usage: WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]]) ; Parameters: ; Folder - The full qualified path of the folder to be watched. ; Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching. ; Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime. ; If not, it will be done internally on exit. ; UserFunc - The name of a user-defined function to call on changes. The function must accept at least two parameters: ; 1: The path of the affected folder. The final backslash is not included even if it is a drive's root ; directory (e.g. C:). ; 2: An array of change notifications containing the following keys: ; Action: One of the integer values specified as FILE_ACTION_... (see below). ; In case of renaming Action is set to FILE_ACTION_RENAMED (4). ; Name: The full path of the changed file or folder. ; OldName: The previous path in case of renaming, otherwise not used. ; IsDir: True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False. ; Pass the string "**DEL" to remove the directory from the list of watched folders. ; SubTree - Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders). ; Default: False - sub-folders aren't watched. ; Watch - The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_... ; values specified below. ; Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME ; Return values: ; Returns True on success; otherwise False. ; Change history: ; 1.0.02.00/2016-11-30/just me - bug-fix for closing handles with the '**END' option. ; 1.0.01.00/2016-03-14/just me - bug-fix for multiple folders ; 1.0.00.00/2015-06-21/just me - initial release ; License: ; The Unlicense -> http://unlicense.org/ ; Remarks: ; Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64) ; folders simultaneously. ; MSDN: ; ReadDirectoryChangesW msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx ; FILE_NOTIFY_CHANGE_FILE_NAME = 1 (0x00000001) : Notify about renaming, creating, or deleting a file. ; FILE_NOTIFY_CHANGE_DIR_NAME = 2 (0x00000002) : Notify about creating or deleting a directory. ; FILE_NOTIFY_CHANGE_ATTRIBUTES = 4 (0x00000004) : Notify about attribute changes. ; FILE_NOTIFY_CHANGE_SIZE = 8 (0x00000008) : Notify about any file-size change. ; FILE_NOTIFY_CHANGE_LAST_WRITE = 16 (0x00000010) : Notify about any change to the last write-time of files. ; FILE_NOTIFY_CHANGE_LAST_ACCESS = 32 (0x00000020) : Notify about any change to the last access time of files. ; FILE_NOTIFY_CHANGE_CREATION = 64 (0x00000040) : Notify about any change to the creation time of files. ; FILE_NOTIFY_CHANGE_SECURITY = 256 (0x00000100) : Notify about any security-descriptor change. ; FILE_NOTIFY_INFORMATION msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx ; FILE_ACTION_ADDED = 1 (0x00000001) : The file was added to the directory. ; FILE_ACTION_REMOVED = 2 (0x00000002) : The file was removed from the directory. ; FILE_ACTION_MODIFIED = 3 (0x00000003) : The file was modified. ; FILE_ACTION_RENAMED = 4 (0x00000004) : The file was renamed (not defined by Microsoft). ; FILE_ACTION_RENAMED_OLD_NAME = 4 (0x00000004) : The file was renamed and this is the old name. ; FILE_ACTION_RENAMED_NEW_NAME = 5 (0x00000005) : The file was renamed and this is the new name. ; GetOverlappedResult msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx ; CreateFile msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx ; FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 ; FILE_FLAG_OVERLAPPED = 0x40000000 ; ================================================================================================================================== WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) { Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}} Static TimerID := "**" . A_TickCount Static TimerFunc := Func("WatchFolder").Bind(TimerID, "") Static MAXIMUM_WAIT_OBJECTS := 64 Static MAX_DIR_PATH := 260 - 12 + 1 Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB) Static SizeOfOVL := 32 ; size of the OVERLAPPED structure (64-bit) Static WatchedFolders := {} Static EventArray := [] Static HandleArray := [] Static WaitObjects := 0 Static BytesRead := 0 Static Paused := False ; =============================================================================================================================== If (Folder = "") Return False SetTimer, % TimerFunc, Off RebuildWaitObjects := False ; =============================================================================================================================== If (Folder = TimerID) { ; called by timer If (ObjCount := EventArray.Length()) && !Paused { ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt") While (ObjIndex >= 0) && (ObjIndex < ObjCount) { FolderName := WatchedFolders[ObjIndex + 1] D := WatchedFolders[FolderName] If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) { Changes := [] FNIAddr := D.FNIAddr FNIMax := FNIAddr + BytesRead OffSet := 0 PrevIndex := 0 PrevAction := 0 PrevName := "" Loop { FNIAddr += Offset OffSet := NumGet(FNIAddr + 0, "UInt") Action := NumGet(FNIAddr + 4, "UInt") Length := NumGet(FNIAddr + 8, "UInt") // 2 Name := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16") IsDir := InStr(FileExist(Name), "D") ? 1 : 0 If (Name = PrevName) { If (Action = PrevAction) Continue If (Action = 1) && (PrevAction = 2) { PrevAction := Action Changes.RemoveAt(PrevIndex--) Continue } } If (Action = 4) PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0}) Else If (Action = 5) && (PrevAction = 4) { Changes[PrevIndex, "Name"] := Name Changes[PrevIndex, "IsDir"] := IsDir } Else PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir}) PrevAction := Action PrevName := Name } Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax) If (Changes.Length() > 0) D.Func.Call(FolderName, Changes) DllCall("ResetEvent", "Ptr", EventArray[D.Index]) DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree , "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0) } ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt") Sleep, 0 } } } ; =============================================================================================================================== Else If (Folder = "**PAUSE") { ; called to pause/resume watching Paused := !!UserFunc RebuildObjects := Paused } ; =============================================================================================================================== Else If (Folder = "**END") { ; called to stop watching For K, D In WatchedFolders If K Is Not Integer DllCall("CloseHandle", "Ptr", D.Handle) For Each, Event In EventArray DllCall("CloseHandle", "Ptr", Event) WatchedFolders := {} EventArray := [] Paused := False Return True } ; =============================================================================================================================== Else { ; called to add, update, or remove folders Folder := RTrim(Folder, "\") VarSetCapacity(LongPath, SizeOfLongPath, 0) If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath) Return False VarSetCapacity(LongPath, -1) Folder := LongPath If (WatchedFolders[Folder]) { ; update or remove Handle := WatchedFolders[Folder, "Handle"] Index := WatchedFolders[Folder, "Index"] DllCall("CloseHandle", "Ptr", Handle) DllCall("CloseHandle", "Ptr", EventArray[Index]) EventArray.RemoveAt(Index) WatchedFolders.RemoveAt(Index) WatchedFolders.Delete(Folder) RebuildWaitObjects := True } If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) { If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) { Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03 , "UInt", 0x42000000, "Ptr", 0, "UPtr") If (Handle > 0) { Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0) Index := EventArray.Push(Event) WatchedFolders[Index] := Folder WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch} WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI) FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff") DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI) WatchedFolders[Folder, "FNIAddr"] := FNIAddr WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL) OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff") DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL) NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr") WatchedFolders[Folder, "OVLAddr"] := OVLAddr DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree , "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0) RebuildWaitObjects := True } } } If (RebuildWaitObjects) { VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0) OffSet := &WaitObjects For Index, Event In EventArray Offset := NumPut(Event, Offset + 0, 0, "Ptr") } } ; =============================================================================================================================== If (EventArray.Length() > 0) SetTimer, % TimerFunc, -100 Return (RebuildWaitObjects) ; returns True on success, otherwise False } ; ================================================================================================================================== ; WatchFolder - Reportfunctions ; ================================================================================================================================== ReportFunction(Directory, Changes) { Global DMSFolder, LowSpaceLimit For Each, Change In Changes { Action := Change.Action Name := Change.Name ; ------------------------------------------------------------------------------------------------------------------------- ; Action 1 (added) = TS added (recording starts) in DMS recording folder, which triggers the Drive Space check If (Action = 1) { SplitPath, Name,, Dir, Ext,, OutDrive If (Dir = DMSFolder) && (Ext = "ts") { FileGetTime, Created, %Name%, C FileGetTime, Modified, %Name%, M If (DMS_Status(statusquery)) && (Created = Modified) { ; DMS has ongoing recording and file "name" is still in recording DriveSpaceFree, FreeSpace, % OutDrive . "\" If (DisplaySize(FreeSpace) < LowSpaceLimit) TrayTip, Low Disk Space, % "You are running out of disk space on Local Disk (" OutDrive "). Only " DisplaySize(FreeSpace) . " GB left. Free space on this drive by deleting old or unnecessary files", 30, 2 } } } } } Quote Share this post Link to post
Webturtle 21 Posted January 15, 2018 Hello majstang, your script is very interesting! Or with other words: Whow or humpf! It seems to be objectorientated. Is this vbs? Last time I've written some programs was with Turbo C. So for me it could be klingon as well. Regards Webturtle Quote Share this post Link to post
majstang 19 Posted January 15, 2018 37 minutes ago, Webturtle said: your script is very interesting! Or with other words: Whow or humpf! It seems to be objectorientated. Is this vbs? Last time I've written some programs was with Turbo C. So for me it could be klingon as well. Yes, it uses both function objects and arrays. All the code is snippets taken from a program I have been working on for years. WatchFolder is the backbone of that program and I intentionally did leave out watching of the NAS (you had in your original script as second DMS recording folder (it can watch all DMS folders if you like)) cuz I thought maybe you wanna add it on your own? If not I will do it for you (it might be a bit tricky if you are not scripting that much). Watchfolder is the replacement for @nuts VBS idea to catch when a recording starts which then triggers the low disk space check. Its a failsafe method really. So no VBS used. I dont even know if the DVBViewer recording.vbs is invoked when a DMS recording is started. It might be, if DVBViewer is running, but if DVBV is not running the low disk space check could fail if using the vbs. Other than that focus on the ReportFunction if you wanna add the sleep and goto label stuff you were using for the second TrayTip (repeat of warning) and the MsgBox, but I recommend using real timers instead. Best regards majstang 1 hour ago, nuts said: It's autohotkey isnt it? Quote Share this post Link to post
majstang 19 Posted January 17, 2018 (edited) Ok, script now retrieves ALL DMS recording folders and checks for low disk space (warning) when a recording is started and located to the corresponding drive. NAS UNC paths untested. regards majstang Spoiler #NoEnv #Persistent Global RS_RECfolderArray := [] LowSpaceLimit := 10 ; Set the lowest allowed Gigabyte (GB) Disk Space limit. When breached triggers the Low Disk Space warning. DMS_RECfolder(BringIt) ;Check for the DMS Recording folders for key, value in RS_RECfolderArray WatchFolders .= (A_Index > 1 ? "|" : "") value "" ; Watching subfolders is not needed for what this script does. Add a star "*" Loop, Parse, WatchFolders,| ; between the last double quotes if wanting watched subfolders { SubTree := (SubStr(A_LoopField, StrLen(A_LoopField)) = "*") WatchFolder(RTrim(A_LoopField, "*"), "ReportFunction", SubTree, Watch := 19) } ; ====================================================================================================================== ; Auxiliary functions ; ====================================================================================================================== loadXML(ByRef data) { o := ComObjCreate("MSXML2.DOMDocument.6.0") o.async := false ; o.preserveWhiteSpace := false ; true o.loadXML(data) return o } ; ---------------------------------------------------------------------------------------------------------------------- DisplayNode(Nodes, Indent := 0) { For Node In Nodes { If Node.nodeName != "#text" Text .= Spaces(Indent) . Node.nodeName ": " . Node.nodeValue . "`n" Else Text .= Spaces(Indent) . Node.nodeValue . "`n" If Node.hasChildNodes Text .= DisplayNode(Node.childNodes, Indent + 2) } Return Text } ; ---------------------------------------------------------------------------------------------------------------------- Spaces(N) { Loop, % N S .= " " Return S } ;--Asynchronous WebRequest URLToVar function---------------------------------------------------------------------- AsyncURLToVar(URL) { WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1") WebRequest.Open("GET", URL, true) WebRequest.Send() WebRequest.WaitForResponse() Return WebRequest.ResponseText } ;--------------------------------------------------------------------------------------------------------- ; Find DVBViewer Media Server recording folder - the first one only DMS_RECfolder(FindIt) { recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html") RS_Status := loadXML(recordingstatus) foldernodes := RS_Status.selectSingleNode("/status/recfolders/folder") for folder in ( foldernodes.parentNode.selectNodes("folder"), folders := "" ) RS_RECfolderArray.Push(folder.text) } ;--------------------------------------------------------------------------------------------------------- ; Check Recordingstatus of the DVBViewer Media Server DMS_Status(CheckForStatus) { recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html") RS_Status := loadXML(recordingstatus) RS_StatusVar := RS_Status.selectSingleNode("/status/reccount").text Return RS_StatusVar } ;--------------------------------------------------------------------------------------------------------- ; Convert Megabytes to Gigabytes DisplaySize(FileSize) { Static GB := 1024 Return (FileSize >= GB) ? (Round(FileSize / GB)) : FileSize } ; ================================================================================================================================== ; Function: Notifies about changes within folders. ; This is a rewrite of HotKeyIt's WatchDirectory() released at ; http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/ ; Tested with: AHK 1.1.23.01 (A32/U32/U64) ; Tested on: Win 10 Pro x64 ; Usage: WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]]) ; Parameters: ; Folder - The full qualified path of the folder to be watched. ; Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching. ; Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime. ; If not, it will be done internally on exit. ; UserFunc - The name of a user-defined function to call on changes. The function must accept at least two parameters: ; 1: The path of the affected folder. The final backslash is not included even if it is a drive's root ; directory (e.g. C:). ; 2: An array of change notifications containing the following keys: ; Action: One of the integer values specified as FILE_ACTION_... (see below). ; In case of renaming Action is set to FILE_ACTION_RENAMED (4). ; Name: The full path of the changed file or folder. ; OldName: The previous path in case of renaming, otherwise not used. ; IsDir: True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False. ; Pass the string "**DEL" to remove the directory from the list of watched folders. ; SubTree - Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders). ; Default: False - sub-folders aren't watched. ; Watch - The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_... ; values specified below. ; Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME ; Return values: ; Returns True on success; otherwise False. ; Change history: ; 1.0.02.00/2016-11-30/just me - bug-fix for closing handles with the '**END' option. ; 1.0.01.00/2016-03-14/just me - bug-fix for multiple folders ; 1.0.00.00/2015-06-21/just me - initial release ; License: ; The Unlicense -> http://unlicense.org/ ; Remarks: ; Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64) ; folders simultaneously. ; MSDN: ; ReadDirectoryChangesW msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx ; FILE_NOTIFY_CHANGE_FILE_NAME = 1 (0x00000001) : Notify about renaming, creating, or deleting a file. ; FILE_NOTIFY_CHANGE_DIR_NAME = 2 (0x00000002) : Notify about creating or deleting a directory. ; FILE_NOTIFY_CHANGE_ATTRIBUTES = 4 (0x00000004) : Notify about attribute changes. ; FILE_NOTIFY_CHANGE_SIZE = 8 (0x00000008) : Notify about any file-size change. ; FILE_NOTIFY_CHANGE_LAST_WRITE = 16 (0x00000010) : Notify about any change to the last write-time of files. ; FILE_NOTIFY_CHANGE_LAST_ACCESS = 32 (0x00000020) : Notify about any change to the last access time of files. ; FILE_NOTIFY_CHANGE_CREATION = 64 (0x00000040) : Notify about any change to the creation time of files. ; FILE_NOTIFY_CHANGE_SECURITY = 256 (0x00000100) : Notify about any security-descriptor change. ; FILE_NOTIFY_INFORMATION msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx ; FILE_ACTION_ADDED = 1 (0x00000001) : The file was added to the directory. ; FILE_ACTION_REMOVED = 2 (0x00000002) : The file was removed from the directory. ; FILE_ACTION_MODIFIED = 3 (0x00000003) : The file was modified. ; FILE_ACTION_RENAMED = 4 (0x00000004) : The file was renamed (not defined by Microsoft). ; FILE_ACTION_RENAMED_OLD_NAME = 4 (0x00000004) : The file was renamed and this is the old name. ; FILE_ACTION_RENAMED_NEW_NAME = 5 (0x00000005) : The file was renamed and this is the new name. ; GetOverlappedResult msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx ; CreateFile msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx ; FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 ; FILE_FLAG_OVERLAPPED = 0x40000000 ; ================================================================================================================================== WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) { Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}} Static TimerID := "**" . A_TickCount Static TimerFunc := Func("WatchFolder").Bind(TimerID, "") Static MAXIMUM_WAIT_OBJECTS := 64 Static MAX_DIR_PATH := 260 - 12 + 1 Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB) Static SizeOfOVL := 32 ; size of the OVERLAPPED structure (64-bit) Static WatchedFolders := {} Static EventArray := [] Static HandleArray := [] Static WaitObjects := 0 Static BytesRead := 0 Static Paused := False ; =============================================================================================================================== If (Folder = "") Return False SetTimer, % TimerFunc, Off RebuildWaitObjects := False ; =============================================================================================================================== If (Folder = TimerID) { ; called by timer If (ObjCount := EventArray.Length()) && !Paused { ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt") While (ObjIndex >= 0) && (ObjIndex < ObjCount) { FolderName := WatchedFolders[ObjIndex + 1] D := WatchedFolders[FolderName] If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) { Changes := [] FNIAddr := D.FNIAddr FNIMax := FNIAddr + BytesRead OffSet := 0 PrevIndex := 0 PrevAction := 0 PrevName := "" Loop { FNIAddr += Offset OffSet := NumGet(FNIAddr + 0, "UInt") Action := NumGet(FNIAddr + 4, "UInt") Length := NumGet(FNIAddr + 8, "UInt") // 2 Name := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16") IsDir := InStr(FileExist(Name), "D") ? 1 : 0 If (Name = PrevName) { If (Action = PrevAction) Continue If (Action = 1) && (PrevAction = 2) { PrevAction := Action Changes.RemoveAt(PrevIndex--) Continue } } If (Action = 4) PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0}) Else If (Action = 5) && (PrevAction = 4) { Changes[PrevIndex, "Name"] := Name Changes[PrevIndex, "IsDir"] := IsDir } Else PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir}) PrevAction := Action PrevName := Name } Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax) If (Changes.Length() > 0) D.Func.Call(FolderName, Changes) DllCall("ResetEvent", "Ptr", EventArray[D.Index]) DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree , "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0) } ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt") Sleep, 0 } } } ; =============================================================================================================================== Else If (Folder = "**PAUSE") { ; called to pause/resume watching Paused := !!UserFunc RebuildObjects := Paused } ; =============================================================================================================================== Else If (Folder = "**END") { ; called to stop watching For K, D In WatchedFolders If K Is Not Integer DllCall("CloseHandle", "Ptr", D.Handle) For Each, Event In EventArray DllCall("CloseHandle", "Ptr", Event) WatchedFolders := {} EventArray := [] Paused := False Return True } ; =============================================================================================================================== Else { ; called to add, update, or remove folders Folder := RTrim(Folder, "\") VarSetCapacity(LongPath, SizeOfLongPath, 0) If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath) Return False VarSetCapacity(LongPath, -1) Folder := LongPath If (WatchedFolders[Folder]) { ; update or remove Handle := WatchedFolders[Folder, "Handle"] Index := WatchedFolders[Folder, "Index"] DllCall("CloseHandle", "Ptr", Handle) DllCall("CloseHandle", "Ptr", EventArray[Index]) EventArray.RemoveAt(Index) WatchedFolders.RemoveAt(Index) WatchedFolders.Delete(Folder) RebuildWaitObjects := True } If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) { If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) { Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03 , "UInt", 0x42000000, "Ptr", 0, "UPtr") If (Handle > 0) { Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0) Index := EventArray.Push(Event) WatchedFolders[Index] := Folder WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch} WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI) FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff") DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI) WatchedFolders[Folder, "FNIAddr"] := FNIAddr WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL) OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff") DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL) NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr") WatchedFolders[Folder, "OVLAddr"] := OVLAddr DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree , "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0) RebuildWaitObjects := True } } } If (RebuildWaitObjects) { VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0) OffSet := &WaitObjects For Index, Event In EventArray Offset := NumPut(Event, Offset + 0, 0, "Ptr") } } ; =============================================================================================================================== If (EventArray.Length() > 0) SetTimer, % TimerFunc, -100 Return (RebuildWaitObjects) ; returns True on success, otherwise False } ; ================================================================================================================================== ; WatchFolder - Reportfunctions ; ================================================================================================================================== ReportFunction(Directory, Changes) { Global LowSpaceLimit For Each, Change In Changes { Action := Change.Action Name := Change.Name ; ------------------------------------------------------------------------------------------------------------------------- ; Action 1 (added) = TS added (recording starts) in DMS recording folder, which triggers the Drive Space check If (Action = 1) { SplitPath, Name,, Dir, Ext,, OutDrive for key, value in RS_RECfolderArray { DMSFolder := value If (Dir = DMSFolder) && (Ext = "ts") { FileGetTime, Created, %Name%, C FileGetTime, Modified, %Name%, M If (DMS_Status(statusquery)) && (Created = Modified) { ; DMS has ongoing recording and file "name" is still in recording DriveSpaceFree, FreeSpace, % OutDrive . "\" If (DisplaySize(FreeSpace) < LowSpaceLimit) TrayTip, Low Disk Space, % "You are running out of disk space on Local Disk (" OutDrive "). Only " DisplaySize(FreeSpace) . " GB left. Free space on this drive by deleting old or unnecessary files", 30, 2 } } } } } } Edited January 17, 2018 by majstang Quote Share this post Link to post
Webturtle 21 Posted May 14 Hallo, ich habe mein ursprüngliches Autohotkey Script für Windows 10 überarbeitet. Die ursprüngliche Fassung war für XP mit Autohotkey Classic geschrieben. Sie hat keine akustische Warnung unter W 10 unterstützt. Mit der aktuellen Version des (jetzt normalen) Autohotkey funktioniert das aber. DiskSpaceWarning_1.1.zip Als Klang verwernde ich die Trompeten-Fanfare von Nero, die am Ende eines Brennvorgangs gespielt wird. Man kann man bei Google oder anderen Suchmaschinen nach MP3s mit Kavalerie Fanfaren suchen oder aus TV-Aufnahmen passende Klänge herausschneiden. Gut dafür eignet sich "Columbo - Des Teufels Corporal" oder auch ein Westen, bei dem sich die Kavallerie per Trompete ankündigt. Quote Share this post Link to post