1
0
mirror of https://github.com/salvadordf/CEF4Delphi.git synced 2024-11-24 08:02:15 +02:00
CEF4Delphi/source/uCEFSentinel.pas
salvadordf ca8bc9dff4 Added cef4delphi.chm help file
Added the PDS file to extract the HTML Help files using PasDoc
Added more XML documentation
Fixed some XML errors.
Removed the license copy from the pas units.
Updated the LICENSE.md file
2023-08-09 19:38:57 +02:00

291 lines
8.3 KiB
ObjectPascal

unit uCEFSentinel;
{$IFDEF FPC}
{$MODE OBJFPC}{$H+}
{$ENDIF}
{$I cef.inc}
{$IFNDEF TARGET_64BITS}{$ALIGN ON}{$ENDIF}
{$MINENUMSIZE 4}
interface
uses
{$IFDEF DELPHI16_UP}
{$IFDEF MSWINDOWS}WinApi.Windows, WinApi.Messages,{$ENDIF}
System.Classes, Vcl.Controls, Vcl.ExtCtrls, System.SysUtils, System.SyncObjs, System.Math,
{$ELSE}
{$IFDEF MSWINDOWS}Windows, {$ENDIF} Classes, Controls, ExtCtrls, SysUtils, SyncObjs, Math,
{$IFDEF FPC}
LCLIntf, LResources, Forms,
{$ELSE}
Messages,
{$ENDIF}
{$ENDIF}
uCEFTypes, uCEFInterfaces, uCEFConstants;
const
CEFSENTINEL_DEFAULT_DELAYPERPROCMS = 200;
CEFSENTINEL_DEFAULT_MININITDELAYMS = 1500;
CEFSENTINEL_DEFAULT_FINALDELAYMS = 100;
CEFSENTINEL_DEFAULT_MINCHILDPROCS = 2;
CEFSENTINEL_DEFAULT_MAXCHECKCOUNTS = 10;
type
TSentinelStatus = (ssIdle, ssInitialDelay, ssCheckingChildren, ssClosing);
{$IFNDEF FPC}{$IFDEF DELPHI16_UP}[ComponentPlatformsAttribute(pfidWindows or pfidOSX or pfidLinux)]{$ENDIF}{$ENDIF}
/// <summary>
/// TCEFSentinel is used as a timer that checks the number of running
/// CEF processes when you close all browsers before shutdown.
/// This component is only used as a last resort when there's an unresolved
/// shutdown issue in CEF or CEF4Delphi that generates exceptions when the
/// application is closed.
/// </summary>
TCEFSentinel = class(TComponent)
protected
{$IFDEF MSWINDOWS}
FCompHandle : HWND;
{$ENDIF}
FStatus : TSentinelStatus;
FStatusCS : TCriticalSection;
FDelayPerProcMs : cardinal;
FMinInitDelayMs : cardinal;
FFinalDelayMs : cardinal;
FMinChildProcs : integer;
FMaxCheckCount : integer;
FCheckCount : integer;
FOnClose : TNotifyEvent;
FTimer : TTimer;
function GetStatus : TSentinelStatus;
function GetChildProcCount : integer;
{$IFDEF MSWINDOWS}
procedure WndProc(var aMessage: TMessage);
{$ENDIF}
procedure doStartMsg({$IFDEF MSWINDOWS}var aMessage : TMessage{$ELSE IFDEF FPC}Data: PtrInt{$ENDIF}); virtual;
procedure doCloseMsg({$IFDEF MSWINDOWS}var aMessage : TMessage{$ELSE IFDEF FPC}Data: PtrInt{$ENDIF}); virtual;
function SendCompMessage(aMsg : cardinal) : boolean;
function CanClose : boolean; virtual;
procedure Timer_OnTimer(Sender: TObject); virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AfterConstruction; override;
procedure Start; virtual;
property Status : TSentinelStatus read GetStatus;
property ChildProcCount : integer read GetChildProcCount;
published
property DelayPerProcMs : cardinal read FDelayPerProcMs write FDelayPerProcMs default CEFSENTINEL_DEFAULT_DELAYPERPROCMS;
property MinInitDelayMs : cardinal read FMinInitDelayMs write FMinInitDelayMs default CEFSENTINEL_DEFAULT_MININITDELAYMS;
property FinalDelayMs : cardinal read FFinalDelayMs write FFinalDelayMs default CEFSENTINEL_DEFAULT_FINALDELAYMS;
property MinChildProcs : integer read FMinChildProcs write FMinChildProcs default CEFSENTINEL_DEFAULT_MINCHILDPROCS;
property MaxCheckCount : integer read FMaxCheckCount write FMaxCheckCount default CEFSENTINEL_DEFAULT_MAXCHECKCOUNTS;
property OnClose : TNotifyEvent read FOnClose write FOnClose;
end;
{$IFDEF FPC}
procedure Register;
{$ENDIF}
implementation
uses
uCEFLibFunctions, uCEFApplicationCore, uCEFMiscFunctions;
// Attribution :
// TCEFSentinel icon made by Everaldo Coelho
// https://www.iconfinder.com/icons/17914/castle_fortress_tower_war_icon
// http://www.everaldo.com/
constructor TCEFSentinel.Create(AOwner: TComponent);
begin
inherited Create(aOwner);
{$IFDEF MSWINDOWS}
FCompHandle := 0;
{$ENDIF}
FDelayPerProcMs := CEFSENTINEL_DEFAULT_DELAYPERPROCMS;
FMinInitDelayMs := CEFSENTINEL_DEFAULT_MININITDELAYMS;
FFinalDelayMs := CEFSENTINEL_DEFAULT_FINALDELAYMS;
FMinChildProcs := CEFSENTINEL_DEFAULT_MINCHILDPROCS;
FMaxCheckCount := CEFSENTINEL_DEFAULT_MAXCHECKCOUNTS;
FOnClose := nil;
FTimer := nil;
FStatusCS := nil;
FStatus := ssIdle;
FCheckCount := 0;
end;
procedure TCEFSentinel.AfterConstruction;
begin
inherited AfterConstruction;
if not(csDesigning in ComponentState) then
begin
{$IFDEF MSWINDOWS}
FCompHandle := AllocateHWnd({$IFDEF FPC}@{$ENDIF}WndProc);
{$ENDIF}
FStatusCS := TCriticalSection.Create;
FTimer := TTimer.Create(nil);
FTimer.Enabled := False;
FTimer.OnTimer := {$IFDEF FPC}@{$ENDIF}Timer_OnTimer;
end;
end;
destructor TCEFSentinel.Destroy;
begin
try
{$IFDEF MSWINDOWS}
if (FCompHandle <> 0) then
begin
DeallocateHWnd(FCompHandle);
FCompHandle := 0;
end;
{$ENDIF}
if (FTimer <> nil) then FreeAndNil(FTimer);
if (FStatusCS <> nil) then FreeAndNil(FStatusCS);
finally
inherited Destroy;
end;
end;
{$IFDEF MSWINDOWS}
procedure TCEFSentinel.WndProc(var aMessage: TMessage);
begin
case aMessage.Msg of
CEF_SENTINEL_START : doStartMsg(aMessage);
CEF_SENTINEL_DOCLOSE : doCloseMsg(aMessage);
else aMessage.Result := DefWindowProc(FCompHandle, aMessage.Msg, aMessage.WParam, aMessage.LParam);
end;
end;
{$ENDIF}
procedure TCEFSentinel.doStartMsg({$IFDEF MSWINDOWS}var aMessage : TMessage{$ELSE IFDEF FPC}Data: PtrInt{$ENDIF});
begin
if (FTimer <> nil) then
begin
FTimer.Interval := max(ChildProcCount * CEFSENTINEL_DEFAULT_DELAYPERPROCMS, FMinInitDelayMs);
FTimer.Enabled := True;
end;
end;
procedure TCEFSentinel.doCloseMsg({$IFDEF MSWINDOWS}var aMessage : TMessage{$ELSE IFDEF FPC}Data: PtrInt{$ENDIF});
begin
if assigned(FOnClose) then FOnClose(self);
end;
function TCEFSentinel.SendCompMessage(aMsg : cardinal) : boolean;
begin
{$IFDEF MSWINDOWS}
Result := (FCompHandle <> 0) and PostMessage(FCompHandle, aMsg, 0, 0);
{$ELSE IFDEF FPC}
case aMsg of
CEF_SENTINEL_START : Application.QueueAsyncCall(@doStartMsg, 0);
CEF_SENTINEL_DOCLOSE : Application.QueueAsyncCall(@doCloseMsg, 0);
end;
{$ENDIF}
end;
procedure TCEFSentinel.Start;
begin
try
if (FStatusCS <> nil) then FStatusCS.Acquire;
if (FStatus = ssIdle) then
begin
FStatus := ssInitialDelay;
SendCompMessage(CEF_SENTINEL_START);
end;
finally
if (FStatusCS <> nil) then FStatusCS.Release;
end;
end;
function TCEFSentinel.GetStatus : TSentinelStatus;
begin
Result := ssIdle;
if (FStatusCS <> nil) then
try
FStatusCS.Acquire;
Result := FStatus;
finally
FStatusCS.Release;
end;
end;
function TCEFSentinel.GetChildProcCount : integer;
begin
if (GlobalCEFApp <> nil) then
Result := GlobalCEFApp.ChildProcessesCount
else
Result := 0;
end;
function TCEFSentinel.CanClose : boolean;
begin
Result := (FCheckCount >= FMaxCheckCount) or
(GlobalCEFApp = nil) or
(ChildProcCount <= FMinChildProcs);
end;
procedure TCEFSentinel.Timer_OnTimer(Sender: TObject);
begin
FTimer.Enabled := False;
try
if (FStatusCS <> nil) then FStatusCS.Acquire;
case FStatus of
ssInitialDelay :
if CanClose then
begin
FStatus := ssClosing;
SendCompMessage(CEF_SENTINEL_DOCLOSE);
end
else
begin
FStatus := ssCheckingChildren;
FCheckCount := 0;
FTimer.Interval := FFinalDelayMs;
FTimer.Enabled := True;
end;
ssCheckingChildren :
if CanClose then
begin
FStatus := ssClosing;
SendCompMessage(CEF_SENTINEL_DOCLOSE);
end
else
begin
inc(FCheckCount);
FTimer.Enabled := True;
end;
end;
finally
if (FStatusCS <> nil) then FStatusCS.Release;
end;
end;
{$IFDEF FPC}
procedure Register;
begin
{$I res/tcefsentinel.lrs}
RegisterComponents('Chromium', [TCEFSentinel]);
end;
{$ENDIF}
end.