delphi中的DLL、表单和线程(合一)问题

发布于 2024-11-03 04:28:18 字数 2830 浏览 1 评论 0原文

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

我创建了一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

初心 2024-11-10 04:28:18

默认情况下,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 that Synchronize() posts to is local to the executable that is calling it. In other words, when Synchronize() is called by the application, it posts to a queue that is local to the exe file. When Synchronize() 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 the TApplication.OnIdle event or in a timer. That exported function can then call the RTL's CheckSynchronize() function to pump the DLL's sync queue.

一身软味 2024-11-10 04:28:18

简单

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

但是在 DLL.pas 单元中,您正在从 TFormUploader 创建其他对象

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

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

执行此操作,问题是解决了

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:

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

Do this, and the problem are solved

雄赳赳气昂昂 2024-11-10 04:28:18

您的问题是由于使用 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()).

海拔太高太耀眼 2024-11-10 04:28:18

我在尝试从 DLL 调用的回调函数更新主 EXE 中的 TToolButton 图标时遇到了类似的问题。 DLL 调用回调函数以响应通过 DataSnap 实现发送的广播到通道消息,我认为在子线程中。

直接从 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;

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 the TToolBar and disappearing the icons.

I created a TThread object and the interaction with the TToolButton is managed using the TThread.Synchonize() function into the main thread: this solved the problem for me.

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;

The callback function into the EXE is:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文