WatchFolder() - updated on 2021-10-14

Post your working scripts, libraries and tools for AHK v1.1 and older
yhtmn
Posts: 1
Joined: 20 Apr 2022, 00:57

Re: WatchFolder() - updated on 2021-10-14

Post by yhtmn » 20 Apr 2022, 01:00

hi there, is it possible to trigger action after folder change happens? Or for example is it possible to trigger an action when .xls is added to watched folder?

ahk7
Posts: 575
Joined: 06 Nov 2013, 16:35

Re: WatchFolder() - updated on 2021-10-14

Post by ahk7 » 20 Apr 2022, 12:49

yhtmn wrote:
20 Apr 2022, 01:00
hi there, is it possible to trigger action after folder change happens? Or for example is it possible to trigger an action when .xls is added to watched folder?
That is the entire purpose of the function :) Example, note the values of what is what

Code: Select all

#Persistent
#SingleInstance, force
SetWorkingDir, %A_ScriptDir%

#include WatchFolder.ahk ; copy/download the code from the first post, save it as WatchFolder.ahk 

;     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("path-to-your-folder", "ReportFunction") ; change the path of course

ReportFunction(Directory, Changes)
	{
	 For Each, Change In Changes
		{
		 Action := Change.Action
		 Name := Change.Name
		 ; -------------------------------------------------------------------------------------------------------------------------
		 ; Action 1 (added) = File gets added in the watched folder
		 If (Action = 1) 
		 	{
			 If RegExMatch(name,"i)\.xlsx?$") check if we have an excel file (ending in xls or xlsx)
			 	{
			 	 ; if excel file do something
			 	 MsgBox % name
			 	}
		 	}
		}
	}

dostroll
Posts: 40
Joined: 03 Nov 2021, 08:56

Re: WatchFolder() - updated on 2021-10-14

Post by dostroll » 27 Jul 2022, 01:57

Thank you just me for your wonderful function.
It works as expected, but there is one problem. I'd love to get some advice on how to resolve this if you'd like:)

WatchFolder is used to convert a RAR file with a specified filename with date to uncompressed ZIP.
The zip software I use generates a .tmp file with the same filename in the same directory as the file being processed.
This causes the unpacking process to be performed three times.

Create file
Pudding.rar → 1 Action
Pudding.tmp → 2 Action(but, Nothing happens because the file is in use.)
Pudding.zip → 3 Action(but, Unpacked, so nothing happens.)

Even if PAUSE is built in, it will be processed multiple times.

Code: Select all

If RegExMatch(name, "Humpty_Dumpty|Pudding|chocolate")
{
WatchFolder("**PAUSE", True)
<unpack code>
WatchFolder("**PAUSE", False)
}
Is there any good solution?

just me
Posts: 9458
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WatchFolder() - updated on 2021-10-14

Post by just me » 27 Jul 2022, 02:31

Code: Select all

If RegExMatch(name, "(Humpty_Dumpty|Pudding|chocolate).*\.rar$") {
	<unpack code>
}
?

dostroll
Posts: 40
Joined: 03 Nov 2021, 08:56

Re: WatchFolder() - updated on 2021-10-14

Post by dostroll » 27 Jul 2022, 11:19

Thanks for getting back to me so quickly!
It worked perfectly.
It was a regular expression problem, I understand.

tester
Posts: 84
Joined: 10 Jun 2021, 23:03

Re: WatchFolder() - updated on 2021-10-14

Post by tester » 19 Aug 2022, 21:18

Just reporting that the function does not detect any changes for directories on a network drive in my environment, Windows 10 64bit with AutoHotkeyU64 1.1.34.03.

tester
Posts: 84
Joined: 10 Jun 2021, 23:03

Re: WatchFolder() - updated on 2021-10-14

Post by tester » 25 Aug 2022, 22:05

@just me I'm wondering if you can implement the ability to accept class methods for the callback function like what SetTimer does.

Code: Select all

oWatchFolder := new TestWatchFolder(A_ScriptDir)
class TestWatchFolder {

    sPathDir := ""
    func := {}

    __New(sPathDir) {

        this.sPathDir := sPathDir
        this.func     := ObjBindMethod(this, "watch")
        WatchFolder(sPathDir, this.func, true)

    }

    watch(sPathFolder, oChanges) {
        ; do something
    }

}
From the manual of SetTimer,

Code: Select all

class SecondCounter {
    __New() {
        this.interval := 1000
        this.count := 0
        ; Tick() has an implicit parameter "this" which is a reference to
        ; the object, so we need to create a function which encapsulates
        ; "this" and the method to call:
        this.timer := ObjBindMethod(this, "Tick")
    }
    Start() {
        ; Known limitation: SetTimer requires a plain variable reference.
        timer := this.timer
        SetTimer % timer, % this.interval
        ToolTip % "Counter started"
    }
    ...
 

Netmano
Posts: 58
Joined: 17 Jun 2020, 16:24

Re: WatchFolder() - updated on 2021-10-14

Post by Netmano » 29 Aug 2022, 08:27

I dont really know much of Autohotkey outside of Sendinput and sleep. I was able to put this together from reading this thread. Here is my code so far:

Code: Select all


#Persistent
#Include, <WatchFolder>
WatchFolder("C:\Users\...\Downloads", "ReportFunction", false, 1 | 2 | 4 | 8 |16 | 32 | 64 | 256)
Return
ReportFunction(Directory, Changes) { 
 For Each, Change In Changes {
       Action := Change.Action
       Name := Change.Name
	   If (Action = 3)
		 Run, Notepad
             Return
}
}
Esc::ExitApp
I am struggling with File modified action, Any autohotkey code I set to execute upon a file being modified is run twice. In the above example Run, Notepad is executed twice, so two notepad windows open. Its the same if I get a .Url link to open. It will be open twice too.

Any ideas what I could be doing wrong?

PS: Watchfolder function is saved in Documents\Autohotkey\Lib folder and I have made no changes to it, it is as I downloaded it.

dostroll
Posts: 40
Joined: 03 Nov 2021, 08:56

Re: WatchFolder() - updated on 2021-10-14

Post by dostroll » 01 Sep 2022, 02:12

Only two files will be processed for more than two files.
Are there any best practices for loop processing to handle all files?

just me
Posts: 9458
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WatchFolder() - updated on 2021-10-14

Post by just me » 01 Sep 2022, 03:35

tester wrote:@just me I'm wondering if you can implement the ability to accept class methods for the callback function like what SetTimer does.
@tester: I'm not interested in doing it, sorry.
Netmano wrote:I am struggling with File modified action, Any autohotkey code I set to execute upon a file being modified is run twice.
@Netmano: What kind of modifications do you do you specify to be watched?
dostroll wrote:Only two files will be processed for more than two files.
@dostroll: Show your code, please.

dostroll
Posts: 40
Joined: 03 Nov 2021, 08:56

Re: WatchFolder() - updated on 2021-10-14

Post by dostroll » 01 Sep 2022, 04:00

I solved it by accident.
Sorry for the inconvenience...
It's because I typed "Return". (I don't understand why there is a problem with Return.)

Code: Select all

; Action 1 (added) = File gets added in the watched folder
If (vAction = 1)
{
	If RegExMatch(Name, "(chocolate|cocoa|sup).*\.(rar|cbz)$")
	{
		Sleep, 500
		Run, rename.exe "%vName%"
		tooltip_func("● Unzip complete", 950)
		; Return
	}

Netmano
Posts: 58
Joined: 17 Jun 2020, 16:24

Re: WatchFolder() - updated on 2021-10-14

Post by Netmano » 01 Sep 2022, 06:43

@just me
Hey, thanks for getting back to me.
I want to be notified only if the files contents has been edited

neogna2
Posts: 591
Joined: 15 Sep 2016, 15:44

Re: WatchFolder() - updated on 2021-10-14

Post by neogna2 » 01 Sep 2022, 11:25

@just me On my PC WatchFolder() (both the v1 and v2 version) calls the UserFunc twice for each change, I think that's the issue @Netmano is reporting too. I tested with the WatchFolder_sample.ahk script.

just me
Posts: 9458
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WatchFolder() - updated on 2021-10-14

Post by just me » 03 Sep 2022, 05:02

Netmano wrote:I want to be notified only if the files contents has been edited
Try to set the Watch parameter to 0x10 (FILE_NOTIFY_CHANGE_LAST_WRITE) in this case.

neogna2
Posts: 591
Joined: 15 Sep 2016, 15:44

Re: WatchFolder() - updated on 2021-10-14

Post by neogna2 » 04 Sep 2022, 02:54

8 second clip that shows the "calls the UserFunc twice for each change" issue
https://i.imgur.com/509vRk5.mp4

just me
Posts: 9458
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WatchFolder() - updated on 2021-10-14

Post by just me » 07 Sep 2022, 09:27

@neogna2:
I cannot reproduce the behaviour here. I'm getting only one call for the UserFunc.

neogna2
Posts: 591
Joined: 15 Sep 2016, 15:44

Re: WatchFolder() - updated on 2021-10-14

Post by neogna2 » 08 Sep 2022, 06:10

just me wrote:
07 Sep 2022, 09:27
@neogna2:
I cannot reproduce the behaviour here. I'm getting only one call for the UserFunc.
Hm, my PC runs Win10 (august 2022 updated) and I get the UserFunc called twice issue with v1 U32 and U64 (and v2.beta7 for v2 WatchFolder) and when using several different apps to trigger the file/folder change. Perhaps @Netmano (and others) can reply and say if they have exactly the same issue as in my clip above. In order to pin down if this is a WatchFolder code issue or more of a neogna2's PC issue.


neogna2
Posts: 591
Joined: 15 Sep 2016, 15:44

Re: WatchFolder() - updated on 2021-10-14

Post by neogna2 » 08 Sep 2022, 13:00

Seems there's no perfect solution among those StackOverflow answers? Here's a quick attempt. If previous call was < 100 ms ago and if the file and type of change is the same as in the previous call then skip that item this call. Possible problem: these extra checks might slow things down or cause other problems if a ton of files are monitored with lots of changes frequently?

Code: Select all

MyUserFunc(Folder, Changes) {
   Static Actions := ["1 (added)", "2 (removed)", "3 (modified)", "4 (renamed)"]
   static PrevTickCount := 0
   static PrevChanges := []
   TickCount := A_TickCount
   GuiControl, -ReDraw, LV
   For Each, Change In Changes
   {
      if (TickCount - OldTickCount) < 100
         for Each, OldChange In OldChanges 
            if Change.Action = OldChange.Action && Change.Name = OldChange.Name
               Continue, 2
      LV_Modify(LV_Add("", TickCount, Folder, Actions[Change.Action], Change.Name, Change.IsDir, Change.OldName, ""), "Vis")
   }
   OldChanges := Changes
   OldTickCount := TickCount
   Loop, % LV_GetCount("Columns")
      LV_ModifyCol(A_Index, "AutoHdr")
   GuiControl, +Redraw, LV
}

just me
Posts: 9458
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WatchFolder() - updated on 2021-10-14

Post by just me » 09 Sep 2022, 05:03

Hi @neogna2,

because I don't use Notepad++ I tested using Notepad. The UserFunc is called only once if you save the file with Notepad. When I tried the same with my favourite editor PSPad, then function was called twice here too. So the number of calls seems to depend on the application writing the file.

Post Reply

Return to “Scripts and Functions (v1)”