Q:

Async operations with I/O Completion Ports return 0 bytes transferred

Asynchronous operations with I/O Completion Ports return 0 bytes transferred, although the I/O operations work as expected (my read buffers become full).
BYTE buffer[1024] = {0};
OVERLAPPED o = {0};
HANDLE file = CreateFile(
    _T("hello.txt"),
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL
);
HANDLE completion_port = CreateIoCompletionPort(
    file,
    NULL,
    0,
    0
);
ReadFile(
    file,
    buffer,
    1024,
    NULL,
    &o
);

In the work thread:
DWORD numBytes = 0;
LPOVERLAPPED po;
GetQueuedCompletionStatus(
    completion_port,
    &numBytes,
    0,
    &po,
    INFINITE
);
GetOverlappedResult(file, &o, &numBytes, FALSE);

Both functions return 0 bytes in numBytes, but buffer is filling. Is this expected behaviour?
Thanks.

A:

For GetIoCompletionPort to work correctly, you need to specify a non-null pointer to a ULONG_PTR for it to write the 'key' value to:
ULONG_PTR key;

GetQueuedCompletionStatus(
    completion_port,
    &numBytes,
    &key,
    &po,
    INFINITE
);

To use GetOverlappedResult successfully, I believe you need to specify an event handle in the OVERLAPPED structure (strongly recommended in any case):
o.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

Calling the two in succession as you were doesn't really accomplish much -- they both tell you about the same things. Though if you do call both in succession, you'll need to change the Event to be a manual-reset by changing the third parameter to CreateEvent to TRUE. My guess is that you were just trying both to see if you could get one to work. All things considered, I'd probably just use GetQueuedCompletionStatus, and leave it at that. Of course, you'll usually do more than call it once and quit. You normally call it in a loop, processing the current buffer you've read, then calling ReadFile again to read another buffer of info, something like this:
DWORD numBytes;
LPOVERLAPPED po;
while (GetQueuedCompletionStatus(completion_port, &numBytes, &key, &po, INFINITE)) {
    std::cout << "\rRead: " << numBytes; // just to show it's set correctly.
    process(buffer);
    po->offset += sizeof(buffer);
    ReadFile(file, buffer, sizeof(buffer), NULL, po);
}

At least in a quick test on my machine, this showed the number of bytes read correctly (sizeof(buffer) up to the last packet, then the remaining size of the file).

