Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2013-08

Shell notifications

18 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Antonio Fortuny

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.

Reply by email

Mark Morgan Lloyd
to Antonio Fortuny
Antonio Fortuny wrote 5 lines
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]

Reply by email

Michael Van Canneyt
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 9 lines
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.

Reply by email

Antonio Fortuny
to Henry Vermaak

Le 20/08/2013 14:56, Henry Vermaak a ?crit :

Show quoted text 7 lines
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.

Show quoted text 9 lines
Henry -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Henry Vermaak
to Antonio Fortuny
Antonio Fortuny wrote 10 lines
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.

Show quoted text 5 lines
>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

Reply by email

Antonio Fortuny
to Henry Vermaak

Le 20/08/2013 15:46, Henry Vermaak a ?crit :

Show quoted text 22 lines
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
Sita Software *Antonio Fortuny* Senior Software engineer 220, avenue de la Libert? L-4602 Niederkorn Tel.: +352 58 00 93 - 93 www.sitasoftware.lu <http://www.sitasoftware.lu> Your IT Partner -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130820/a66cad69/attachment-0001.html> -------------- next part -------------- A non-text attachment was scrubbed... Name: cffbecif.jpg Type: image/jpeg Size: 2306 bytes Desc: not available URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130820/a66cad69/attachment-0002.jpg> -------------- next part -------------- A non-text attachment was scrubbed... Name: ciidbjii.jpg Type: image/jpeg Size: 921 bytes Desc: not available URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130820/a66cad69/attachment-0003.jpg>

Reply by email

Mark Morgan Lloyd
to Michael Schnell
Michael Schnell wrote 6 lines
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]

Reply by email

Henry Vermaak
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 12 lines
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

Reply by email

Michael Schnell
to Mark Morgan Lloyd: I know it's the obvious way, but surely there is something more elegant than having a…
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

Reply by email

Mark Morgan Lloyd
to Michael Schnell
Michael Schnell wrote 9 lines
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]

Reply by email

Flávio Etrusco
to Mark Morgan Lloyd: I know it's the obvious way, but surely there is something more elegant than having a…
Mark Morgan Lloyd wrote 20 lines
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

Reply by email

Antonio Fortuny
to Antonio Fortuny: Hi Folks. Do you know any component / tool to allow an application to be notified by the…

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.

Reply by email

Antonio Fortuny
to 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

Antonio.
-------------- next part --------------
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)]));
          Break
        end;
        P := 0;
        Ret := WaitForMultipleObjects(2, events, False, 200);
        case Ret of
          WAIT_OBJECT_0: begin
          end;
          else begin
            if GetOverlappedResult(wHandle, Overlap, TotalRead, True) then begin
              pFileInfo := PFILE_NOTIFY_INFORMATION(Buffer);
            end;
          end;
        end;
        if TotalRead > 0 then begin
          while P < TotalRead do begin
            pFileInfo := PFILE_NOTIFY_INFORMATION(Buffer + P);
            if (((pFileInfo^.Action and FILE_ACTION_ADDED) = FILE_ACTION_ADDED)
                //or (((evnt^.mask and IN_ATTRIB)=IN_ATTRIB))
                or (((pFileInfo^.Action and FILE_ACTION_MODIFIED) = FILE_ACTION_MODIFIED))) then begin
              FileName := Copy(String(@pFileInfo^.FileName), 1, pFileInfo^.FileNameLength);
              if (FileName  '.') and (FileName  '..') then begin
                Memo1.Lines.Add(Format('Next:%d, Action:%s, Cookie:%d, Len:%d, Name:%s',
                                      [pFileInfo^.NextEntryOffset, decodeMask(pFileInfo^.Action), pFileInfo^.FileNameLength, FileName]));
              end;
            end;
            if pFileInfo^.NextEntryOffset = 0 then begin
              FStopNotify := True;
              Break;
            end;
            Inc(P, pFileInfo^.NextEntryOffset);
            Application.ProcessMessages
          end;
        end;
      end;
      Application.ProcessMessages;
      if not FindNextChangeNotification(wHandle) then begin
        MessageDlg('Error','Invalid get next notification', mtError, [mbOK], 0, mbOK);
        Break
      end;
    end;
  finally
    if Assigned(PData) then
      ReAllocMem(PData, 0);
    FindCloseChangeNotification(wHandle)
  end;
  {$ENDIF}
  {$IFDEF LINUX}
  instance_handle := inotify_init;
  try
    if instance_handle = -1 then begin
      MessageDlg('Error','Invalid notify handle', mtError, [mbOK], 0, mbOK);
      Exit
    end;
    FStopNotify := False;
    inotify_add_watch(instance_handle, PChar(FolderName), IN_MODIFY+IN_CREATE+IN_DONT_FOLLOW);
    while not FStopNotify do begin
      fpFD_Zero(rfds);
      fpFD_Set(instance_handle, rfds);
      Ret := fpSelect (instance_handle+1, @rfds, nil, nil, Timeout);
      if Ret < 0 then begin
        MessageDlg('Error',Format('Return error for select (%d)', [Ret]), mtError, [mbOK], 0, mbOK);
        Break
      end;
      Application.ProcessMessages;
      if Ret = 0 then
        Continue;
      Buffer := nil;
      ReAllocMem(Buffer, buflen);
      TotalRead := fpRead(instance_handle, Buffer, buflen);
      P := 0;
      while P < TotalRead do begin
        evnt := pinotify_event(buffer + P);
        if (((evnt^.mask and IN_MODIFY)=IN_MODIFY)
            //or (((evnt^.mask and IN_ATTRIB)=IN_ATTRIB))
            or (((evnt^.mask and IN_CREATE)=IN_CREATE)))
        and (String(@evnt^.Name)  '.') and (String(@evnt^.Name)  '..') then begin
          Memo1.Lines.Add(Format('Wd:%d, Mask:%s, Cookie:%d, Len:%d, Name:%s',
                                [evnt^.Wd, decodeMask(evnt^.mask), evnt^.cookie, evnt^.Len, pchar(@evnt^.Name)]));
        end;
        Inc(P, evnt^.len+16);
        Application.ProcessMessages
      end;
      ReAllocMem(Buffer, 0);
    end
  finally
    FpClose(instance_handle)
  end;
  {$ENDIF}
