ReadDirectoryChangesW und WaitForMultipleObjects aber wie!

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
silbersurfer
Beiträge: 174
Registriert: 06.07.2014 12:21

ReadDirectoryChangesW und WaitForMultipleObjects aber wie!

Beitrag von silbersurfer »

Hallo Leute,
wieder mal ein Problem, diesesmal geht es um ReadDirectoryChangesW() und dem WaitForMultipleObjects()
habe nach allen möglichen gesucht bin auch zum Teil fündig geworden, nur leider scheitere ich am Abbruch der Überwachung für ein Verzeichnis,
da ja ReadDirectoryChangesW() weiterhin auf ein Event wartet.
Ich habe dann etwas unter Delphi gefunden, wo eben mittels WaitForMultipleObjects() ein Abbruch erzeugt werden kann, leider
bekomme ich das nicht in Purebasic übersetzt :evil: , hoffe das ihr mir mal wieder da weiter helfen könnt

Her der Delphi Code
public:
/**
* Thread procedure
*/
void run()
{
looping = true;

const TCHAR* dir = (const TCHAR*)directory;
if (GetFileAttributes(dir) == 0xffffffff) {
//2008/09/24 Should be changed to be able to make multiple subdirectories
if (CreateDirectory(dir, NULL)) {
_tprintf(_T("Create a new directory %s\n"), dir); //2012/06/22
}
}

this->hFolder = CreateFile(dir,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
// Specify FILE_FLAG_OVERLAPPED flags
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED,
NULL);

if(this->hFolder == INVALID_HANDLE_VALUE){
_tprintf(_T("Failed to open dir %s\n"), dir); //2012/06/22
return;
}

OVERLAPPED ol;
ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
HANDLE waitEvents[2];
waitEvents[0] = terminateEvent;
waitEvents[1] = ol.hEvent;

//Start a loop of call ReadDirectoryChangesW().
while(looping) {

DWORD dwBytes = 0;
TCHAR buf[1024];

BOOL rc = ReadDirectoryChangesW(hFolder, buf, 1022,1,
this->filters,
/*
//2008/12/10
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE|
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_SECURITY,
*/
&dwBytes, &ol, NULL);

if (rc == FALSE) {
printf("Failed:ReadDirectoryChanges()\n");
break;
}
if (looping == false) {
printf("looping ends\n");
break;
}

int r = WaitForMultipleObjects(2, waitEvents, FALSE, INFINITE );
if (r == WAIT_OBJECT_0 ) {
//printf("Stopped 0\n");
printf("\nTerminateEvent has been set, so break this thread loop\n");
break;
}

BOOL b = GetOverlappedResult(hFolder, &ol, &dwBytes, TRUE );
if (b == FALSE) {
continue;
}

//printf("buf ReadSize=%d\n",dwBytes);

FILE_NOTIFY_INFORMATION *fn = (FILE_NOTIFY_INFORMATION *)buf;

while (fn) {
//FileName will be "NULL" terminated
fn->FileName[fn->FileNameLength/2] = 0;

wchar_t * action = L"";

switch(fn->Action){

case FILE_ACTION_ADDED:
action = L"Added";
break;
case FILE_ACTION_REMOVED:
action = L"Removed";
break;
case FILE_ACTION_MODIFIED:
action = L"Accessed/Modified";
break;
case FILE_ACTION_RENAMED_OLD_NAME:
action = L"Renamed(Old name)";
break;
case FILE_ACTION_RENAMED_NEW_NAME:
action = L"Renamed(New name)";
break;
default:
action = L"(Unknown)";
}

changedFileName(action, fn->FileName);

if (fn->NextEntryOffset == NULL) {
break;
} else {
fn = (FILE_NOTIFY_INFORMATION *)(((char *)fn) + fn->NextEntryOffset);
}
} //while

}

CloseHandle(ol.hEvent);

close();

}
};

}
und Hier eine Purebasic Variante aber eben ohne Abbruch

