Delphi中的DLL,Form和Thread(all in one)问题

时间:2011-04-26 15:23:25

标签: multithreading delphi forms dll

我尝试构建一个非常复杂的应用程序。

我创建了一个DLL库。我在其中放了一个表格,然后在其中放入一个主题。

DLL中的

我有一个函数:

procedure ShowForm; stdcall;
var
Form1 : TFormSNVFL7;
begin
  Form1 := TFormSNVFL7.Create(nil);
  Form1.Show;
end;

我创建一个表单并显示它。这里没有问题。 我给这个dll添加了一个线程。 我在表格上放了一个计时器。几秒钟后,我创建一个线程并运行它。一切都很正常但是当我试图改变形式的任何东西时,没有任何事情发生。

在同步功能中,我尝试更改标签,但没有任何反应。

以下是文件:

DLL pas:

library uploader;

uses
  SysUtils,
  Classes,
  Forms,
  UploaderForm in 'UploaderForm.pas' {FormUploader},
  ThreadUpload in 'ThreadUpload.pas';

{$R *.res}

procedure ShowForm; stdcall;
var
  upForm: TFormUploader;
begin
  upForm := TFormUploader.Create(nil);
  upForm.Show;
end;

exports
ShowForm;

begin
end.

表格:

unit UploaderForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, acPNG, ExtCtrls, JvExExtCtrls, JvImage, JvExControls, JvLabel,
  JvAnimatedImage, JvGIFCtrl, ComCtrls, JvExComCtrls, JvProgressBar, StdCtrls,
  FileCtrl, JvDriveCtrls;

type
  TFormUploader = class(TForm)
    imgRunning: TJvImage;
    imgReady: TJvImage;
    imgUpdate: TJvImage;
    JvLabel1: TJvLabel;
    JvLabel2: TJvLabel;
    imgConnect: TJvImage;
    imgUpload: TJvImage;
    imgCheck: TJvImage;
    JvLabel3: TJvLabel;
    JvLabel4: TJvLabel;
    JvLabel5: TJvLabel;
    JvLabel6: TJvLabel;
    imgRun: TJvImage;
    imgOK: TJvImage;
    imgDone: TJvImage;
    JvProgressBar1: TJvProgressBar;
    JvLabel7: TJvLabel;
    fileList: TJvFileListBox;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormUploader: TFormUploader;

implementation

{$R *.dfm}

Uses ThreadUpload;

procedure TFormUploader.FormCreate(Sender: TObject);
begin
imgUpdate.Picture := imgReady.Picture;
imgConnect.Picture := imgReady.Picture;
imgUpload.Picture := imgReady.Picture;
imgCheck.Picture := imgReady.Picture;
imgRun.Picture := imgReady.Picture;
imgOK.Picture := imgReady.Picture;
fileList.Directory := ExtractFilePath(Application.ExeName) + 'csvexport/';
end;

procedure TFormUploader.Timer1Timer(Sender: TObject);
var
UpThread: TThread;
begin
Timer1.Enabled := False;

UpThread := UploadThread.Create(true);
UpThread.Create;
UpThread.Resume;

end;

end.

线程:

unit ThreadUpload;

interface

uses
  Classes, UploaderForm;

type
  UploadThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

implementation

{ UploadThread }

procedure UploadThread.Execute;
begin
  With FormUploader do
  begin
    imgUpdate.Picture := imgRunning.Picture;
  end;
end;

end.

我无法解决这个问题。

4 个答案:

答案 0 :(得分:6)

默认情况下,

TThread.Synchronize()在DLL中不起作用,因为Synchronize()发布到的同步队列对于调用它的可执行文件是本地的。换句话说,当应用程序调用{​​{1}}时,它会发布到exe文件本地的队列。当DLL调用Synchronize()时,它会发布到dll文件本地的队列。当应用程序在空闲时间泵送其同步队列时,它不会自动泵送DLL的队列。您必须从DLL导出一个函数,您的应用程序可以在需要时调用该函数,例如在Synchronize()事件或计时器中。然后,导出的函数可以调用RTL的TApplication.OnIdle函数来抽取DLL的同步队列。

答案 1 :(得分:0)

简单

您正在从UpThread中的单元UploaderForm更改FormUploader var中的属性

但是在DLL.pas单元中,您正在从TFormUploader

创建其他对象

尝试在显示表单的过程中执行此操作:

procedure ShowForm; stdcall;
begin
  FormUploader := TFormUploader.Create(nil);
  FormUploader.Show;
end;

这样做,问题就解决了

答案 2 :(得分:0)

使用VCL和线程导致问题。您应该从不从线程调用VCL相关代码而不使用同步机制。

通常使用TApplication和TApplication.Run()创建一个VCL应用程序来创建程序的主循环。主循环处理Windows消息和其他内容,但也调用CheckSynchronize(),而CheckSynchronize()查找是否存在应该同步的调用队列(即通过使用TThread.Synchronize()添加到队列的调用) 。因此,当您创建一个线程时,我会同时运行到主循环,这就是您的问题开始的地方。

您应该将图片分配代码移动到TFormUploader中的单独方法,并使用TThread.Synchronize()调用该方法,或使用其他同步机制,如事件对象(TEvent / CreateEvent())。

答案 3 :(得分:0)

我有一个类似的问题,尝试从DDL调用的回调函数更新主EXE中的TToolButton图标。 DLL调用回调函数以响应通过DataSnap implementation发送的广播到频道消息,我认为在子线程中。 直接从EXE回调函数访问TToolButton会导致TToolBar闪烁并消失图标。
我创建了一个TThread对象,并使用TThread.Synchonize()函数将与TToolButton的交互管理到主线程中:这为我解决了。

interface    
type
  TCallBackThread=class(TThread)

  private
    procedure DoInSync;
  public
    procedure Execute; override;
  end;
var
  CallBackThread: TCallBackThread;

implementation

procedure TCallBackThread.DoInSync;
begin
  // Jobs to be done in main thread
end;

procedure TCallBackThread.Execute;
begin
  inherited;
  Synchronize(DoInSync);
end;

EXE中的回调函数是:

procedure ConnectWf_Callback(s: PAnsiChar); stdcall;
begin
  if not Assigned(CallBackThread) then begin
    CallBackThread := TCallBackThread.Create(true);
    CallBackThread.Resume;
  end else begin
    CallBackThread.Execute;
  end;
end;