Walid
Walid

Reputation: 197

Loop preventing event from firing

This code is a solution suggested for another problem. When I try to run it the event in my code isn't firing.
The loop is preventing event from firing.

Any suggestions on how to avoid this problem and allow event to fire ? If I remove the loop the event fires as expected. I need to make sure the event fires (or times out) before I proceed. Any suggestions ?

Thank you.

uses
 ..., DateUtils, SyncObjs;

type
  MyClass = class
  private
    doneEvent: TEvent;
    procedure COMEventHandler(parameters);
    procedure Loop(bWait: Boolean);
    ...
  public
    constructor Create;
    destructor Destroy; override;
   procedure DoIt;
  end;

constructor MyClass.Create;
begin
  inherited;
  ...
 doneEvent := TEvent.Create(True);
end;

destructor MyClass.Destroy;
begin
  ...
  doneEvent.Free;
  inherited;
end;

procedure MyClass.COMEventHandler(parameters);
begin
  doneEvent.SetEvent;
end;

procedure MyClass.Loop(bWait: Boolean);
var
  s: TDateTime;
begin
  if not bWait then Exit;
  try
    doneEvent.ResetEvent;
    s := Now;

    repeat
      case doneEvent.WaitFor(30) of
        wrSignaled: begin
          // Event fired, all good=> exit
          Break;
        end;
        wrTimeout: begin
          if MillisecondsBetween(Now, s) > (1000 * 1000) then
          begin
            // Timed out=> exit
            Break;  
          end;
          if GetQueueStatus(QS_ALLINPUT) <> 0 then
            Application.ProcessMessages;
        end;
        wrError: begin
          RaiseLastOSError(doneEvent.LastError);
        end;
      end;
    until False;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end;

procedure MyClass.DoIt;
begin
  // invoke COM function that will eventually trigger the COM event...
  Loop(True); // wait for event to fire or timer to elapse...
  ...
end;

Upvotes: 0

Views: 384

Answers (2)

Pavel Minenkov
Pavel Minenkov

Reputation: 403

Your approach based on inserting long time peocedures to MAinThread loop. Take a look to my examples:

Timer:

Timer.Interval := <event check timeout>

....

procedure MyClass.OnTimer(Sender: TObject)
begin
  case doneEvent.WaitFor(0) of
    wrSignaled: ...
    wrTimeout: ...
    wrError: ...
  end
end

Thread:

TEventCheckThread = class(TThread)
protected
  ... 
  procedure Execute; override;
  // Callback to be fired in Main thread if event is OK
  procedure ExecuteInMainThread;
  ...
end;
...
procedure TEventCheckThread.Execute;
begin
  while not Terminated do
  begin
    case doneEvent.WaitFor(TIMEOUT) of
      wrSignaled: 
      begin
        Synchronize(ExecuteInMainThread)
      end;

      wrTimeout: ...

      wrError: ...

    end
  end;
end;
...

Upvotes: -1

Stijn Sanders
Stijn Sanders

Reputation: 36850

Instead of a loop like this, I would strongly suggest you investigate one or more of these options:

  • TApplicationEvents.OnIdle
  • TTimer.OnTimer
  • threads, either by inheriting from TThread yourself, or one of the new threading methods (which I haven't worked with myself yet as I've done most work with threading on older Delphi versions),
  • one of the many third party solutions that provide a ready-made threading-framework to add your work to, e.g. OmniThreadLibrary

Upvotes: 4

Related Questions