ReadDirectoryChangesW schreibt nur eine einzige Änderung in den Buffer



  • Soweit ich das verstanden habe, soll ReadDirectoryChangesW alle Änderung in einem Verzeichnis seit dem letzten Aufruf in einen Buffer schreiben. Allerdings erhalte ich immer nur eine einzige Änderung, auch wenn ich mehrere Datei kopiere. Eigentlich hat das auch schon einmal funktioniert. Ich weiß nicht, was ich jetzt anders mache.

    _directoryHandle = CreateFile(directoryName.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | 
    		FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | 
    		FILE_FLAG_OVERLAPPED, 0);
    	// ...
    
    	_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    	_overlapped.Offset = 0;
    	_overlapped.OffsetHigh = 0;
    	_overlapped.Internal = 0;
    	_overlapped.InternalHigh = 0;
    
    	BOOL result = ReadDirectoryChangesW(_directoryHandle, _fileNotifyInfo, fileNotifyInfoSize, TRUE, 
    		FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | 
    		FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME | 
    		FILE_NOTIFY_CHANGE_ATTRIBUTES, NULL, &_overlapped, NULL);
    	// ...
    
    	// Irgendwann später
    	// ...
    
    	BOOL result = GetOverlappedResult(_directoryHandle, &_overlapped, &bytesTransferred, FALSE);
    	// ...
    
    	Size offset = 0;
    	for(;;)
    	{
    		FILE_NOTIFY_INFORMATION* info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(_fileNotifyInfo + offset);
    		std::string filename(Util::utf16ToUtf8(info->FileName, wcslen(info->FileName)));
    
    		switch(info->Action)
    		{
    		case FILE_ACTION_ADDED : _listener->onCreate(filename); break;
    		case FILE_ACTION_REMOVED : _listener->onRemove(filename); break;
    		case FILE_ACTION_MODIFIED : _listener->onModify(filename); break;
    		case FILE_ACTION_RENAMED_OLD_NAME : _listener->onOldName(filename); break;
    		case FILE_ACTION_RENAMED_NEW_NAME : _listener->onNewName(filename); break;
    		}
    
    		offset += info->NextEntryOffset;
    		if(info->NextEntryOffset == 0)
    			break;
    	}
    

    Wahrscheinlich wier mal ein nur kleiner Fehler. Ich sehe den Wald vor lauter Bäumen nicht mehr.
    Vielen Dank.


  • Mod

    Wie groß sind denn _fileNotifyInfo, fileNotifyInfoSize?



  • Vielen Dank für die Antwort.

    class DirectoryMonitor {
    private:
        static const Size fileNotifyInfoSize = 16 * 1024;
        char _fileNotifyInfo[fileNotifyInfoSize];
        // ...
    };
    

    Laut Dokumentation wird bei einem zu kleinem Buffer numBytesTransferred auf 0 gesetzt. Diesem Fall habe ich berücksichtigt.



  • Das Problem (Meldung von nur einer der Änderungen, in diesem Fall dem Kopieren mehrerer Dateien in das entsprechende Verzeichnis) besteht bei folgendem, auf das notwendigste reduzierte Testprogramm fort. Ich bin alle Punkte der Dokumentation durchgegeangen, kann aber keinen Fehler erkennen. Die FILE_NOTIFY_*-FILTER habe ich auch schon einzeln durchgetestet.

    #include <iostream>
    #include <string>
    #include <Windows.h>
    
    int main(int argc, char** argv) 
    {
    	static const unsigned int bufferSize = 32 * 1024;
    	char buffer[bufferSize] = { 0 };
    	DWORD numBytesReturned;
    
    	HANDLE directoryHandle = CreateFile(L"c:\\users\\hoascht\\desktop\\", FILE_LIST_DIRECTORY, 
    		FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 
    		FILE_FLAG_BACKUP_SEMANTICS, NULL);
    	if(INVALID_HANDLE_VALUE == directoryHandle)
    	{
    		std::wcout << L"'CreateFile' failed!";
    		return -1;
    	}
    
    	if(FALSE == ReadDirectoryChangesW(directoryHandle, buffer, bufferSize, FALSE, 
    		FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE |
    		FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY |
    		FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_LAST_WRITE,
    		&numBytesReturned, NULL, NULL))
    	{
    		std::wcout << L"'ReadDirectoryChangesW' failed!";
    		CloseHandle(directoryHandle);
    		return -1;
    	}
    
    	// 'buffer' enthält tatsächlich nur den ersten Eintrag. Alles danach sind 0. Am verbleibenden
    	// Code liegt also nicht.
    	// numBytesReturned == 72
    
    	for(DWORD offset = 0;;)
    	{
    		FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*) (buffer + offset);
    		std::wstring filename(fni->FileName, fni->FileName + fni->FileNameLength / 2);
    		std::wcout << filename << std::endl;
    
    		offset += fni->NextEntryOffset;
    		if(fni->NextEntryOffset == 0)
    			break;
    	}
    
    	CloseHandle(directoryHandle);
    	return 0;
    }
    

    Wäre nett, wenn mir jemand weiterhelfen könnte.



  • Seltsames Verhalten: Wenn ich 15 Dateien kopiere, kommt bei der ersten Benachrichtigung 1 an, bei der zweiten die verbliebenen 14.
    Wenigstens funktioniert es jetzt.


Anmelden zum Antworten