Code: Alles auswählen

EnableExplicit
; überwachung Constanten
#FILE_NOTIFY_CHANGE_FILE_NAME = 1
#FILE_NOTIFY_CHANGE_DIR_NAME = 2
#FILE_NOTIFY_CHANGE_ATTRIBUTES = 4
#FILE_NOTIFY_CHANGE_SIZE = 8
#FILE_NOTIFY_CHANGE_LAST_WRITE = $10
#FILE_NOTIFY_CHANGE_LAST_ACCESS = $20
#FILE_NOTIFY_CHANGE_CREATION = $40
#FILE_NOTIFY_CHANGE_SECURITY = $100
#FILE_NOTIFY_CHANGE_ALL = $17F
#FILE_SHARE_DELETE = 4
#FILE_ACTION_ADDED = 1
#FILE_ACTION_REMOVED = 2
#FILE_ACTION_MODIFIED = 3
#FILE_ACTION_RENAMED_OLD_NAME = 4
#FILE_ACTION_RENAMED_NEW_NAME = 5

Import "kernel32.lib"
  ReadDirectoryChangesW(a, b, c, d, e, f, g, h)
EndImport
Structure FILE_NOTIFY_INFORMATION ;für ReadDirectoryChangesW
  NextEntryOffset.l
  Action.l : stop.i
  FileNameLength.l
  Filename.s{255}
EndStructure
Structure Thread ; ThreadStruktur
	Thread.i
	Stop.i
	Mutex.i
	Pfad.s
EndStructure	
Global.Thread Watch

