Асинхронный NtQueryDirectoryFile?

Кому- нибудь удалось выяснить, как работают асинхронные вызовы NtQueryDirectoryFile?

Под асинхронным вызовом я имею в виду вызов NtQueryDirectoryFile в каталогах, не открытых с помощью FILE_SYNCHRONOUS_IO_ALERT или с FILE_SYNCHRONOUS_IO_NONALERT.

Для меня это, кажется, вернуться STATUS_PENDING так же, как обычный запрос NtReadFile для обычного файла, но когда я попытался использовать NtWaitForSingleObject для каталога, он не завершился должным образом, и я все еще не могу получить все данные... почему это происходит?

2 ответа

Насколько я знаю, ни одна из файловых систем Windows не поддерживает асинхронные вызовы каталогов запросов. Интерфейсы Win32 API никогда не вызывают NtQueryDirectoryFile асинхронно, поэтому его поддержка может быть случайной.

NTFS теоретически поддерживает асинхронный NtQueryDirectoryFile, но (как я уже упоминал) не подвергается всестороннему тестированию, поэтому может не работать.

Ваш ответ указал, что вы вызвали WaitForSingleObject в каталоге - это не то, как асинхронный шаблон работает в NT - вам нужно вызвать WaitForSingleObject для дескриптора события, предоставленного в качестве параметра для NtQueryDirectoryFile.

Это обновление является результатом запроса NTFS разработчика для получения дополнительной информации, он проверил этот сценарий на своем компьютере, и он работал для него (на Windows 7).

хорошо работает в асинхронном режиме!

передать обратный вызов в ApcRoutineи данные обратного вызова в ApcContext

асинхронные вызовы процедур вызываются только тогда, когда поток находится в состоянии предупреждения (например: вызов SleepEx(INFINITE, TRUE), WSAaccept)

эта программа показывает, как асинхронно NtQueryDirectoryFileРабота.

      #define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <winternl.h>
#include <winnt.h>

#define LIST_DIR_SIZE 2000
#define STATUS_NO_MORE_FILES ((NTSTATUS)80000006)

typedef struct _FILE_NAMES_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_NAMES_INFORMATION, * PFILE_NAMES_INFORMATION;

typedef struct {
    HANDLE hFile;
    OVERLAPPED ol;
    DECLSPEC_ALIGN(4) FILE_NAMES_INFORMATION buf[LIST_DIR_SIZE];
    IO_STATUS_BLOCK iob;
    bool finished;
} LIST_DIR_DATA, * PLIST_DIR_DATA; // my private data


__kernel_entry NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryDirectoryFile(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID FileInformation,
    _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass,
    _In_ BOOLEAN ReturnSingleEntry,
    _In_opt_ PUNICODE_STRING FileName,
    _In_ BOOLEAN RestartScan
);

#define NTDLL_extern(s) typedef decltype(&s) s##T;s##T s##F;
#define NTDLL_import(s) s##F = (s##T)GetProcAddress(ntdll, #s);

NTDLL_extern(NtOpenFile);
NTDLL_extern(NtQueryDirectoryFile);
NTDLL_extern(NtClose);
NTDLL_extern(RtlInitUnicodeString);

HMODULE ntdll;

VOID NTAPI callback(
    IN PVOID ApcContext,
    IN PIO_STATUS_BLOCK IoStatusBlock,
    IN ULONG Reserved) {

    UNREFERENCED_PARAMETER(Reserved);
    PFILE_NAMES_INFORMATION file_info = ((PLIST_DIR_DATA)ApcContext)->buf;
    do {
        fputws(file_info->FileName, stdout);
        putwchar(L'\t');
        file_info = (PFILE_NAMES_INFORMATION)((char*)file_info + file_info->NextEntryOffset);
    } while (file_info->NextEntryOffset);
    fputws(file_info->FileName, stdout);
    putwchar(L'\t');
    PLIST_DIR_DATA c = (PLIST_DIR_DATA)ApcContext;
    if (IoStatusBlock->Information != 0) {
        NTSTATUS status = NtQueryDirectoryFileF(
            c->hFile,
            NULL,
            callback,
            ApcContext,
            &c->iob,
            c->buf,
            sizeof(c->buf),
            FILE_INFORMATION_CLASS(12),
            FALSE, NULL, FALSE);
        switch (status) {
        case STATUS_PENDING:
            break;
        default:
            fputs("warning: status != STATUS_PENDING", stderr);
        }
    }
    else {
        c->finished = true;
    }
}
BOOL init() {
    ntdll = LoadLibraryW(L"NtDLL.dll");
    if (ntdll == NULL) {
        fputs("LoadLibraryW", stderr);
        return FALSE;
    }
    NTDLL_import(NtQueryDirectoryFile);
    NTDLL_import(NtOpenFile);
    NTDLL_import(NtClose);
    NTDLL_import(RtlInitUnicodeString);
    if (NtCloseF != NULL && NtOpenFileF != NULL && NtCloseF != NULL) {
        return TRUE;
    }
    else {
        fputs("GetProcAddress", stderr);
        return FALSE;
    }
}

