Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме / 6 сообщений из 6, страница 1 из 1
24.06.2021, 13:14
    #40079821
hlopotun
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме
Всем доброго дня,

хотелось бы работать с этой функцией в синхронном режиме т.к. асинхронный сильно всё усложняет ( например ).
Ход мысли такой, если мы имеем THandle папки логично было бы предположить что ему можно послать сообщение которое приведёт к срабатыванию ReadDirectoryChangesW. Можно конечно что то тупо записать в папку но это подходит только для случая если "слушатель" имеет право на запись в эту папку.
Среди флагов фильтра на события в папке: FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME or FILE_NOTIFY_CHANGE_ATTRIBUTES or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE or FILE_NOTIFY_CHANGE_LAST_ACCESS or FILE_NOTIFY_CHANGE_CREATION or FILE_NOTIFY_CHANGE_SECURITY
на роль выключателя наверное подходят только FILE_NOTIFY_CHANGE_LAST_ACCESS т.к. остальные требуют права на запись (хотя возможно и этот флаг требует права на запись, ещё не проверил).
Из описания функции сделать однозначный вывод каким сообщением можно вывести её из ожидания сделать не получается. Тут и тут человек разбирался с проблеммой но всё выглядит немного запутанно.
Хорошо было бы заглянуть в исходники ReadDirectoryChangesW, возможно тогда можно было бы понять как обойти эту проблему и вывести ReadDirectoryChangesW из режима ожидания через PostMessage или ещё как.

п.с. TShellChangeNotifier не подходит т.к. вроде не даёт информации о том какой файл изменился.

Спасибо если кто подскажет хорошую идею.
...
Рейтинг: 0 / 0
24.06.2021, 13:20
    #40079822
_Vasilisk_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме
hlopotun
хотелось бы работать с этой функцией в синхронном режиме
Очень странное желание.

Ну используйте тогда FindFirstChangeNotification
...
Рейтинг: 0 / 0
24.06.2021, 13:27
    #40079826
_Vasilisk_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме
Вот код, работающий уже лет 15

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
unit wfsU;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  Windows, SysUtils, Classes, ActiveX;

type
  // Структура с информацией об изменении в файловой системе (передается в callback процедуру)
  PInfoCallback = ^TFileNotifyInfo;
  TFileNotifyInfo = record
    Action      : Integer; // тип изменения (константы FILE_ACTION_XXX)
    Path        : string;  // путь, по которому было изменение
    OldFileName : string;  // имя файла до переименования
    NewFileName : string;  // имя файла после переименования
  end;

  // callback процедура, вызываемая при изменении в файловой системе
  TWatchFileSystemCallback = procedure (const AInfo: TFileNotifyInfo) of object;

  TWFS = class(TThread)
  private
    FPath           : string;
    FFilter         : Cardinal;
    FSubTree        : Boolean;
    FInfoCallback   : TWatchFileSystemCallback;

    FWatchHandle    : THandle;
    FCompletionPort : THandle;
    FOldFileName    : string;
  private
    procedure HandleEvent(AFileNotifyInfo: Pointer);
  protected
    procedure Execute; override;
    procedure DoProcess;
  public
  { Запуск мониторинга файловой системы
  Праметры:
    APath    - имя папки для мониторинга
    AFilter  - комбинация констант FILE_NOTIFY_XXX
    ASubTree - мониторить ли все подпапки заданной папки
    AInfoCallback - адрес callback процедуры, вызываемой при изменении в файловой системе}
    constructor Create(const APath: string; AFilter: Cardinal; ASubTree: Boolean;
      AInfoCallback: TWatchFileSystemCallback);
    destructor Destroy; override;
    procedure Stop;
  end;

function TestFileAccess(const AFileName: string): Cardinal;

implementation

uses
  UThreads;

const
  FILE_LIST_DIRECTORY   = $0001;

type
  {$IF not Declared(ULONG_PTR)}
    {$IFDEF CPUX64}
    ULONG_PTR = UInt64;
    {$ELSE}
    ULONG_PTR = Cardinal;
    {$ENDIF}
  {$IFEND}

  PFileNotifyInformation = ^TFileNotifyInformation;
  TFileNotifyInformation = record
     NextEntryOffset : DWORD;
     Action          : DWORD;
     FileNameLength  : DWORD;
     FileName        : array[0..0] of WideChar;
  end;

function TestFileAccess(const AFileName: string): Cardinal;
var
  LHandle: THandle;
