cwinapiiocpoverlapped-ioio-completion-ports

How to juggle FILE_SKIP_COMPLETION_PORT_ON_SUCCESS, IOCP, and cleanup


If FILE_SKIP_COMPLETION_PORT_ON_SUCCESS is set on a file handle that is bound to an I/O completion port, then an OVERLAPPED structure needs to be deallocated when its I/O completes synchronously. Otherwise, it needs to stay alive until a worker processes the notification from an I/O completion port.

This all sounds good until you realize that this only works if you manage the file handle yourself.
But if someone else gives you the file handle, how are you supposed to know when you should free the OVERLAPPED structure? Is there any way to discover this after the fact?
Otherwise, does this basically imply you cannot correctly perform overlapped I/O on any file handle that you cannot guarantee the completion notification state of...?


Solution

  • Is there any way to discover this after the fact?

    yes, exist - need use ZwQueryInformationFile with FileIoCompletionNotificationInformation FILE_IO_COMPLETION_NOTIFICATION_INFORMATION is defined in wdm.h

    so code which we need for query:

    FILE_IO_COMPLETION_NOTIFICATION_INFORMATION ficni;
    ZwQueryInformationFile(hFile, &iosb, &ficni, sizeof(ficni), FileIoCompletionNotificationInformation);
    

    demo code for set and query

    HANDLE hFile;
    IO_STATUS_BLOCK iosb;
    STATIC_OBJECT_ATTRIBUTES(oa, "\\systemroot\\notepad.exe");
    if (0 <= ZwOpenFile(&hFile, FILE_GENERIC_READ, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0))
    {
        FILE_IO_COMPLETION_NOTIFICATION_INFORMATION ficni = { FILE_SKIP_COMPLETION_PORT_ON_SUCCESS };
        if (0 <= ZwSetInformationFile(hFile, &iosb, &ficni, sizeof(ficni), FileIoCompletionNotificationInformation))
        {
            ficni.Flags = 0x12345678;
            if (
                0 > ZwQueryInformationFile(hFile, &iosb, &ficni, sizeof(ficni), FileIoCompletionNotificationInformation)
                ||
                !(ficni.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)
                )
            {
                __debugbreak();
            }
        }
    
        ZwClose(hFile);
    }
    

    also let copy paste from wdm.h (for not say that this is "undocumented" )

    //
    // Don't queue an entry to an associated completion port if returning success
    // synchronously.
    //
    #define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS    0x1
    
    //
    // Don't set the file handle event on IO completion.
    //
    #define FILE_SKIP_SET_EVENT_ON_HANDLE           0x2
    
    //
    // Don't set user supplied event on successful fast-path IO completion.
    //
    #define FILE_SKIP_SET_USER_EVENT_ON_FAST_IO     0x4
    
    typedef  struct _FILE_IO_COMPLETION_NOTIFICATION_INFORMATION {
        ULONG Flags;
    } FILE_IO_COMPLETION_NOTIFICATION_INFORMATION, *PFILE_IO_COMPLETION_NOTIFICATION_INFORMATION;
    

    I have question - for what reason this is declared in wdm.h ?