为什么不在dll中调用threadTerminate

时间:2015-01-14 23:07:57

标签: delphi dll

我遇到的问题是,与普通应用程序中的相同代码相比,我的dll中的代码表现不同。经过一些调试后,我发现线程的OnTerminate永远不会在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;

如果我从普通的VLC Delphi应用程序运行此代码,我会在文本文件中获得 end 。 如果我从dll运行相同的代码(将其加载到静态或动态到VLC应用程序中),我会在文本文件中获得 start

我的问题:为什么?或者更好的问一下,我怎么能让我的dll和我的VLC一样。目前使用的版本是XE7。

1 个答案:

答案 0 :(得分:10)

TThread.OnTerminate事件在主UI线程的上下文中通过调用TThread.Synchronize()触发,该调用将请求存储在主UI线程定期检查的队列中,在可用时执行挂起的请求。

如果在启用运行时软件包的情况下编译DLL和EXE,它们将共享RTL的单个副本(因此需要您在应用程序中部署rtl.bpl)。当EXE检查RTL的Synchronize()队列时,它将看到来自EXE和DLL的待处理请求。

但是,如果他们没有共享单个RTL,那么它们将使用不相互链接的RTL的单独副本进行编译。默认情况下,EXE中没有任何内容可以检查和处理来自DLL的Synchronize()队列的待处理请求,只能来自EXE的Synchronize()队列。要解决这个问题,你必须从调用DLL的RTL的CheckSynchronize()函数的DLL中导出一个函数,然后定期进行导出DLL函数的EXE调用,例如在计时器中。

否则,解决此问题的另一种方法是通过覆盖线程的虚拟Synchronize()方法绕过触发OnTerminate事件的DoTerminate()调用(您无法做到这一点) TThread.CreateAnonymousThread())。您可以直接DoTerminate()拨打OnTerminate,或者只在DoTerminate()内执行您需要的操作。但无论哪种方式,您必须确保此代码是线程安全的,因为DoTerminate()在工作线程的上下文中运行。