Skip to content

Commit 823b65e

Browse files
committed
Improved handling of threads
1 parent 8f80b20 commit 823b65e

3 files changed

Lines changed: 101 additions & 114 deletions

File tree

forms/mainform.pas

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3959,42 +3959,43 @@ function TformTrayslate.TranslateThread(ATrans: TTranslate; AText: string; AMemo
39593959
try
39603960
FCancelled := False;
39613961
ATrans.TextToTranslate := AText;
3962-
Th := TTranslateThread.Create(ATrans, False);
3962+
FRawTranslate := string.Empty;
3963+
Th := TTranslateThread.Create(ATrans, @FRawTranslate);
39633964
FTranslateThread := Th;
39643965
FActiveThreads.Add(Th);
39653966
FTranslateTarget := AMemo;
39663967
UpdateTranslateButtonState;
39673968
Screen.Cursor := crAppStart;
39683969
TimerAnimate.Enabled := True;
3970+
Th.OnTerminate := @ThreadDone;
39693971
Th.Start;
39703972
try
39713973
while Assigned(Th) and (not Th.Finished) do
39723974
begin
3973-
if FCancelled then
3974-
Break;
3975-
Application.ProcessMessages;
39763975
// If the thread was replaced or freed externally, abandon local reference
3977-
if FTranslateThread <> Th then
3976+
if FCancelled or (FTranslateThread <> Th) then
39783977
begin
39793978
Th := nil;
3979+
Result := string.Empty;
39803980
Break;
39813981
end;
3982+
Application.ProcessMessages;
39823983
Sleep(1);
39833984
end;
39843985
// Result only if the thread is still ours and not cancelled
3985-
if not FCancelled and Assigned(Th) and (Th.ResultTextSync <> string.Empty) then
3986+
if not FCancelled and (FRawTranslate.Trim <> string.Empty) then
39863987
begin
3987-
Result := Th.ResultTextSync;
3988+
Result := FRawTranslate;
39883989
if Assigned(AMemo) then
39893990
AMemo.Text := Result;
3990-
if ATrans = Trans then
3991-
ThreadDone(Th);
3991+
//if ATrans = Trans then
3992+
// ThreadDone(Th);
39923993
end;
39933994
finally
39943995
if Assigned(Th) then
39953996
begin
39963997
FActiveThreads.Remove(Th);
3997-
Th.Free; // Th is nil if the thread was replaced/force-killed
3998+
Th := nil; // Th is nil if the thread was replaced/force-killed
39983999
end;
39994000
FTranslateThread := nil; // always clear shared reference
40004001
end;
@@ -4017,7 +4018,7 @@ procedure TformTrayslate.ThreadDone(Sender: TObject);
40174018
end;
40184019

40194020
if FAutoCopy then
4020-
Clipboard.AsText := (Sender as TTranslateThread).ResultTextSync;
4021+
Clipboard.AsText := (Sender as TTranslateThread).ResultText;
40214022

40224023
if not Visible and (not Assigned(formPopupTrayslate) or not formPopupTrayslate.Visible) then
40234024
ShowCustomHint(TrayIcon.Hint);

units/network.pas

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,14 @@ class function TNetwork.WebRequest(AMethod: TWebMethod; const AUrl: string; cons
381381
if SameText(contentEncoding, 'gzip') and IsGzip(rawStream) then
382382
begin
383383
decompressedStream := DecompressGzipToStream(rawStream);
384+
if Assigned(decompressedStream) then
384385
try
385386
bodyStream.CopyFrom(decompressedStream, 0);
386387
finally
387388
FreeAndNil(decompressedStream);
388-
end;
389+
end
390+
else
391+
bodyStream.CopyFrom(rawStream, 0); // Fallback to compressed body
389392
end
390393
else
391394
bodyStream.CopyFrom(rawStream, 0);
@@ -419,11 +422,14 @@ class function TNetwork.WebRequest(AMethod: TWebMethod; const AUrl: string; cons
419422
if SameText(contentEncoding, 'gzip') and IsGzip(rawStream) then
420423
begin
421424
decompressedStream := DecompressGzipToStream(rawStream);
425+
if Assigned(decompressedStream) then
422426
try
423427
bodyStream.CopyFrom(decompressedStream, 0);
424428
finally
425429
FreeAndNil(decompressedStream);
426-
end;
430+
end
431+
else
432+
bodyStream.CopyFrom(rawStream, 0); // Fallback to compressed body
427433
end
428434
else
429435
bodyStream.CopyFrom(rawStream, 0);
@@ -463,55 +469,54 @@ class function TNetwork.DecompressGzipToStream(Compressed: TMemoryStream): TMemo
463469
dataPos: integer;
464470
begin
465471
zstream := Default(TZStream);
472+
Result := TMemoryStream.Create;
473+
try
474+
// Basic validation: gzip header
475+
if Compressed.Size < 10 then
476+
raise Exception.Create('Compressed data too small for gzip');
477+
p := Compressed.Memory;
478+
if (p[0] <> $1F) or (p[1] <> $8B) then
479+
raise Exception.Create('Not a gzip stream (invalid ID bytes)');
480+
481+
// Check compression method (must be deflate, 8)
482+
if p[2] <> 8 then
483+
raise Exception.Create('Unsupported compression method (not deflate)');
484+
485+
flags := p[3];
486+
dataPos := 10; // start after fixed header (10 bytes)
487+
488+
// Skip extra field (FEXTRA) if present
489+
if (flags and $04) <> 0 then
490+
begin
491+
if Compressed.Size < int64(dataPos) + 2 then
492+
raise Exception.Create('Truncated gzip: FEXTRA length missing');
493+
xlen := p[dataPos] or (p[dataPos + 1] shl 8);
494+
Inc(dataPos, 2 + xlen);
495+
end;
466496

467-
// Basic validation: gzip header
468-
if Compressed.Size < 10 then
469-
raise Exception.Create('Compressed data too small for gzip');
470-
p := Compressed.Memory;
471-
if (p[0] <> $1F) or (p[1] <> $8B) then
472-
raise Exception.Create('Not a gzip stream (invalid ID bytes)');
473-
474-
// Check compression method (must be deflate, 8)
475-
if p[2] <> 8 then
476-
raise Exception.Create('Unsupported compression method (not deflate)');
477-
478-
flags := p[3];
479-
dataPos := 10; // start after fixed header (10 bytes)
480-
481-
// Skip extra field (FEXTRA) if present
482-
if (flags and $04) <> 0 then
483-
begin
484-
if Compressed.Size < int64(dataPos) + 2 then
485-
raise Exception.Create('Truncated gzip: FEXTRA length missing');
486-
xlen := p[dataPos] or (p[dataPos + 1] shl 8);
487-
Inc(dataPos, 2 + xlen);
488-
end;
489-
490-
// Skip original filename (FNAME) if present (null-terminated)
491-
if (flags and $08) <> 0 then
492-
begin
493-
while (dataPos < Compressed.Size) and (p[dataPos] <> 0) do
494-
Inc(dataPos);
495-
Inc(dataPos); // skip null terminator
496-
end;
497+
// Skip original filename (FNAME) if present (null-terminated)
498+
if (flags and $08) <> 0 then
499+
begin
500+
while (dataPos < Compressed.Size) and (p[dataPos] <> 0) do
501+
Inc(dataPos);
502+
Inc(dataPos); // skip null terminator
503+
end;
497504

498-
// Skip file comment (FCOMMENT) if present (null-terminated)
499-
if (flags and $10) <> 0 then
500-
begin
501-
while (dataPos < Compressed.Size) and (p[dataPos] <> 0) do
505+
// Skip file comment (FCOMMENT) if present (null-terminated)
506+
if (flags and $10) <> 0 then
507+
begin
508+
while (dataPos < Compressed.Size) and (p[dataPos] <> 0) do
509+
Inc(dataPos);
502510
Inc(dataPos);
503-
Inc(dataPos);
504-
end;
511+
end;
505512

506-
// Skip header CRC (FHCRC) if present (2 bytes)
507-
if (flags and $02) <> 0 then
508-
Inc(dataPos, 2);
513+
// Skip header CRC (FHCRC) if present (2 bytes)
514+
if (flags and $02) <> 0 then
515+
Inc(dataPos, 2);
509516

510-
if dataPos >= Compressed.Size then
511-
raise Exception.Create('No compressed data after gzip header');
517+
if dataPos >= Compressed.Size then
518+
raise Exception.Create('No compressed data after gzip header');
512519

513-
Result := TMemoryStream.Create;
514-
try
515520
{$PUSH}
516521
{$NOTES OFF}
517522

@@ -534,23 +539,25 @@ class function TNetwork.DecompressGzipToStream(Compressed: TMemoryStream): TMemo
534539

535540
err := inflate(zstream, Z_NO_FLUSH);
536541
if err < 0 then
537-
raise Exception.Create('inflate error: ' + IntToStr(err));
542+
begin
543+
FreeAndNil(Result);
544+
Exit;
545+
end;
538546

539547
bytesWritten := SizeOf(outBuffer) - zstream.avail_out;
540548
if bytesWritten > 0 then
541549
Result.Write(outBuffer, bytesWritten);
542550

543551
until err = Z_STREAM_END; // End of stream reached
544-
545552
finally
546553
inflateEnd(zstream);
547554
end;
548555
{$POP}
549556

550557
Result.Position := 0;
551558
except
552-
Result.Free;
553-
raise;
559+
FreeAndNil(Result);
560+
// No raise;
554561
end;
555562
end;
556563

units/translate.pas

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -174,29 +174,29 @@ TTranslate = class
174174
property InitLiveTime: integer read FInitLiveTime write FInitLiveTime;
175175
end;
176176

177+
type
178+
PString = ^string;
179+
177180
{ TTranslateThread }
178181
TTranslateThread = class(TThread)
179182
private
180183
FTrans: TTranslate;
184+
FResult: PString;
181185
FSourceText: string;
182-
FResultText: string;
183-
FResultTextSync: string;
184-
FException: Exception;
186+
FExceptionMessage: string;
185187
FCancelled: boolean;
186188
protected
187189
procedure Execute; override;
188-
procedure AfterExecute;
189190
function GetIsTerminated: boolean;
190191
function GetIsCancelled: boolean;
192+
function GetResultText: string;
191193
public
192-
constructor Create(ATrans: TTranslate; AFreeOnTerminate: boolean = True);
193-
destructor Destroy; override;
194+
constructor Create(ATrans: TTranslate; AResult: PString; AFreeOnTerminate: boolean = True);
194195
procedure Cancel;
195196
property IsTerminated: boolean read GetIsTerminated;
196197
property IsCancelled: boolean read GetIsCancelled;
197-
property ExceptionObj: Exception read FException;
198-
property ResultText: string read FResultText;
199-
property ResultTextSync: string read FResultTextSync;
198+
property ExceptionMessage: string read FExceptionMessage;
199+
property ResultText: string read GetResultText;
200200
end;
201201

202202
const
@@ -341,6 +341,8 @@ procedure TTranslate.AbortRequest;
341341
i: integer;
342342
HTTP: THTTPSend;
343343
begin
344+
if not Assigned(FHTTPList) then Exit;
345+
344346
for i := 0 to FHTTPList.Count - 1 do
345347
begin
346348
HTTP := THTTPSend(FHTTPList[i]);
@@ -1753,21 +1755,16 @@ class function TTranslate.GetIniDirectory(fileName: string = string.Empty): stri
17531755

17541756
{%Region -fold TTranslateThread }
17551757

1756-
constructor TTranslateThread.Create(ATrans: TTranslate; AFreeOnTerminate: boolean = True);
1758+
constructor TTranslateThread.Create(ATrans: TTranslate; AResult: PString; AFreeOnTerminate: boolean = True);
17571759
begin
17581760
inherited Create(True);
17591761
FreeOnTerminate := AFreeOnTerminate;
17601762

17611763
FTrans := ATrans;
17621764
FSourceText := FTrans.TextToTranslate;
17631765
FCancelled := False;
1764-
end;
1765-
1766-
destructor TTranslateThread.Destroy;
1767-
begin
1768-
if Assigned(FException) then
1769-
FreeAndNil(FException);
1770-
inherited Destroy;
1766+
FExceptionMessage := string.Empty;
1767+
FResult := AResult;
17711768
end;
17721769

17731770
function TTranslateThread.GetIsTerminated: boolean;
@@ -1780,53 +1777,35 @@ function TTranslateThread.GetIsCancelled: boolean;
17801777
Result := FCancelled or Terminated or Application.Terminated;
17811778
end;
17821779

1780+
function TTranslateThread.GetResultText: string;
1781+
begin
1782+
if Assigned(FResult) then
1783+
Result := FResult^
1784+
else
1785+
Result := string.Empty;
1786+
end;
1787+
17831788
procedure TTranslateThread.Execute;
17841789
begin
1790+
if IsCancelled then Exit;
17851791
try
1786-
try
1787-
if IsCancelled then Exit;
1788-
1789-
if Length(Trim(FSourceText)) > 0 then
1790-
FResultText := FTrans.Translate
1791-
else
1792-
FResultText := string.Empty;
1793-
1794-
if IsCancelled then Exit;
1795-
except
1796-
on E: Exception do
1797-
if not Application.Terminated then
1798-
FException := Exception.Create(E.Message);
1799-
end;
1800-
finally
1801-
// Call AfterExecute in main thread to handle exceptions
1802-
if not IsCancelled then
1803-
Synchronize(@AfterExecute);
1792+
if Length(Trim(FSourceText)) > 0 then
1793+
FResult^ := FTrans.Translate
1794+
else
1795+
FResult^ := string.Empty;
1796+
except
1797+
on E: Exception do
1798+
FResult^ := E.ClassName + ': ' + E.Message;
18041799
end;
18051800
end;
18061801

18071802
procedure TTranslateThread.Cancel;
1808-
begin
1809-
FCancelled := True;
1810-
FTrans.AbortRequest;
1811-
FreeOnTerminate := True;
1812-
end;
1813-
1814-
procedure TTranslateThread.AfterExecute;
18151803
begin
18161804
if IsCancelled then Exit;
1805+
FCancelled := True;
18171806

1818-
// Handle exception in main thread if occurred
1819-
if Assigned(FException) then
1820-
begin
1821-
if Assigned(Application.OnException) then
1822-
Application.OnException(Self, FException)
1823-
else
1824-
Application.ShowException(FException);
1825-
1826-
FreeAndNil(FException); // free manually
1827-
end
1828-
else
1829-
FResultTextSync := FResultText;
1807+
if Assigned(FTrans) then
1808+
FTrans.AbortRequest;
18301809
end;
18311810

18321811
{%EndRegion}

0 commit comments

Comments
 (0)