Question

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?

Was it helpful?

Solution

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'.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top