Reputation: 315
I keep getting "Thread error: The handle is invalid (6)" when I attempt to use this thread but I can't see the problem. Please help if you can, thanks!
stackoverlow is complaining that I didn't explain this enough. So, I created a thread class which compiles without any errors but when I invoke it with execute it seems to run ok but throws this error in the destructor.
tdownloadthread = class(tthread)
private
furl,
ffilename,
fmsg: string;
fdl: tidhttp;
readings,bpstotal,avgbps: int64;
fpercent: word;
fsuccess,fcanceled: boolean;
start: tdatetime;
fspeed,fremaining: string;
public
constructor create(url,filename: string);
destructor destroy; override;
procedure execute; override;
procedure DlWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
procedure cancel;
property success: boolean read fsuccess;
property canceled: boolean read fcanceled;
property speed: string read fspeed;
property percent: word read fpercent;
property remaining: string read fremaining;
property msg: string read fmsg;
end;
constructor tdownloadthread.create(url,filename: string);
begin
fsuccess:=false;
fcanceled:=false;
fdl:=tidhttp.Create(nil);
fdl.OnWork:=dlwork;
fdl.HandleRedirects:=true;
furl:=url;
ffilename:=filename;
freeonterminate:=false;
end;
destructor tdownloadthread.Destroy;
begin
fdl.Free;
end;
procedure tdownloadthread.cancel;
begin
fdl.Disconnect;
fcanceled:=true;
end;
procedure tdownloadthread.DlWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
Http: TIdHTTP;
ContentLength: Int64;
i,h,m,esec,sec,rsec,bps: Integer;
f: tdatetime;
begin
Http := TIdHTTP(ASender);
ContentLength := Http.Response.ContentLength;
if (Pos('chunked', LowerCase(Http.Response.ResponseText)) = 0) and
(ContentLength > 0) then
begin
fpercent := trunc(100*(AWorkCount / ContentLength));
esec := secondsbetween(now, start);
if (avgbps > 0) then
begin
i:=contentlength div avgbps;
f:=incsecond(now, i);
rsec:=secondsbetween(now, f);
sec:=rsec - esec;
h:=sec div 3600;
m:=sec div 60 - H * 60;
sec:=sec - (H * 3600 + M * 60) ;
end;
if (esec > 0) then
bps:=(aworkcount div esec)
else
bps:=0;
inc(readings);
inc(bpstotal, bps);
avgbps:=bpstotal div readings;
if (avgbps / 1024 < 1024) then
fspeed:=format('%2f KB/s', [avgbps / 1024])
else
fspeed:=format('%2f MB/s', [(avgbps div 1024) / 1024]);
fremaining:=format('%s m %s s', [zero(m), zero(secno)]);
end;
end;
procedure tdownloadthread.Execute;
var fn,cd: string;
data: tmemorystream;
begin
try
start:=now;
data:=tmemorystream.Create;
fdl.Get(furl, data);
cd:=fdl.Response.ContentDisposition;
if (pos('filename=', cd) > 0) then
fn:=extractfilepath(paramstr(0))+copy(cd, pos('=', cd)+1, length(cd))
else
fn:=ffilename;
data.SaveToFile(fn);
ffilename:=fn;
fsuccess:=true;
data.Free;
except
on e: exception do
begin
fsuccess:=false;
fmsg:=e.Message;
end;
end;
end;
procedure testdownload;
var downloadthread: tdownloadthread;
begin
downloadthread:=tdownloadthread.create('http://www.someurl.com/filename.old',extractfilepath(paramstr(0))+'filename.new');
downloadthread.Execute;
if downloadthread.canceled then showmessage('canceled');
if downloadthread.success then showmessage('success');
if not downloadthread.success then showmessage('error: '+downloadthread.msg);
downloadthread.Free;
end;
Upvotes: 0
Views: 2040
Reputation: 24857
You forgot to call the inherited ctor for TThread:
constructor tdownloadthread.create(url,filename: string);
begin
inherited create(true); // construct TThread
fsuccess:=false;
fcanceled:=false;
fdl:=tidhttp.Create(nil);
fdl.OnWork:=dlwork;
fdl.HandleRedirects:=true;
furl:=url;
ffilename:=filename;
freeonterminate:=false;
resume; // actually run thread
end;
Also, as poinnted out by @LU RD, there is no need to directly call the Execute() method of the TThread instance - the thread is given execution directly by the OS.
As for freeing it, there are some choices. In order of preference:
1) Don't free it at all. If you intend to make more than one download during your app run, loop the thread round a producer-consumer queue pop, so that the thread can be re-used to download more files without the misery of trying to free it at all.
2) Let the thread instance free itself, (freeOnTerminate=true) after freeing its internal resources, (eg. the TidHTTP instance), in a try/finally.
-999999) Use an OnTerminate handler or any of the other horrible Delphi TThread stuff like TThread.WaitFor.
Upvotes: 4