iocmpprt.c

来自「一本已经绝版的好书」· C语言 代码 · 共 411 行

C
411
字号
/************************************************************
Module name: IOCmpPrt.C
Notices: Copyright (c) 1997 Jeffrey Richter
************************************************************/


#include "..\CmnHdr.H"                  /* See Appendix C. */
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include "Resource.H"


/////////////////////////////////////////////////////////////


HANDLE CreateNewCompletionPort(DWORD cSimultaneousClients) {
   return(CreateIoCompletionPort(INVALID_HANDLE_VALUE, 
      NULL, 0, cSimultaneousClients));
}


BOOL AssociateDeviceWithCompletionPort(
   HANDLE hCompPort, HANDLE hDevice, DWORD dwCompKey) {
   HANDLE h = CreateIoCompletionPort(hDevice, 
      hCompPort, dwCompKey, 0);
   return(h == hCompPort);
}


/////////////////////////////////////////////////////////////


static BOOL PrepareDstFile(LPCTSTR pszFileSrc, 
   LPCTSTR pszFileDst, PHANDLE phFileSrc, PHANDLE phFileDst, 
   PULARGE_INTEGER pulFileSize, DWORD dwPageSize) {

   ULARGE_INTEGER ulFileSizeDst;
   DWORD dwError;
   BOOL fOk = FALSE;

   __try {
      // Open the existing source file for input.
      *phFileSrc = CreateFile(pszFileSrc, GENERIC_READ,
         FILE_SHARE_READ, NULL, OPEN_EXISTING, 
         FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);

      if (*phFileSrc == INVALID_HANDLE_VALUE) 
         __leave;
   
      // Create the new destination file for output.
      *phFileDst = CreateFile(pszFileDst, GENERIC_WRITE,
         0, NULL, CREATE_ALWAYS, 
         FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, 
         *phFileSrc);

      if (*phFileDst == INVALID_HANDLE_VALUE) 
         __leave;

      // Get the size of the original file.
      pulFileSize->LowPart = GetFileSize(*phFileSrc, 
         &pulFileSize->HighPart);

      // Round up the source file size 
      // to an even multiple of pages.
      ulFileSizeDst.QuadPart = (pulFileSize->QuadPart + 
         dwPageSize - 1) & ~(dwPageSize - 1);

      // Force the destination file to the size needed.
      dwError = SetFilePointer(*phFileDst, ulFileSizeDst.LowPart, 
         (PLONG) ulFileSizeDst.HighPart, FILE_BEGIN);
      if ((dwError == 0xffffffff) && (GetLastError() != NO_ERROR))
         __leave;

      if (!SetEndOfFile(*phFileDst)) 
         __leave;

      fOk = TRUE; // We did everything successfully   
   }
   __finally {

      // If anything failed, clean up entirely
      if (!fOk) {
         if (*phFileSrc != INVALID_HANDLE_VALUE) {
            CloseHandle(*phFileSrc);
            *phFileSrc = INVALID_HANDLE_VALUE;
         }
         if (*phFileSrc != INVALID_HANDLE_VALUE) {
            CloseHandle(*phFileDst);
            *phFileDst = INVALID_HANDLE_VALUE;
         }
      }
   }
   return(fOk);
}


/////////////////////////////////////////////////////////////


static void CopyCleanup(LPCTSTR pszFileDst, HANDLE hFileSrc, 
   HANDLE hFileDst, PULARGE_INTEGER pulFileSize) {

   // Close the source and destination files
   CloseHandle(hFileDst);
   CloseHandle(hFileSrc);
   
   // We need another handle to the destination file that is
   // opened without FILE_FLAG_NO_BUFFERING. This allows us 
   // to set the end-of-file marker to a position that is 
   // not sector-aligned.
   hFileDst = CreateFile(pszFileDst, GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
      OPEN_EXISTING, 0, NULL);

   if (hFileDst != INVALID_HANDLE_VALUE) {

      // Set the destination file's size to the size of the
      // source file, in case the size of the source file was
      // not a multiple of the page size.
      SetFilePointer(hFileDst, pulFileSize->LowPart, 
         (PLONG) pulFileSize->HighPart, FILE_BEGIN);
      SetEndOfFile(hFileDst);
      CloseHandle(hFileDst);
   }
}


/////////////////////////////////////////////////////////////


typedef struct {
   // The page size used on the host machine
   DWORD dwPageSize;

   // Handles of the source and destination files
   HANDLE hFileSrc, hFileDst;

   // Size of source file
   ULARGE_INTEGER ulFileSize;

   // Offset in source file where next read begins
   ULARGE_INTEGER ulNextReadOffset;

   // The number of pending read & write I/O requests
   int nReadsInProgress;
   int nWritesInProgress;
} COPYSTATE, *PCOPYSTATE;


