unit cThread;
interface
uses
SysUtils, Classes, Controls, SyncObjs, Windows, ShellApi, ComCtrls;
const
FILE_LIST_DIRECTORY = $0001;
WaitDir = WAIT_OBJECT_0;
WaitTerm = WAIT_OBJECT_0+1;
WaitSusp = WAIT_OBJECT_0+2;
type
CtrlThread =
class(TThread)
private
FhFile : DWORD;
// Handle von CreateFile auf die kontrollierte Ordnerstruktur
FsDirPath :
string;
// der Pfad zu der Ordnerstruktur, die zu überwachen ist
FsFileName :
string;
// ein Dateiname aus einen Überwachungsereignis der Ordner
FAction : DWORD;
// Verzeichnisaktion, die erfolgte
FileEvent : THandle;
// Handle für das Ordnerüberwachungsereignis
SuspEvent : TEvent;
// Ereignis für das suspendieren des Threads
SuspEvName :
String;
// Name dieses Ereignisses
TermEvent : TEvent;
// eigenes Ereignis, das für den Abbruch des Threads erforderlich ist
TermEvName :
String;
// Name des Abbruchereignisses
Owner : TComponent;
// Besitzer des Threads
FFilter : DWord;
// Filter für
procedure SendData;
// eine Beispielfunktion für die Datenübergabe
public
constructor Create(AOwner: TComponent;
const AsDirPath:
string; AFilter: DWORD);
destructor Destroy;
override;
procedure Execute;
override;
Procedure SetEventNames(AwEvName,AnEvName:
string);
end;
PFILE_NOTIFY_INFORMATION = ^FILE_NOTIFY_INFORMATION;
// Strukturzeiger
FILE_NOTIFY_INFORMATION =
packed record // Ereignisrecord für !ein! überwachungsereignis
dwNextEntryOffset : DWORD;
// Offset zum nächsten Eintrag
dwAction : DWORD;
// Ereignisgrund
dwFileNameLength : DWORD;
// Länge des Dateinamens
dwFileName : WideString;
// Dateiname
end;
implementation
uses FldrControl;
constructor CtrlThread.Create(AOwner: TComponent;
const AsDirPath:
string; AFilter: DWORD);
begin
inherited Create(TRUE);
// Thread erstellen und nicht laufen lassen
FsDirPath := AsDirPath;
// der zu überwachende Pfad
Owner := AOwner;
// der Besitzer des Threads
FFIlter := AFilter;
// Filter für .. übernehmen
FreeOnTerminate := true;
// Thread nach Beendigung vernichten lassen
end;
// Namen für die Ereignisse:
// werden von der Komponente aus gesetzt, was aber erst geschehen kann,
// wenn der Thread creiert ist
Procedure CtrlThread.SetEventNames(AwEvName,AnEvName:
String);
begin
SuspEvName:=AwEvName;
TermEvName:=AnEvName;
end;
procedure CtrlThread.Execute;
var
pBuffer : Pointer;
// Puffer für die Rückgabedaten von ReadDirectoryChangesW
dwBufLen : DWORD;
// Größe des Puffers
dwRead : DWORD;
// Anzahl der Daten im Puffer
PInfo : PFILE_NOTIFY_INFORMATION;
// Maske für die Pufferdaten
dwNextOfs : DWORD;
// Position des Folgesatzes im Puffer (bei 0 - keiner)
dwFnLen : DWORD;
// Dateinamenlänge
Overlap : TOverlapped;
// Asynchronstruktur
WaitResult: DWORD;
// Rückgabewert von WaitForMultipleObjects
EventArray :
Array[0..2]
of THandle;
// Array der Handles für WaitForMultipleObjects
begin
// Handle auf das zu überwachende Verzeichnis
FhFile:= CreateFile(PChar(FsDirPath),
FILE_LIST_DIRECTORY
or GENERIC_READ,
FILE_SHARE_READ
or FILE_SHARE_WRITE
or FILE_SHARE_DELETE,
nil,
OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS
or FILE_FLAG_OVERLAPPED,
0);
if (FhFile = INVALID_HANDLE_VALUE)
or (FhFile = 0)
then exit;
// Ereignis für Asynchronbehandlung von ReadDirectoryChangesW
FileEvent:=CreateEvent(
nil,FALSE,FALSE,
nil);
// Asynchronstruktur mit Ereignis besetzen
Overlap.hEvent:=FileEvent;
// Neues Ereignis erstellen, um später WaitForMultipleObjects abzubrechen
// der Abbruch erfolgt von außen durch die Erzeugung des gleichen Events
// welcher dann auf signaled gesetzt werden muß
TermEvent:=TEvent.Create(
nil,FALSE,FALSE,TermEvName);
// Neues Ereignis erstellen, um später WaitForMultipleObjects abzubrechen
// und den Thread auf suspendet zu setzen
// der Abbruch erfolgt von außen durch die Erzeugung des gleichen Events
// welcher dann auf signaled gesetzt werden muß
SuspEvent:=TEvent.Create(
nil,FALSE,FALSE,SuspEvName);
// die drei Ereignishandles für WaitForMultipleObjects in ein Array füllen
EventArray[0]:=FileEvent;
// Ordneränderung
EventArray[1]:=TermEvent.Handle;
// Abbruch
EventArray[2]:=SuspEvent.Handle;
// Suspend
// Rückgabepuffer für ReadDirectoryChangesW erstellen
dwBufLen := 65535;
pBuffer := AllocMem(dwBufLen);
try
// Ablauf starten ...
while not terminated
do begin
// Ordnerüberwachung asynchron erstellen
dwRead:=0;
if ReadDirectoryChangesW(FhFile,pBuffer,dwBufLen,true,
FFilter,
//FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME,
@dwRead,@Overlap,
NIL)
then
begin
// unendliches warten bis eines der Ereignisse eintritt
WaitResult:=WaitForMultipleObjects(3,@EventArray,FALSE,infinite);
// wenn das Warten beendet wird, dann ...
case WaitResult
of
// ... weil sich etwas in den Ordnern getan hat
WaitDir:
begin
// Maske über den Puffer legen, um auf die Daten zuzugreifen
PInfo:= pBuffer;
// und dies wiederholen ...
repeat
// Offset zum folgenden Datensatz
dwNextOfs :=PInfo.dwNextEntryOffset;
// Aktion, die im Ordnerbaum sattfand
fAction :=PInfo.dwAction;
// Länge des betroffenen Ordners oder der Datei
dwFnLen :=PInfo.dwFileNameLength;
// Dateinamen in AnsiString umsetzen
fsFileName:=WideCharLenToString(@PInfo.dwFileName,dwFnLen
div 2);
// Daten in den Haupttread übergeben
Synchronize(SendData);
// Maske auf den nächsten Datensatz verschieben
pChar(PInfo):=pChar(PInfo)+dwNextOfs;
// wenn keiner mehr vorhanden ist, beenden
until dwNextOfs=0;
end;
// ... oder das Abbruchereignis eingetreten ist
WaitTerm: Terminate;
// ... oder das Ereignis zum suspendieren eingetreten ist
WaitSusp: Suspend;
else break;
end;
end;
end;
finally
FreeMem(pBuffer,dwBufLen);
end;
end;
destructor CtrlThread.Destroy;
begin // diverse Handles freigeben
try
if FhFile <> INVALID_HANDLE_VALUE
then CloseHandle(FhFile);
CloseHandle(FileEvent);
TermEvent.Free;
SuspEvent.Free;
except
end;
end;
// Daten an das Control-Objekt übergeben
procedure CtrlThread.SendData;
begin
TFldrControl(Owner).EvAction:=FAction;
TFldrControl(Owner).EvOrdner:=fsFileName;
// und dort die Benachrichtigungsfunktion aufrufen
TFldrControl(Owner).GetData;
end;
end.