How to trigger or simulate keyboard interrupt? - c

I'm writing a keyboard filter driver for Windows and I need to insert my custom keystrokes data into Windows message queue. I've managed to capture all the keys that are pressed setting OnReadCompletion() callback to IoSetCompletionRoutine() in my driver's Read() function like so:
NTSTATUS Read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, OnReadCompletion, DeviceObject, TRUE, TRUE, TRUE);
return IoCallDriver (deviceExtension->pKeyboardDevice, Irp);
}
NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
// ...
}
This filter driver is attached to the kbdclass driver like so:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
// ...
CCHAR ntNameBuffer[64] = "\\Device\\KeyboardClass0";
status = IoAttachDevice(deviceObject, &uKeyboardDeviceName, &DeviceExtension->pKeyboardDevice);
// ...
}
So, I can catch all keys pressed in OnReadCompletion(). But I need to insert my own information into keyboard message flow. Here are 2 problems with that:
OnReadCompletion() is only invoked when a key pressed. Ideally I'd like somehow it to be called when nothing is pressed. Can I do that somehow? I need to trigger a keyboard interrupt? I tried to write commands to a keyboard ports (0x60 and 0x64) with WRITE_PORT_UCHAR() but that didn't work out.
I tried to insert my data into IRP in OnReadCompletion() to make it look like for example a key was pressed twice while actually it was pressed only once. Can someone help me out on that one too, because the following didn't work out?
NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
PIO_STACK_LOCATION IrpStackLocation = NULL;
INT BufferLength;
INT numKeys = 0, i = 0;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
IrpStackLocation = IoGetCurrentIrpStackLocation(Irp);
BufferLength = IrpStackLocation->Parameters.Read.Length;
if(Irp->IoStatus.Status == STATUS_SUCCESS)
{
PCHAR newSystemBuffer, oldSystemBuffer;
PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
numKeys = Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
for(i = 0; i < numKeys; i++)
{
// here we print whatever was pressed
DbgPrint("%s -- ScanCode: %x\n", __FUNCTION__, keys[i].MakeCode);
}
// allocate new buffer twice as big as original
newSystemBuffer = ExAllocatePool(NonPagedPool, Irp->IoStatus.Information * 2);
// copy existing buffer twice into new buffer
RtlCopyMemory(newSystemBuffer, keys, Irp->IoStatus.Information);
RtlCopyMemory(newSystemBuffer + Irp->IoStatus.Information, keys, Irp->IoStatus.Information);
// assign new buffer to Irp->AssociatedIrp.SystemBuffer
oldSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
Irp->AssociatedIrp.SystemBuffer = newSystemBuffer;
// tell IRP that we now have twice as much data
Irp->IoStatus.Information *= 2;
// free the old buffer
ExFreePool(oldSystemBuffer);
}
if(Irp->PendingReturned)
IoMarkIrpPending(Irp);
return Irp->IoStatus.Status;
}
And when I test it for example in Notepad, all I get is just one letter per keystroke.
I'm really desperate. Please help!

Four options which I think should work:
1) You could create a new IRP to call the kbdclass driver with, as opposed to passing the IRP that you received down. You would complete the original IRP whenever you wanted to insert data as well as whenever you had real keystrokes to pass on.
2) You could have two devices, the second being a keyboard device. You would then use the kbdclass filter for removing keystrokes and the keyboard device for adding them.
3) You could redesign your driver to be an upper filter for the keyboard devices, similar to the MSDN sample driver kbfiltr.
4) You could have two devices, the second being an upper filter for one or more of the keyboard devices. You would use the kbdclass filter for removing keystrokes and the keyboard device filter for adding them.
I think the first option would be best, but I'm no expert.

Related

Windows WFP Driver: Getting BSOD when processing packets in ClassifyFn callback