int main() {
    if (init() == FALSE) {
        fputs("error: init() failed!", stderr);
        return -1;
    }
    NTSTATUS status;
    PLIST_DIR_DATA data = new LIST_DIR_DATA{};
    {
        OBJECT_ATTRIBUTES ObjectAttributes;
        UNICODE_STRING s;
        RtlInitUnicodeStringF(&s, L"\\??\\c:\\Windows\\System32");
        InitializeObjectAttributes(
            &ObjectAttributes,
            &s,
            OBJ_CASE_INSENSITIVE,
            NULL,
            NULL);
        status = NtOpenFileF(
            &data->hFile,
            FILE_READ_DATA | FILE_LIST_DIRECTORY, // | FILE_TRAVERSE | SYNCHRONIZE
            &ObjectAttributes,
            &data->iob,
            FILE_SHARE_READ,
            FILE_DIRECTORY_FILE); // | FILE_SYNCHRONOUS_IO_NONALERT
    }
    if (status < 0 || data->hFile == NULL) {
        fputs("error: NtOpenFile failed", stderr);
        return -2;
    }
    status = NtQueryDirectoryFileF(
        data->hFile,
        NULL,
        callback,
        data,
        &data->iob,
        data->buf,
        sizeof(data->buf),
        FILE_INFORMATION_CLASS(12),
        FALSE, NULL, FALSE);
    switch (status) {
        case STATUS_PENDING: 
            break;
        default:
            fputs("warning: status != STATUS_PENDING", stderr);
    }
    for (;data->finished==false;) SleepEx(INFINITE, TRUE); // put main thread into alertable wait

    NtCloseF(data->hFile);
    FreeLibrary(ntdll);
    return 0;
}

если вы хотите вывод UTF-8, попробуйте это (примечание: рекомендуется использовать поддержку терминала UTF-8)

      #define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winternl.h>
#include <winnt.h>
#include <crtdbg.h>
#include <cstdio>

#define LIST_DIR_SIZE 200
#define STATUS_NO_MORE_FILES ((NTSTATUS)80000006)

typedef struct _FILE_NAMES_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_NAMES_INFORMATION, * PFILE_NAMES_INFORMATION;

typedef struct {
    HANDLE hFile;
    OVERLAPPED ol;
    DECLSPEC_ALIGN(4) FILE_NAMES_INFORMATION buf[LIST_DIR_SIZE];
    IO_STATUS_BLOCK iob;
    bool finished;
} LIST_DIR_DATA, * PLIST_DIR_DATA; // my private data


__kernel_entry NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryDirectoryFile(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID FileInformation,
    _In_ ULONG Length,
    _In_ FILE_INFORMATION_CLASS FileInformationClass,
    _In_ BOOLEAN ReturnSingleEntry,
    _In_opt_ PUNICODE_STRING FileName,
    _In_ BOOLEAN RestartScan
);

#define NTDLL_extern(s) typedef decltype(&s) s##T;s##T s##F;
#define NTDLL_init(s) s##F = (s##T)GetProcAddress(ntdll, #s);

NTDLL_extern(NtOpenFile);
NTDLL_extern(NtQueryDirectoryFile);
NTDLL_extern(NtClose);
NTDLL_extern(RtlInitUnicodeString);

HMODULE ntdll;
HANDLE heap;