Procedure WatchFiles(f) ;Thread von ReadDirectoryChangesW
	Protected DirectoryName.s=Watch\Pfad
	Protected NotifyFilter.l = #FILE_NOTIFY_CHANGE_ALL
	Protected buffer.FILE_NOTIFY_INFORMATION, ovlp.OVERLAPPED
	Protected Filename.s
	Protected hDir,a
	Protected bytesRead
	Protected path$,item.i
	hDir = CreateFile_(DirectoryName, #FILE_LIST_DIRECTORY, #FILE_SHARE_READ | #FILE_SHARE_WRITE | #FILE_SHARE_DELETE, #Null, #OPEN_EXISTING, #FILE_FLAG_BACKUP_SEMANTICS, #Null)
	Debug "Überwachung von "+DirectoryName+"  gestartet !"
	Repeat	
		If Watch\Stop : Break : EndIf  ; wird erst augeführt sobald ReadDirectoryChangesW etwas regestiert (Problem !!!!)
		ReadDirectoryChangesW(hDir, @buffer, SizeOf(FILE_NOTIFY_INFORMATION), #True, NotifyFilter, bytesRead, ovlp, 0) 
		Filename = PeekS(@buffer\Filename, -1, #PB_Unicode)
		Debug "Überwachung läuft !"
		path$=GetPathPart(DirectoryName+Filename)
		If FindString(path$,Watch\Pfad)	 
			Select buffer\Action
				Case #FILE_ACTION_ADDED		
					Debug "Added File: "+Filename
				Case #FILE_ACTION_REMOVED
					Debug "Remove File: "+Filename
				Case #FILE_ACTION_MODIFIED
					Debug "Modifed File: "+Filename
				Case #FILE_ACTION_RENAMED_OLD_NAME
				  ;Dateiumbenennung: Alter Dateiname
				Case #FILE_ACTION_RENAMED_NEW_NAME
				  ;Dateiumbenennung: Neuer Dateiname
			EndSelect
		EndIf
		buffer\Filename = ""
	ForEver

   Debug "Überwachung von "+DirectoryName+"  des Listfenster00 abgebrochen !"
EndProcedure

Procedure StartWatchVerzeichnis(Pfad.s) ;Threadaufruf für ReadDirectoryChangesW
	If IsThread(Watch\Thread)
		Debug "Überwachung von "+Watch\Pfad+" abgebrochen !"
		Watch\stop= #True 
	EndIf 	
	Watch\Pfad=Pfad
	Watch\stop=0
	Watch\Thread=CreateThread(@WatchFiles(),@Watch)
EndProcedure	
If OpenWindow(0,0,0,500,400,"Warte auf Debug",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
	StartWatchVerzeichnis("c:\")
EndIf 	
Repeat
	Define event = WaitWindowEvent()
Until event=#PB_Event_CloseWindow
Intel Quad Core 3,2 Ghz - GTX 1060 - BlitzBasic Plus 1.48 , PureBasic 5.70 LTS / Aktuelles Projekt PureCommander
Benutzeravatar
Bisonte
Beiträge: 2430
Registriert: 01.04.2007 20:18

Re: ReadDirectoryChangesW und WaitForMultipleObjects aber wi

Beitrag von Bisonte »

Ich versteh nicht ganz was du mit abbruch meinst... Du kannst doch auf ein Ereignis warten, wenn dieses Eintritt aus dem "Callback" aussteigen ...

Edit : Eventuell hilft dir ja mein Modul mAPI weiter. Dort hab ich auch ReadDirectoryChangesW benutzt um Ordner zu überwachen...

Ich hab dort eine While:Wend Schleife genommen, keine Repeat:Forever, vielleicht ist das ja schon der Stein der es zum lösen bringt.
PureBasic 6.10 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
Benutzeravatar
silbersurfer
Beiträge: 174
Registriert: 06.07.2014 12:21

Re: ReadDirectoryChangesW und WaitForMultipleObjects aber wi

Beitrag von silbersurfer »

Hallo Bisonte,
Du kannst doch auf ein Ereignis warten, wenn dieses Eintritt aus dem "Callback" aussteigen ...
Wenn ich meinen Thread ein Stop Signal sende mittels Watch\Stop, wird dieser solange nicht ausgeführt bis ReadDirectoryChangesW()
ein Ereignis registriert, das ist schlecht da dann die überwachung nicht Abgebrochen wird, da ReadDirectoryChangesW() die ausführung der Schleife verhindert


Edit: nur mit KillThread() scheint es doch zu gehen, also kein Eigenständiger Thread Sorry, sondern eine art WaitEvent
wofür steht dann die ovlp.OVERLAPPED ? es muß also eine weg geben, ReadDirectoryChangesW() ein Stopsignal zu übergeben
denn wenn keine veränderung in meinen verzeichnis mehr passiert wartet mein Thread ewig, oder habe ich da was übersehen ?
Egal ob mit While Wend oder Repeat Forever die Schleife wartet am ReadDirectoryChangesW() (W= WaitEreignis denke ich mal)
danke für dein Edit Bisonte, werde das mal durcharbeiten

Edit2 habe mir jetzt mal deinen Code angeschaut der ist soweit echt super, nur umgehst du das eigendliche Problem in deiner StopProcedure
da du dein Mapeintrag löscht bleibt dein Thread trotzdem erhalten, er wird nur nicht mehr abgefragt, setzt du z.b WaitThread() ein siehst du was ich meine dein Thread stoppt nicht

Code: Alles auswählen

 Procedure.i StopSuperviseDirectory(Directory.s)  
    If FindMapElement(SuperVisor(), LCase(Directory))
      If IsThread(SuperVisor()\ID)
      	SuperVisor()\Halt = #True
      EndIf
      WaitThread(SuperVisor()\ID) ;Wartet bis Thread geschlossen ist
      DeleteMapElement(SuperVisor(), LCase(Directory))
    EndIf
  EndProcedure
Intel Quad Core 3,2 Ghz - GTX 1060 - BlitzBasic Plus 1.48 , PureBasic 5.70 LTS / Aktuelles Projekt PureCommander
Antworten