Задача следующая. Имеются два драйвера, обрабатывающие 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. Что здесь не так?