//EnsureCleanup.h
//EnsureCleanup.h
/******************************************************************************
Module: EnsureCleanup.h
Notices: Copyright (c) 2007 Jeffrey Richter & Christophe Nasarre
Purpose: These classes ensure object cleanup when an object goes out of scope.
See Appendix A.
******************************************************************************/
#pragma once // Include this header file once per compilation unit
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
///////////////////////////////////////////////////////////////////////////////
// Data type representing the address of the object's cleanup function.
// I used UINT_PTR so that this class works properly in 64-bit Windows.
typedef VOID (WINAPI* PFNENSURECLEANUP)(UINT_PTR);
///////////////////////////////////////////////////////////////////////////////
// Each template instantiation requires a data type, address of cleanup
// function, and a value that indicates an invalid value.
template<class TYPE, PFNENSURECLEANUP pfn, UINT_PTR tInvalid = NULL>
class CEnsureCleanup {
public:
// Default constructor assumes an invalid value (nothing to cleanup)
CEnsureCleanup() { m_t = tInvalid; }
// This constructor sets the value to the specified value
CEnsureCleanup(TYPE t) : m_t((UINT_PTR) t) { }
// The destructor performs the cleanup.
~CEnsureCleanup() { Cleanup(); }
// Helper methods to tell if the value represents a valid object or not..
BOOL IsValid() { return(m_t != tInvalid); }
BOOL IsInvalid() { return(!IsValid()); }
// Re-assigning the object forces the current object to be cleaned-up.
TYPE operator=(TYPE t) {
Cleanup();
m_t = (UINT_PTR) t;
return(*this);
}
// Returns the value (supports both 32-bit and 64-bit Windows).
operator TYPE() {
return (TYPE) m_t;
}
// Cleanup the object if the value represents a valid object
void Cleanup() {
if (IsValid()) {
// In 64-bit Windows, all parameters are 64-bits,
// so no casting is required
pfn(m_t); // Close the object.
m_t = tInvalid; // We no longer represent a valid object.
}
}
private:
UINT_PTR m_t; // The member representing the object
};
///////////////////////////////////////////////////////////////////////////////
// Macros to make it easier to declare instances of the template
// class for specific data types.
#define MakeCleanupClass(className, tData, pfnCleanup) \
typedef CEnsureCleanup<tData, (PFNENSURECLEANUP) pfnCleanup> className;
#define MakeCleanupClassX(className, tData, pfnCleanup, tInvalid) \
typedef CEnsureCleanup<tData, (PFNENSURECLEANUP) pfnCleanup, \
(INT_PTR) tInvalid> className;
///////////////////////////////////////////////////////////////////////////////
// Instances of the template C++ class for common data types.
MakeCleanupClass(CEnsureCloseHandle, HANDLE, CloseHandle);
MakeCleanupClassX(CEnsureCloseFile, HANDLE, CloseHandle,
INVALID_HANDLE_VALUE);
MakeCleanupClass(CEnsureLocalFree, HLOCAL, LocalFree);
MakeCleanupClass(CEnsureGlobalFree, HGLOBAL, GlobalFree);
MakeCleanupClass(CEnsureRegCloseKey, HKEY, RegCloseKey);
MakeCleanupClass(CEnsureCloseServiceHandle, SC_HANDLE, CloseServiceHandle);
MakeCleanupClass(CEnsureCloseWindowStation, HWINSTA, CloseWindowStation);
MakeCleanupClass(CEnsureCloseDesktop, HDESK, CloseDesktop);
MakeCleanupClass(CEnsureUnmapViewOfFile, PVOID, UnmapViewOfFile);
MakeCleanupClass(CEnsureFreeLibrary, HMODULE, FreeLibrary);
///////////////////////////////////////////////////////////////////////////////
// Special class for releasing a reserved region.
// Special class is required because VirtualFree requires 3 parameters
class CEnsureReleaseRegion {
public:
CEnsureReleaseRegion(PVOID pv = NULL) : m_pv(pv) { }
~CEnsureReleaseRegion() { Cleanup(); }
PVOID operator=(PVOID pv) {
Cleanup();
m_pv = pv;
return(m_pv);
}
operator PVOID() { return(m_pv); }
void Cleanup() {
if (m_pv != NULL) {
VirtualFree(m_pv, 0, MEM_RELEASE);
m_pv = NULL;
}
}
private:
PVOID m_pv;
};
///////////////////////////////////////////////////////////////////////////////
// Special class for freeing a block from a heap
// Special class is required because HeapFree requires 3 parameters
class CEnsureHeapFree {
public:
CEnsureHeapFree(PVOID pv = NULL, HANDLE hHeap = GetProcessHeap())
: m_pv(pv), m_hHeap(hHeap) { }
~CEnsureHeapFree() { Cleanup(); }
PVOID operator=(PVOID pv) {
Cleanup();
m_pv = pv;
return(m_pv);
}
operator PVOID() { return(m_pv); }
void Cleanup() {
if (m_pv != NULL) {
HeapFree(m_hHeap, 0, m_pv);
m_pv = NULL;
}
}
private:
HANDLE m_hHeap;
PVOID m_pv;
};
template <class TV, class TM>
inline TV chROUNDDOWN(TV Value, TM Multiple) {
return((Value / Multiple) * Multiple);
}
// This inline function rounds a value down to the nearest multiple
template <class TV, class TM>
inline TV chROUNDUP(TV Value, TM Multiple) {
return(chROUNDDOWN(Value, Multiple) +
(((Value % Multiple) > 0) ? Multiple : 0));
}
///////////////////////////////// End of File /////////////////////////////////
<pre class="cpp" name="code">//IoCompletionPort.h
/******************************************************************************
Module: IOCP.h
Notices: Copyright (c) 2007 Jeffrey Richter & Christophe Nasarre
Purpose: This class wraps an I/O Completion Port.
See Appendix B.
******************************************************************************/
#pragma once // Include this header file once per compilation unit
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
#define chVERIFY
#define chASSERT
///////////////////////////////////////////////////////////////////////////////
class CIOCP {
public:
CIOCP(int nMaxConcurrency = -1) {
m_hIOCP = NULL;
if (nMaxConcurrency != -1)
(void) Create(nMaxConcurrency);
}
~CIOCP() {
if (m_hIOCP != NULL)
chVERIFY(CloseHandle(m_hIOCP));
}
BOOL Close() {
BOOL bResult = CloseHandle(m_hIOCP);
m_hIOCP = NULL;
return(bResult);
}
BOOL Create(int nMaxConcurrency = 0) {
m_hIOCP = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, NULL, 0, nMaxConcurrency);
chASSERT(m_hIOCP != NULL);
return(m_hIOCP != NULL);
}
BOOL AssociateDevice(HANDLE hDevice, ULONG_PTR CompKey) {
BOOL fOk = (CreateIoCompletionPort(hDevice, m_hIOCP, CompKey, 0)
== m_hIOCP);
chASSERT(fOk);
return(fOk);
}
BOOL AssociateSocket(SOCKET hSocket, ULONG_PTR CompKey) {
return(AssociateDevice((HANDLE) hSocket, CompKey));
}
BOOL PostStatus(ULONG_PTR CompKey, DWORD dwNumBytes = 0,
OVERLAPPED* po = NULL) {
BOOL fOk = PostQueuedCompletionStatus(m_hIOCP, dwNumBytes, CompKey, po);
chASSERT(fOk);
return(fOk);
}
BOOL GetStatus(ULONG_PTR* pCompKey, PDWORD pdwNumBytes,
OVERLAPPED** ppo, DWORD dwMilliseconds = INFINITE) {
return(GetQueuedCompletionStatus(m_hIOCP, pdwNumBytes,
pCompKey, ppo, dwMilliseconds));
}
private:
HANDLE m_hIOCP;
};
///////////////////////////////// End of File /////////////////////////////////
//main.cpp
/******************************************************************************
Module: FileCopy.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
******************************************************************************/
#include "IOCompletionPort.h" // See Appendix A.
#include "EnsureCleanup.h" // See Appendix A.
#include <WindowsX.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <string>
///////////////////////////////////////////////////////////////////////////////
// Each I/O Request needs an OVERLAPPED structure and a data buffer
class CIOReq : public OVERLAPPED {
public:
CIOReq() {
Internal = InternalHigh = 0;
Offset = OffsetHigh = 0;
hEvent = NULL;
m_nBuffSize = 0;
m_pvData = NULL;
}
~CIOReq() {
if (m_pvData != NULL)
VirtualFree(m_pvData, 0, MEM_RELEASE);
}
BOOL AllocBuffer(SIZE_T nBuffSize) {
m_nBuffSize = nBuffSize;
m_pvData = VirtualAlloc(NULL, m_nBuffSize, MEM_COMMIT, PAGE_READWRITE);
return(m_pvData != NULL);
}
BOOL Read(HANDLE hDevice, PLARGE_INTEGER pliOffset = NULL) {
if (pliOffset != NULL) {
Offset = pliOffset->LowPart;
OffsetHigh = pliOffset->HighPart;
}
return(::ReadFile(hDevice, m_pvData, m_nBuffSize, NULL, this));
}
BOOL Write(HANDLE hDevice, PLARGE_INTEGER pliOffset = NULL) {
if (pliOffset != NULL) {
Offset = pliOffset->LowPart;
OffsetHigh = pliOffset->HighPart;
}
return(::WriteFile(hDevice, m_pvData, m_nBuffSize, NULL, this));
}
public:
SIZE_T m_nBuffSize;
PVOID m_pvData;
};
///////////////////////////////////////////////////////////////////////////////
#define BUFFSIZE (64 * 1024) // The size of an I/O buffer
//DWORD BUFFSIZE = 2;
#define MAX_PENDING_IO_REQS 4 // The maximum # of I/Os
// The completion key values indicate the type of completed I/O.
#define CK_READ 1
#define CK_WRITE 2
HANDLE hFileSrc;
HANDLE hFileDst;
PTP_IO piosrc = NULL;
PTP_IO piodst = NULL;
CIOReq ior[8];
LARGE_INTEGER liNextReadOffset = { 0 };
//volatile BYTE buf[2] = { 0 };
///////////////////////////////////////////////////////////////////////////////
VOID CALLBACK OverlappedCompletionRoutineRead(PTP_CALLBACK_INSTANCE pIns,
PVOID pvContext,
PVOID pOverlapped,
ULONG IoResult,ULONG_PTR NumberofBytesTs,PTP_IO pio)
{
StartThreadpoolIo(piodst);
liNextReadOffset.LowPart = (*(CIOReq*)(pOverlapped)).Offset;
liNextReadOffset.HighPart = (*(CIOReq*)(pOverlapped)).OffsetHigh;
ior[1].Offset = liNextReadOffset.LowPart;
ior[1].OffsetHigh = liNextReadOffset.HighPart;
::WriteFile(hFileDst,(*(CIOReq*)(pOverlapped)).m_pvData,(*(CIOReq*)(pOverlapped)).InternalHigh,NULL,&ior[1]);
}
VOID CALLBACK OverlappedCompletionRoutineWrite(PTP_CALLBACK_INSTANCE pIns,
PVOID pvContext,
PVOID pOverlapped,
ULONG IoResult,ULONG_PTR NumberofBytesTs,PTP_IO pio)
{
if((*(CIOReq*)(pOverlapped)).InternalHigh == BUFFSIZE)
{
StartThreadpoolIo(piosrc);
liNextReadOffset.LowPart = (*(CIOReq*)(pOverlapped)).Offset;
liNextReadOffset.HighPart = (*(CIOReq*)(pOverlapped)).OffsetHigh;
liNextReadOffset.QuadPart += BUFFSIZE;
ior[0].Offset = liNextReadOffset.LowPart;
ior[0].OffsetHigh = liNextReadOffset.HighPart;
::ReadFile(hFileSrc,ior[0].m_pvData,BUFFSIZE,NULL,&ior[0]);
}
else
{
CancelThreadpoolIo(piosrc);
}
}
BOOL FileCopy(PCTSTR pszFileSrc, PCTSTR pszFileDst) {
BOOL bOk = FALSE; // Assume file copy fails
LARGE_INTEGER liFileSizeSrc = { 0 }, liFileSizeDst;
// Open the source file without buffering & get its size
hFileSrc = CreateFile(pszFileSrc, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
/*FILE_FLAG_NO_BUFFERING |*/ FILE_FLAG_OVERLAPPED, NULL);
if (hFileSrc == INVALID_HANDLE_VALUE) goto leave;
// Get the file's size
GetFileSizeEx(hFileSrc, &liFileSizeSrc);
// Nonbuffered I/O requires sector-sized transfers.
// I'll use buffer-size transfers since it's easier to calculate.
liFileSizeDst.QuadPart = chROUNDUP(liFileSizeSrc.QuadPart, BUFFSIZE);
// Open the destination file without buffering & set its size
hFileDst = CreateFile(pszFileDst, GENERIC_WRITE,
0, NULL, CREATE_ALWAYS,
/*FILE_FLAG_NO_BUFFERING |*/ FILE_FLAG_OVERLAPPED, hFileSrc);
if (hFileDst == INVALID_HANDLE_VALUE) goto leave;
// File systems extend files synchronously. Extend the destination file
// now so that I/Os execute asynchronously improving performance.
piosrc = CreateThreadpoolIo(hFileSrc,OverlappedCompletionRoutineRead,L"src",NULL);
piodst = CreateThreadpoolIo(hFileDst,OverlappedCompletionRoutineWrite,L"dst",NULL);
ior[0].AllocBuffer(BUFFSIZE);
ior[1].AllocBuffer(BUFFSIZE);
StartThreadpoolIo(piosrc); //每次发出IO请求需要调用些函数,否则回调函数不将被调用
::ReadFile(hFileSrc,ior[0].m_pvData,BUFFSIZE,NULL,&ior[0]);
//WaitForThreadpoolIoCallbacks(piosrc,TRUE);
//WaitForThreadpoolIoCallbacks(piodst,TRUE); //why this is fail if replace getchar()
getchar();
// CloseThreadpoolIo(piosrc);
// CloseThreadpoolIo(piodst);
SetFilePointerEx(hFileDst, liFileSizeSrc, NULL, FILE_BEGIN);
SetEndOfFile(hFileDst);
leave:;
return 0;
}
int wmain(int argc,wchar_t **argv)
{
//GetDiskFreeSpaceW(NULL,NULL,&BUFFSIZE,NULL,NULL); //cache bytes of per sector for support FILE_FLAG_NO_BUFFERING flag.
if(argc < 3) return -1;
FileCopy(argv[1],argv[2]);
return(0);
}
//////////////////////////////// End of File //////////////////////////////////