Question

Note: I only want to use wininet, not urlmon-urldownloadtofile.

Well, I have the following code which works perfectly in XE2 to download a file:

procedure DownloadFile(URL: string; Path: string);
const
  BLOCK_SIZE = 1024;
var
  InetHandle: Pointer;
  URLHandle: Pointer;
  FileHandle: Cardinal;
  BytesRead: Cardinal;
  DownloadBuffer: Pointer;
  Buffer: array [1 .. BLOCK_SIZE] of byte;
  BytesWritten: Cardinal;
begin
  InetHandle := InternetOpen(PWideChar(URL), 0, 0, 0, 0);
  URLHandle := InternetOpenUrl(InetHandle, PWideChar(URL), 0, 0, 0, 0);
  FileHandle := CreateFile(PWideChar(Path), GENERIC_WRITE, FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
  DownloadBuffer := @Buffer;
  repeat
    InternetReadFile(URLHandle, DownloadBuffer, BLOCK_SIZE, BytesRead);
    if not WriteFile(FileHandle, DownloadBuffer, BytesRead, BytesWritten, 0) or
  (BytesWritten <> BytesRead) then
      RaiseLastOSError;
  until BytesRead < BLOCK_SIZE;
  CloseHandle(FileHandle);
  InternetCloseHandle(URLHandle);
  InternetCloseHandle(InetHandle);
end;

The above code's credits go to jachguate. He edited my code to correct it, and I thank him for that.

Now, this code only works correctly under Delphi XE2. I try to use it under Delphi 7 and it is not working correctly. It seems to store the same "line" or "byte sequence" to the file over and over a few times.

The following are the two reformations of the above code I have attemted to use in Delphi 7 - neither of which worked properly.

procedure DownloadFile(URL: string; Path: string);
const
  BLOCK_SIZE = 1024;
var
  InetHandle: Pointer;
  URLHandle: Pointer;
  FileHandle: Cardinal;
  BytesRead: Cardinal;
  DownloadBuffer: Pointer;
  Buffer: array [1 .. BLOCK_SIZE] of byte;
  BytesWritten: Cardinal;
begin
  InetHandle := InternetOpen(PChar(URL), 0, 0, 0, 0);
  URLHandle := InternetOpenUrl(InetHandle, PChar(URL), 0, 0, 0, 0);
  FileHandle := CreateFile(PChar(Path), GENERIC_WRITE, FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
  DownloadBuffer := @Buffer;
  repeat
    InternetReadFile(URLHandle, DownloadBuffer, BLOCK_SIZE, BytesRead);
    if not WriteFile(FileHandle, DownloadBuffer, BytesRead, BytesWritten, 0) or
  (BytesWritten <> BytesRead) then
      RaiseLastOSError;
  until BytesRead < BLOCK_SIZE;
  CloseHandle(FileHandle);
  InternetCloseHandle(URLHandle);
  InternetCloseHandle(InetHandle);
end;

procedure DownloadFile(URL: string; Path: string);
const
  BLOCK_SIZE = 1024;
var
  InetHandle: Pointer;
  URLHandle: Pointer;
  FileHandle: Cardinal;
  BytesRead: Cardinal;
  DownloadBuffer: Pointer;
  Buffer: array [1 .. BLOCK_SIZE] of byte;
  BytesWritten: Cardinal;
begin
  InetHandle := InternetOpen(PAnsiChar(URL), 0, 0, 0, 0);
  URLHandle := InternetOpenUrl(InetHandle, PAnsiChar(URL), 0, 0, 0, 0);
  FileHandle := CreateFile(PAnsiChar(Path), GENERIC_WRITE, FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
  DownloadBuffer := @Buffer;
  repeat
    InternetReadFile(URLHandle, DownloadBuffer, BLOCK_SIZE, BytesRead);
    if not WriteFile(FileHandle, DownloadBuffer, BytesRead, BytesWritten, 0) or
  (BytesWritten <> BytesRead) then
      RaiseLastOSError;
  until BytesRead < BLOCK_SIZE;
  CloseHandle(FileHandle);
  InternetCloseHandle(URLHandle);
  InternetCloseHandle(InetHandle);
end;

Only thing edited was the data type conversions. Example one "PChar" was used, and example two "PAnsiChar" was used.

Was it helpful?

Solution

You are getting garbage in the file because you need dereference the pointer DownloadBuffer using the ^ char, so to fix your method just replace this code

WriteFile(FileHandle, DownloadBuffer, BytesRead, BytesWritten, 0)

by this

WriteFile(FileHandle, DownloadBuffer^, BytesRead, BytesWritten, 0)

btw, remember add try..finally blocks to your function to ensure release the handles when a exception occurs.

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