Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is threadTerminate not called inside a dll

Tags:

dll

delphi

I have a problem that code inside my dll is acting different compared to the same code within a normal application. After some debugging I found that the thread's OnTerminate is never called within the dll.

type
  TTest = class
  private
  public
     procedure threadStart();
     procedure threadEnd(Sender: TObject);
     procedure lines(value: String);
  end;

procedure TTest.threadStart();
var aThread : TThread;
begin
 aThread :=
    TThread.CreateAnonymousThread(
      procedure
      begin
         lines('start')
      end
    );
  aThread.FreeOnTerminate := True;
  aThread.OnTerminate := self.threadEnd;
  aThread.Start;
end;

procedure TTest.threadEnd;
begin
  lines('end')
end;

procedure TTest.lines(value: String);
  var MyText: TStringlist;
begin
  MyText:= TStringlist.create;
  MyText.Add(value);
  MyText.SaveToFile('.\filename.txt');
  MyText.Free
end;

If I run this code from a normal VLC Delphi Application, I get end in the text file. If I run the same code from a dll (loading it either static or dynamic into a VLC Application), I get start in the text file.

My question: Why? Or better asked, how can I let my dll act the same way as my VLC. Current version I'm using is XE7.

like image 940
Zibelas Avatar asked Jan 14 '15 23:01

Zibelas


1 Answers

The TThread.OnTerminate event is triggered in the context of the main UI thread via a call to TThread.Synchronize(), which stores requests in a queue that the main UI thread checks periodically, executing pending requests when available.

If the DLL and EXE are compiled with Runtime Packages enabled, they share a single copy of the RTL (and thus require you to deploy rtl.bpl with your app). When the EXE checks the RTL's Synchronize() queue, it will see pending requests from both EXE and DLL.

However, if they are not sharing a single RTL, then they will be compiled with separate copies of the RTL that are not linked to each other. By default, there is nothing in the EXE that checks and processes pending requests from the DLL's Synchronize() queue, only from the EXE's Synchronize() queue. To address that, you have to export a function from the DLL that calls the CheckSynchronize() function of the DLL's RTL, and then make the EXE call that exported DLL function periodically, such as in a timer.

Otherwise, the other way to get around this problem is to bypass the Synchronize() call that triggers the OnTerminate event, by overriding the thread's virtual DoTerminate() method (which you cannot do with TThread.CreateAnonymousThread()). You can have DoTerminate() call OnTerminate directly, or just do what you need inside of DoTerminate() itself. But either way, you have to make sure this code is thread safe, as DoTerminate() runs in the context of the worker thread.

like image 135
Remy Lebeau Avatar answered Sep 20 '22 22:09

Remy Lebeau