Странное поведение DeviceIoControl
От: Аноним  
Дата: 09.01.08 09:02
Оценка:
Задача следующая. Имеются два драйвера, обрабатывающие IRP, и приложение,
отправляющее DeviceIoControl.

Код приложения


unsigned CIoChannel::_ThreadProc(PVOID pParam)
{
    OVERLAPPED ov = {0};
    HANDLE events[2];
    IO_CONTROLLER_DATA data;
    DWORD dwRet;
    DWORD dwRes;
    char pBuffer[MAX_DATA_SIZE];
    DWORD dwSize = MAX_DATA_SIZE;
    char szData[MAX_DATA_SIZE];

    ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if(!ov.hEvent)
        goto FINISH;

    data.command = eSend;
    data.channelId = m_channelId;
    data.pluginId = m_pluginId;
    data.ioMode = m_ioMode;

    events[0] = m_hStopEvent;
    events[1] = ov.hEvent;

    for(;;)
    {
                // DeviceIoControl не возвращает управление сразу 
        DeviceIoControl(m_hDevice, IOCTL_TDI_IO_CONTROLLER, &data, sizeof(data), 
            pBuffer, dwSize, &dwRet, &ov);

        dwRes = WaitForMultipleObjects(2, events, FALSE, INFINITE);
        switch(dwRes - WAIT_OBJECT_0)
        {
        case 0:
            goto FINISH;
        case 1:
            memcpy(szData, pBuffer, dwRet);
            ::MessageBoxA(NULL, szData, "IO channel thread", MB_OK);
            break;
        }
    }

FINISH:
    if(ov.hEvent)
        CloseHandle(ov.hEvent);

    return 0;
    
}


Код драйвера 1 (пендинг IRP)


NTSTATUS KIoChannel::HoldIrp()
{
    KIRQL irql;
    NTSTATUS status = STATUS_PENDING;

    KeAcquireSpinLock(&m_lock, &irql);
    IoMarkIrpPending(m_pIrp);
    KeReleaseSpinLock(&m_lock, irql);
    
    IoAcquireCancelSpinLock(&irql);
    IoSetCancelRoutine (m_pIrp, m_pCancel);
    IoReleaseCancelSpinLock(irql);

    return status;
}


IRP запоминается и пендится до тех пор, пока не приходит запрос от другого драйвера.

Код драйвера 2


bool KIoChannel::SendData (IKSerialize * pSerialize)
{
    NTSTATUS status = STATUS_SUCCESS;

    if((!pSerialize) || (!m_pIrp))
        return false;
    
    char* pData = (char*)m_pIrp->AssociatedIrp.SystemBuffer;
    PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(m_pIrp);
    ULONG nSize = pSl->Parameters.DeviceIoControl.OutputBufferLength;
    memset(pData, 0, nSize);
    
    bool bRetval = pSerialize->Serialize((PUCHAR)pData, nSize);

    KIRQL irql;
    PIRP pIrp = NULL;
    
    KeAcquireSpinLock(&m_lock, &irql);
    pIrp = m_pIrp;
    m_pIrp = NULL;
    KeReleaseSpinLock(&m_lock, irql);

    if(pIrp)
    {
        IoAcquireCancelSpinLock(&irql);
        IoSetCancelRoutine(pIrp, NULL);
        IoReleaseCancelSpinLock(irql);
    
        pIrp->IoStatus.Status = (bRetval) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;;
        pIrp->IoStatus.Information = nSize;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    }

    return bRetval;
}


Другой драйвер записывает в IRP данные и завершает его.

Хотелось бы узнать, почему DeviceIoControl, несмотря на присутствие OVERLAPPED, не возвращает управление немедленно. Таким образом, выход из DeviceIoControl происходит только тогда, когда IRP уже завершен.
Я предполагал, что выход произойдет сразу и поток будет ждать на событии, которое придет сразу по завершении IRP. Что здесь не так?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.