I am trying to code a simple firewall application which can allow or block network connection attempts made from userlevel processes.
To do so, following the WFPStarterKit tutorial, I created a WFP Driver which is set to intercept data at FWPM_LAYER_OUTBOUND_TRANSPORT_V4 layer.
The ClassifyFn callback function is responsible for intercepting the connection attempt, and either allow or deny it.
Once the ClassifyFn callback gets hit, the ProcessID of the packet is sent, along with a few other info, to a userlevel process through the FltSendMessage function.
The userlevel process receives the message, checks the ProcessID, and replies a boolean allow/deny command to the driver.
While this approach works when blocking a first packet, in some cases (expecially when allowing multiple packets) the code generates a BSOD with the INVALID_PROCESS_ATTACH_ATTEMPT error code.
The error is triggered at the call to FltSendMessage.
While I am still unable to pinpoint the exact problem,
it seems that making the callout thread wait (through FltSendMessage) for a reply from userlevel can generate this BSOD error on some conditions.
I would be very grateful if you can point me to the right direction.
Here is the function where I register the callout:
NTSTATUS register_example_callout(DEVICE_OBJECT * wdm_device)
{
NTSTATUS status = STATUS_SUCCESS;
FWPS_CALLOUT s_callout = { 0 };
FWPM_CALLOUT m_callout = { 0 };
FWPM_DISPLAY_DATA display_data = { 0 };
if (filter_engine_handle == NULL)
return STATUS_INVALID_HANDLE;
display_data.name = EXAMPLE_CALLOUT_NAME;
display_data.description = EXAMPLE_CALLOUT_DESCRIPTION;
// Register a new Callout with the Filter Engine using the provided callout functions
s_callout.calloutKey = EXAMPLE_CALLOUT_GUID;
s_callout.classifyFn = example_classify;
s_callout.notifyFn = example_notify;
s_callout.flowDeleteFn = example_flow_delete;
status = FwpsCalloutRegister((void *)wdm_device, &s_callout, &example_callout_id);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to register callout functions for example callout, status 0x%08x", status);
goto Exit;
}
// Setup a FWPM_CALLOUT structure to store/track the state associated with the FWPS_CALLOUT
m_callout.calloutKey = EXAMPLE_CALLOUT_GUID;
m_callout.displayData = display_data;
m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
m_callout.flags = 0;
status = FwpmCalloutAdd(filter_engine_handle, &m_callout, NULL, NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to register example callout, status 0x%08x", status);
}
else {
DbgPrint("Example Callout Registered");
}
Exit:
return status;
}
Here is the callout function:
/*************************
ClassifyFn Function
**************************/
void example_classify(
const FWPS_INCOMING_VALUES * inFixedValues,
const FWPS_INCOMING_METADATA_VALUES * inMetaValues,
void * layerData,
const void * classifyContext,
const FWPS_FILTER * filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT * classifyOut)
{
UNREFERENCED_PARAMETER(layerData);
UNREFERENCED_PARAMETER(classifyContext);
UNREFERENCED_PARAMETER(flowContext);
UNREFERENCED_PARAMETER(filter);
UNREFERENCED_PARAMETER(inMetaValues);
NETWORK_ACCESS_QUERY AccessQuery;
BOOLEAN SafeToOpen = TRUE;
classifyOut->actionType = FWP_ACTION_PERMIT;
AccessQuery.remote_address = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32;
AccessQuery.remote_port = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16;
// Get Process ID
AccessQuery.ProcessId = (UINT64)PsGetCurrentProcessId();
if (!AccessQuery.ProcessId)
{
return;
}
// Here we connect to our userlevel application using FltSendMessage.
// Some checks are done and the SafeToOpen variable is populated with a BOOLEAN which indicates if to allow or block the packet.
// However, sometimes, a BSOD is generated with an INVALID_PROCESS_ATTACH_ATTEMPT error on the FltSendMessage call
QueryUserLevel(QUERY_NETWORK, &AccessQuery, sizeof(NETWORK_ACCESS_QUERY), &SafeToOpen, NULL, 0);
if (!SafeToOpen) {
classifyOut->actionType = FWP_ACTION_BLOCK;
}
return;
}
WFP drivers communicate to user-mode applications using the inverted call model. In this method, you keep an IRP from the user-mode pending at your kernel-mode driver instance and whenever you want to send data back to the user-mode you complete the IRP along with the data you want to send back.
The problem was that sometimes the ClassifyFn callback function can be called at IRQL DISPATCH_LEVEL.
FltSendMessage does not support DISPATCH_LEVEL, as it can only be run at IRQL <= APC_LEVEL.
Running at DISPATCH_LEVEL can cause this function to generate a BSOD.
I solved the problem by invoking FltSendMessage from a worker thread which runs at IRQL PASSIVE_LEVEL.
The worker thread can be created using IoQueueWorkItem.

