Francois Piette wrote:
>>>> Don't use TTimer in any worker thread since it is NOT thread-safe!
>>> 
>>> Why is it used in HttpCli then ?
>>> see THttpCli.SendRequest
>> 
>> That's true, I would not define UseBandwidthControl in multithreaded
>> applications until a thread-safe timer becomes available.
> 
> We could use a bare bone API timer instead.
> What do you think ?

Something like attached below? It creates its window thread-save,
but I prefer a timer that was able to use the hidden window(s)
of V6, or may be we think about a windowless timer (Thread, signals,
and WaitForMultipleObjects)? 

--
Arno Garrels [TeamICS]
http://www.overbyte.be/eng/overbyte/teamics.html


-----------------------------------------------------------------------
unit IcsTimers;

interface

uses
  Windows, Messages, Sysutils, Consts, Classes, Forms;

type
  EIcsTimerException = class(Exception);
  TIcsTimer = class(TComponent)
  private
    FInterval: Cardinal;
    FOnTimer: TNotifyEvent;
    FEnabled: Boolean;
    FWindowHandle : Hwnd;
    procedure UpdateTimer;
    procedure SetInterval(const Value: Cardinal);
    procedure SetOnTimer(Value: TNotifyEvent);
    procedure SetEnabled(const Value: Boolean);
    procedure AllocateHwnd;
    procedure DeallocateHwnd;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Enabled: Boolean read FEnabled write SetEnabled default True;
    property Interval: Cardinal read FInterval write SetInterval default 1000;
    property OnTimer: TNotifyEvent read FOnTimer write SetOnTimer;
  end;

var
  CritSectWndClass: TRtlCriticalSection;
  WndCnt: Integer = 0;

implementation

procedure Register;
begin
    RegisterComponents('FPiette', [TIcsTimer]);
end;

const
  WndClassName: PChar = 'OverbyteIcsTimerWndClass';

{ TIcsTimer }

function TimerWndProc(aWnd: HWND; aMsg : Integer; aWParam : WPARAM;
  aLParam : LPARAM): Integer; stdcall;
var
  Obj : TObject;
begin
  if (aMsg <> WM_TIMER) then
    Result := DefWindowProc(aWnd, aMsg, aWParam, aLParam)
  else begin
    Obj := TObject(aWParam);
    if (Obj is TIcsTimer) then
    begin
      Result := 0;
      try
        if Assigned(TIcsTimer(Obj).OnTimer) then TIcsTimer(Obj).OnTimer(Obj);
      except
        Application.HandleException(Obj);
      end
    end
    else
      Result := DefWindowProc(aWnd, aMsg, aWParam, aLParam)
  end;
end;

procedure TIcsTimer.AllocateHwnd;
var
  WndClass: TWndClass;
  Res: Hwnd;
begin
  EnterCriticalSection(CritSectWndClass);
  try
    if FWindowHandle <> 0 then Exit;
    if not GetClassInfo(HInstance, WndClassName, WndClass) then
    begin
      ZeroMemory(@WndClass, SizeOf(TWndClass));
      with WndClass do
      begin
        lpfnWndProc := @TimerWndProc;
        cbWndExtra := SizeOf(Pointer);
        hInstance := SysInit.HInstance;
        lpszClassName := WndClassName;
      end;
      Res := Windows.RegisterClass(WndClass);
      if Res = 0 then
      begin
        Res := GetLastError;
        raise EIcsTimerException.CreateFmt('RegisterClass failed. Error #%d %s',
                                          [Res, SysErrorMessage(Res)]);
      end;
    end;
    Res := CreateWindowEx(WS_EX_TOOLWINDOW, WndClassName,
                          '', WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, nil);
    if Res = 0 then
    begin
      Res := GetLastError;
      raise EIcsTimerException.CreateFmt('CreateWindowEx failed. Error #%d %s',
                                         [Res, SysErrorMessage(Res)]);
    end;
    Inc(WndCnt);
    FWindowHandle := Res;
  finally
    LeaveCriticalSection(CritSectWndClass);
  end;
end;

procedure TIcsTimer.DeallocateHwnd;
begin
  EnterCriticalSection(CritSectWndClass);
  try
    if FWindowHandle = 0 then Exit;
    DestroyWindow(FWindowHandle);
    Dec(WndCnt);
    if WndCnt <= 0 then
      Windows.UnregisterClass(WndClassName, HInstance);
  finally
    LeaveCriticalSection(CritSectWndClass);
  end;
end;

constructor TIcsTimer.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEnabled := True;
  FInterval := 1000;
  AllocateHWnd;
end;

destructor TIcsTimer.Destroy;
begin
  FEnabled := False;
  UpdateTimer;
  DeallocateHWnd;
  inherited Destroy;
end;

procedure TIcsTimer.SetEnabled(const Value: Boolean);
begin
  if Value <> FEnabled then
  begin
    FEnabled := Value;
    UpdateTimer;
  end;
end;

procedure TIcsTimer.SetInterval(const Value: Cardinal);
begin
  if Value <> FInterval then
  begin
    FInterval := Value;
    UpdateTimer;
  end;
end;

procedure TIcsTimer.SetOnTimer(Value: TNotifyEvent);
begin
  FOnTimer := Value;
  UpdateTimer;
end;

procedure TIcsTimer.UpdateTimer;
begin
  KillTimer(FWindowHandle, Cardinal(Self));
  if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then
    if SetTimer(FWindowHandle, Cardinal(Self), FInterval, nil) = 0 then
      raise EOutOfResources.Create(SNoTimers);
end;

initialization
  InitializeCriticalSection(CritSectWndClass);

finalization
  DeleteCriticalSection(CritSectWndClass);
end.

-----------------------------------------------------------------------


-- 
To unsubscribe or change your settings for TWSocket mailing list
please goto http://www.elists.org/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be

Reply via email to