{******************************************} { } { FastReport VCL } { GUI Thread Synchonization } { } { Copyright (c) 1998-2021 } { by Fast Reports Inc. } { } {******************************************} unit frxThreading; interface {$I frx.inc} uses {$IFNDEF FPC} Windows, Messages, {$ENDIF} {$IFDEF FPC} LResources, LCLType, LazHelper, LMessages, {$ENDIF} SysUtils, Classes; {$IFNDEF FPC} const WM_FRX_SYNC_THREAD = WM_USER + 250; WM_FRX_SYNC_MESSAGE = WM_USER + 251; type TfrxThreadSynchronizer = class private FIsMain: Boolean; FWindowHandle: HWND; procedure WndProc(var Message: TMessage); public constructor Create; destructor Destroy; override; property WindowHandle: HWND read FWindowHandle; end; TfrxGuiThread = class(TThread) protected procedure Execute; override; public constructor Create; destructor Destroy; override; end; function GetThreadSynchronizer: TfrxThreadSynchronizer; function IsThreadSynchronizerActive: Boolean; function frxSynchSendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; {$ENDIF} procedure frxThreadSynchronize(Method: TThreadMethod); {$IFNDEF FPC} var frxDisableThreadSynchronizer: Boolean; {$ENDIF} implementation {$IFNDEF FPC} uses Forms, frxClass; var frxThreadSynchronizer: TfrxThreadSynchronizer; frxGuiThread: TfrxGuiThread; ThreadRunEvent: THandle; type PTThreadMethod = ^TThreadMethod; procedure InitSynchronizer; begin if Assigned(frxThreadSynchronizer) and (not frxThreadSynchronizer.FIsMain) or (Application.Handle <> 0) or not IsLibrary then Exit; if (Application.Handle = 0) and (frxGUIThreadID <> GetCurrentThreadID) then begin FreeAndNil(frxThreadSynchronizer); frxGuiThread := TfrxGuiThread.Create; ThreadRunEvent := CreateEvent(nil, true, false, 'FRX_GUI_THREAD_R'); frxGuiThread.Resume; WaitForSingleObject(ThreadRunEvent, 100000); CloseHandle(ThreadRunEvent); end; end; { TfrxThreadSynchronizer } {$WARNINGS OFF} constructor TfrxThreadSynchronizer.Create; begin FWindowHandle := AllocateHWnd(WndProc); end; destructor TfrxThreadSynchronizer.Destroy; begin DeallocateHWnd(FWindowHandle); end; {$WARNINGS ON} procedure TfrxThreadSynchronizer.WndProc(var Message: TMessage); var lMessage: PMessage; begin if Message.Msg = WM_FRX_SYNC_THREAD then begin if Message.WParam <> 0 then PTThreadMethod(Message.WParam)^(); end else if Message.Msg = WM_FRX_SYNC_MESSAGE then begin lMessage := PMessage(Message.WParam); lMessage^.Result := SendMessage(Message.LParam, lMessage^.Msg, lMessage^.WParam, lMessage^.LParam); end else Message.Result := DefWindowProc(FWindowHandle, Message.Msg, Message.wParam, Message.lParam); end; function GetThreadSynchronizer: TfrxThreadSynchronizer; begin Result := frxThreadSynchronizer; end; function IsThreadSynchronizerActive: Boolean; begin Result := Assigned(frxThreadSynchronizer); end; function frxSynchSendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; var lMessage: TMessage; begin lMessage.Msg := Msg; lMessage.WParam := wParam; lMessage.LParam := lParam; lMessage.Result := 0; Result := 0; if Assigned(frxThreadSynchronizer) then Result := SendMessage(frxThreadSynchronizer.WindowHandle, WM_FRX_SYNC_MESSAGE, NativeInt(@lMessage), NativeInt(hWnd)); end; procedure frxThreadSynchronize(Method: TThreadMethod); begin if frxDisableThreadSynchronizer and Assigned(Method) then begin Method; Exit; end; InitSynchronizer; if not Assigned(Method) then Exit; if Assigned(frxThreadSynchronizer) and (frxThreadSynchronizer.WindowHandle <> 0) then SendMessage(frxThreadSynchronizer.WindowHandle, WM_FRX_SYNC_THREAD, NativeInt(@TMethod(Method)), 0) else if (Application.Handle = 0) or (frxGUIThreadID = GetCurrentThreadID) then Method else TThread.Synchronize(nil, Method); end; { TTestThread } constructor TfrxGuiThread.Create; begin inherited Create(True); end; destructor TfrxGuiThread.Destroy; begin Terminate; if Assigned(frxThreadSynchronizer) then SendMessage(frxThreadSynchronizer.FWindowHandle, WM_QUIT, 0, 0); inherited; end; procedure TfrxGuiThread.Execute; var Msg: TMsg; ThSynch: TfrxThreadSynchronizer; IsUnicode, IsMsgExists: Boolean; begin ThSynch := TfrxThreadSynchronizer.Create; {$IFDEF DELPHI16} InterlockedExchangePointer(Pointer(frxThreadSynchronizer), ThSynch); {$ELSE} InterlockedExchange(frxInteger(frxThreadSynchronizer), frxInteger(ThSynch)); {$ENDIF} {$IFDEF MSWINDOWS} InterlockedExchange(Integer(frxGUIThreadID), GetCurrentThreadID); {$ENDIF} {$IFDEF POSIX} InterlockedExchange64(Int64(frxGUIThreadID), GetCurrentThreadID); {$ENDIF} SetEvent(ThreadRunEvent); while not Terminated and (ThSynch.FWindowHandle <> 0) do begin if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin IsUnicode := (Msg.hwnd = 0) or IsWindowUnicode(Msg.hwnd); if IsUnicode then IsMsgExists := PeekMessageW(Msg, 0, 0, 0, PM_REMOVE) else IsMsgExists := PeekMessageA(Msg, 0, 0, 0, PM_REMOVE); if IsMsgExists then begin if Msg.Message = WM_QUIT then Break else begin TranslateMessage(Msg); if IsUnicode then DispatchMessageW(Msg) else DispatchMessageA(Msg); end; end end else if not Terminated then WaitMessage; end; {$IFDEF DELPHI16} InterlockedExchangePointer(Pointer(frxThreadSynchronizer), nil); {$ELSE} InterlockedExchange(frxInteger(frxThreadSynchronizer), 0); {$ENDIF} FreeAndNil(ThSynch); end; initialization frxDisableThreadSynchronizer := False; if IsLibrary then begin frxThreadSynchronizer := TfrxThreadSynchronizer.Create; frxThreadSynchronizer.FIsMain := True; end; frxGuiThread := nil; finalization FreeAndNil(frxGuiThread); FreeAndNil(frxThreadSynchronizer); {$ELSE} procedure frxThreadSynchronize(Method: TThreadMethod); inline; begin Method; end; {$ENDIF} end.