I am trying to write to copy a file by calling a separate thread. Here is my form code:
unit frmFileCopy; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls; type TForm2 = class(TForm) Button3: TButton; procedure Button3Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); private ThreadNumberCounter : integer; procedure HandleTerminate (Sender: Tobject); end; var Form2: TForm2; implementation uses fileThread; {$R *.dfm} { TForm2 } const sourcePath = 'source\'; //' destPath = 'dest\'; //' fileSource = 'bigFile.zip'; fileDest = 'Copy_bigFile.zip'; procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose := true; if ThreadNumberCounter >0 then begin if MessageDlg('The file is being copied. Do you want to quit?', mtWarning, [mbYes, mbNo],0) = mrNo then CanClose := false; end; end; procedure TForm2.FormCreate(Sender: TObject); begin ThreadNumberCounter := 0; end; procedure TForm2.Button3Click(Sender: TObject); var sourceF, destF : string; copyFileThread : TCopyThread; begin sourceF := ExtractFilePath(ParamStr(0)) + sourcePath + fileSource; destF := ExtractFilePath(ParamStr(0)) + sourcePath + fileDest; copyFileThread := TCopyThread.create(sourceF,destF); copyFileThread.FreeOnTerminate := True; try Inc(ThreadNumberCounter); copyFileThread.Execute; copyFileThread.OnTerminate := HandleTerminate; copyFileThread.Resume; except on Exception do begin copyFileThread.Free; ShowMessage('Error in thread'); end; end; end; procedure TForm2.HandleTerminate(Sender: Tobject); begin Dec(ThreadNumberCounter); end;
Here is my class:
unit fileThread; interface uses Classes, SysUtils; type TCopyThread = class(TThread) private FIn, FOut : string; procedure copyfile; public procedure Execute ; override; constructor create (const source, dest : string); end; implementation { TCopyThread } procedure TCopyThread.copyfile; var streamSource, streamDest : TFileStream; bIn, bOut : byte; begin streamSource := TFileStream.Create(FIn, fmOpenRead); try streamDest := TFileStream.Create(FOut,fmCreate); try streamDest.CopyFrom(streamSource,streamSource.Size); streamSource.Position := 0; streamDest.Position := 0; {check file consinstency} while not (streamSource.Position = streamDest.Size) do begin streamSource.Read(bIn, 1); streamDest.Read(bOut, 1); if bIn <> bOut then raise Exception.Create('files are different at position' + IntToStr(streamSource.Position)); end; finally streamDest.Free; end; finally streamSource.Free; end; end; constructor TCopyThread.create(const source, dest: string); begin FIn := source; FOut := dest; end; procedure TCopyThread.Execute; begin copyfile; inherited; end; end.
When I launch the application, I received the following error:
Project prjFileCopyThread an EThread exception class with the message: "Unable to call" Start a running or paused thread. "
I have no experience with threads. I use the Martin Harvey tutorial as a guide, but any advice on how to improve it, make a safe flow will be appreciated.
Based on the answers, I changed my code. This time it worked. I would appreciate it if you could review it again and tell what should be improved.
procedure TForm2.Button3Click(Sender: TObject); var sourceF, destF : string; copyFileThread : TCopyThread; begin sourceF := ExtractFilePath(ParamStr(0)) + sourcePath + fileSource; destF := ExtractFilePath(ParamStr(0)) + destPath + fileDest; copyFileThread := TCopyThread.create; try copyFileThread.InFile := sourceF; copyFileThread.OutFile := destF; except on Exception do begin copyFileThread.Free; ShowMessage('Error in thread'); end; end;
Here is my class:
type TCopyThread = class(TThread) private FIn, FOut : string; procedure setFin (const AIN : string); procedure setFOut (const AOut : string); procedure FCopyFile; protected procedure Execute ; override; public constructor Create; property InFile : string write setFin; property OutFile : string write setFOut; end; implementation { TCopyThread } procedure TCopyThread.FCopyfile; var streamSource, streamDest : TFileStream; bIn, bOut : byte; begin {removed the code to make it shorter} end; procedure TCopyThread.setFin(const AIN: string); begin FIn := AIN; end; procedure TCopyThread.setFOut(const AOut: string); begin FOut := AOut; end; constructor TCopyThread.create; begin FreeOnTerminate := True; inherited Create(FALSE); end; procedure TCopyThread.Execute; begin FCopyfile; end; end.