Xlib XSendEvent Custom ClientEvent

I'm having problem of sending my program specific event with XSendEvent().
I setup XEvent as follows and send it:
XEvent evt;
evt.xclient.type = ClientMessage;
evt.xclient.serial = 0;
evt.xclient.send_event = true;
evt.xclient.message_type = XInternAtom(xconn, "_APP_EVT", false);
evt.xclient.format = 32;
evt.xclient.window = xwin;
evt.xclient.data.l[0] = XInternAtom(xconn, "_APP_EVT_PTR", false);
*reinterpet_cast<void**>(evt.xclient.data.l+1) = <pointer_value>
XSendEvent(xconn, xwin, false, NoEventMask, &evt);
Later in event loop I try read the sent pointer from the
recived message, but the read pointer value is always bad.
So far I have no idea why Xlib even mangles the evt.xclient.data.l array,
since on XLib documentation says X won't interpet the data at all.
for(;;) {
XEvent evt;
XNextEvent(xconn, &evt);
if(evt.type == ClientMessage) {
if(evt.xclient.message_type == XInternAtom(xconn, "_APP_EVT", false)) {
if(evt.xclient.data.l[0] == XInternAtom(xconn, "_APP_EVT_PTR", false)) {
// Prints pointers,
// that have only partially corrects bits in place:
printf("_APP_EVT_PTR:%p", *reinterpet_cast<void**>(evt.xclient.data.l+1));
}
}
}
Another detail here is that the message is sent from main thread,
and the message loop is in separate GUI thread.
I call XInitThreads() on the program before doing anything else with Xlib.

How to enable 802.11 monitor mode (DOT11_OPERATION_MODE_NETWORK_MONITOR) in a NDIS 6 filter driver?

I have ported WinPcap to a NDIS 6 filter driver: https://github.com/nmap/npcap. But it still doesn't support capturing all 802.11 native packets (like control and management frames are not captured).
I noticed there is a way setting DOT11_OPERATION_MODE_NETWORK_MONITOR for the wireless adapter using WlanSetInterface function. But this call succeeds (the return value is OK, and my wi-fi network disconnects after this call). But the problem is I can't see any packets on the Wi-Fi interface using Wireshark, not even the 802.11 data in fake ethernet form. So there must be something wrong with it.
I know that from NDIS 6 and vista, enabing this feature is possible (at least Microsoft's own Network Monitor 3.4 supports this).
So I want to know how to enable monitor mode for the NDIS 6 version WinPcap? Thanks.
My code is shown as below:
// WlanTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <wlanapi.h>
#define WLAN_CLIENT_VERSION_VISTA 2
void SetInterface(WLAN_INTF_OPCODE opcode, PVOID* pData, GUID* InterfaceGuid)
{
DWORD dwResult = 0;
HANDLE hClient = NULL;
DWORD dwCurVersion = 0;
DWORD outsize = 0;
// Open Handle for the set operation
dwResult = WlanOpenHandle(WLAN_CLIENT_VERSION_VISTA, NULL, &dwCurVersion, &hClient);
dwResult = WlanSetInterface(hClient, InterfaceGuid, opcode, sizeof(ULONG), pData, NULL);
WlanCloseHandle(hClient, NULL);
}
// enumerate wireless interfaces
UINT EnumInterface(HANDLE hClient, WLAN_INTERFACE_INFO sInfo[64])
{
DWORD dwError = ERROR_SUCCESS;
PWLAN_INTERFACE_INFO_LIST pIntfList = NULL;
UINT i = 0;
__try
{
// enumerate wireless interfaces
if ((dwError = WlanEnumInterfaces(
hClient,
NULL, // reserved
&pIntfList
)) != ERROR_SUCCESS)
{
__leave;
}
// print out interface information
for (i = 0; i < pIntfList->dwNumberOfItems; i++)
{
memcpy(&sInfo[i], &pIntfList->InterfaceInfo[i], sizeof(WLAN_INTERFACE_INFO));
}
return pIntfList->dwNumberOfItems;
}
__finally
{
// clean up
if (pIntfList != NULL)
{
WlanFreeMemory(pIntfList);
}
}
return 0;
}
// open a WLAN client handle and check version
DWORD
OpenHandleAndCheckVersion(
PHANDLE phClient
)
{
DWORD dwError = ERROR_SUCCESS;
DWORD dwServiceVersion;
HANDLE hClient = NULL;
__try
{
*phClient = NULL;
// open a handle to the service
if ((dwError = WlanOpenHandle(
WLAN_API_VERSION,
NULL, // reserved
&dwServiceVersion,
&hClient
)) != ERROR_SUCCESS)
{
__leave;
}
// check service version
if (WLAN_API_VERSION_MAJOR(dwServiceVersion) < WLAN_API_VERSION_MAJOR(WLAN_API_VERSION_2_0))
{
// No-op, because the version check is for demonstration purpose only.
// You can add your own logic here.
}
*phClient = hClient;
// set hClient to NULL so it will not be closed
hClient = NULL;
}
__finally
{
if (hClient != NULL)
{
// clean up
WlanCloseHandle(
hClient,
NULL // reserved
);
}
}
return dwError;
}
// get interface state string
LPWSTR
GetInterfaceStateString(__in WLAN_INTERFACE_STATE wlanInterfaceState)
{
LPWSTR strRetCode;
switch (wlanInterfaceState)
{
case wlan_interface_state_not_ready:
strRetCode = L"\"not ready\"";
break;
case wlan_interface_state_connected:
strRetCode = L"\"connected\"";
break;
case wlan_interface_state_ad_hoc_network_formed:
strRetCode = L"\"ad hoc network formed\"";
break;
case wlan_interface_state_disconnecting:
strRetCode = L"\"disconnecting\"";
break;
case wlan_interface_state_disconnected:
strRetCode = L"\"disconnected\"";
break;
case wlan_interface_state_associating:
strRetCode = L"\"associating\"";
break;
case wlan_interface_state_discovering:
strRetCode = L"\"discovering\"";
break;
case wlan_interface_state_authenticating:
strRetCode = L"\"authenticating\"";
break;
default:
strRetCode = L"\"invalid interface state\"";
}
return strRetCode;
}
int main()
{
HANDLE hClient = NULL;
WLAN_INTERFACE_INFO sInfo[64];
RPC_CSTR strGuid = NULL;
TCHAR szBuffer[256];
DWORD dwRead;
if (OpenHandleAndCheckVersion(&hClient) != ERROR_SUCCESS)
return -1;
UINT nCount = EnumInterface(hClient, sInfo);
for (UINT i = 0; i < nCount; ++i)
{
if (UuidToStringA(&sInfo[i].InterfaceGuid, &strGuid) == RPC_S_OK)
{
printf(("%d. %s\n\tDescription: %S\n\tState: %S\n"),
i,
strGuid,
sInfo[i].strInterfaceDescription,
GetInterfaceStateString(sInfo[i].isState));
RpcStringFreeA(&strGuid);
}
}
UINT nChoice = 0;
// printf("for choice wireless card:");
//
// if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE), szBuffer, _countof(szBuffer), &dwRead, NULL) == FALSE)
// {
// puts("error input");
// return -1;
// }
// szBuffer[dwRead] = 0;
// nChoice = _ttoi(szBuffer);
//
// if (nChoice > nCount)
// {
// puts("error input.");
// return -1;
// }
//ULONG targetOperationMode = DOT11_OPERATION_MODE_EXTENSIBLE_STATION;
ULONG targetOperationMode = DOT11_OPERATION_MODE_NETWORK_MONITOR;
SetInterface(wlan_intf_opcode_current_operation_mode, (PVOID*)&targetOperationMode, &sInfo[nChoice].InterfaceGuid);
return 0;
}
Update:
Guy has made me clear about what should the high-level library side of WinPcap do about the monitor mode, in nature is setting/getting OID values. But what should the WinPcap driver do, do I need to change the driver? I think the WlanSetInterface call is actually doing the same thing as setting the DOT11_OPERATION_MODE_NETWORK_MONITOR using OID request? Does the fact that it doesn't work mean that the npf driver also needs some kind of changes?
(Answer updated for question update and followup comments.)
Use pcap_oid_set_request_win32(), which is in pcap-win32.c in the version of libpcap in the master branch, to do OID setting/getting operations. If p->opt.rfmon is set in pcap_activate_win32(), set the OID OID_DOT11_CURRENT_OPERATION_MODE with a DOT11_CURRENT_OPERATION_MODE structure with uCurrentOpMode set to DOT11_OPERATION_MODE_NETWORK_MONITOR.
For pcap_can_set_rfmon_win32(), try to get a handle for the device (note that this is done before the activate call) and, if that succeeds, use pcap_oid_get_request_win32() to attempt to get the value of that OID; if it succeeds, you can set it, otherwise, either you can't set it or you got an error.
The driver already supports a generic get/set OID operation - that's what PacketRequest() uses, and pcap_oid_get_request_win32()/pcap_oid_set_request_win32() are implemented atop PacketRequest(), so it's what they use.
As I indicated in messages in the thread you started on the wireshark-dev list, the code that handles receive indications from NDIS has to be able to handle "raw packet" receive indications, and you might have to add those to the NDIS packet filter as well. (And you'll have to hack dumpcap, if you're going to use Wireshark to test the changes; you won't be able to change NPcap so that people can just drop it in and existing versions of Wireshark will support monitor mode.)
I also indicated there how to query a device to find out whether it supports monitor mode.
As for turning monitor mode back off, that's going to require driver, packet.dll, and libpcap work. In the drivers:
in the NDIS 6 driver, for each interface, have a count of "monitor mode instances" and a saved operating mode and, for each opened NPF instance for an interface, have a "monitor mode" flag;
in the Windows 9x and NDIS 4/5 drivers, add a "turn on monitor mode" BIOC call, which always fails with ERROR_NOT_SUPPORTED;
in the NDIS 6 driver, add the same BIOC call, which, if the instance's "monitor mode" flag isn't set, attempts to set the operating mode to monitor mode and, if it succeeds, saves the old operating mode if the interface's monitor mode count is zero, increments the interface's monitor mode count and sets the instance's "monitor mode" flag (it could also add the appropriate values to the packet filter);
have the routine that closes an opened NPF instance check the "monitor mode" flag for the instance and, if it's set, decrement the "monitor mode instances" count and, if the count reaches zero, restores the old operating mode.
In packet.dll, add a PacketSetMonitorMode() routine, which is a wrapper around the BIOC ioctl in question.
In pcap-win32.c, call PacketSetMonitorMode() if monitor mode was requested, rather than setting the operation mode directly.
For setting OIDs in drivers, see the code path for BIOCQUERYOID and BIOCSETOID in NPF_IoControl() - the new BIOC ioctl would be handled in NPF_IoControl().
(And, of course, do the appropriate MP locking.)
The monitor mode count might not be necessary, if you can enumerate all the NPF instances for an interface - the count is just the number of instances that have the monitor mode flag set.
Doing it in the driver means that if a program doing monitor-mode capturing terminates abruptly, so that no user-mode code gets to do any cleanup, the mode can still get reset.

sending messages to the main window in a thread in win32

I am relatively new to working with threads in Win32 api and have reached a problem that i am unable to work out.
Heres my problem, i have 4 threads (they work as intended) that allow the operator to test 4 terminals. In each thread i am trying to send a message to the main windows form with either Pass or Fail, this is placed within a listbox. Below is one of the threads, the remaining are exactly the same.
void Thread1(PVOID pvoid)
{
for(int i=0;i<numberOfTests1;i++) {
int ret;
double TimeOut = 60.0;
int Lng = 1;
test1[i].testNumber = getTestNumber(test1[i].testName);
unsigned char Param[255] = {0};
unsigned char Port1 = port1;
ret = PSB30_Open(Port1, 16);
ret = PSB30_SendOrder (Port1, test1[i].testNumber, &Param[0], &Lng, &TimeOut);
ret = PSB30_Close (Port1);
if(*Param == 1) SendDlgItemMessage(hWnd,IDT_RESULTLIST1,LB_ADDSTRING,i,(LPARAM)"PASS");
else SendDlgItemMessage(hWnd,IDT_RESULTLIST1,LB_ADDSTRING,i,(LPARAM)"FAIL");
}
_endthread();
}
I have debugged the code and it does everything except populate the listbox, i assume because its a thread i am missing something as the same code works outwith the thread. Do i need to put the thread to sleep while it sends the message to the main window?
Any help is appreciated.
Cheers
You don't want your secondary threads trying to manipulate your UI elements directly (such as the SendDlgItemMessage). Instead, you normally want to post something like a WM_COMMAND or WM_USER+N to the main window, and let that manipulate the UI elements accordingly.

Creating an asynchronous "ReadFileMany()" on top of ReadFile()

I'm trying to make a function, ReadFileMany, which imitates ReadFile's interface, which is given a list of (offset, length) pairs to read from, and which reads all portions of the file asynchronously.
I'm doing this inside ReadFileMany by using RegisterWaitForSingleObject to direct a thread pool thread to wait for I/O to complete, so that it can call ReadFile again to read the next portion of the file, etc.
The trouble I'm running into is that I can't seem to be able to mimic a certain behavior of ReadFile.
Specifically, file handles can themselves be used like events, without the need for an event handle:
OVERLAPPED ov = {0};
DWORD nw;
if (ReadFile(hFile, buf, buf_size, &nw, &ov))
{
if (WaitForSingleObject(hFile, INFINITE) == WAIT_OBJECT_0)
{
...
}
}
but of course, if the user waits on a file handle, he might get a notification that an intermediate read has complete, rather than the final read.
So, inside ReadFileMany, I have no choice but to pass an hEvent parameter to ReadFile for all but the last portion.
The question is, is there any way to still allow the user to wait for the file handle to become signaled when all the portions have been read?
At first the answer seems obvious: just avoid passing an event handle when reading the last portion!
That works fine if the read will be successful, but not if there are errors. If ReadFile starts suddenly returning an error on the last read, I will need to set the file handle to a signaled state manually in order to allow the reader to wake up from his potential call to WaitForSingleObject(hFile).
But there seems to be no way to set a file handle to a signaled state without performing actual I/O... so this is where I get stuck: is there any way for me to write this function so that it behaves like ReadFile on the outside, or is it impossible to do it correctly?
Assuming you are trying to read multiple sections of a single file, I would simply allocate an array of OVERLAPPED structures, one for each requested section of the file, kick off all of the ReadFile() calls at one time, and then use WaitForMultipleObjects() to wait for all of the I/Os to finish, eg:
struct sReadInfo
{
OVERLAPPED ov;
LPVOID pBuffer;
DWORD dwNumBytes;
DWORD dwNumBytesRead;
bool bPending;
sReadInfo()
{
memset(&ov, 0, sizeof(OVERLAPPED));
pBuffer = NULL;
dwNumBytes = 0;
dwNumBytesRead = 0;
bPending = false;
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ov.hEvent) throw std::exception();
}
~sReadInfo()
{
CloseHandle(hEvent);
}
};
.
bool error = false;
try
{
std::vector<sReadInfo> ri(numSections);
std::vector<HANDLE> h;
h.reserve(numSections);
for(int i = 0; i < numSections; ++i)
{
ULARGE_INTEGER ul;
ul.QuadPart = ...; // desired file offset to read from
sReadInfo *r = &ri[i];
r->ov.Offset = ul.LowPart;
r->ov.OffsetHigh = ul.HighPart;
r->pBuffer = ...; // desired buffer to read into
r->dwNumBytes = ...; // desired number of bytes to read
if (!ReadFile(hFile, r->pBuffer, r->dwNumBytes, &r->dwNumBytesRead, &r->ov))
{
if (GetLastError() != ERROR_IO_PENDING)
throw std::exception();
r->bPending = true;
h.push_back(r->ov.hEvent);
}
}
if (!h.empty())
{
if (WaitForMultipleObjects(h.size(), &h[0], TRUE, INFINITE) != WAIT_OBJECT_0)
throw std::exception();
}
for (int i = 0; i < numSections; ++i)
{
sReadInfo *r = &ri[i];
if (r->bPending)
GetOverlappedResult(hFile, &r->ov, &r->dwNumBytesRead, FALSE);
// ...
}
}
catch (const std::exception &)
{
CancelIo(hFile);
return false;
}
return true;

Resources