[gelöst] ReadDirectoryChangesW asynchron - Problem mit FILE_ACTION_MODIFIED



  • Hallo Leute,

    Entwicklungsumgebung: BC++ Builder 6

    ich habe die Verzeichnis-Überwachung jetzt soweit hinbekommen. Jetzt habe ich nur noch ein kleines Problem.
    Wenn ich eine Datei lösche, funktioniert es super. Alle Dateien werden aufgezeigt und auch der Dateiname ist richtig. Dies funktioniert auch, wenn ich 20 Dateien auf einmal lösche.
    Wenn ich aber neue Dateien anlege oder vorhandene Dateien ändere, dann befindet sich meistens hinten am Dateinamen noch ein Zeichen, das da nicht hingehört(*?}^). Auch werden bei Änderung von mehreren Dateien dann nicht alle erfasst. kann mir da jemand den Fehler nennen? Ist schon komisch das das nur bei FILE_ACTION_MODIFIED auftritt.

    hier mein Code:

    void __fastcall TDirWatchX::Execute()
    {
    	DWORD dwRead;       // Anzahl gelesener Bytes
    	DWORD dwBufLen;    // Bufferlänge
    	char* fInfo;
    	fInfo = new char[65536];
    	LPOVERLAPPED lpOverlapped;
    	DWORD numBytes;
    	char* CompKey;
    	CompKey = new char[8];
    
    	FDirectoryHandle = CreateFile
    						(
    						FWatchPath.c_str(),
    						FILE_LIST_DIRECTORY,
    						FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    						NULL,
    						OPEN_EXISTING,
    						FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
    						NULL
    						);
    
    	FhComPort = CreateIoCompletionPort(FDirectoryHandle,0,0x1234ABCD,0);
    
    	OVERLAPPED *Fovl = new OVERLAPPED;
    	FILE_NOTIFY_INFORMATION *FNI = new FILE_NOTIFY_INFORMATION;
    
    	dwBufLen = 65536;
    
    	int RDCwResult;
    
    	while (!Terminated)
    	{
    		if (FDirectoryHandle != INVALID_HANDLE_VALUE) 
    		{
    			ZeroMemory(fInfo, dwBufLen);
    			RDCwResult = 0;
    			RDCwResult = ReadDirectoryChangesW
    			(
    				FDirectoryHandle,
    				fInfo,
    				dwBufLen,
    				true,
    				FILE_NOTIFY_CHANGE_FILE_NAME        // Löschen, Erstellen oder Umbenennen
    				| FILE_NOTIFY_CHANGE_DIR_NAME        // Löschen oder Erstellen oder Umbenennen eines Verzeichnisses
    				| FILE_NOTIFY_CHANGE_SIZE            // Größe ändert sich
    //				| FILE_NOTIFY_CHANGE_LAST_WRITE     // Dateizeit Letzte Änderung
    				| FILE_NOTIFY_CHANGE_ATTRIBUTES    // Attribute geändert
    //				| FILE_NOTIFY_CHANGE_LAST_ACCESS     // Dateizeit Letzter Zugriff (auch Lesen)
    				| FILE_NOTIFY_CHANGE_CREATION     // Dateizeit Erstellungsdatum ändert sich
    //				| FILE_NOTIFY_CHANGE_SECURITY
    				,
    				NULL,
    				Fovl,
    				NULL
    			);
    		}
    		else
    		{
    			FDirectoryHandle = 0;
    			FsResult = "";
    			FsResult = "CreateFile fails. " + SysErrorMessage(GetLastError());
    			Synchronize(ShowResult);
    			Terminate();
    		}
    		if(RDCwResult != 0)
    		{
    			if(GetQueuedCompletionStatus(FhComPort, &numBytes, (LPDWORD)CompKey, &lpOverlapped, INFINITE) != 0)
    			{
    				if(Terminated)
    				{
    					break;
    				}
    				ZeroMemory(FNI,sizeof(FNI));
    				FNI = ((FILE_NOTIFY_INFORMATION*) fInfo);
    				do
    				{
                        FsResult = "";
    					FsResult = (AnsiString)FNI->FileName;
    					// Das Ergebnis ist immer relativ zum überwachten Verzeichnis zu sehen
    					if(FsResult == 0)
    					{
    						FsResult = "ERROR";
    					}
    					else
    					{
    						FsResult = Trim(FsResult);
    						FsResult = FWatchPath + '\\' + FsResult;
    					}
    					switch(FNI->Action)
    					{
    						case FILE_ACTION_ADDED: FsResult = "ADDED: " + FsResult; break;
    						case FILE_ACTION_REMOVED: FsResult = "REMOVED: " + FsResult; break;
    						case FILE_ACTION_MODIFIED: FsResult = "MODIFIED: " + FsResult; break;
    						case FILE_ACTION_RENAMED_OLD_NAME: FsResult = "OLD NAME: " + FsResult; break;
    						case FILE_ACTION_RENAMED_NEW_NAME: FsResult = "NEW NAME: " + FsResult; break;
    						default: FsResult = "UNKNOWN: " + FsResult; break;
    					};
    
    					Synchronize(ShowResult);
    
    					if(FNI->NextEntryOffset != 0)
    					{
    						ZeroMemory(FNI,sizeof(FNI));
    						FNI = FNI + FNI->NextEntryOffset;
    					}
    				}
    				while(/*((FILE_NOTIFY_INFORMATION*) fInfo)->NextEntryOffset != 0*/
    					  FNI->NextEntryOffset != 0);
    			}
    			else
    			{
    				FsResult = "GetQueuedCompletionStatus fails. " + SysErrorMessage(GetLastError());
    				Synchronize(ShowResult);
    				Terminate();
    			}
    		}
    		else
    		{
    			FsResult = "ReadDirectoryChangesW fails. " + SysErrorMessage(GetLastError());
    			Synchronize(ShowResult);
    			Terminate();
    		}
    	}
    	delete Fovl;
    	delete FNI;
    }
    

    danke im Voraus und eine schöne Vorweihnachstzeit wünsche ich allen :xmas1:

    mfg Stephan



  • ich habe noch ein bischen rumgespiel und etwas entdeckt.
    Das bei FILE_ACTION_MODIFIED nicht immer alle geänderten Dateien erfasst werden, liegt wohl daran, das ReadDirectoryChangesW auch im asynchronen Modus nicht ganz zuverlässig ist, wenn zu viele Ereignisse eintreten. Da wird dann schonmal was verschluckt.

    Was mich wundert:
    Ändert man einen Dateinamen, treten nicht, wie man denken würde folgende Ereignisse ein:

    FILE_ACTION_RENAMED_OLD_NAME xxx.xyz
    FILE_ACTION_RENAMED_NEW_NAME yyy.xyz

    sondern:
    FILE_ACTION_RENAMED_OLD_NAME xxx.xyz
    FILE_ACTION_MODIFIED yyy.xyz

    finde ich auch etwas seltsam



  • habs gelöst. so einfach geht es dann doch nicht. komischerweise funktioniert es so in dem Delphi-Beispiel, von dem ich mir die Technik abgeschaut habe. Aber beim BCB6 geht es so nicht, jedenfalls nicht zuverlässig. Wenn ich mal dazu komme, poste ich ein Beispiel oder Beispielprojekt im BCB Forum.

    danke für evtle Hilfeversuche.

    frohe Weihnachten :xmas1:

    gruß Stephan


Anmelden zum Antworten