Reputation: 23
I made an application to copy a file using thread and the method of TFileStream, but I was a little disappointed with the speed, especially when copying large files. Then I heard about the file mapping, which apparently could certainly yield a method of copying a lot faster since access to the files would be much faster.
I'm a beginner so I'm trying to, but I have not managed to copy a file via file mapping. (The file is created test2.iso but instead of doing 0ko 3GB ^ ^.)
Here is my code.
procedure TForm1.Button1Click(Sender: TObject);
var
FFilehandle: THANDLE;
FFileMap: THANDLE;
FmappingPtr: pchar;
hFile2: THANDLE ;
SizeFile1,BytesWritten: DWORD ;
begin
FFilehandle := CreateFile('titan.iso',
GENERIC_WRITE OR GENERIC_READ,
FILE_SHARE_READ OR FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (FFilehandle <> INVALID_HANDLE_VALUE) then
begin
FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
nil, // optional security attributes
PAGE_READWRITE, // protection for mapping object
0, // high-order 32 bits of object size
2*1024, // low-order 32 bits of object size
0); //
if (FFileMap <> NULL) then
begin
FMappingPtr := MapViewOfFile(FFileMap,
FILE_MAP_WRITE,
0,
0,
0);
if Assigned(FMappingPtr) then
begin
// Manipulation de FMappingPtr
hFile2 := CreateFile('test.iso', GENERIC_WRITE, 0, nil,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile2 <> INVALID_HANDLE_VALUE) then
begin
SizeFile1 := GetFileSize(FFilehandle, NIL); // recupere taille du fichier 1
WriteFile(hFile2, Fmappingptr, SizeFile1, &BytesWritten, NIL); // Transfert la mémoire mappé dans file 2
// libere les ressources
end
else
MessageBox(0, 'Impossible de lire le fichier mappé', 'Error', 0);
UnmapViewOfFile(Fmappingptr);
CloseHandle(FFileMap);
CloseHandle(FFilehandle);
CloseHandle(hFile2);
end
else
begin
CloseHandle (FFileMap);
CloseHandle (FFileHandle);
MessageBox(0, 'Impossible de lire le fichier mappé', 'Error', 0);
end;
end
else
begin
CloseHandle (FFilemap);
MessageBox(0, 'Impossible de mappe le fichier en mémoire', 'Error', 0);
end;
end
else
MessageBox(NULL, 'Impossible d''ouvrir le fichier', 'Error', NULL);
end;
Where is my problem?
Upvotes: 2
Views: 3229
Reputation: 54812
Not that I disagree with the comments to your question, but there are two reasons that fail your code.
First is, you're specifying 2Kb for dwFileOffsetLow
with CreateFileMapping
but then passing the entire file size to WriteFile
. With this mapping view, 'WriteFile' should be called at most with 2Kb for nNumberOfBytesToWrite
.
Second is, you're not passing the starting address of your file data correct, try this:
[...]
FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
nil, // optional security attributes
PAGE_READWRITE, // protection for mapping object
0, // high-order 32 bits of object size
0, // low-order 32 bits of object size
0); //
if (FFileMap <> NULL) then
begin
FMappingPtr := MapViewOfFile(FFileMap,
FILE_MAP_WRITE,
0,
0,
0);
if Assigned(FMappingPtr) then
begin
// Manipulation de FMappingPtr
hFile2 := CreateFile('test.iso', GENERIC_WRITE, 0, nil,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile2 <> INVALID_HANDLE_VALUE) then
begin
SizeFile1 := GetFileSize(FFilehandle, NIL); // recupere taille du fichier 1
WriteFile(hFile2, Fmappingptr[0], SizeFile1, &BytesWritten, NIL); // Transfert la mémoire mappé dans file 2
// libere les ressources
end
[...]
BTW, the reason the code is not returning any errors is that you're not checking the return of 'WriteFile'.
Upvotes: 2
Reputation: 3381
The system FileCopy does use memory mapped files. If the file is large you can watch the amount of virtual memory decrease on your system as the mapping takes place. There was a 'feature' in Windows Server recently that would use all of the available virtual RAM to build up the mapping. So... I would use FileCopy (or FileCopyEx) and let the OS decide on the best way to move the data (it is likely to know best). If you do it in a separate thread you can even do it without stopping your program - and it will be a very quick copy as most CPUs will spend most of the time buffing their nails waiting for the disk/network.
In your example shouldn't you be CreateFileMapping with PAGE_READONLY and MapViewOfFile with FILE_MAP_READ? The pointer returned (FMappingPtr) should point to valid be visible in the debugger - and it should look like you file.
Upvotes: 2