mrtakdnz
mrtakdnz

Reputation: 149

DLL, Form and Thread (all in one) problem in delphi

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.

Upvotes: 3

Views: 3611

Answers (4)

Giorgio Calzolato
Giorgio Calzolato

Reputation: 389

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;

Upvotes: 0

ViRuSTriNiTy
ViRuSTriNiTy

Reputation: 1

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()).

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 595305

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.

Upvotes: 8

KelvinS
KelvinS

Reputation: 11

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

Upvotes: 0

Related Questions