我尝试构建一个非常复杂的应用程序。
我创建了一个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.
我无法解决这个问题。
答案 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;