使用 winInet.h 或 urlmon.h 下载和恢复下载?

Posted

技术标签:

【中文标题】使用 winInet.h 或 urlmon.h 下载和恢复下载?【英文标题】:download and resuming download using winInet.h or urlmon.h? 【发布时间】:2014-09-15 17:38:07 【问题描述】:

我正在使用 winInet 功能从互联网下载文件 这是问题: 如何恢复中止的下载? 有没有非常简单的样本? 我读了http://www.clevercomponents.com/articles/article015/resuming.asp,但它不起作用! 我只知道必须使用 InternetSetFilePointer 和 HttpSendRequest 但我不知道该怎么做。知道吗? 谢谢大家

【问题讨论】:

Don't re-ask the same question.如果你觉得有什么需要补充的,edit your previous question。 使用 BITS(后台智能传输服务)进行文件下载会更容易和更强大:msdn.microsoft.com/en-us/library/aa362708(v=vs.85).aspx。它是内置服务,支持恢复和带宽控制。 【参考方案1】:

我有这个完整的解决方案。试试看。它是在 Delphi 7 中编写的,我希望它可以在更多版本的 Delphi 中使用。

unit IEDownloadFile;

interface

uses
  classes, windows;
(*
type
  TDownloadStatus =
  (
    dsNULL                , dsFindingresource         , dsConnecting              , dsRedirecting,
    dsBegindownloaddata   , dsDownloadingdata         , dsEnddownloaddata         , dsBegindownloadcomponents,
    dsInstallingcomponents, dsEnddownloadcomponents   , dsUsingcachedcopy         , dsSendingrequest,
    dsClassidavailable    , dsMimetypeavailable       , dsCachefilenameavailable  , dsBeginsyncoperation,
    dsEndsyncoperation    , dsBeginuploaddata         , dsUploadingdata           , dsEnduploadingdata,
    dsProtocolclassid     , dsEncoding                , dsVerfiedmimetypeavailable, dsClassinstalllocation,
    dsDecoding            , dsLoadingmimehandler      , dsContentdispositionattach, dsFilterreportmimetype,
    dsClsidcaninstantiate , dsIunknownavailable       , dsDirectbind              , dsRawmimetype,
    dsProxydetecting      , dsAcceptranges
  );

const
  StatusStrArray: array[TDownloadStatus] of String =
  (
    'NULL'                 , 'Finding resource'       , 'Connecting'                 , 'Redirecting',
    'Begin download data'  , 'Downloading data'       , 'End download data'          , 'Begin download components',
    'Installing components', 'End download components', 'Using cached copy'          , 'Sending request',
    'Classid available'    , 'Mime type available'    , 'Cache filename available'   , 'Begin sync operation',
    'End sync operation'   , 'Begin upload data'      , 'Uploading data'             , 'End uploading data',
    'Protocol classid'     , 'Encoding'               , 'Verfied mime type available', 'Class install location',
    'Decoding'             , 'Loading mime handler'   , 'Content disposition attach' , 'Filter report mime type',
    'Clsid can instantiate', 'Iunknown available'     , 'Direct bind'                , 'Raw mime type',
    'Proxy detecting'      , 'Accept ranges'
    );

type
  TLWOnDownloading = procedure(Sender: TObject; size: Integer; var cancel: Boolean) of object;
  TLWDOnProgress   = procedure(Sender: TObject; position, max: Integer; status: TDownLoadStatus; statusStr, extraInfoStr: string; var cancel: Boolean) of object;
  TOnDownloadEnded = procedure(Sender: TObject; aborted, Error: Boolean; errorCode: HResult; ErrorStr: string) of object;
*)

