[Lazarus] Shell notifications

Antonio Fortuny a.fortuny at sitasoftware.lu
Thu Aug 22 13:33:25 CEST 2013


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;


More information about the Lazarus mailing list