begin
  LHandle := CreateFile(PChar(AFileName), GENERIC_READ, FILE_SHARE_READ, nil,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if LHandle <> INVALID_HANDLE_VALUE then begin
    CloseHandle(LHandle);
    Result := 0;
  end else
    Result := GetLastError;
end;

{ TWFS }

constructor TWFS.Create(const APath: string; AFilter: Cardinal; ASubTree: Boolean;
  AInfoCallback: TWatchFileSystemCallback);
begin
  inherited Create(False);
  {$IFOPT D+}
  NameThreadForDebugging('TWFS', ThreadID);
  {$ENDIF}
  TThreadNamesManager.SetThreadMapName(Format('%s.%s', [ExtractFileName(GetModuleName(HInstance)), ClassName]), ThreadID);
  FreeOnTerminate := True;
  FPath := IncludeTrailingPathDelimiter(APath);
  FFilter := AFilter;
  FSubTree := ASubTree;
  FOldFileName := EmptyStr;
  FInfoCallback := AInfoCallback;

  FWatchHandle:=CreateFile(PChar(FPath), FILE_LIST_DIRECTORY,
    FILE_SHARE_READ or FILE_SHARE_DELETE or FILE_SHARE_WRITE, nil,
    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0);
  Win32Check(FWatchHandle <> INVALID_HANDLE_VALUE);
  FCompletionPort := CreateIoCompletionPort(FWatchHandle, 0, Longint(Pointer(Self)), 0);
  Win32Check(LongBool(FCompletionPort));
end;

destructor TWFS.Destroy;
begin
  PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil);
  CloseHandle(FCompletionPort);
  CloseHandle(FWatchHandle);
  TThreadNamesManager.RemoveThreadMapName(ThreadID);
  inherited Destroy;
end;

procedure TWFS.Execute;
begin
  CoInitializeEx(nil, COINIT_MULTITHREADED or COINIT_SPEED_OVER_MEMORY);
  try
    DoProcess;
  finally
    CoUninitialize;
  end;
end;

procedure TWFS.DoProcess;
const
  CWatchBufSize = 1024 * 1024;
var
  LCompletionKey: ULONG_PTR;
  LOverlapped: TOverlapped;
  LOverlappedPtr: POverlapped;
  LWatchBuf: Pointer;
  LBytesWrite: DWORD;
  LNumBytes: Cardinal;
begin
  {$OPTIMIZATION ON}
  LOverlappedPtr := @LOverlapped;
  FillChar(LOverlapped, SizeOf(LOverlapped), 0);
  GetMem(LWatchBuf, CWatchBufSize);
  try
    while not Terminated do begin
      FillChar(LWatchBuf^, CWatchBufSize, 0);
      LBytesWrite := 0;
      Win32Check(ReadDirectoryChangesW(FWatchHandle, LWatchBuf, CWatchBufSize,
        FSubTree, FFilter, @LBytesWrite, LOverlappedPtr, nil));
      GetQueuedCompletionStatus(FCompletionPort, LNumBytes, LCompletionKey, LOverlappedPtr, INFINITE);
      if LCompletionKey <> 0 then
        HandleEvent(LWatchBuf)
      else
        Terminate;
    end;
  finally
    FreeMem(LWatchBuf);
  end;
end;

procedure TWFS.HandleEvent(AFileNotifyInfo: Pointer);
var
  LFileNotifyInfo: PFileNotifyInformation absolute AFileNotifyInfo;
  LInfoCallback: TFileNotifyInfo;
  LOffset: DWORD;
begin
  repeat
    LInfoCallback.Action := LFileNotifyInfo^.Action;
    LInfoCallback.Path := FPath;
    LInfoCallback.NewFileName := WideCharLenToString(
      @LFileNotifyInfo^.FileName[0], LFileNotifyInfo^.FileNameLength shr 1);
    case LFileNotifyInfo^.Action of
      FILE_ACTION_RENAMED_OLD_NAME: FOldFileName := LInfoCallback.NewFileName;
      FILE_ACTION_RENAMED_NEW_NAME: begin
        LInfoCallback.OldFileName := FOldFileName;
        FOldFileName := EmptyStr;
      end;
    end;
    FInfoCallback(LInfoCallback);
    LOffset := LFileNotifyInfo^.NextEntryOffset;
    LFileNotifyInfo := PFileNotifyInformation(ULONG_PTR(LFileNotifyInfo) + LOffset);
  until (LOffset = 0) or Terminated;
end;

procedure TWFS.Stop;
var
  LPID: THandle;
  LHandle: THandle;
begin
  LPID := GetCurrentProcess;
  Win32Check(DuplicateHandle(LPID, Handle, LPID, @LHandle, 0, False, DUPLICATE_SAME_ACCESS));
  try
    Terminate;
    Win32Check(PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil));
    Win32Check(WaitForSingleObject(LHandle, INFINITE) <> WAIT_FAILED);
  finally
    CloseHandle(LHandle);
  end;
end;

end.

...
Рейтинг: 0 / 0
24.06.2021, 13:35
    #40079832
kapas
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме
...
Рейтинг: 0 / 0
24.06.2021, 14:02
    #40079843
hlopotun
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме
kapas, _Vasilisk_,

спасибо за пример и ссылку. Углубился в изучение ...
...
Рейтинг: 0 / 0
24.06.2021, 14:36
    #40079850
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме
Любой асинхронный режим легко превращается в синхронный втыкновением
WaitForMultipleObjects(), куда точно так же легко добавляется возможность его выбить с
помощью дополнительного события.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / ReadDirectoryChangesW инициировать срабатывание без реальной записи в синхронном режиме / 6 сообщений из 6, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]