Webturtle Posted January 4, 2018 Share 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 P.S. Zitat Hier die aktuelle Version: DiskSpaceAlarm_1.2.zip Edited December 31, 2020 by Webturtle Fehler in Script (Freespace statt Freespace_I) beseitigt Quote Link to comment
nuts Posted January 4, 2018 Share 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 Link to comment
majstang Posted January 4, 2018 Share 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 Link to comment
Webturtle Posted January 5, 2018 Author Share 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 Link to comment
nuts Posted January 5, 2018 Share 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 Link to comment
majstang Posted January 13, 2018 Share 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 Link to comment
Webturtle Posted January 15, 2018 Author Share 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 Link to comment
nuts Posted January 15, 2018 Share Posted January 15, 2018 It's autohotkey isnt it? Quote Link to comment
majstang Posted January 15, 2018 Share 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 Link to comment
majstang Posted January 17, 2018 Share 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 Link to comment
Webturtle Posted May 14, 2019 Author Share Posted May 14, 2019 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 Link to comment
Webturtle Posted April 29, 2020 Author Share Posted April 29, 2020 (edited) Hallo, ich habe das Script aufgrund der Beiträge überarbeitet. Zunächst mußte ich erst mit einer neueren Version von AutoHotkey dafür sorgen, daß auch unter W10 eine akustische Warnung ausgegeben wird. Dann habe ich das Script vereinfacht, es wird nur noch ein Laufwerk überwacht, und das Script als Exe-Datei DiskSpaceAlarm.exe kompiliert. Das Programm wird folgendermaßen aufgerufen: DiskSpaceAlarm [Laufwerk] [Freier Mindestspeicher in MB] [Sounddatei] Falls Leerzeichen im Namen oder Pfad der Sounddatei vorkommen, muß dieser Parameter in Anführungszeichen eingeschlossen werden. z.B. DiskSpaceAlarm C:\ 9000 "C:\Program Files\DVBViewer\Kavallerie Trompetensignal (Attacke) als MP3-Download.mp3" Wenn man das Programm beenden will, muß man im Systray mit der rechten Maustaste auf das grüne Quadrat mit dem weißen H (DiskSpaceAlarm) klicken und Exit wählen. Als Alarmsignal verwende ich die Fanfare von Nero, dem Brennprogramm. Die kann ich natürlich nicht weitergeben. Ich habe daher zwei freie Fanfarensignale beigefügt. Diese dürfen nicht kommerziell verwendet werden und bei einer Weitergabe ist die Angabe der Quelle erforderlich. Ich habe dazu die exportierte Webseite dazugepackt. Viele Grüße und viel Spaß damit Webturtle P.S. Das ursprüngliche Script hat mir schon oft die Aufnahmen gerettet Edited December 3, 2020 by Webturtle Quote Link to comment
Webturtle Posted December 3, 2020 Author Share Posted December 3, 2020 (edited) Hallo, ich habe DiskSpacaAlarm etwas überarbeitet. Im Alarmfall bietet dei Message-Box die Moglichkeit die erneuten Warnungen vorübergehend (1 - 60 Minuten) auszusetzen. Wenn man gerade schon dabei ist, Dateien zu verschieben, kann das ständige Fanfarensignal schon mal stören. Wenn man das Script abstellt, kann man auch vergessen es wieder aufzurufen und dann von einer vollen HD überrascht werden. DiskSpaceAlarm_1.1.zip Viele Grüße Webturtle Edited December 3, 2020 by Webturtle Quote Link to comment
Webturtle Posted December 30, 2020 Author Share Posted December 30, 2020 (edited) Hallo, ich habe DiskSpaceAlarm noch etwas verbessert. Das Programm merkt sich jetzt die Einstellungen von Volume und Mute (Lautstärke und Ton aus/ein), schaltet dann den Ton ein und die Lautstärke auf 100 (voll). Dann spielt es den Sound ab. Der Aufruf von SoundPlay innerhalb der Datei hat nicht funktioniert, was ich auch probiert habe (bei AutoHotkey gibt es leider keinen Debugger wie bei Turbo C, mit dem man Zeile für Zeile ablaufen lassen kann). Daher wird dazu Alarm_Trumpet.exe aufgerufen und der Name der Sounddatei übergeben. Danach werden die Wert für Volume und Mute wieder eingestellt. Hier die aktuelle Version: DiskSpaceAlarm_1.2.zip Viele Grüße und ein gutes neues Jahr Webturtle Edited December 31, 2020 by Webturtle Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.