// Each IO requires an OVERLAPPED structure and a buffer.
typedef struct {
	OVERLAPPED Overlapped;
	PBYTE      pbData;
} IOREQ, *PIOREQ;

#define MAX_PENDING_IO_REQS   4
#define BUFFSIZE              (64 * 1024)

// The keys indicating the type of completed IO
#define COMPKEY_READ  1
#define COMPKEY_WRITE 2


/////////////////////////////////////////////////////////////


BOOL FileCopy (LPCTSTR pszFileSrc, LPCTSTR pszFileDst) {

   // We need a local copy state variable
   COPYSTATE cs;

   // Maintain info about each IO request.
   IOREQ IOReq[MAX_PENDING_IO_REQS] = { 0 };

   SYSTEM_INFO si;
   int nIOReq;
   BOOL fOk, fDone = FALSE;

   // I/O Completion Port where read and writes complete to
   HANDLE hIOCompPort = NULL;

   DWORD dwNumBytesXfer, dwCompKey;
   LPOVERLAPPED pOverlapped;

   // Initialize the global COPYSTATE variable
   GetSystemInfo(&si);
   cs.dwPageSize = si.dwPageSize;
   cs.ulNextReadOffset.QuadPart = 0;
   cs.nReadsInProgress = 0;
   cs.nWritesInProgress = 0;

   // Open the existing source file for input.
   fOk = PrepareDstFile(pszFileSrc, pszFileDst, 
      &cs.hFileSrc, &cs.hFileDst, 
      &cs.ulFileSize, cs.dwPageSize);
   if (!fOk) 
      return(fOk);

   // Create a new completion port kernel object
   hIOCompPort = CreateNewCompletionPort(0);
   chASSERT(hIOCompPort != NULL);

   // Associate completed IOs to/from the source file with
   // IO Competion Port.  All completed IOs have a 
   // completion key of COMPKEY_READ.
   chVERIFY(AssociateDeviceWithCompletionPort(hIOCompPort, 
      cs.hFileSrc, COMPKEY_READ));

   // Associate completed IOs to/from the destination file 
   // with IO Competion Port.  All completed IOs have a 
   // completion key of COMPKEY_WRITE.
   chVERIFY(AssociateDeviceWithCompletionPort(hIOCompPort, 
      cs.hFileDst, COMPKEY_WRITE));

   // Start the copy engine by posting a number of read 
   // IO requests against the source file.
   for (nIOReq = 0; nIOReq < MAX_PENDING_IO_REQS; nIOReq++) {
      IOReq[nIOReq].Overlapped.Internal = 0;   
      IOReq[nIOReq].Overlapped.InternalHigh = 0;   
      IOReq[nIOReq].Overlapped.Offset = 0;   
      IOReq[nIOReq].Overlapped.OffsetHigh = 0;   
      IOReq[nIOReq].Overlapped.hEvent = NULL;
      IOReq[nIOReq].pbData = VirtualAlloc(NULL, 
         BUFFSIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

      cs.nWritesInProgress++;
      PostQueuedCompletionStatus(hIOCompPort, 0, 
         COMPKEY_WRITE, &IOReq[nIOReq].Overlapped);
   }

   // Loop until an error has occurred or until the
   // destination file has been written.
   while ((cs.nReadsInProgress > 0) || 
          (cs.nWritesInProgress > 0)) {
      PIOREQ pIOReq;
      fOk = GetQueuedCompletionStatus(hIOCompPort, 
         &dwNumBytesXfer, &dwCompKey, &pOverlapped, INFINITE);
      chASSERT(fOk || (GetLastError() == NO_ERROR));
      pIOReq = (PIOREQ) pOverlapped;

      switch (dwCompKey) {
         case COMPKEY_READ:
            cs.nReadsInProgress--;
            // Round up the number of bytes to write
            // to a sector boundary.
            dwNumBytesXfer = 
               (dwNumBytesXfer + cs.dwPageSize - 1) & 
                  ~(cs.dwPageSize - 1);
            fOk = WriteFile(cs.hFileDst, pIOReq->pbData, 
               dwNumBytesXfer, &dwNumBytesXfer, 
               pOverlapped);
            // The write wither completed or is pending.
            chASSERT(fOk || (!fOk && 
               (GetLastError() == ERROR_IO_PENDING)));
            cs.nWritesInProgress++;
            break;

         case COMPKEY_WRITE:
            cs.nWritesInProgress--;
            if (cs.ulNextReadOffset.QuadPart >= 
                cs.ulFileSize.QuadPart)
               break;

            // We haven't read past the end of the file yet.
            // Read the next chunk of data.
            pOverlapped->Offset     = 
               cs.ulNextReadOffset.LowPart;   
            pOverlapped->OffsetHigh = 
               cs.ulNextReadOffset.HighPart;   
            fOk = ReadFile(cs.hFileSrc, pIOReq->pbData,
               BUFFSIZE, &dwNumBytesXfer, pOverlapped);
            // The read either completed or is pending.
            chASSERT(fOk || (!fOk && 
               (GetLastError() == ERROR_IO_PENDING)));
            cs.nReadsInProgress++;
            // Offset in source file where next read begins
            cs.ulNextReadOffset.QuadPart += BUFFSIZE;
            break;
      }
   }

   // Destroy the I/O completion port
   CloseHandle(hIOCompPort);

   // Free the memory buffers used for the copy
   for (nIOReq = 0; nIOReq < MAX_PENDING_IO_REQS; nIOReq++) {
      VirtualFree(IOReq[nIOReq].pbData, 0, MEM_RELEASE);
   }

   // Close the source and destination files; force the
   // destination file to be the same size as the source
   CopyCleanup(pszFileDst, cs.hFileSrc, 
      cs.hFileDst, &cs.ulFileSize);

   return(TRUE);
}


/////////////////////////////////////////////////////////////


BOOL Dlg_OnInitDialog (HWND hwnd, HWND hwndFocus, 
   LPARAM lParam) {

   // Associate an icon with the dialog box.
   chSETDLGICONS(hwnd, IDI_IOCMPPRT, IDI_IOCMPPRT);

   // Disable the "Copy" button because no file 
   // has been selected yet.
   EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
   
   return(TRUE);
}


/////////////////////////////////////////////////////////////


void Dlg_OnCommand (HWND hwnd, int id, 
   HWND hwndCtl, UINT codeNotify) {
   
   TCHAR szPathname[_MAX_DIR];
   BOOL fOk;
   OPENFILENAME ofn;

   switch (id) {
      case IDOK:
         // Copy the source file to the destination file.
         Static_GetText(GetDlgItem(hwnd, IDC_SRCFILE), 
            szPathname, sizeof(szPathname));
         SetCursor(LoadCursor(NULL, IDC_WAIT));
         FileCopy(szPathname, __TEXT("IOCmpPrt.CPY"));
         break;

      case IDC_BROWSE:
         chINITSTRUCT(ofn, TRUE);
         ofn.hwndOwner = hwnd;
         ofn.lpstrFilter = __TEXT("*.*\0");
         _tcscpy(szPathname, __TEXT("*.*"));
         ofn.lpstrFile = szPathname;
         ofn.nMaxFile = sizeof(szPathname);
         ofn.Flags = OFN_FILEMUSTEXIST;
         fOk = GetOpenFileName(&ofn);
         
         if (fOk) {
            HANDLE hFile;
            Static_SetText(GetDlgItem(hwnd, IDC_SRCFILE), 
               szPathname);
            hFile = CreateFile(szPathname, GENERIC_READ,
               0, NULL, OPEN_EXISTING, 0, NULL);

            SetDlgItemInt(hwnd, IDC_SRCFILESIZE, 
               GetFileSize(hFile, NULL), FALSE);
            CloseHandle(hFile);
         }
         
         // Enable the "Copy" button if the user selected
         // a valid pathname.
         GetWindowText(GetDlgItem(hwnd, IDC_SRCFILE), 
            szPathname, sizeof(szPathname));
         EnableWindow(GetDlgItem(hwnd, IDOK), 
            szPathname[0] != __TEXT('('));
          
         if (fOk) {
            // If the user pressed the OK button in the file
            // dialog box, change focus to the "Copy" button.
            FORWARD_WM_NEXTDLGCTL(hwnd, 
               GetDlgItem(hwnd, IDOK), TRUE, SendMessage);
          }
         break;
         
      case IDCANCEL:
         EndDialog(hwnd, id);
         break;
   }
}

/////////////////////////////////////////////////////////////


BOOL CALLBACK Dlg_Proc (HWND hwnd, UINT uMsg, 
   WPARAM wParam, LPARAM lParam) {

   switch (uMsg) {
      chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
      chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
   }

   return(FALSE);
}


/////////////////////////////////////////////////////////////


int WINAPI _tWinMain (HINSTANCE hinstExe,
   HINSTANCE hinstPrev, LPTSTR pszCmdLine, int nCmdShow) {

   chWARNIFUNICODEUNDERWIN95();
   DialogBox(hinstExe, MAKEINTRESOURCE(IDD_IOCMPPRT), 
      NULL, Dlg_Proc);

   return(0);
}


//////////////////////// End Of File ////////////////////////

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?