type
  TIEDownLoader = class(TComponent)
  private
    FUrl: string;
    FOnProgress: TLWDOnProgress;
    FOnBegin: TNotifyEvent;
    FOnLowResources: TNotifyEvent;
    FOnEnd: TOnDownloadEnded;
    FDestinationFolder: string;
    //      FTriggeredDownloadingEvent: boolean;
    FLWOnDownloading: TLWOnDownloading;
    FFilePath: string;
    FCaching: Boolean;
    procedure SetUrl(const Value: string);
    procedure SetFilePath(const Value: string);
    procedure SetCaching(const Value: Boolean);
  protected
    procedure doLowREsources; virtual;
    procedure doBegin; virtual;
    procedure doEnded(aborted, error: Boolean; errorCode: HResult; statusStr: string); virtual;
    procedure doProgress(position, max: Integer; status: TDownLoadStatus; statusStr, extraInfoStr: string; var cancel: Boolean); virtual;
    procedure doDownloading(size: Integer; var cancel: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    function Execute: Boolean; virtual;
  published
    //use Caching
    property Caching: Boolean read FCaching write SetCaching;
    //where to get data
    property Url: string read FUrl write SetUrl;
    //where to put it
    property DestinationFolder: string read FDestinationFolder write FDestinationFolder;
    //filepath overrides destinationFolder and the filename in the url
    property FilePath: string read FFilePath write SetFilePath;
    //process has begun - simple notification
    property OnBegin: TNotifyEvent read FOnBegin write FOnBegin;
    //is able to download (in fact is downloading like IE download dialog)
    //like IE you oppotunity to cancel (cancel:=true or exception);
    //only triggered once (at begining of download)
    property OnDownloading: TLWOnDownloading read FLWOnDownloading write FLWOnDownloading;
    //download progressing, also triggered when OnDownloading is triggered. Cancel=false by default
    property OnProgress: TLWDOnProgress read FOnProgress write FOnProgress;
    //completed : indicates success through Error parameter (file probably gets removed by windows)
    property OnEnd: TOnDownloadEnded read FOnEnd write FOnEnd;
  end;

function ExtractObjectName(url: string): string;

$EXTERNALSYM DeleteUrlCacheEntryA
function DeleteUrlCacheEntryA(pszUrl: PAnsiChar): BOOL; stdcall;
$EXTERNALSYM DeleteUrlCacheEntryW
function DeleteUrlCacheEntryW(pszUrl: PWideChar): BOOL; stdcall;
$EXTERNALSYM DeleteUrlCacheEntry
function DeleteUrlCacheEntry(pszUrl: PChar): BOOL; stdcall;

procedure Register;

implementation

uses
  HttpApp, UrlMon, SysUtils, ActiveX;

type
  TUrlCallBack = class(TInterfacedObject, IBindStatusCallBack)
  private
    FErrorCode: HResult;
    FExcepted: Boolean;
    FExceptStr: string;
    FExceptionType: ExceptClass;
    FOwner: TIEDownLoader;
    FHResultVal: HRESULT;
    FTriggeredDownloadingEvent: Boolean;
    FCaching: Boolean;
    procedure SetHResultVal(const Value: HRESULT);
    procedure handleException(e: Exception);
    procedure SetCaching(const Value: Boolean);
  public
    procedure Init(Owner: TIEDownLoader);
    function OnStartBinding(dwReserved: DWORD; pib: IBinding): HResult; stdcall;
    function GetPriority(out nPriority): HResult; stdcall;
    function OnLowResource(reserved: DWORD): HResult; stdcall;
    function OnProgress(ulProgress, ulProgressMax, ulStatusCode: ULONG; szStatusText: LPCWSTR): HResult; stdcall;
    function OnStopBinding(hresult: HResult; szError: LPCWSTR): HResult; stdcall;
    function GetBindInfo(out grfBINDF: DWORD; var bindinfo: TBindInfo): HResult; stdcall;
    function OnDataAvailable(grfBSCF: DWORD; dwSize: DWORD; formatetc: PFormatEtc; stgmed: PStgMedium): HResult; stdcall;
    function OnObjectAvailable(const iid: TGUID; punk: IUnknown): HResult; stdcall;
    property Caching: Boolean read FCaching write SetCaching;
    property ErrorCode: HResult read FErrorCode;
    property Excepted: Boolean read FExcepted;
    property ExceptStr: string read FExceptStr;
    property ExceptionType: ExceptClass read FExceptionType;
    property HResultVal: HRESULT read FHResultVal write SetHResultVal;
  end;

   TUrlCallBack 

function TUrlCallBack.GetBindInfo(out grfBINDF: DWORD; var bindinfo: TBindInfo): HResult;
begin
  Result := E_NOTIMPL;
  if not FCaching then
  begin
    DeleteUrlCacheEntry(Pointer(FOwner.FUrl));
    Result := S_OK;
  end;
end;

function TUrlCallBack.GetPriority(out nPriority): HResult;
begin
  Result := E_NOTIMPL;
end;

procedure TUrlCallBack.handleException(e: Exception);
begin
  FExcepted := True;
  FExceptStr := e.message;
  FExceptionType := ExceptClass(e.ClassType);
end;

procedure TUrlCallBack.Init(Owner: TIEDownLoader);
begin
  FOwner := owner;
end;

function TUrlCallBack.OnDataAvailable(grfBSCF, dwSize: DWORD; formatetc: PFormatEtc; stgmed: PStgMedium): HResult;
begin
  Result := E_NOTIMPL;
end;

function TUrlCallBack.OnLowResource(reserved: DWORD): HResult;
begin
  Result := S_OK;
  try
    FOwner.doLowREsources;
  except
    on e: Exception do
    begin
      handleException(e);
      Result := E_FAIL;
    end;
  end;
end;

function TUrlCallBack.OnObjectAvailable(const iid: TGUID; punk: IUnknown): HResult;
begin
  Result := E_NOTIMPL;
end;

function TUrlCallBack.OnProgress(ulProgress, ulProgressMax, ulStatusCode: ULONG; szStatusText: LPCWSTR): HResult;
var
  cancel: Boolean;
begin
  Result := s_OK;
  cancel := False;
  try
    try
      if (ulStatusCode = 5) and (not FTriggeredDownloadingEvent) then
      begin
        Fowner.doDownloading(ulProgressMax, cancel);
        FTriggeredDownloadingEvent := True;
        if cancel then
          Exit;
      end;

      if ulStatusCode > ulong(high(TDownloadStatus)) then
        ulStatusCode := 0;

      FOwner.doProgress(ulprogress, ulProgressmax, TDownloadStatus(ulstatusCode), statusStrArray[TDownloadStatus(ulstatusCode)], string(szStatusText), cancel);
    except
      on e: Exception do
      begin
        Cancel := True;
        handleException(e);
      end;
    end;
  finally
    if Cancel then
      Result := E_ABORT;
  end;
end;

function TUrlCallBack.OnStartBinding(dwReserved: DWORD; pib: IBinding): HResult;
begin
  Result := S_OK;
  try
    Fowner.doBegin;
  except
    on e: Exception do
    begin
      handleException(e);
      Result := E_INVALIDARG;
    end;
  end;
end;

function TUrlCallBack.OnStopBinding(hresult: HResult; szError: LPCWSTR): HResult;
begin
  Result := S_OK;
  FHREsultVal := HResult;
  try
    FOwner.doEnded(HResult = E_ABORT, (HResult <> 1) and (HResult <> E_ABORT), FHREsultVal, sysErrorMessage(hresult))
  except
    on e: Exception do
      HandleException(e);
  end;
end;

procedure TUrlCallBack.SetCaching(const Value: Boolean);
begin
  FCaching := Value;
end;

procedure TUrlCallBack.SetHResultVal(const Value: HRESULT);
begin
  FHResultVal := Value;
end;

 TIEDownLoader 

procedure TIEDownLoader.doEnded(Aborted, Error: Boolean; ErrorCode: HResult; StatusStr: string);
begin
  if Assigned(FOnEnd) then
    FOnEnd(Self, Aborted, Error, ErrorCode, StatusStr);
end;

procedure TIEDownLoader.doLowREsources;
begin
  if Assigned(FOnLowResources) then
    FOnLowResources(Self);
end;

procedure TIEDownLoader.doProgress(Position, Max: Integer; Status: TDownLoadStatus; StatusStr, ExtraInfoStr: string; var Cancel: Boolean);
begin
  if Assigned(FOnProgress) then
    FOnProgress(Self, Position, Max, Status, StatusStr, ExtraInfoStr, Cancel);
end;

procedure TIEDownLoader.doBegin;
begin
  if Assigned(FOnBegin) then
    FonBegin(Self);
end;

function TIEDownLoader.Execute: Boolean;
var
  HttpFields: TStringList;
  CallBack: TUrlCallBack;
  CallBackI: IBindStatusCallBack;
  hr: HRESULT;
begin
  if FDestinationFolder = '' then
    FDestinationFolder := ExtractFilePath(ParamStr(0));

  CallBackI := nil;
  HttpFields := nil;
  Result := False;
  CoInitialize(nil); //allow multithreading

  try
    try
      HttpFields := TStringList.Create;
      CallBack := TUrlCallBack.Create;
      CallBack.Caching := FCaching;
      CallBackI := CallBack; //for avoiding premature free
      CallBack.Init(Self);

      ExtractHttpFields(['/'], [], Pointer(httpDecode(Trim(FUrl))), HttpFields, False);

      if FFilePath = '' then
        hr := UrlDownloadToFile(nil, Pointer(FUrl), Pointer(IncludeTrailingPathDelimiter(Trim(FDestinationFolder)) + HttpFields[HttpFields.Count - 1]), 0, CallBackI)
      else
      begin
        SetLength(FFilePath, MAX_PATH + 1);
        hr := UrlDownloadToFile(nil, Pointer(FUrl), Pointer(FFilePath), URLOSTRM_GETNEWESTVERSION, CallBackI);
      end;

      if hr = E_OUTOFMEMORY then
        raise EOSError.Create(SysErrorMessage(hr));

      Result := hr = S_OK;

      if CallBack.excepted then
        raise CallBack.ExceptionType.Create(CallBack.FExceptStr);
    finally
      CallBackI := nil;
    end;
  finally
    HttpFields.Free;
    couninitialize;
  end;
end;

procedure TIEDownLoader.SetUrl(const Value: string);
begin
  FUrl := Value;
end;

procedure TIEDownLoader.doDownloading(size: Integer; var cancel: Boolean);
begin
  if Assigned(FLWOnDownloading) then
    FLWOnDownloading(Self, size, cancel);
end;

function ExtractObjectName(url: string): string;
var
  HttpFields: TStringList;
begin
  HttpFields := TStringList.Create;
  try
    ExtractHttpFields(['/'], [], Pointer(httpDecode(Trim(Url))), HttpFields, False);
    Result := HttpFields[HttpFields.Count - 1]
  finally
    HttpFields.Free;
  end;
end;

procedure Register;
begin
  RegisterComponents('Borrisholt', [TIEDownLoader]);
end;

procedure TIEDownLoader.SetFilePath(const Value: string);
begin
  FFilePath := Value;
end;

constructor TIEDownLoader.Create(AOwner: TComponent);
begin
  inherited;
  FDestinationFolder := '';
  FFilePath := '';
  FCaching := False;
end;

procedure TIEDownLoader.SetCaching(const Value: Boolean);
begin
  FCaching := Value;
end;

const
  WinNetLib = 'Wininet.DLL';

function DeleteUrlCacheEntryA; external WinNetLib name 'DeleteUrlCacheEntryA';
function DeleteUrlCacheEntryW; external WinNetLib name 'DeleteUrlCacheEntryW';
function DeleteUrlCacheEntry; external WinNetLib name 'DeleteUrlCacheEntry';

end.

这是我使用该组件时的她:

首先是 DFM 文件:

对象 Form1:TForm1 左 = 311 顶部 = 153 宽度 = 745 身高 = 248 颜色 = clBtnFace Font.Charset = DEFAULT_CHARSET 字体颜色 = clWindowText 字体高度 = -11 Font.Name = 'MS 无衬线' 字体样式 = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 文本高度 = 13 对象标签2:TLabel 左 = 8 顶部 = 117 宽度 = 32 高度 = 13 标题 = '标签 2' 结尾 对象标签3:TLabel 左 = 8 顶部 = 132 宽度 = 32 高度 = 13 标题 = '标签 3' 结尾 对象标签4:TLabel 左 = 8 顶部 = 147 宽度 = 32 高度 = 13 标题 = '标签 4' 结尾 对象Label12:TLabel 左 = 96 顶部 = 147 宽度 = 38 高度 = 13 标题 = '标签 12' 结尾 对象Label11:TLabel 左 = 96 顶部 = 132 宽度 = 38 高度 = 13 标题 = '标签 11' 结尾 对象Label10:TLabel 左 = 96 顶部 = 117 宽度 = 38 高度 = 13 标题 = '标签 10' 结尾 对象 ProgressBar1:TProgressBar 左 = 8 顶部 = 48 宽度 = 713 身高 = 57 平滑 = 真 TabOrder = 0 结尾 对象备忘录1:TMemo 左 = 304 顶部 = 120 宽度 = 417 身高 = 89 TabOrder = 1 结尾 对象取消:TButton 左 = 8 顶部 = 8 宽度 = 75 高度 = 25 标题 = '取消' TabOrder = 2 OnClick = CANCELClick 结尾 对象 Timer1:TTimer 间隔 = 500 OnTimer = Timer1Timer 左 = 32 顶部 = 152 结尾 结尾

然后是pas文件:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, IEDownloadFile,
  StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label12: TLabel;
    Label11: TLabel;
    Label10: TLabel;
    Memo1: TMemo;
    Timer1: TTimer;
    CANCEL: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure CANCELClick(Sender: TObject);
  private
    STime: TDateTime;
    BytesToTransfer: Integer;
    CancelTransfer : Boolean;

     Private declarations 
    procedure DownLoaderBegin(Sender : TObject);
    procedure DownLoaderDownloading(Sender: TObject; size: Integer; var cancel: Boolean);
    procedure DownLoaderProgress(Sender: TObject; position, max: Integer; status: TDownLoadStatus; statusStr, extraInfoStr: String; var cancel: Boolean);
    procedure DownLoaderDownloadEnded(Sender: TObject; aborted, Error: Boolean; errorCode: HResult; ErrorStr: String);
  public
     Public declarations 
    DownLoader : TIEDownLoader;
  end;

var
  Form1: TForm1;

implementation
uses
  Math;
$R *.DFM

var
  AverageSpeed: Double = 0;

procedure TForm1.DownLoaderBegin(Sender: TObject);
begin
  ProgressBar1.Position := 0;
  STime := Now;
  CancelTransfer := false;
end;

procedure TForm1.DownLoaderDownloadEnded(Sender: TObject; aborted, Error: Boolean; errorCode: HResult; ErrorStr: String);
begin
  Label10.Caption := '';
  Label11.Caption := '';
  Label12.Caption := '';
  ProgressBar1.Position := 0;
end;

procedure TForm1.DownLoaderDownloading(Sender: TObject; size: Integer; var cancel: Boolean);
begin
end;

function ConvertBytes(i: Int64): string;
begin
  if i < Power(1024, 1) then
    Result := IntToStr(i) + ' Bytes';

  if (i >= Power(1024, 1)) and (i < Power(1024, 2)) then
    Result := Format('%7.2f', [i / Power(1024, 1)]) + ' KB';

  if (i >= Power(1024, 2)) and (i < Power(1024, 3)) then
    Result := Format('%7.2f', [i / Power(1024, 2)]) + ' MB';

  if (i >= Power(1024, 3)) and (i < Power(1024, 4)) then
    Result := Format('%7.2f', [i / Power(1024, 3)]) + ' GB';

  Result := Trim(Result);
end;

Function FormatSeconds(TotalSeconds : Double; WholeSecondsOnly : Boolean = True; DisplayAll : Boolean = False): String;
Var
  Centuries,Years,Months,Minutes,Hours,Days,Weeks : Word;
  Secs : Double;
  TmpStr: Array[1..8] of String;
  SecondsPerCentury: Int64;
  FS : String;
begin
  (** Suppress the decimal part if Whole Seconds Only is desired **)
  If WholeSecondsOnly then
    FS:='%.0f'
  else
    FS:='%.2f';

  (** Split the calculation to avoid an overflow **)
  SecondsPerCentury:= 36500*24;
  SecondsPerCentury:= SecondsPerCentury * 3600;
  SecondsPerCentury:= SecondsPerCentury + ( 4 Leap years per century 4 * 24 * 3600);
  (** Get centuries **)
  Centuries:=Trunc(TotalSeconds / SecondsPerCentury);
  TotalSeconds:=TotalSeconds-(Centuries * SecondsPerCentury);

  (** Get years **)
  Years:=Trunc(TotalSeconds / (SecondsPerCentury / 100));
  TotalSeconds:=TotalSeconds-(Years * (SecondsPerCentury / 100));

  (** Get months **)
  Months:=Trunc(TotalSeconds / (SecondsPerCentury / 1200));
  TotalSeconds:=TotalSeconds-(Months * (SecondsPerCentury / 1200));

  (** Get weeks **)
  Weeks:=Trunc(TotalSeconds / (24 * 3600 * 7));
  TotalSeconds:=TotalSeconds-(Weeks * (24 * 3600 * 7));

  (** Get days **)
  Days:=Trunc(TotalSeconds / (24 * 3600));
  TotalSeconds:=TotalSeconds-(Days * (24 * 3600));

  (** Get Hours **)
  Hours:=Trunc(TotalSeconds / 3600);
  TotalSeconds:=TotalSeconds-(Hours * 3600);

  (** Get minutes **)
  Minutes:=Trunc(TotalSeconds / 60);
  TotalSeconds:=TotalSeconds-(Minutes * 60);

  (** Get seconds **)
  If WholeSecondsOnly then
    Secs:=Trunc(TotalSeconds)
  else
    Secs:=TotalSeconds;

  (** Deal with single values **)
  if Centuries = 1 then
    TmpStr[1] := ' Century, '
  else
    TmpStr[1] := ' Centuries, ';

  if Years = 1 then
    TmpStr[2] := ' Year, '
  else
    TmpStr[2] := ' Years, ';

  if Months = 1 then
    TmpStr[3] := ' Month, '
  else
    TmpStr[3] :=' Months, ';

  if Weeks = 1 then
    TmpStr[4] := ' Week, '
  else
    TmpStr[4] := ' Weeks, ';

  if Days = 1 then
    TmpStr[5] := ' Day, '
  else
    TmpStr[5] := ' Days, ';

  if Hours = 1 then
    TmpStr[6] :=' Hour, '
  else
    TmpStr[6]:=' Hours, ';

  if Minutes = 1 then
    TmpStr[7] :=' Minute, '
  else
    TmpStr[7]:=' Minutes, ';

  if Secs = 1 then
    TmpStr[8] :=' Second.'
  else
    TmpStr[8]:=' Seconds.';

  If DisplayAll then
   Result:= Format('%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s' + FS + '%s', [Centuries,TmpStr[1],Years,TmpStr[2],Months,TmpStr[3],Weeks,TmpStr[4],Days,TmpStr[5],Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]])
  else
    begin
      if Centuries >= 1 then
       begin
         Result:= Format('%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s' + FS + '%s', [Centuries,TmpStr[1],Years,TmpStr[2],Months,TmpStr[3],Weeks,TmpStr[4],Days,TmpStr[5],Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]]);
         Exit;
       end;

      if Years >= 1 then
       begin
         Result:= Format('%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s' + FS + '%s',[Years,TmpStr[2],Months,TmpStr[3],Weeks,TmpStr[4],Days,TmpStr[5],Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]]);
         Exit;
       end;

      if Months >= 1 then
       begin
         Result:= Format('%.0d%s%.0d%s%.0d%s%.0d%s%.0d%s' + FS + '%s',[Months,TmpStr[3],Weeks,TmpStr[4],Days,TmpStr[5],Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]]);
         Exit;
       end;

      if Weeks >= 1 then
       begin
         Result:= Format('%.0d%s%.0d%s%.0d%s%.0d%s' + FS + '%s', [Weeks,TmpStr[4],Days,TmpStr[5],Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]]);
         Exit;
       end;

      if Days >= 1 then
       begin
         Result:= Format('%.0d%s%.0d%s%.0d%s' + FS + '%s', [Days,TmpStr[5],Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]]);
         Exit;
       end;

      if Hours >= 1 then
       begin
         Result:= Format('%.0d%s%.0d%s' + FS + '%s', [Hours,TmpStr[6],Minutes,TmpStr[7],Secs,TmpStr[8]]);
         Exit;
       end;

      if Minutes >= 1 then
       begin
         Result:= Format('%.0d%s' + FS + '%s', [Minutes,TmpStr[7],Secs,TmpStr[8]]);
         exit;
       end;

      Result:= Format(FS + '%s', [Secs,TmpStr[8]]);
    end;
end;

procedure TForm1.DownLoaderProgress(Sender: TObject; Position, max: Integer; status: TDownLoadStatus; statusStr, extraInfoStr: String;  var cancel: Boolean);
Var
  S, T : String;
  TotalTime: TDateTime;
  H, M, Sec, MS: Word;
  DLTime: Double;
begin
  Cancel := CancelTransfer;

  if Cancel then
    exit;

  ProgressBar1.Max := max;
  ProgressBar1.Position := position;
  BytesToTransfer  := max;

  if BytesToTransfer = 0 then //No Update File
    exit;

  TotalTime :=  Now - STime;
  DecodeTime(TotalTime, H, M, Sec, MS);
  Sec := Sec + M * 60 + H * 3600;
  DLTime := Sec + MS / 1000;
  if DLTime > 0 then
    AverageSpeed := (Position / 1024) / DLTime;

  if AverageSpeed > 0 then
  begin
    T := Label11.Caption;
    Sec := Trunc(((BytesToTransfer - Position) / 1024) / AverageSpeed);
    S := FormatSeconds(Sec);
    Label3.Caption := 'Time remaining';
    Label11.Caption := ' : ' + s;
  end;

  Label2.Caption := 'Transfering';
  Label10.Caption := ' : ' + ConvertBytes(BytesToTransfer);
  S := FormatFloat('0 KB/s', AverageSpeed);
  Label4.Caption := 'Download speed';
  Label12.Caption := ' : ' + S;
  ProgressBar1.Position := Position;

  if Memo1.Lines.IndexOf(statusStr) = -1 then
    Memo1.Lines.Add(statusStr);

  if T <>  S then
    Application.ProcessMessages;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Label2.Caption := 'Transfering';
  Label3.Caption := 'Time remaining';
  Label4.Caption := 'Download speed';
  Label10.Caption := '';
  Label11.Caption := '';
  Label12.Caption := '';
  ProgressBar1.Position := 0;
  Timer1.Enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := false;
  DownLoader := TIEDownLoader.Create(Self);
  DownLoader.Caching := false;
  DownLoader.OnBegin := DownLoaderBegin;
  DownLoader.OnDownloading := DownLoaderDownloading;
  DownLoader.OnProgress := DownLoaderProgress;
  DownLoader.OnEnd := DownLoaderDownloadEnded;
  DownLoader.Url := 'http://download.microsoft.com/download/c/d/f/cdfd58f1-3973-4c51-8851-49ae3777586f/MDAC_TYP.EXE';
  DownLoader.Url := 'http://download.microsoft.com/download/1/2/7/127c5938-d36a-4405-9df1-f00d57495652/WindowsServer2003-KB889101-SP1-x86-ENU.exe';
  DownLoader.Execute;
  FreeAndNil(DownLoader);
end;

procedure TForm1.CANCELClick(Sender: TObject);
begin
  CancelTransFer := true;
end;

end.

【讨论】:

它正在使用 Winnet : const WinNetLib = 'Wininet.DLL'; 它可以在 Unicode 版本中使用,只需进行一些小的更改:1) 如果定义了 UNICODE,则需要将 DeleteUrlCacheEntry() 导入的 name 属性更改为 'DeleteUrlCacheEntryW''DeleteUrlCacheEntry''DeleteUrlCacheEntryA' 名称映射到同一个导出函数; 2) Pointer(FUrl) 类型转换需要改为PChar(FUrl)

以上是关于使用 winInet.h 或 urlmon.h 下载和恢复下载?的主要内容,如果未能解决你的问题,请参考以下文章

在异步模式下使用 FtpFindFirstFile unicode 版本的访问冲突

Wininet 给出链接器错误我正在使用 devc++

wininet.h 不使用带有代码块的 GNU GCC 编译器进行编译

<WinInet.h> API 中的 InternetOpenUrl 和 InternetReadFile 帮助

Frida - Windows 原生应用程序 - 读取 Wininet.h 公开的结构

Delphi WinAPI InternetGetConnectedState(wininet.h)