delphi中的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.
Form pas:
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.
Thread pas:
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.
我无法解决这个问题。
there is a very complex application which i try to build.
There is a DLL library which i create. I put a form in it and i put a Thread in it.
in DLL i have a function:
procedure ShowForm; stdcall;
var
Form1 : TFormSNVFL7;
begin
Form1 := TFormSNVFL7.Create(nil);
Form1.Show;
end;
i create a form and show it. there isn't problem in here.
I add a thread to this dll.
i put a timer on the form. after a couple of seconds i create a thread and run it. everything is going normal but when i try to change anything of the form, nothing happen.
in synchronize function i try to change a label on it but nothing happen.
Here is the files:
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.
Form pas:
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.
Thread pas:
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.
i cannot solve this problem.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
默认情况下,
TThread.Synchronize()
在 DLL 中不起作用,因为Synchronize()
发布到的同步队列对于调用它的可执行文件来说是本地的。换句话说,当应用程序调用Synchronize()
时,它会发布到 exe 文件本地的队列。当 DLL 调用 Synchronize() 时,它会发布到 dll 文件本地的队列。当应用程序在空闲时间泵送其同步队列时,它不会自动泵送 DLL 的队列。您必须从 DLL 导出一个函数,然后应用程序可以在需要时调用该函数,例如在TApplication.OnIdle
事件或计时器中。然后,导出的函数可以调用 RTL 的CheckSynchronize()
函数来泵送 DLL 的同步队列。TThread.Synchronize()
does not work in a DLL by default, as the sync queue thatSynchronize()
posts to is local to the executable that is calling it. In other words, whenSynchronize()
is called by the application, it posts to a queue that is local to the exe file. WhenSynchronize()
is called by a DLL, it posts to a queue that is local to the dll file. When the application pumps its sync queue during idle times, it will not pump the DLL's queue automatically. You have to export a function from your DLL that your application can then call when needed, such as in theTApplication.OnIdle
event or in a timer. That exported function can then call the RTL'sCheckSynchronize()
function to pump the DLL's sync queue.简单
您正在从 UpThread 中的 UploaderForm 单元更改 FormUploader var 的属性,
但是在 DLL.pas 单元中,您正在从 TFormUploader 创建其他对象
尝试在显示表单的过程中执行此操作:
执行此操作,问题是解决了
Simple
You are changing a property from the FormUploader var from the unit UploaderForm in the UpThread
But in the unit DLL.pas you are creating other object from TFormUploader
Try to do this in the procedure that show the form:
Do this, and the problem are solved
您的问题是由于使用 VCL 和线程造成的。如果不使用同步机制,您绝对不应该从线程中调用 VCL 相关代码。
通常,您通过使用 TApplication 和 TApplication.Run() 创建程序的主循环来创建 VCL 应用程序。主循环处理窗口消息和其他内容,但也调用 CheckSynchronize(),而 CheckSynchronize() 查找是否有应同步的调用排队(即使用 TThread.Synchronize() 添加到队列中的调用) 。因此,当您创建一个线程时,我会与主循环同时运行,这就是您的问题开始的地方。
您应该将图片分配代码移至 TFormUploader 中的单独方法,并使用 TThread.Synchronize() 调用该方法,或者使用其他同步机制,如事件对象 (TEvent / CreateEvent())。
Your problem results from using VCL and threading. You should never call VCL related code from a thread without using synchronization mechanisms.
Normally you create a VCL application by using TApplication and TApplication.Run() to create a main loop of your program. The main loop handles windows messages and other stuff, but also calls CheckSynchronize() whereas CheckSynchronize() looks up whether there is call queued that should be synchronized (that is a call that is added to the queue by using TThread.Synchronize()). So when you create a thread i runs concurrently to the main loop and that's where your problem begins.
You should either move the picture assignment code to a separate method in TFormUploader and call that method by using TThread.Synchronize() or use other synchronization mechanisms like event objects (TEvent / CreateEvent()).
我在尝试从 DLL 调用的回调函数更新主 EXE 中的
TToolButton
图标时遇到了类似的问题。 DLL 调用回调函数以响应通过 DataSnap 实现发送的广播到通道消息,我认为在子线程中。直接从 EXE 回调函数访问
TToolButton
会导致TToolBar
闪烁并消失图标。我创建了一个 TThread 对象,并使用主线程中的 TThread.Synchonize() 函数来管理与 TToolButton 的交互:这解决了对我来说有问题。
EXE 中的回调函数是:
I had a similar issue trying to update a
TToolButton
icon in the main EXE, from a callback function invoked by the DLL. The DLL invokes the callback function in response to a broadcast to channel message sent via a DataSnap implementation, I think in a child thread.Accessing the
TToolButton
directly from the EXE callback function results in flickering theTToolBar
and disappearing the icons.I created a
TThread
object and the interaction with theTToolButton
is managed using theTThread.Synchonize()
function into the main thread: this solved the problem for me.The callback function into the EXE is: