[Lazarus] Shell notifications
Hi Folks. Do you know any component / tool to allow an application to be notified by the host OS when something changes in a disk directory ? Both Windows & Linux. Thanks, Antonio. -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Antonio Fortuny wrote: Hi Folks. Do you know any component / tool to allow an application to be notified by the host OS when something changes in a disk directory ? Both Windows & Linux. I think on Linux that you need something like the File Alteration Monitor (FAM) daemon, but I don't know how universally it's implemented. -- Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues] -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On Tue, 20 Aug 2013, Mark Morgan Lloyd wrote: Antonio Fortuny wrote: Hi Folks. Do you know any component / tool to allow an application to be notified by the host OS when something changes in a disk directory ? Both Windows & Linux. I think on Linux that you need something like the File Alteration Monitor (FAM) daemon, but I don't know how universally it's implemented. Nothing that complicated. Just use inotify. The inotify unit is in the rtl. I didn't get around to writing a cross-platform component for this yet. Michael. -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On Tue, Aug 20, 2013 at 02:22:02PM +0200, Antonio Fortuny wrote: > Hi Folks. > > Do you know any component / tool to allow an application to be > notified by the host OS when something changes in a disk directory ? > Both Windows & Linux. inotify on linux (together with select()). I'm told FindFirstChangeNotification() is the thing to use on Windows, but I've never tried. Henry -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Le 20/08/2013 14:56, Henry Vermaak a écrit : On Tue, Aug 20, 2013 at 02:22:02PM +0200, Antonio Fortuny wrote: Hi Folks. Do you know any component / tool to allow an application to be notified by the host OS when something changes in a disk directory ? Both Windows & Linux. inotify on linux (together with select()). I'm told Couldn'f find any inotify.* unit into my FPC/Lazarus installation Only an inotify.h FindFirstChangeNotification() is the thing to use on Windows, but I've never tried. I did and it runs OK as it is part of the kernel32.dll. But I'll have to run into a loop as this shell function is not event driven. I'll maybe make a wrapper onto it using a separated thread. Thanks. Antonio. Henry -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On Tue, Aug 20, 2013 at 03:21:01PM +0200, Antonio Fortuny wrote: > Le 20/08/2013 14:56, Henry Vermaak a écrit : > >On Tue, Aug 20, 2013 at 02:22:02PM +0200, Antonio Fortuny wrote: > >>Hi Folks. > >> > >>Do you know any component / tool to allow an application to be > >>notified by the host OS when something changes in a disk directory ? > >>Both Windows & Linux. > >inotify on linux (together with select()). I'm told > Couldn'f find any inotify.* unit into my FPC/Lazarus installation > Only an inotify.h It's in the linux unit, since inotify is linux specific. > >FindFirstChangeNotification() is the thing to use on Windows, but I've > >never tried. > I did and it runs OK as it is part of the kernel32.dll. But I'll > have to run into a loop as this shell function is not event driven. > I'll maybe make a wrapper onto it using a separated thread. You can use a wait function (e.g. WaitForMultipleObjects) with the handle that FindFirstChangeNotification() returns. So start a thread, get the handle, use wait function and call an event when the wait function indicated that something happened with the handle. Repeat. You'll have to do something similar for the linux implementation: start a thread, get inotify fd, add that to fpselect(), call some event when fpselect() indicates that the fd is readable. Repeat. Henry -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Le 20/08/2013 15:46, Henry Vermaak a écrit : On Tue, Aug 20, 2013 at 03:21:01PM +0200, Antonio Fortuny wrote: Le 20/08/2013 14:56, Henry Vermaak a écrit : On Tue, Aug 20, 2013 at 02:22:02PM +0200, Antonio Fortuny wrote: Hi Folks. Do you know any component / tool to allow an application to be notified by the host OS when something changes in a disk directory ? Both Windows & Linux. inotify on linux (together with select()). I'm told Couldn'f find any inotify.* unit into my FPC/Lazarus installation Only an inotify.h It's in the linux unit, since inotify is linux specific. FindFirstChangeNotification() is the thing to use on Windows, but I've never tried. I did and it runs OK as it is part of the kernel32.dll. But I'll have to run into a loop as this shell function is not event driven. I'll maybe make a wrapper onto it using a separated thread. You can use a wait function (e.g. WaitForMultipleObjects) with the handle that FindFirstChangeNotification() returns. So start a thread, get the handle, use wait function and call an event when the wait function indicated that something happened with the handle. Repeat. This was in my mind too. You'll have to do something similar for the linux implementation: start a thread, get inotify fd, add that to fpselect(), call some event when fpselect() indicates that the fd is readable. Repeat. I'll try a little bit later. Thanks, Antonio. Henry -- Antonio Fortuny Senior Software engineer 220, avenue de la Liberté L-4602 Niederkorn Tel.: +352 58 00 93 - 93 www.sitasoftware.lu -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On 08/20/2013 02:56 PM, Henry Vermaak wrote: ... together with select().. In Object Pascal I think this should be encapsulated in a thread and same fires a main Thread event (via TThread.Queue, TThread.Synchronize, or Application.QueueAsyncCall) to notify the user (aka Main Thread). -Michael -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Michael Schnell wrote: On 08/20/2013 02:56 PM, Henry Vermaak wrote: ... together with select().. In Object Pascal I think this should be encapsulated in a thread and same fires a main Thread event (via TThread.Queue, TThread.Synchronize, or Application.QueueAsyncCall) to notify the user (aka Main Thread). I know it's the obvious way, but surely there is something more elegant than having a thread which exists solely to transfer the result of a select() to the main part of the app, and then repeats. -- Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues] -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On Wed, Aug 21, 2013 at 12:36:56PM +, Mark Morgan Lloyd wrote: > Michael Schnell wrote: > >On 08/20/2013 02:56 PM, Henry Vermaak wrote: > >>... together with select().. > > > >In Object Pascal I think this should be encapsulated in a thread > >and same fires a main Thread event (via TThread.Queue, > >TThread.Synchronize, or Application.QueueAsyncCall) to notify the > >user (aka Main Thread). > > I know it's the obvious way, but surely there is something more > elegant than having a thread which exists solely to transfer the > result of a select() to the main part of the app, and then repeats. Well, the thread exists only because of the blocking nature of the wait/select function. If that's the only task of the application the thread isn't needed, of course. It's more portable and self contained than the alternatives I can think of. The most efficient way to do this would probably be to plumb it straight into the event loop that is in use (if there is one). E.g. you can add file descriptors to the glib event loop. I don't know how that can be done with the Windows message loop, mind you. This approach isn't easy to abstract, though. Henry -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On 08/21/2013 02:36 PM, Mark Morgan Lloyd wrote: I know it's the obvious way, but surely there is something more elegant than having a thread which exists solely to transfer the result of a select() to the main part of the app, and then repeats I suppose a thread is the only way to do this in a portable way. The cost of a thread is close to zero. It just blocks in the requests and does a single turn for any change. -Michael -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Michael Schnell wrote: On 08/21/2013 02:36 PM, Mark Morgan Lloyd wrote: I know it's the obvious way, but surely there is something more elegant than having a thread which exists solely to transfer the result of a select() to the main part of the app, and then repeats I suppose a thread is the only way to do this in a portable way. The cost of a thread is close to zero. It just blocks in the requests and does a single turn for any change. [Noting also Henry's comment] I suppose that, at least for unix platforms, you could have a single app-wide thread which selects on designated handles and syncs a magic number to the foreground. The tricky bit, which would merit attention from somebody who really knows the native APIs, would be adding and removing handles on the fly. -- Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues] -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On Wed, Aug 21, 2013 at 9:36 AM, Mark Morgan Lloyd wrote: > Michael Schnell wrote: >> >> On 08/20/2013 02:56 PM, Henry Vermaak wrote: >>> >>> ... together with select().. >> >> >> In Object Pascal I think this should be encapsulated in a thread and same >> fires a main Thread event (via TThread.Queue, TThread.Synchronize, or >> Application.QueueAsyncCall) to notify the user (aka Main Thread). > > > I know it's the obvious way, but surely there is something more elegant than > having a thread which exists solely to transfer the result of a select() to > the main part of the app, and then repeats. > > > -- > Mark Morgan Lloyd > markMLl .AT. telemetry.co .DOT. uk IMO there should be a basic component which just provides the platform abstraction to block/wait on the change notification (probably inheriting from SyncObjs.THandleObjects?), and build on that to implement the daemon/thread. Unless, of course, it make the design for handling several events with a single thread much more cluttered; but at least Windows (FindFirstChangeNotification) and Linux (inotify) provide wait/blocking-read semantics for these events... -Flávio -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Hi All. I've built some code which works fine under Linux. I'm now working on the Windows part using FindFirstChangeNotificationA to get the directory handle and ReadDirectoryChangesW to fetch events data. And I'm blocked there. The receiving buffer is set to 32kb. After changing a file content, ReadDirectoryChangesW does not return any error but the return length is crazy (28 megs) and nothing in the returned buffer contents looks right. After reading some ms docs I've read thah the buffer has to be DWORD aligned. So there is may question: how to do that ? Thanks, Antonio. -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
See the code in attachment I changed the code a little bit adding the OVERLAP parameter and events to use the async method. Now the same function returns error code 6 Antonio. procedure TFrmMain.StartNotify(const FolderName: String); {$IFDEF LINUX} function decodeMask(Mask:Longint):string; var Rslt:String; begin Rslt:=''; if (Mask and IN_ACCESS)=IN_ACCESS then Rslt:=Rslt+' File was accessed.'; if (Mask and IN_MODIFY)=IN_MODIFY then Rslt:=Rslt+' File was modified.'; if (Mask and IN_ATTRIB)=IN_ATTRIB then Rslt:=Rslt+' Attribute was changed.'; if (Mask and IN_CLOSE_WRITE)=IN_CLOSE_WRITE then Rslt:=Rslt+' Writtable file was closed.'; if (Mask and IN_CLOSE_NOWRITE)=IN_CLOSE_NOWRITE then Rslt:=Rslt+' Unwrittable file was closed.'; if (Mask and IN_CLOSE)=IN_CLOSE then Rslt:=Rslt+' File was closed.'; if (Mask and IN_OPEN)=IN_OPEN then Rslt:=Rslt+' File was opened.'; if (Mask and IN_MOVED_FROM)=IN_MOVED_FROM then Rslt:=Rslt+' File was moved from '; if (Mask and IN_MOVED_TO)=IN_MOVED_TO then Rslt:=Rslt+' File was moved to '; if (Mask and IN_MOVE)=IN_MOVE then Rslt:=Rslt+' File is moving'; if (Mask and IN_CREATE)=IN_CREATE then Rslt:=Rslt+' Subfile was created'; if (Mask and IN_DELETE)=IN_DELETE then Rslt:=Rslt+' Subfile was deleted'; if (Mask and IN_DELETE_SELF)=IN_DELETE_SELF then Rslt:=Rslt+' Self was deleted'; if (Mask and IN_MOVE_SELF)=IN_MOVE_SELF then Rslt:=Rslt+' Self was moved'; if (Mask and IN_UNMOUNT)=IN_UNMOUNT then Rslt:=Rslt+' Filesystem was unmounted'; if (Mask and IN_Q_OVERFLOW)=IN_Q_OVERFLOW then Rslt:=Rslt+' Event queued overflowed'; if (Mask and IN_IGNORED)=IN_IGNORED then Rslt:=Rslt+' File was ignored'; if (Mask and IN_ONLYDIR)=IN_ONLYDIR then Rslt:=Rslt+' Only watch the path if it is a directory'; if (Mask and IN_DONT_FOLLOW)=IN_DONT_FOLLOW then Rslt:=Rslt+' Do not follow a sym link'; if (Mask and IN_MASK_ADD)=IN_MASK_ADD then Rslt:=Rslt+' Add to the mast of an already existing watch'; if (Mask and IN_ISDIR)=IN_ISDIR then Rslt:=Rslt+' Event occurred against dir'; if (Mask and IN_ONESHOT)=IN_ONESHOT then Rslt:=Rslt+' Only send event once'; decodeMask:=Rslt; end; {$ENDIF} {$IFDEF MSWINDOWS} function decodeMask(Mask:Longint):string; var Rslt:String; begin Result := ''; if (Mask and FILE_ACTION_ADDED) = FILE_ACTION_ADDED then Result := Result + ' file ADDED'; if (Mask and FILE_ACTION_REMOVED) = FILE_ACTION_REMOVED then Result := Result + ' file REMOVED'; if (Mask and FILE_ACTION_MODIFIED) = FILE_ACTION_MODIFIED then Result := Result + ' file CHANGED'; end; {$ENDIF} var wFolderName: String; FileName: String; Timeout: Integer=200; Buffer: PChar; PData: Pointer; P: Integer; {$IFDEF LINUX} Ret: Integer; instance_handle: cint; notify_handle: cint; rfds: tfdset; length: Integer; iEvent: inotify_event; buflen: Integer=(SizeOf(iEvent) + 16) * 512; evnt: Pinotify_event; TotalRead: TSsize; FileName: String; {$ENDIF} {$IFDEF MSWINDOWS} wHandle: THANDLE; Ret: Integer; Res: BOOL; pFileInfo: PFILE_NOTIFY_INFORMATION; buflen: DWORD=SizeOf(FILE_NOTIFY_INFORMATION) * 512; TotalRead: DWORD; Error: Integer; Overlap: OVERLAPPED; events: array[0..1] of THANDLE; Ev1: TEvent; Ev2: TEvent; {$ENDIF} begin wFolderName := ExcludeTrailingPathDelimiter(Trim(FolderName)); {$IFDEF MSWINDOWS} wHandle := FindFirstChangeNotificationA(PChar(wFolderName), DWORD(LongBool(False)), FILE_NOTIFY_CHANGE_SIZE + FILE_NOTIFY_CHANGE_LAST_WRITE); try if wHandle = INVALID_HANDLE_VALUE then begin MessageDlg('Error','Invalid notify handle', mtError, [mbOK], 0, mbOK); Exit end; Ev1 := TEvent.Create(nil, False, False, 'FEV1_tralala'); Ev2 := TEvent.Create(nil, False, False, 'FEV2_tralala'); events[0] := THANDLE(Ev1.Handle); events[1] := THANDLE(Ev2.Handle); FillMemory(@Overlap, SizeOf(OVERLAPPED), byte(#0)); Overlap.hEvent := THANDLE(Ev1.Handle); FStopNotify := False; PData := nil; buflen := 32 * 1024; ReAllocMem(PData, buflen); //Buffer := system.Align(PData, 16); Buffer := PData; while not FStopNotify do begin Ret := WaitForSingleObject(wHandle, 200); if Ret = WAIT_OBJECT_0 then begin FillMemory(PData, buflen, byte(#0)); Res := ReadDirectoryChangesW(wHandle, Buffer, buflen, False, FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_SIZE + FILE_NOTIFY_CHANGE_LAST_WRITE, @TotalRead, @Overlap, nil); if not Res then begin Error := GetLastError; FStopNotify := True; Memo1.Lines.Add(Format('error on read changes: %d %s', [Error, SysErrorMessage(Error)])); Brea
Re: [Lazarus] Shell notifications
Am 22.08.2013 13:33, schrieb Antonio Fortuny: See the code in attachment I changed the code a little bit adding the OVERLAP parameter and events to use the async method. Now the same function returns error code 6 Error code 6 is ERROR_INVALID_HANDLE (would be nice if you'd mention the error message as well next time when you're already using SysErrorMessage). FindFirstChangeNotification returns a notification handle, but ReadDirectoryChanges requires a directory handle (with FILE_LIST_DIRECTORY priviledge), so you need to open the directory yourself. Regards, Sven -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
On Thu, Aug 22, 2013 at 01:49:28PM +0200, Sven Barth wrote: > Am 22.08.2013 13:33, schrieb Antonio Fortuny: > >See the code in attachment > > > >I changed the code a little bit adding the OVERLAP parameter and > >events to use the async method. > >Now the same function returns error code 6 > Error code 6 is ERROR_INVALID_HANDLE (would be nice if you'd mention > the error message as well next time when you're already using > SysErrorMessage). FindFirstChangeNotification returns a notification > handle, but ReadDirectoryChanges requires a directory handle (with > FILE_LIST_DIRECTORY priviledge), so you need to open the directory > yourself. To add to this, the remarks section on msdn says this: "To obtain a handle to a directory, use the CreateFile function with the FILE_FLAG_BACKUP_SEMANTICS flag." In addition to this you're also passing an OVERLAPPED record to ReadDirectoryChangesW(), so you'll have to use FILE_FLAG_OVERLAPPED when you open the directory, too. Henry -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
Re: [Lazarus] Shell notifications
Le 22/08/2013 14:06, Henry Vermaak a écrit : On Thu, Aug 22, 2013 at 01:49:28PM +0200, Sven Barth wrote: Am 22.08.2013 13:33, schrieb Antonio Fortuny: See the code in attachment I changed the code a little bit adding the OVERLAP parameter and events to use the async method. Now the same function returns error code 6 Error code 6 is ERROR_INVALID_HANDLE (would be nice if you'd mention the error message as well next time when you're already using SysErrorMessage). FindFirstChangeNotification returns a notification handle, but ReadDirectoryChanges requires a directory handle (with FILE_LIST_DIRECTORY priviledge), so you need to open the directory yourself. To add to this, the remarks section on msdn says this: "To obtain a handle to a directory, use the CreateFile function with the FILE_FLAG_BACKUP_SEMANTICS flag." In addition to this you're also passing an OVERLAPPED record to ReadDirectoryChangesW(), so you'll have to use FILE_FLAG_OVERLAPPED when you open the directory, too. This did it. Don't even need FindFirstChangeNotification Thanks Antonio. Henry -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus -- ___ Lazarus mailing list Lazarus@lists.lazarus.freepascal.org http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus