在不阻止GUI的情况下下载文件



我有一个记录列表。每个记录都有

url:= string

字段。通过GUI,用户可以完全编辑Revords,甚至可以完全删除记录(行)。我想在" url字段指向的所有在线文件"线程中下载。当然,当线程下载文件时,我不想锁定GUI。那么,如何确保程序/用户无法访问线程当前处理的记录?

我真的很喜欢使用位进行下载。从Delphi访问很容易。在您的定义工作中,在后台下载。准备就绪时,您可以致电EXE,您可以在闲置循环中进行轮询以获取结果,也可以获得事件。

这是一个样本 - 您需要绝地lib!该样本需要扩展以提高生产质量(错误处理,记录,作业名称)!

unit uc_DownloadBits;
interface
uses
  ExtActns;
type
  TDownloadBits = class
  public
    class procedure DownloadForground(ziel, downloadurl: WideString; DownloadFeedback:TDownloadProgressEvent);
    class procedure DownloadBackground(ziel, downloadurl, ExeName, Params: WideString);
    class procedure CompleteJob(JobId: WideString);
  end;
implementation
uses
  ComObj, ActiveX, SysUtils,
  JwaBits, JwaBits1_5, Windows;
{ TDownloadBits }
class procedure TDownloadBits.CompleteJob(JobId: WideString);
var
  bi: IBackgroundCopyManager;
  job: IBackgroundCopyJob;
  g: TGuid;
begin
  bi:=CreateComObject(CLSID_BackgroundCopyManager) as IBackgroundCopyManager;
  g:=StringToGUID(jobid);
  bi.GetJob(g,job);
  job.Complete();
end;
class procedure TDownloadBits.DownloadBackground(ziel, downloadurl,
  ExeName, Params: WideString);
var
  bi: IBackgroundCopyManager;
  job: IBackgroundCopyJob;
  job2: IBackgroundCopyJob2;
  jobId: TGUID;
  r: HRESULT;
begin
  bi:=CreateComObject(CLSID_BackgroundCopyManager) as IBackgroundCopyManager;
  r:=bi.CreateJob('Updatedownload', BG_JOB_TYPE_DOWNLOAD, JobId, job);
  if not Succeeded(r) then
    raise Exception.Create('Create Job Failed');
  r:=Job.AddFile(PWideChar(downloadurl), PWideChar(ziel));
  if not Succeeded(r) then
    raise Exception.Create('Add File Failed');
  // Download starten
  Job.Resume();  
  Params:=Params+' '+GUIDToString(jobId);
  Job2 := Job as IBackgroundCopyJob2;
  Job2.SetNotifyCmdLine(pWideChar(ExeName), PWideChar(Params));
  Job.SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED);
end;
class procedure TDownloadBits.DownloadForground(ziel, downloadurl: widestring; DownloadFeedback:TDownloadProgressEvent);
var
  bi: IBackgroundCopyManager;
  job: IBackgroundCopyJob;
  jobId: TGUID;
  r: HRESULT;
  // Status Zeug
  p: BG_JOB_PROGRESS;
  s: BG_JOB_STATE;
  // Timer Zeug
  hTimer: THandle;
  DueTime: TLargeInteger;
  c: boolean;
begin
  bi:=CreateComObject(CLSID_BackgroundCopyManager) as IBackgroundCopyManager;
  r:=bi.CreateJob('Updatedownload', BG_JOB_TYPE_DOWNLOAD, JobId, job);
  if not Succeeded(r) then
    raise Exception.Create('Create Job Failed');
  r:=Job.AddFile(PWideChar(downloadurl), PWideChar(ziel));
  if not Succeeded(r) then
    raise Exception.Create('Add File Failed');
  // Download starten
  Job.Resume();
  DueTime:=-10000000;
  hTimer:=CreateWaitableTimer(nil, false, 'EinTimer');
  SetWaitableTimer(hTimer, DueTime, 1000, nil, nil, false);
  while True do
  begin
    Job.GetState(s);
    if s in [BG_JOB_STATE_TRANSFERRING, BG_JOB_STATE_TRANSFERRED] then
    begin
      Job.GetProgress(p);
      DownloadFeedback(nil, p.BytesTransferred, p.BytesTotal, dsDownloadingData, '', c);
      if c then
        break;
    end;
    if s in [BG_JOB_STATE_TRANSFERRED,
      BG_JOB_STATE_ERROR,
      BG_JOB_STATE_TRANSIENT_ERROR] then
        break;
    WaitForSingleObject(hTimer, INFINITE);
  end;
  CancelWaitableTimer(hTimer);
  CloseHandle(hTimer);
  if s=BG_JOB_STATE_TRANSFERRED then
    job.Complete();
  job:=nil;
  bi:=nil;
end;
end.

因此,如何确保程序/用户无法访问记录 目前由线程处理?

在" Modern"(我认为Sine Delphi 2006)中,您可以与类和类一样使用getters和setter的属性。在设置器中,您可以预防或允许对基础字段进行更改。

一个天真的例子:

type
  TMyRecord = record
  private
    FURL: string;
    FDownloading: boolean;
    procedure SetTheURL(NewURL: string);
  public
    property TheURL: string read FURL write SetTheURL;
    procedure DownLoad;
  end;
procedure TMyRecord.SetTheURL(NewURL: string);
begin
  if not FDownloading then
    FURL := NewURL;
  else
    // signal inability to change
end;
procedure TMyRecord.DownLoad;
begin
  FDownLoading := True;
  // hand the downloading task to a thread
end;

这是记录(高级)下的文档

以下是基于使用记录的Tom Brunberg解决方案。记录将开始通过TThread下载的想法(我理解,下载本身的实现不可能)。可能有点粗糙,让我知道例如处理线程中是否存在严重错误。

下载时,数据无法访问,我决定在访问时提出异常,但这取决于GUI的实现详细信息。property IsDownLoading: Boolean可用于例如禁用通常也可以使数据访问的控件。

仍然可以通过用户始终更改URL,从而终止当前下载。

只有在需要时才会存在TDownloadThread。如果有很多这些记录,这将减少不需要的资源。

unit Unit1;
interface
uses
  System.Classes, System.SysUtils;
type
  TDownLoadThread = class(TThread)
  private
    FURL: string;
    FData: Variant;
    procedure SetURL(const Value: string);
  protected
    procedure Execute; override;    
  public
    property Data: Variant read FData;
    property URL: string read FURL write SetURL;
  end;
  TDownLoadRecord = record
  private
    FData: Variant;
    FURL: string;
    FDownLoadThread: TDownLoadThread;
    procedure DownLoadThreadTerminate(Sender: TObject);
    function GetIsDownLoading: Boolean;
    procedure SetURL(const Value: string);
    procedure URLChanged;
    function GetData: Variant;
  public
    property Data: Variant read GetData;  
    property URL: string read FURL write SetURL;
    property IsDownLoading: Boolean read GetIsDownLoading;
  end;
implementation
{ TDownLoadRecord }
procedure TDownLoadRecord.DownLoadThreadTerminate(Sender: TObject);
begin
  FData := FDownLoadThread.Data;
  FDownLoadThread := nil;
end;
function TDownLoadRecord.GetData: Variant;
begin
  if not IsDownLoading then
    Result := FData
  else
    raise Exception.Create('Still downloading');
end;
function TDownLoadRecord.GetIsDownLoading: Boolean;
begin
  Result := (FDownLoadThread <> nil) and not FDownLoadThread.Finished;
end;
procedure TDownLoadRecord.SetURL(const Value: string);
begin
  if FURL <> Value then
  begin
    FURL := Value;
    URLChanged;
  end;
end;
procedure TDownLoadRecord.URLChanged;
begin
  if FURL <> '' then
  begin
    if FDownLoadThread <> nil then
      TDownLoadThread.Create(True)
    else
      if not FDownLoadThread.CheckTerminated then
        FDownLoadThread.Terminate;
    FDownLoadThread.URL := FURL;
    FDownLoadThread.FreeOnTerminate := True;
    FDownLoadThread.OnTerminate := DownLoadThreadTerminate;
    FDownLoadThread.Start;
  end;
end;
{ TDownLoadThread }
procedure TDownLoadThread.Execute;
begin
  // Download
end;
procedure TDownLoadThread.SetURL(const Value: string);
begin
  FURL := Value;
end;
end.

相关内容