{--   Version 1.0                                                 --}
{--   Written By : Matthew Augier (Data Product Services)         --}
{--   Date : 30 April 96                                          --}
{--   Purpose : To create an easier to use thread                 --}
{--   Comments : Use at your own risk  !                          --}
{--   Please send comments to Compuserve 100255,531               --}
{--                                                               --}
{--   You are free to use this for any purpose                    --}
{--   If you do make any modifications, please let me know,       --}
{--   same thing if you spot a bug... Happy threading !           --}
{--                                                               --}
{--   P.S Using this will be a fraction slower than using a       --}
{--   normal thread, but it's great for beginners like me.  Mat 8-) }
{--                                                               --}
{--   Data Product Services                                       --}
{--   15 Cranleigh Court,Cove,Farnborough,Hampshire               --}
{--   England Gu14 0he. Tel / Fax 0 (+44) 1252 372140             --}

unit MaThread;

interface

Uses  Classes, SysUtils;

type
  {- Define an exception to use for this component -}
    EMaThreadException = class(Exception);
   {- Define a routine class for this component }
   TMaProcRoutine = procedure of object;
  {- Define THandle as an integer -}
   THandle = Word;
   {- Define the actual thread that will be used -}
  TMaRealThread = class(TThread)
    Private
      fVCLRoutine : TMaProcRoutine;    { users vcl routine }
       fUserProcedure : TMaProcRoutine;  { Users thread routine }
      Procedure SyncWithVCL;        { internal sync with vcl routine }
    Public
    Procedure VCLUpdate(Var VCLProc:TMaProcRoutine);  { my visual method }
     Procedure Execute; OverRide;    { my execute method }
    end;
  {- Define the non-visual component, that the user will use/see -}
  TMaThread = class(TComponent)
    private
    { Private declarations }
      fActualThread : TMaRealThread;
      fActualThreadCreated : Boolean;
      fPriority : TThreadPriority;
    fOnStart : TNotifyEvent;
       fOnTerminate : TNotifyEvent;
       fOnThread : TThreadMethod;
       fSuspended : Boolean;
    protected
    { Protected declarations }
      Constructor Create(AOwner : TComponent);Override;
      Destructor Destroy; Override;
       Procedure SetPriority(NewPriority:TThreadPriority);
       Procedure SetSuspended(NewValue:Boolean);
    public
    { Public declarations }
    Procedure VCLUpdate(UserProc:TMaProcRoutine);  { Update screen }
       Procedure Execute;              { Start the thread }
       Procedure Terminate;            { Kill thread }
       Procedure Suspend;              { Suspend thread - note you need to resume as many times }
       Procedure Resume;              { as you have suspended (nested) }
       Function GetThreadHandle : tHandle;      { allows you to get access to the actual thread handle }
       Function GetThreadID : tHandle;        { allows you access to the actual threads id }
    published
    { Published declarations }
      { your routine - called over and over again til terminated don't loop your routine internally !}
       Property OnBeforeThread : TNotifyEvent Read fOnStart Write fOnStart;
      { your routine - called over and over again til terminated don't loop your routine internally !}
       Property OnThread : TThreadMethod Read fOnThread Write fOnThread;
    { once the thread is completed (terminated) this is run if set }
       Property OnAfterThread : TNotifyEvent Read fOnTerminate Write fOnTerminate;
       { allows you to change the priority of the thread }
       Property Priority : TThreadPriority Read fPriority Write SetPriority Default tpNormal;
       { allows you to change it to suspended or not }
       Property Suspended : Boolean Read fSuspended Write SetSuspended Default False;
  end;

procedure Register;

implementation
{------------------------------------------------------------------------------}
{- tMAThreadClass                                                             -}
{------------------------------------------------------------------------------}
Procedure TMaRealThread.Execute;
begin
  While Not Terminated do fUserProcedure;
end;
{------------------------------------------------------------------------------}
Procedure TMaRealThread.VCLUpdate(Var VCLProc:TMaProcRoutine);
begin
  fVCLRoutine := VCLProc;
  Synchronize(SyncWithVCL);
end;
{------------------------------------------------------------------------------}
Procedure TMaRealThread.SyncWithVCL;
begin
  fVCLRoutine;
end;
{------------------------------------------------------------------------------}
{- tMAThread                                                                  -}
{------------------------------------------------------------------------------}
Constructor TMAThread.Create(AOwner:TComponent);
begin
  inherited Create(AOwner);
   fPriority := tpNormal;
   fSuspended := False;
end;
{------------------------------------------------------------------------------}
Destructor  TMaThread.Destroy;
begin
  If fActualThreadCreated then begin
     { Don't run event if form is being destroyed ! }
       OnAfterThread := Nil;
     Terminate;
       end;
  inherited Destroy;
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.VCLUpdate(UserProc:TMaProcRoutine);
begin
  fActualThread.VCLUpdate(UserProc);
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.SetPriority(NewPriority:TThreadPriority);
begin
  fPriority := NewPriority;
  If csDesigning in ComponentState then Exit;
  If not fActualThreadCreated then Exit;
   fActualThread.Priority := fPriority;
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.Execute;
begin
  If Not Assigned(OnThread) then begin
     Raise EMaThreadException.Create('Nothing For The Thread To Run');
       Exit;
       end;
  If fActualThreadCreated then begin
     Raise EMaThreadException.Create('Thread Already Started');
       exit;
       end;
  If Assigned(OnBeforeThread) then fOnStart(self);
  fActualThread := TMaRealThread.Create(False);
  fActualThread.fUserProcedure := OnThread;
  fActualThread.FreeOnTerminate := True;
  fActualThreadCreated := True;
   fActualThread.Priority := Priority;
   fActualThread.Suspended := Suspended;
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.Terminate;
begin
  If Not fActualThreadCreated then begin
     Raise EMaThreadException.Create('Thread Has Not Been Started');
       exit;
       end;
   { in case it is suspended remove all before terminate }
   While fActualThread.Suspended do fActualThread.Resume;
  FActualThread.Terminate;
  fActualThreadCreated := False;
   If Assigned(OnAfterThread) then OnAfterThread(Self);
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.Suspend;
begin
  fSuspended := True;
  If not fActualThreadCreated then Exit;
  fActualThread.Suspend;
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.Resume;
begin
  If not fActualThreadCreated then begin
     fSuspended := False;
     Exit;
       end;
  fActualThread.Resume;
   fSuspended := fActualThread.Suspended;
end;
{------------------------------------------------------------------------------}
Procedure TMAThread.SetSuspended(NewValue:Boolean);
begin
  fSuspended := NewValue;
  If csDesigning in ComponentState then Exit;
  If not fActualThreadCreated then Exit;
   Suspend;
end;
{------------------------------------------------------------------------------}
Function TMAThread.GetThreadHandle : THandle;
begin
  Result := 0;
  If not fActualThreadCreated then Exit;
  Result := fActualThread.Handle;
end;
{------------------------------------------------------------------------------}
Function TMaThread.GetThreadID : THandle;
begin
  Result := 0;
  If not fActualThreadCreated then Exit;
  Result := fActualThread.ThreadId;
end;
{------------------------------------------------------------------------------}
{- Registration Time                                                          -}
{------------------------------------------------------------------------------}
procedure Register;
begin
  RegisterComponents('Custom', [TMaThread]);
end;
{------------------------------------------------------------------------------}
end.

Here is some more information about this component:

Properties
Name   String, Standard component name.

Suspended  Boolean, Allows you to suspend or resume a thread whilst running, or before it has started (see also : Methods Suspend/Resume).

Priority  TThreadPriority, Allows you to change to level at which this thread will run at, the options are (in lowest to highest order) : tpIdle, tpLowest, tpLower, tpNormal (Default), tpHigher, tpHighest, tpTimeCritical.

Tag   Integer, Standard component tag.

Methods
Execute  Starts (and creates) the actual thread running, if the OnThread has not been assigned, an exception will occur, if the thread has already started then an exception will occur.

Terminate  Stops (and frees) the actual thread. If the thread is not running then an exception will occur.

Suspend  Temporarily Stops the thread , this is a nested call, so for each time you call suspend, you must call a resume (calling suspend twice, then calling resume 1 will leave the thread suspended, 2-1=1 !).

Resume  Starts a temporarily stopped thread, see Suspend.

GetThreadHandle Returns the actual threads handle (tHandle)

GetThreadId  Returns the actual thread Id (THandle)

VCLUpdate(UserProc:TProcRoutine);
   Users routine to update a vcl component, send with an update routine in it.

Events
OnAfterThread Optional, allows you to do something after the thread has finished (terminated), show results etc.

OnBeforeThread Optional, allows you to do something before the thread starts, so you can reset counters etc.

OnThread  The actual thread - Single cycle only... The thread will automatically call this routine, forever until you terminate,  or the thread terminates itself (see demo program)