title: Copying files which the main thread adds to a stringlist using a thread
body: <p>I have a web creation program which, when building a site, creates hundreds of files. </p>
  
  <p>When the internet root folder is situated on the local pc, the program runs fine. If the internet root folder is situated on a network drive, the copying of a created page takes longer than creating the page itself (the creation of the page is fairly optimized).</p>
  
  <p>I was thinking of creating the files locally, adding the names of the created files to a TStringList and let another thread copy them to the network drive (removing the copied file from the TStringList).</p>
  
  <p>Howerver, I have never, ever used threads before and I couldn't find an existing answer in the other Delphi questions involving threads (<em>if only we could use an <code>and</code> operator in the search field</em>), so I am now asking if anyone has got a working example which does this (or can point me to some article with working Delphi code) ?</p>
  
  <p>I am using Delphi 7.</p>
  
  <p>EDITED: My sample project (thx to the original code by <code>mghie</code> - who is hereby thanked once again).</p>
  
  <pre><code>  ...
    fct : TFileCopyThread;
    ...
  
    procedure TfrmMain.FormCreate(Sender: TObject);
    begin
       if not DirectoryExists(DEST_FOLDER)
       then
          MkDir(DEST_FOLDER);
       fct := TFileCopyThread.Create(Handle, DEST_FOLDER);
    end;
  
  
    procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
       FreeAndNil(fct);
    end;
  
    procedure TfrmMain.btnOpenClick(Sender: TObject);
    var sDir : string;
        Fldr : TedlFolderRtns;
        i : integer;
    begin
       if PickFolder(sDir,'')
       then begin
          // one of my components, returning a filelist [non threaded  :) ] 
          Fldr := TedlFolderRtns.Create();
          Fldr.FileList(sDir,'*.*',True);
          for i := 0 to Fldr.TotalFileCnt -1 do
          begin
             fct.AddFile( fldr.ResultList[i]);
          end;
       end;
    end;
  
    procedure TfrmMain.wmFileBeingCopied(var Msg: Tmessage);
    var s : string;
    begin
       s := fct.FileBeingCopied;
       if s &lt;&gt; ''
       then
          lbxFiles.Items.Add(fct.FileBeingCopied);
       lblFileCount.Caption := IntToStr( fct.FileCount );
    end;
  </code></pre>
  
  <p>and the unit </p>
  
  <pre><code>  unit eFileCopyThread;
    interface
    uses
       SysUtils, Classes, SyncObjs, Windows, Messages;
    const
      umFileBeingCopied = WM_USER + 1;
    type
  
      TFileCopyThread = class(TThread)
      private
        fCS: TCriticalSection;
        fDestDir: string;
        fSrcFiles: TStrings;
        fFilesEvent: TEvent;
        fShutdownEvent: TEvent;
        fFileBeingCopied: string;
        fMainWindowHandle: HWND;
        fFileCount: Integer;
        function GetFileBeingCopied: string;
      protected
        procedure Execute; override;
      public
        constructor Create(const MainWindowHandle:HWND; const ADestDir: string);
        destructor Destroy; override;
  
        procedure AddFile(const ASrcFileName: string);
        function IsCopyingFiles: boolean;
        property FileBeingCopied: string read GetFileBeingCopied;
        property FileCount: Integer read fFileCount;
      end;
  
    implementation
    constructor TFileCopyThread.Create(const MainWindowHandle:HWND;const ADestDir: string);
    begin
      inherited Create(True);
      fMainWindowHandle := MainWindowHandle;
      fCS := TCriticalSection.Create;
      fDestDir := IncludeTrailingBackslash(ADestDir);
      fSrcFiles := TStringList.Create; 
      fFilesEvent := TEvent.Create(nil, True, False, ''); 
      fShutdownEvent := TEvent.Create(nil, True, False, ''); 
      Resume; 
    end; 
  
    destructor TFileCopyThread.Destroy; 
    begin 
      if fShutdownEvent &lt;&gt; nil then 
        fShutdownEvent.SetEvent; 
      Terminate;
      WaitFor;
      FreeAndNil(fFilesEvent);
      FreeAndNil(fShutdownEvent);
      FreeAndNil(fSrcFiles);
      FreeAndNil(fCS);
      inherited;
    end;
  
    procedure TFileCopyThread.AddFile(const ASrcFileName: string);
    begin
      if ASrcFileName &lt;&gt; ''
      then begin
        fCS.Acquire;
        try
          fSrcFiles.Add(ASrcFileName);
          fFileCount := fSrcFiles.Count;
          fFilesEvent.SetEvent;
        finally
          fCS.Release;
        end;
      end;
    end;
  
    procedure TFileCopyThread.Execute;
    var
      Handles: array[0..1] of THandle;
      Res: Cardinal;
      SrcFileName, DestFileName: string;
    begin
      Handles[0] := fFilesEvent.Handle;
      Handles[1] := fShutdownEvent.Handle;
      while not Terminated do
      begin
        Res := WaitForMultipleObjects(2, @Handles[0], False, INFINITE);
        if Res = WAIT_OBJECT_0 + 1 then
          break;
        if Res = WAIT_OBJECT_0
        then begin
          while not Terminated do
          begin
            fCS.Acquire;
            try
              if fSrcFiles.Count &gt; 0
              then begin
                SrcFileName := fSrcFiles[0];
                fSrcFiles.Delete(0);
                fFileCount := fSrcFiles.Count;
                PostMessage( fMainWindowHandle,umFileBeingCopied,0,0 );
             end else
                 SrcFileName := '';
             fFileBeingCopied := SrcFileName;
              if SrcFileName = '' then
                fFilesEvent.ResetEvent;
            finally
              fCS.Release;
            end;
  
            if SrcFileName = '' then
              break;
            DestFileName := fDestDir + ExtractFileName(SrcFileName);
            CopyFile(PChar(SrcFileName), PChar(DestFileName), True);
          end;
        end;
      end;
    end;
  
    function TFileCopyThread.IsCopyingFiles: boolean;
    begin 
      fCS.Acquire; 
      try 
        Result := (fSrcFiles.Count &gt; 0) 
          // last file is still being copied 
          or (WaitForSingleObject(fFilesEvent.Handle, 0) = WAIT_OBJECT_0); 
      finally 
        fCS.Release; 
      end; 
    end; 
  
    // new version - edited after receiving comments 
    function TFileCopyThread.GetFileBeingCopied: string; 
    begin 
       fCS.Acquire; 
       try 
          Result := fFileBeingCopied; 
       finally 
          fCS.Release; 
       end; 
    end; 
  
    // old version - deleted after receiving comments 
    //function TFileCopyThread.GetFileBeingCopied: string;
    //begin
    //  Result := '';
    //  if fFileBeingCopied &lt;&gt; ''
    //  then begin
    //    fCS.Acquire;
    //    try
    //      Result := fFileBeingCopied;
    //      fFilesEvent.SetEvent;
    //    finally
    //      fCS.Release;
    //    end;
    //  end;
    //end;
  
    end.
  </code></pre>
  
  <p><strong>Any additional comments would be much appreciated.</strong> </p>
  
  <p>Reading the comments and looking at the examples, you find different approaches to the solutions, with pro and con comments on all of them. </p>
  
  <p>The problem when trying to implement a complicated new feature (as threads are to me), is that you almost always find something which seems to work ... at first. Only later on you find out the hard way that things should have been done differently. And threads are a very good example of this.</p>
  
  <p>Sites like StackOverflow are great. What a community.</p>