Wednesday, August 17, 2011

Новое в Vista's IOCP

В Vista к IOCP наконец то добавили штуку, которую логично было бы добавить с самого начала: возможность отключать ненужные нотификации успешных завершений с помощью функции SetFileCompletionNotificationModes. Просто выставляешь флаг FILE_SKIP_COMPLETION_PORT_ON_SUCCESS на сокет и все. Теперь если асинхронная функция выполнилась успешно, можно сразу же освобождать используемые ей ресурсы и инициировать новый запрос, все как и в нормальном асинхронном IO без использования портов завершения.
Интересно, что некоторые функции, типа AcceptEx, и так всегда обладали таким поведением.
Поскольку аналога GetFileCompletionNotificationModes в МСДН не наблюдается, пришлось из любопытства написать свой:

namespace kernel
{
typedef LONG NTSTATUS;
//---------------------------------------
typedef struct _IO_STATUS_BLOCK 
{
    union 
    {
        NTSTATUS Status;
        PVOID Pointer;
    };
    ULONG_PTR Information;
}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
//---------------------------------------
typedef
NTSYSAPI 
DWORD
(NTAPI * RtlNtStatusToDosErrorPtr)(IN NTSTATUS status);

typedef
NTSYSAPI 
NTSTATUS
(NTAPI * NtQueryInformationFilePtr)(IN HANDLE FileHandle
                                   OUT PIO_STATUS_BLOCK IoStatusBlock
                                   OUT PVOID FileInformation
                                   IN ULONG Length
                                   IN DWORD FileInformationClass);
}
class CUndocumentedNtdll
{
    HMODULE m_hNtDll;
    kernel::NtQueryInformationFilePtr m_pNtQueryInformationFile;
    kernel::RtlNtStatusToDosErrorPtr m_pRtlNtStatusToDosError;

public:
    CUndocumentedNtdll()
    {
        m_hNtDll = GetModuleHandle(L"ntdll.dll");
        m_pNtQueryInformationFile =
            (kernel::NtQueryInformationFilePtr)
            GetProcAddress(m_hNtDll, "NtQueryInformationFile");

        m_pRtlNtStatusToDosError =
            (kernel::RtlNtStatusToDosErrorPtr)
            GetProcAddress(m_hNtDll, "RtlNtStatusToDosError");
    }

    ULONG GetFileCompletionNotificationModes(HANDLE hFile,
                                             ULONG * pFlags)
    {
        if(!m_pNtQueryInformationFile || !m_pRtlNtStatusToDosError)
            return ERROR_NOT_SUPPORTED;

        ULONG flags = 0;
        kernel::IO_STATUS_BLOCK statusBlock;
        kernel::NTSTATUS status = m_pNtQueryInformationFile(hFile, &statusBlock, pFlags, 4, 0x29);
        if (status >= 0)
            return NO_ERROR;
        return m_pRtlNtStatusToDosError(status);
    }
};

No comments: