Exactly as it sounds, I’m attempting asynchronous ReadDirectoryChangesW with IO Completion and it isn’t working, specifically, GetLastError repeatedly returns 258 (GetQueuedCompletionStatus timeout).
I have structs:
typedef struct dirinfo_struct
{
HANDLE hDirFH; // directory handle
OVERLAPPED Overlapped; // overlapped storage
int len_buffer; // buffer length
wchar_t* buffer; // buffer itself
wchar_t* directory_name; // target name
} dirinfo_t;
typedef struct dirmon_struct
{
HANDLE hDirOPPort; // handle to the IO port.
dirinfo_t* dirinfo; // pointer to the struct above.
} dirmon_t;
for storing the relevant information. This is initialised:
dirinfo_t* t = malloc(1*sizeof(dirinfo_t));
dirmon_t* d = malloc(1*sizeof(dirmon_t));
dirinfo_init(t); // does t->buffer = malloc(8192*sizeof(wchar_t));
Then I create my Directory Handle and com port:
t->hDirFH = CreateFile(L"C:\\test",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
d->dirinfo = t;
d->hDirOPPort = CreateIoCompletionPort(d->dirinfo->hDirFH,
NULL,
(ULONG_PTR)(d->dirinfo),
1);
Then I pass this information via d to a new thread. Now on said new thread I have:
bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, lpBytes,
(ULONG_PTR*)d->dirinfo,
lpOverlapped, 1000);
if ( bResultQ )
{
bResultR = ReadDirectoryChangesW(d->dirinfo->hDirFH,
(void*)d->dirinfo->buffer,
8192, TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_SECURITY,
lpReadDirBytes,
&d->dirinfo->Overlapped,
NULL );
}
else
{
printf("GetQueuedCompletionStatus(): Failed, ");
errorcode = GetLastError();
printf("Error Code %d\n", errorcode);
Sleep(500);
}
So I set this off running and I merrily get timeouts (258 errors) as I should since the directory hasn’t changed. However, even if I alter the directory, I’m still getting error messages; in other words those alterations are not being picked up. Which leads me to believe I’ve got this set up incorrectly.
Any ideas?
Caveats:
- Ironically, this will eventually be converted to Python via pywin32. However, until I understand how this is supposed to work in C, I’m not going there.
- Synchronous
ReadDirectoryChangesWis not an option. If no events are fired, I need the thread this is on to timeout so it can check if it should still be running. - I am writing in C specifically, not C++.
FindFirstChangeNotificationetc not an option either – I don’t want to continually be comparing directory listings to work out what has changed.
Other notes:
- The Directory exists, that handle is not NULL. Likewise for the comport handle.
- Everything’s being passed to the thread
I’ve taken a look at CDirectoryChangeWatcher from code project, but use of C++ and many more threads aside, I can’t see what I’m doing differently. Feel free to point it out if I’m missing something though!
Output, if it helps, is basically on repeat, no matter how much I alter the directory in question.
GetQueuedCompletionStatus(): Failed, Error Code 258
I realise posting walls of code is generally considered horrendous, but here’s how I got this working:
New structs:
Allocation methods:
The important stuff from
main()does this:Then finally my waiting thread. Here’s the key: I’m not passing an overlapped structure in. I’m passing in a structure containing an
OVERLAPPEDplus a fair amount ofwchar_tbased storage. For reasons I don’t fully understand, this works. Edit see this answer. I believe the data region here acts as the overlapped buffer.Then we get onto the main thread itself. Trial and error suggests you’re supposed to call both ReadDirectoryW AND GetQueueCompletionStatus. I think what this means is that we’re supposed to not touch the buffer from
ReadDirectoryChangeW**unless* we’re told we can byGetQueue. Corrections on that hypothesis welcome however.So, now we’ve called those functions, we then test that they both returned true. big ugly warning if you’ve got your parameters set up right
bResultRalways returns true, or so it seems to me.bResultQhowever varies depending on whether new data is on the port.So here we cast that buffer from
ReadDirectoryChangesWand access the info from the struct.Otherwise, and thanks to Tony for this, you can safely ignore WAIT_TIMEOUT errors, but anything else probably means you’re in trouble.
And that completes what I think is a working example.
Some notes:
8192and missed off an item or two, here and there. So I don’t expect this will always pick up everything. My solution would be to say every 100 events, verify the file tree is what you think it is if using this method. An infinitely better solution, however, to constantly enumerating the potentially large tree.