277 lines
8.5 KiB
C++
277 lines
8.5 KiB
C++
/*---------------------------------------------------------------------------
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
PARTICULAR PURPOSE.
|
|
|
|
Copyright (C) 1999 - 2000 Microsoft Corporation. All rights reserved.
|
|
|
|
fcopy.cpp
|
|
|
|
This program demonstrates how to use file mappings to implement a file-copy
|
|
program. It can copy any size file on Windows NT.
|
|
|
|
The size of the file mapping view is a multiple of the system's
|
|
allocation size. With relatively large views, this program
|
|
runs faster than if it used many small views. The size of the view
|
|
can be adjusted up or down by changing the ALLOCATION_MULTIPLIER constant.
|
|
The only recommendation is that the view size must be no more than can fit
|
|
into the process's address space.
|
|
|
|
Note: Supports 64-bit file systems.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
|
|
|
|
#if defined (DEBUG)
|
|
#define DEBUG_PRINT(X) printf(X)
|
|
#else
|
|
#define DEBUG_PRINT(X)
|
|
#endif
|
|
|
|
#include <crtdbg.h>
|
|
|
|
// maximum view size
|
|
DWORD dwMaxViewSize ;
|
|
|
|
// multiplying the system allocation size by the following constant
|
|
// determines the maximum view size
|
|
const WORD ALLOCATION_MULTIPLIER = 1 ;
|
|
|
|
const int SUCCESS = 0; /* for return value from main() */
|
|
const int FAILURE = 1; /* for return value from main() */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
main (argc, argv)
|
|
|
|
The main program. Takes the command line arguments, copies the source file
|
|
to the destination file.
|
|
|
|
Parameters
|
|
argc
|
|
Count of command-line arguments, including the name of the program.
|
|
argv
|
|
Array of pointers to strings that contain individual command-line
|
|
arguments.
|
|
|
|
Returns
|
|
Zero if program executed successfully, non-zero otherwise.
|
|
---------------------------------------------------------------------------*/
|
|
int main (int argc, char **argv)
|
|
{
|
|
int fResult = FAILURE;
|
|
|
|
SYSTEM_INFO siSystemInfo ;
|
|
DWORD dwMaxViewSize ;
|
|
|
|
ULARGE_INTEGER liSrcFileSize,
|
|
liBytesRemaining,
|
|
liMapSize,
|
|
liOffset;
|
|
|
|
HANDLE hSrcFile = INVALID_HANDLE_VALUE,
|
|
hDstFile = INVALID_HANDLE_VALUE,
|
|
hSrcMap = 0,
|
|
hDstMap = 0;
|
|
|
|
BYTE * pSrc = 0,
|
|
* pDst = 0;
|
|
|
|
char * pszSrcFileName = 0,
|
|
* pszDstFileName = 0;
|
|
|
|
if (argc != 3)
|
|
{
|
|
printf("usage: fcopy <srcfile> <dstfile>\n");
|
|
return (FAILURE);
|
|
}
|
|
|
|
pszSrcFileName = argv[argc-2]; // Src is second to last argument
|
|
pszDstFileName = argv[argc-1]; // Dst is the last argument
|
|
|
|
// Obtain the system's allocation granularity, then multiply it by an
|
|
// arbitrary factor to obtain the maximum view size
|
|
GetSystemInfo(&siSystemInfo);
|
|
dwMaxViewSize = siSystemInfo.dwAllocationGranularity * ALLOCATION_MULTIPLIER;
|
|
|
|
/*
|
|
Steps to open and access a file's contents:
|
|
1) Open the file,
|
|
2) Create a mapping of the file,
|
|
3) Map a view of the file.
|
|
|
|
This yields a pointer to the file's contents, which can then be used
|
|
to access the file, just as if it's contents were in a memory buffer.
|
|
|
|
For the source file, open and map it as read only; for the destination
|
|
file, open and map it as read-write. We allow other processes to read
|
|
the source file while we're copying it, but do not allow access to the
|
|
destination file since we're writing it.
|
|
*/
|
|
|
|
// Open the source and destination files
|
|
hSrcFile = CreateFile (pszSrcFileName, GENERIC_READ, FILE_SHARE_READ,
|
|
0, OPEN_EXISTING, 0, 0);
|
|
if (INVALID_HANDLE_VALUE == hSrcFile)
|
|
{
|
|
printf("fcopy: couldn't open source file.\n");
|
|
goto DONE;
|
|
}
|
|
|
|
hDstFile = CreateFile (pszDstFileName, GENERIC_READ|GENERIC_WRITE, 0,
|
|
0, CREATE_ALWAYS, 0, 0);
|
|
if (INVALID_HANDLE_VALUE == hDstFile)
|
|
{
|
|
printf("fcopy: couldn't create destination file.\n");
|
|
goto DONE;
|
|
}
|
|
|
|
|
|
// Need source file's size to know how big to make the destination mapping.
|
|
liSrcFileSize.LowPart = GetFileSize(hSrcFile, &liSrcFileSize.HighPart);
|
|
if ( (-1 == liSrcFileSize.LowPart) && (GetLastError() != NO_ERROR) )
|
|
{
|
|
DEBUG_PRINT("couldn't get size of source file.\n");
|
|
goto DONE;
|
|
}
|
|
|
|
/*
|
|
Special case: If the source file is zero bytes, we don't map it because
|
|
there's no need to and CreateFileMapping cannot map a zero-length file.
|
|
But since we've created the destination, we've successfully "copied" the
|
|
source.
|
|
*/
|
|
if (0 == liSrcFileSize.QuadPart)
|
|
{
|
|
fResult = SUCCESS;
|
|
goto DONE;
|
|
}
|
|
|
|
|
|
/*
|
|
Map the source and destination files. A mapping size of zero means the
|
|
whole file will be mapped.
|
|
*/
|
|
hSrcMap = CreateFileMapping (hSrcFile, 0, PAGE_READONLY, 0, 0, 0);
|
|
if (!hSrcMap)
|
|
{
|
|
DEBUG_PRINT("couldn't map source file\n");
|
|
goto DONE;
|
|
}
|
|
|
|
hDstMap = CreateFileMapping (hDstFile, 0, PAGE_READWRITE,
|
|
liSrcFileSize.HighPart,
|
|
liSrcFileSize.LowPart, 0);
|
|
if (!hDstMap)
|
|
{
|
|
DEBUG_PRINT("couldn't map destination file.\n");
|
|
goto DONE;
|
|
}
|
|
|
|
|
|
/*
|
|
Now that we have the source and destination mapping objects, map views
|
|
of the source and destination files, and do the file copy.
|
|
|
|
To minimize the amount of memory consumed for large files and make it
|
|
possible to copy files that couldn't be mapped into our virtual address
|
|
space entirely (those over 2GB), we limit the source and destination
|
|
views to the smaller of the file size or a specified maximum view size
|
|
(dwMaxViewSize, which is ALLOCATION_MULTIPLIER times the system's
|
|
allocation size).
|
|
|
|
If the file is smaller than the max view size, we'll just map and copy
|
|
it. Otherwise, we'll map a portion of the file, copy it, then map the
|
|
next portion, copy it, etc. until the entire file is copied.
|
|
|
|
MAP_SIZE is 32 bits because MapViewOfFile requires a 32-bit value for
|
|
the size of the view. This makes sense because a Win32 process's
|
|
address space is 4GB, of which only 2GB (2^31) bytes may be used by the
|
|
process. However, for the sake of making 64-bit arithmetic work below
|
|
for file offets, we need to make sure that all 64 bits of liMapSize
|
|
are initialized correctly.
|
|
|
|
Note structured exception handling is used in case a MapViewOfFile call
|
|
failed. That should never happen in this program, but in case it does,
|
|
we should handle it. Since the possibility is so remote, it is faster
|
|
to handle the exception when it occurs rather than test for failure in
|
|
the loop.
|
|
*/
|
|
__try
|
|
{
|
|
liBytesRemaining.QuadPart = liSrcFileSize.QuadPart;
|
|
|
|
// stan bug fix
|
|
liMapSize.QuadPart = dwMaxViewSize;
|
|
|
|
// Make sure that the arithmetic below is correct during debugging.
|
|
_ASSERT(liMapSize.HighPart == 0);
|
|
|
|
do
|
|
{
|
|
liMapSize.QuadPart = min(liBytesRemaining.QuadPart, liMapSize.QuadPart);
|
|
|
|
liOffset.QuadPart = liSrcFileSize.QuadPart - liBytesRemaining.QuadPart;
|
|
|
|
pSrc = (BYTE *)MapViewOfFile (hSrcMap, FILE_MAP_READ, liOffset.HighPart,
|
|
liOffset.LowPart, liMapSize.LowPart);
|
|
|
|
pDst = (BYTE *)MapViewOfFile (hDstMap, FILE_MAP_WRITE, liOffset.HighPart,
|
|
liOffset.LowPart, liMapSize.LowPart);
|
|
|
|
CopyMemory (pDst, pSrc, liMapSize.LowPart);
|
|
|
|
UnmapViewOfFile (pSrc);
|
|
UnmapViewOfFile (pDst);
|
|
|
|
liBytesRemaining.QuadPart -= liMapSize.QuadPart;
|
|
}
|
|
while (liBytesRemaining.QuadPart > 0);
|
|
|
|
fResult = SUCCESS;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/*
|
|
Only way we should get here is if a MapViewOfFile failed. That
|
|
shouldn't happen, but in case it does clean up the resources.
|
|
*/
|
|
if (pSrc)
|
|
UnmapViewOfFile (pSrc);
|
|
|
|
if (pDst)
|
|
UnmapViewOfFile (pDst);
|
|
}
|
|
|
|
|
|
DONE:
|
|
/* Clean up all outstanding resources. Note views are already unmapped. */
|
|
if (hDstMap)
|
|
CloseHandle (hDstMap);
|
|
|
|
if (hDstFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle (hDstFile);
|
|
|
|
if (hSrcMap)
|
|
CloseHandle (hSrcMap);
|
|
|
|
if (hSrcFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle (hSrcFile);
|
|
|
|
|
|
// Report to user only if a problem occurred.
|
|
if (fResult != SUCCESS)
|
|
{
|
|
printf("fcopy: copying failed.\n");
|
|
DeleteFile (pszDstFileName);
|
|
}
|
|
|
|
|
|
return (fResult);
|
|
}
|
|
|