存在 3 种对文件进行异步操作的方法:
- 使用Apc例程
- 使用 IoCompletionPort
- 使用事件 - 最差
您选择最差的变体。我将在你的地方使用 IoCompletionPort 。在这种情况下,您不需要创建事件、线程、调用GetOverlappedResult
,不需要任何循环..
所有需要打电话的事情BindIoCompletionCallback (or RtlSetIoCompletionCallback
)在文件上和所有!
关于取消——CancelIoExXP 中不存在(“我正在尝试支持 Windows XP”
),
但你可以简单地关闭目录句柄 - 在这种情况下 IO 将被取消STATUS_NOTIFY_CLEANUP。所以代码可以如下所示:
RUNDOWN_REF_EVENT g_rundown; // Run-Down Protection
class SPYDATA :
#ifdef _USE_NT_VERSION_
IO_STATUS_BLOCK
#else
OVERLAPPED
#endif
{
HANDLE _hFile;
LONG _dwRef;
union {
FILE_NOTIFY_INFORMATION _fni;
UCHAR _buf[PAGE_SIZE];
};
void DumpDirectoryChanges()
{
union {
PVOID buf;
PBYTE pb;
PFILE_NOTIFY_INFORMATION pfni;
};
buf = _buf;
for (;;)
{
DbgPrint("%x <%.*S>\n", pfni->Action, pfni->FileNameLength >> 1, pfni->FileName);
ULONG NextEntryOffset = pfni->NextEntryOffset;
if (!NextEntryOffset)
{
break;
}
pb += NextEntryOffset;
}
}
#ifdef _USE_NT_VERSION_
static VOID WINAPI _OvCompRoutine(
_In_ NTSTATUS dwErrorCode,
_In_ ULONG_PTR dwNumberOfBytesTransfered,
_Inout_ PIO_STATUS_BLOCK Iosb
)
{
static_cast<SPYDATA*>(Iosb)->OvCompRoutine(dwErrorCode, (ULONG)dwNumberOfBytesTransfered);
}
#else
static VOID WINAPI _OvCompRoutine(
_In_ DWORD dwErrorCode, // really this is NTSTATUS
_In_ DWORD dwNumberOfBytesTransfered,
_Inout_ LPOVERLAPPED lpOverlapped
)
{
static_cast<SPYDATA*>(lpOverlapped)->OvCompRoutine(dwErrorCode, dwNumberOfBytesTransfered);
}
#endif
VOID OvCompRoutine(NTSTATUS status, DWORD dwNumberOfBytesTransfered)
{
DbgPrint("[%x,%x]\n", status, dwNumberOfBytesTransfered);
if (0 <= status)
{
if (status != STATUS_NOTIFY_CLEANUP)
{
if (dwNumberOfBytesTransfered) DumpDirectoryChanges();
DoRead();
}
else
{
DbgPrint("\n---- NOTIFY_CLEANUP -----\n");
}
}
Release();
g_rundown.ReleaseRundownProtection();
}
~SPYDATA()
{
Cancel();
}
public:
void DoRead()
{
if (g_rundown.AcquireRundownProtection())
{
AddRef();
#ifdef _USE_NT_VERSION_
NTSTATUS status = ZwNotifyChangeDirectoryFile(_hFile, 0, 0, this, this, &_fni, sizeof(_buf), FILE_NOTIFY_VALID_MASK, TRUE);
if (NT_ERROR(status))
{
OvCompRoutine(status, 0);
}
#else
if (!ReadDirectoryChangesW(_hFile, _buf, sizeof(_buf), TRUE, FILE_NOTIFY_VALID_MASK, (PDWORD)&InternalHigh, this, 0))
{
OvCompRoutine(RtlGetLastNtStatus(), 0);
}
#endif
}
}
SPYDATA()
{
_hFile = 0;// ! not INVALID_HANDLE_VALUE because use ntapi for open file
_dwRef = 1;
#ifndef _USE_NT_VERSION_
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
#endif
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef))
{
delete this;
}
}
BOOL Create(POBJECT_ATTRIBUTES poa)
{
IO_STATUS_BLOCK iosb;
NTSTATUS status = ZwOpenFile(&_hFile, FILE_GENERIC_READ, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_DIRECTORY_FILE);
if (0 <= status)
{
return
#ifdef _USE_NT_VERSION_
0 <= RtlSetIoCompletionCallback(_hFile, _OvCompRoutine, 0);
#else
BindIoCompletionCallback(_hFile, _OvCompRoutine, 0);
#endif
}
return FALSE;
}
void Cancel()
{
if (HANDLE hFile = InterlockedExchangePointer(&_hFile, 0))
{
NtClose(hFile);
}
}
};
void DemoF()
{
if (g_rundown.Create())
{
STATIC_OBJECT_ATTRIBUTES(oa, "\\systemroot\\tmp");//SOME_DIRECTORY
if (SPYDATA* p = new SPYDATA)
{
if (p->Create(&oa))
{
p->DoRead();
}
MessageBoxW(0, L"wait close program...", L"", MB_OK);
p->Cancel();
p->Release();
}
g_rundown.ReleaseRundownProtection();
g_rundown.WaitForRundown();
}
}
当我使用的所有 IO 完成时等待停机保护。不幸的是,这没有在用户模式下实现,但您自己并不难实现这个非常有用的功能。我的实现:
class __declspec(novtable) RUNDOWN_REF
{
LONG _LockCount;
protected:
virtual void RundownCompleted() = 0;
public:
RUNDOWN_REF()
{
_LockCount = 1;
}
BOOL AcquireRundownProtection()
{
LONG LockCount = _LockCount, prevLockCount;
do
{
if (!LockCount)
{
return FALSE;
}
LockCount = InterlockedCompareExchange(&_LockCount, LockCount + 1, prevLockCount = LockCount);
} while (LockCount != prevLockCount);
return TRUE;
}
void ReleaseRundownProtection()
{
if (!InterlockedDecrement(&_LockCount))
{
RundownCompleted();
}
}
};
class RUNDOWN_REF_EVENT : public RUNDOWN_REF
{
HANDLE _hEvent;
virtual void RundownCompleted()
{
SetEvent(_hEvent);
}
public:
BOOL Create()
{
return (_hEvent = CreateEvent(0, TRUE, FALSE, 0)) != 0;
}
RUNDOWN_REF_EVENT()
{
_hEvent = 0;
}
~RUNDOWN_REF_EVENT()
{
if (_hEvent) CloseHandle(_hEvent);
}
void WaitForRundown()
{
if (WaitForSingleObject(_hEvent, INFINITE) != WAIT_OBJECT_0) __debugbreak();
}
};