unit CompThread; interface {$WARN SYMBOL_DEPRECATED OFF} {$WARN SYMBOL_PLATFORM OFF} uses Classes, Windows; type { TCompThread } TCompThread = class private FHandle: THandle; FThreadID: TThreadID; FTerminated: Boolean; FSuspended: Boolean; FFreeOnTerminate: Boolean; FFinished: Boolean; FReturnValue: Integer; FOnTerminate: TNotifyEvent; FMethod: TThreadMethod; FSynchronizeException: TObject; procedure CallOnTerminate; function GetPriority: TThreadPriority; procedure SetPriority(Value: TThreadPriority); procedure SetSuspended(Value: Boolean); protected procedure DoTerminate; virtual; procedure Execute; virtual; abstract; procedure Synchronize(Method: TThreadMethod); property ReturnValue: Integer read FReturnValue write FReturnValue; property Terminated: Boolean read FTerminated; public constructor Create(CreateSuspended: Boolean); destructor Destroy; override; procedure Resume; procedure Suspend; procedure Terminate; virtual; function WaitFor(Milliseconds: Cardinal = INFINITE): Boolean; property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate; property Handle: THandle read FHandle; property Priority: TThreadPriority read GetPriority write SetPriority; property Suspended: Boolean read FSuspended write SetSuspended; property ThreadID: TThreadID read FThreadID; property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate; end; implementation uses SysUtils, DateUtils; const CM_EXECPROC = $8FFF; CM_DESTROYWINDOW = $8FFE; type PRaiseFrame = ^TRaiseFrame; TRaiseFrame = record NextRaise: PRaiseFrame; ExceptAddr: Pointer; ExceptObject: TObject; ExceptionRecord: PExceptionRecord; end; var ThreadLock: TRTLCriticalSection; ThreadWindow: HWND; ThreadCount: Integer; procedure FreeThreadWindow; begin if ThreadWindow <> 0 then begin DestroyWindow(ThreadWindow); ThreadWindow := 0; end; end; function ThreadWndProc(Window: HWND; Message, wParam, lParam: Longint): Longint; stdcall; begin case Message of CM_EXECPROC: with TCompThread(lParam) do begin Result := 0; try FSynchronizeException := nil; FMethod; except FSynchronizeException := AcquireExceptionObject; end; end; CM_DESTROYWINDOW: begin EnterCriticalSection(ThreadLock); try Dec(ThreadCount); if ThreadCount = 0 then FreeThreadWindow; finally LeaveCriticalSection(ThreadLock); end; Result := 0; end; else Result := DefWindowProc(Window, Message, wParam, lParam); end; end; var ThreadWindowClass: TWndClass = ( style: 0; lpfnWndProc: @ThreadWndProc; cbClsExtra: 0; cbWndExtra: 0; hInstance: 0; hIcon: 0; hCursor: 0; hbrBackground: 0; lpszMenuName: nil; lpszClassName: 'TThreadWindow'); procedure AddThread; function AllocateWindow: HWND; var TempClass: TWndClass; ClassRegistered: Boolean; begin ThreadWindowClass.hInstance := HInstance; ClassRegistered := GetClassInfo(HInstance, ThreadWindowClass.lpszClassName, TempClass); if not ClassRegistered or (TempClass.lpfnWndProc <> @ThreadWndProc) then begin if ClassRegistered then Windows.UnregisterClass(ThreadWindowClass.lpszClassName, HInstance); Windows.RegisterClass(ThreadWindowClass); end; Result := CreateWindow(ThreadWindowClass.lpszClassName, '', 0, 0, 0, 0, 0, 0, 0, HInstance, nil); end; begin EnterCriticalSection(ThreadLock); try if ThreadCount = 0 then ThreadWindow := AllocateWindow; Inc(ThreadCount); finally LeaveCriticalSection(ThreadLock); end; end; procedure RemoveThread; begin EnterCriticalSection(ThreadLock); try if ThreadCount = 1 then PostMessage(ThreadWindow, CM_DESTROYWINDOW, 0, 0); finally LeaveCriticalSection(ThreadLock); end; end; {type PThreadRec = ^TThreadRec; TThreadRec = record Func: TThreadFunc; Parameter: Pointer; end; function ThreadWrapper(Parameter: Pointer): Integer; stdcall; asm CALL _FpuInit XOR ECX,ECX PUSH EBP PUSH offset _ExceptionHandler MOV EDX,FS:[ECX] PUSH EDX MOV EAX,Parameter MOV FS:[ECX],ESP MOV ECX,[EAX].TThreadRec.Parameter MOV EDX,[EAX].TThreadRec.Func PUSH ECX PUSH EDX CALL _FreeMem POP EDX POP EAX CALL EDX XOR EDX,EDX POP ECX MOV FS:[EDX],ECX POP ECX POP EBP end; function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord; ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; var ThreadId: LongWord): Integer; var P: PThreadRec; begin New(P); P.Func := ThreadFunc; P.Parameter := Parameter; IsMultiThread := TRUE; Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P, CreationFlags, ThreadID); end; procedure EndThread(ExitCode: Integer); begin ExitThread(ExitCode); end; } { TCompThread } function ThreadProc(Thread: TCompThread): Integer; var FreeThread: Boolean; begin try Thread.Execute; finally FreeThread := Thread.FFreeOnTerminate; Result := Thread.FReturnValue; Thread.FFinished := True; Thread.DoTerminate; if FreeThread then begin Thread.Free; end; EndThread(Result); // it should not get past EndThread end; end; constructor TCompThread.Create(CreateSuspended: Boolean); var Flags: Cardinal; //DWORD; begin inherited Create; AddThread; FSuspended := CreateSuspended; Flags := 0; if CreateSuspended then Flags := CREATE_SUSPENDED; FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), Flags, FThreadID); end; destructor TCompThread.Destroy; begin if not FFinished and not Suspended then begin Terminate; WaitFor; end; if FHandle <> 0 then CloseHandle(FHandle); inherited Destroy; RemoveThread; end; procedure TCompThread.CallOnTerminate; begin if Assigned(FOnTerminate) then FOnTerminate(Self); end; procedure TCompThread.DoTerminate; begin if Assigned(FOnTerminate) then Synchronize(CallOnTerminate); end; const Priorities: array [TThreadPriority] of Integer = (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL); function TCompThread.GetPriority: TThreadPriority; var P: Integer; I: TThreadPriority; begin P := GetThreadPriority(FHandle); Result := tpNormal; for I := Low(TThreadPriority) to High(TThreadPriority) do if Priorities[I] = P then Result := I; end; procedure TCompThread.SetPriority(Value: TThreadPriority); begin SetThreadPriority(FHandle, Priorities[Value]); end; procedure TCompThread.Synchronize(Method: TThreadMethod); begin FSynchronizeException := nil; FMethod := Method; SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self)); if Assigned(FSynchronizeException) then raise FSynchronizeException; end; procedure TCompThread.SetSuspended(Value: Boolean); begin if Value <> FSuspended then if Value then Suspend else Resume; end; procedure TCompThread.Suspend; begin FSuspended := True; SuspendThread(FHandle); end; procedure TCompThread.Resume; begin if ResumeThread(FHandle) = 1 then FSuspended := False; end; procedure TCompThread.Terminate; begin FTerminated := True; end; function TCompThread.WaitFor(Milliseconds: Cardinal): Boolean; var Msg: TMsg; H: THandle; Start: TDateTime; R: DWORD; begin H := FHandle; if GetCurrentThreadID = MainThreadID then begin Start := Now; repeat R := MsgWaitForMultipleObjects(1, H, False, Milliseconds, QS_SENDMESSAGE); if R = WAIT_OBJECT_0 + 1 then PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) until (R <> WAIT_OBJECT_0 + 1) or ((Milliseconds <> INFINITE) and (MilliSecondsBetween(Now, Start) >= Milliseconds)); end else begin R := WaitForSingleObject(H, Milliseconds); end; Result := (R = WAIT_OBJECT_0); end; initialization InitializeCriticalSection(ThreadLock); finalization DeleteCriticalSection(ThreadLock); end.