[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 3453
  • Last Modified:

Delphi: How to set timeout for InternetReadFile?

I need to download a file but it could happen that the sender doesn't send the data smoothly. It could happen that after a few megabytes there is a period in which no data is sent. After approx. 30 to 180 seconds the sender resumes providing data for the downloader.

I attached the function that I use to download the data. Unfortunately, this function returns when there is a delay on the sender's side and consequently the receiver gets an incomplete file.

If I could specify a timeout that the InternetReadFile would allow to wait for the data stream to continue that would solve my problem.

If you have a different solution let me know. I prefer using WinInet since our software runs in environments where network configuration is done through Internet Explorer settings.

Regards,
Dirk.
function THttpManager.DownloadFile(const aUrl: string; const pFileName: String): Boolean;
var
  hSession: HINTERNET;
  hService: HINTERNET;
  lpBuffer: array[0..1024 + 1] of Char;
  dwBytesRead: DWORD;
  lFileStream: TFileStream;
begin
  Result := False;
  hSession := InternetOpen('MyApp', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    if Assigned(hSession) then
    begin
      hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, 0, 0);
      if Assigned(hService) then
      begin
        try
          lFileStream := TFileStream.Create( pFileName, fmCreate );
          while True do
          begin
            dwBytesRead := 1024;
            InternetReadFile(hService, @lpBuffer, 1024, dwBytesRead);
            if dwBytesRead = 0 then break;
            lpBuffer[dwBytesRead] := #0;
            lFileStream.WriteBuffer( lpBuffer, dwBytesRead );
          end;
          Result := True;
        finally
          InternetCloseHandle(hService);
          lFileStream.Free;
        end;
      end;
    end;
  finally
    InternetCloseHandle(hSession);
  end;
end;

Open in new window

0
dirkil2
Asked:
dirkil2
  • 2
1 Solution
 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
What about use the wininet flags INTERNET_OPTION_CONNECT_TIMEOUT,  INTERNET_OPTION_SEND_TIMEOUT, and INTERNET_OPTION_RECEIVE_TIMEOUT

For example


var
TimeOut: Integer
[..]
Result := False;
  hSession := InternetOpen('MyApp', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    if Assigned(hSession) then
    begin
      hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, 0, 0);
      if Assigned(hService) then
      begin
        TimeOut := 2000; // in milleseconds.
        InternetSetOption(hservice,INTERNET_OPTION_RECEIVE_TIMEOUT,
                          Pointer(@TimeOut), SizeOf(TimeOut));
[..]

Open in new window

0
 
dirkil2Author Commented:
Unfortunately this doesn't make any difference. When it pauses for a while the call returns and download isn't continued. Maybe it helps: when the download starts pausing for a while it doesn't return immediately but after about 10 seconds.

I read the definition of INTERNET_OPTION_RECEIVE_TIMEOUT. As I understand it is the overall timeout. As I understand it it defines the maximum allowd time for the whole download. I set it to 3600000 but that didn't work either.

I tried the same by using the Indy component TIdHttp and it didn't work either. But then I set ReadTimeout and then it worked. So I reckon it definitely is a timeout problem. Now I need to know how to configure WinInet that it can handle that. I am pretty sure that this must be possible because when I try to download the file in Internet Explorer it also pauses for a while but then it resumes the download.

So if someone has other ideas please let me know.
0
 
dirkil2Author Commented:
The problem cannot be solved with setting timeouts. So the solution is to call InternetQueryDataAvailable() before every call to InternetReadFile() and when it returns false I go into some polling and sleeping routine to wait for the server to continue. This has the merit that the connection stays usable.


function THttpManager.DownloadFileToStream(const aUrl: string; const pStream: TStream): Boolean;
var
  hSession     : HINTERNET;
  hService     : HINTERNET;
  lpBuffer     : array[0..1023] of Byte;
  dwBytesRead  : DWORD;
  dwBytesAvail : DWORD;
  dwTimeOut    : DWORD;
  i            : Integer;
  lSucc        : LongBool;
  lRetries     : Integer;
begin
  Result := False;
  hSession := InternetOpen('MyApp', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if not Assigned(hSession) then Exit;
  try
    hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, 0, 0);
    if not Assigned(hService) then Exit;
    try
      dwTimeOut := 1800000; // in milleseconds.
      InternetSetOption(hService, INTERNET_OPTION_RECEIVE_TIMEOUT, @dwTimeOut, SizeOf(dwTimeOut));
      repeat
        lRetries := 0;
        repeat
          lSucc := InternetQueryDataAvailable( hService, dwBytesAvail, 0, 0);
          if not lSucc then
          begin
            Sleep( 20000 );
          end;
        until (lSucc=true) OR (lRetries > 20);
        if not InternetReadFile(hService, @lpBuffer[0], SizeOf(lpBuffer), dwBytesRead) then Exit;
        if dwBytesRead = 0 then Break;
        pStream.WriteBuffer(lpBuffer[0], dwBytesRead);
      until False;
    finally
      InternetCloseHandle(hService);
    end;
  finally
    InternetCloseHandle(hSession);
  end;
  Result := True;
end;

Open in new window

0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now