end;
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)]));
          Break
        end;
        P := 0;
        Ret := WaitForMultipleObjects(2, events, False, 200);
        case Ret of
          WAIT_OBJECT_0: begin
          end;
          else begin
            if GetOverlappedResult(wHandle, Overlap, TotalRead, True) then begin
              pFileInfo := PFILE_NOTIFY_INFORMATION(Buffer);
            end;
          end;
        end;
        if TotalRead > 0 then begin
          while P < TotalRead do begin
            pFileInfo := PFILE_NOTIFY_INFORMATION(Buffer + P);
            if (((pFileInfo^.Action and FILE_ACTION_ADDED) = FILE_ACTION_ADDED)
                //or (((evnt^.mask and IN_ATTRIB)=IN_ATTRIB))
                or (((pFileInfo^.Action and FILE_ACTION_MODIFIED) = FILE_ACTION_MODIFIED))) then begin
              FileName := Copy(String(@pFileInfo^.FileName), 1, pFileInfo^.FileNameLength);
              if (FileName  '.') and (FileName  '..') then begin
                Memo1.Lines.Add(Format('Next:%d, Action:%s, Cookie:%d, Len:%d, Name:%s',
                                      [pFileInfo^.NextEntryOffset, decodeMask(pFileInfo^.Action), pFileInfo^.FileNameLength, FileName]));
              end;
            end;
            if pFileInfo^.NextEntryOffset = 0 then begin
              FStopNotify := True;
              Break;
            end;
            Inc(P, pFileInfo^.NextEntryOffset);
            Application.ProcessMessages
          end;
        end;
      end;
      Application.ProcessMessages;
      if not FindNextChangeNotification(wHandle) then begin
        MessageDlg('Error','Invalid get next notification', mtError, [mbOK], 0, mbOK);
        Break
      end;
    end;
  finally
    if Assigned(PData) then
      ReAllocMem(PData, 0);
    FindCloseChangeNotification(wHandle)
  end;
  {$ENDIF}
  {$IFDEF LINUX}
  instance_handle := inotify_init;
  try
    if instance_handle = -1 then begin
      MessageDlg('Error','Invalid notify handle', mtError, [mbOK], 0, mbOK);
      Exit
    end;
    FStopNotify := False;
    inotify_add_watch(instance_handle, PChar(FolderName), IN_MODIFY+IN_CREATE+IN_DONT_FOLLOW);
    while not FStopNotify do begin
      fpFD_Zero(rfds);
      fpFD_Set(instance_handle, rfds);
      Ret := fpSelect (instance_handle+1, @rfds, nil, nil, Timeout);
      if Ret < 0 then begin
        MessageDlg('Error',Format('Return error for select (%d)', [Ret]), mtError, [mbOK], 0, mbOK);
        Break
      end;
      Application.ProcessMessages;
      if Ret = 0 then
        Continue;
      Buffer := nil;
      ReAllocMem(Buffer, buflen);
      TotalRead := fpRead(instance_handle, Buffer, buflen);
      P := 0;
      while P < TotalRead do begin
        evnt := pinotify_event(buffer + P);
        if (((evnt^.mask and IN_MODIFY)=IN_MODIFY)
            //or (((evnt^.mask and IN_ATTRIB)=IN_ATTRIB))
            or (((evnt^.mask and IN_CREATE)=IN_CREATE)))
        and (String(@evnt^.Name)  '.') and (String(@evnt^.Name)  '..') then begin
          Memo1.Lines.Add(Format('Wd:%d, Mask:%s, Cookie:%d, Len:%d, Name:%s',
                                [evnt^.Wd, decodeMask(evnt^.mask), evnt^.cookie, evnt^.Len, pchar(@evnt^.Name)]));
        end;
        Inc(P, evnt^.len+16);
        Application.ProcessMessages
      end;
      ReAllocMem(Buffer, 0);
    end
  finally
    FpClose(instance_handle)
  end;
  {$ENDIF}
end;

Reply by email

Sven Barth
to Antonio Fortuny
Antonio Fortuny wrote 5 lines
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

Reply by email

Henry Vermaak
to Sven Barth
Sven Barth wrote 12 lines
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

Reply by email

Antonio Fortuny
to Henry Vermaak

Le 22/08/2013 14:06, Henry Vermaak a ?crit :

Show quoted text 21 lines
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.

Show quoted text 9 lines
Henry -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email