2025-11-28 00:35:46 +09:00

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);
}