VOID NTAPI callback(
    IN PVOID ApcContext,
    IN PIO_STATUS_BLOCK IoStatusBlock,
    IN ULONG Reserved) {
    UNREFERENCED_PARAMETER(Reserved);
    PLIST_DIR_DATA c = (PLIST_DIR_DATA)ApcContext;
    if (IoStatusBlock->Information){
        PFILE_NAMES_INFORMATION file_info = c->buf;
        ULONG_PTR length = 0;
        ULONG last;
        do {
            last = file_info->NextEntryOffset;
            file_info->FileNameLength /= 2; // wide char length always base of 2 in bytes
            length += (
                file_info->FileIndex=WideCharToMultiByte(
                    CP_UTF8, WC_ERR_INVALID_CHARS, 
                    file_info->FileName, file_info->FileNameLength, 
                    NULL, 0, 
                    NULL, NULL)
                )+1;
            if (file_info->FileIndex == 0) { // FileIndex is how many byte is the UTF-8 string
                _RPTF0(_CRT_WARN, "WideCharToMultiByte failed!");
            }
            file_info = (PFILE_NAMES_INFORMATION)((char*)file_info + file_info->NextEntryOffset);
        } while (last);
        LPSTR pData = (LPSTR)HeapAlloc(heap, HEAP_NO_SERIALIZE, length), ptr=pData;
        if (ptr == NULL) {
            _RPTF0(_CRT_ERROR, "HeapAlloc failed!");
            return;
        }
        file_info = c->buf;
        do {
            last = file_info->NextEntryOffset;
            if (WideCharToMultiByte(
                CP_UTF8, WC_ERR_INVALID_CHARS,
                file_info->FileName, file_info->FileNameLength, 
                pData, file_info->FileIndex, 
                NULL, NULL)==0) {
                _RPTF0(_CRT_WARN, "WideCharToMultiByte failed!");
            }
            pData += file_info->FileIndex;
            *pData++ = '\n';
            file_info = (PFILE_NAMES_INFORMATION)((char*)file_info + file_info->NextEntryOffset);
        } while (last);

        // use data here
        fwrite(ptr, length, 1, stdout);
        // use data here

        HeapFree(heap, HEAP_NO_SERIALIZE, ptr);
        NTSTATUS status = NtQueryDirectoryFileF(
            c->hFile,
            NULL,
            callback,
            ApcContext,
            &c->iob,
            c->buf,
            sizeof(c->buf),
            FILE_INFORMATION_CLASS(12),
            FALSE, NULL, FALSE);

        switch (status) {
        case STATUS_PENDING:
            break;
        default:
            _RPTF0(_CRT_WARN, "status != STATUS_PENDING");
        }
    }else{
        c->finished = true;
    }
}
BOOL init() {
    ntdll = LoadLibraryW(L"NtDLL.dll");
    if (ntdll == NULL) {
        _RPTF0(_CRT_ERROR, "fail to load NtDLL.dll");
        return FALSE;
    }
    NTDLL_init(NtQueryDirectoryFile);
    NTDLL_init(NtOpenFile);
    NTDLL_init(NtClose);
    NTDLL_init(RtlInitUnicodeString);
    if (NtCloseF != NULL && 
        NtOpenFileF != NULL && 
        NtCloseF != NULL && 
        (heap = HeapCreate(HEAP_NO_SERIALIZE, 4096,0))!=NULL
        ){
        return TRUE;
    }
    else {
        _RPTF0(_CRT_ERROR, "failed to load function and create heap");
        return FALSE;
    }
}

int main() {
    if (init() == FALSE) {
        _RPTF0(_CRT_ERROR, "init failed");
        return -1;
    }
    SetConsoleCP(CP_UTF8);
    NTSTATUS status;
    PLIST_DIR_DATA data = new LIST_DIR_DATA{};
    {
        OBJECT_ATTRIBUTES ObjectAttributes;
        UNICODE_STRING s;
        RtlInitUnicodeStringF(&s, L"\\??\\c:\\Users");
        InitializeObjectAttributes(
            &ObjectAttributes,
            &s,
            OBJ_CASE_INSENSITIVE,
            NULL,
            NULL);
        status = NtOpenFileF(
            &data->hFile,
            FILE_READ_DATA | FILE_LIST_DIRECTORY, // | FILE_TRAVERSE | SYNCHRONIZE
            &ObjectAttributes,
            &data->iob,
            FILE_SHARE_READ,
            FILE_DIRECTORY_FILE); // | FILE_SYNCHRONOUS_IO_NONALERT
    }
    if (status < 0 || data->hFile == NULL) {
        _RPTF0(_CRT_ERROR, "NtOpenFile failed!");
        return -2;
    }
    status = NtQueryDirectoryFileF(
        data->hFile,
        NULL,
        callback,
        data,
        &data->iob,
        data->buf,
        sizeof(data->buf),
        FILE_INFORMATION_CLASS(12),
        FALSE, NULL, FALSE);
    switch (status) {
        case STATUS_PENDING: 
            break;
        default:
            _RPTF0(_CRT_WARN, "status != STATUS_PENDING");
    }
    for (;data->finished==false;) SleepEx(INFINITE, TRUE); // put main thread into alertable wait

    if (NtCloseF(data->hFile)<0) {
        _RPTF0(_CRT_ERROR, "NtClose failed!");
    }
    if (FreeLibrary(ntdll) == FALSE) {
        _RPTF0(_CRT_WARN, "failed to Free libary");
    }
    if (HeapDestroy(heap) == FALSE) {
        _RPTF0(_CRT_WARN, "fail to destroy heap");
    }
}

Другие вопросы по тегам