mirror of
https://github.com/winscp/winscp.git
synced 2026-10-24 02:28:55 +03:00
364 lines
8.6 KiB
ObjectPascal
364 lines
8.6 KiB
ObjectPascal
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: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; 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, LPARAM(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.
|