Проблема с библиотекой DLL, формой и потоком (все в одном) в delphi

#multithreading #delphi #forms #dll

#многопоточность #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.
  

Передача формы:

 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.
  

я не могу решить эту проблему.

Комментарии:

1. Почему вы вызываете Upload. Создать после того, как вы создали свой объект thread? Я не вижу imgRunning, объявленного в вашем коде. Почему вы используете thread для такой задачи?

2. imgRunning — это объект формы. это создание при создании формы. я помещаю компонент jvImage в свою форму и переименовываю его в imgRunning. это не имеет значения. когда я пытаюсь это сделать, та же проблема: Label1.Caption := ‘anything’; ничего не изменилось…

3. кстати, это начало выполнения задачи. Я буду использовать IndyFTP для загрузки файлов базы данных. Я получу данные с сервера interbase, затем конвертирую их в csv-файл, загружу их на сервер и запущу на сервере (мой серверный производитель не поддерживает удаленное подключение для MySQL) и т.д.

4. Я собираюсь быть откровенным. Похоже, вы пытаетесь сделать что-то сложное просто ради этого. Это не очень практично. Мы пишем программы для решения проблем , а не усложняем жизнь. Без цели гораздо сложнее помочь вам. Это наиболее удачно иллюстрируется тем фактом, что ваш поток ничего не делает, кроме произвольного назначения, завершается и готово! У вашего потока нет назначения . Решите, что вы хотите сделать в первую очередь, а не как вы хотите это сделать.

5. Когда вам нужно решить конкретную проблему, есть все возможности, мы можем помочь вам найти решение без кучи сложностей, которые вы создаете для себя.

Ответ №1:

TThread.Synchronize() по умолчанию не работает в DLL, поскольку очередь синхронизации, в которую Synchronize() отправляются сообщения, является локальной для вызывающего ее исполняемого файла. Другими словами, когда Synchronize() вызывается приложением, оно отправляет сообщения в очередь, локальную для exe-файла. Когда Synchronize() вызывается DLL, она отправляет сообщения в очередь, которая является локальной для файла dll. Когда приложение загружает свою очередь синхронизации во время простоя, оно не будет автоматически загружать очередь DLL. Вы должны экспортировать функцию из вашей библиотеки DLL, которую ваше приложение может затем вызывать при необходимости, например, в TApplication.OnIdle событии или по таймеру. Затем экспортированная функция может вызвать CheckSynchronize() функцию RTL для загрузки очереди синхронизации DLL.

Ответ №2:

Простой

Вы изменяете свойство из переменной FormUploader из unit UploaderForm в восходящем потоке

Но в unit DLL.pas вы создаете другой объект из TFormUploader

Попробуйте сделать это в процедуре, которая показывает форму:

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

Сделайте это, и проблема будет решена

Комментарии:

1. Это сработает, но я полагаю, что вы сделаете обзор в своей dll, проверяя, действительно ли таймер необходим и реализация должна быть в другом потоке … но если вы делаете это только для изучения многопоточности, это не проблема

Ответ №3:

Ваша проблема возникает из-за использования VCL и многопоточности. Вы никогда не должны вызывать связанный с VCL код из потока без использования механизмов синхронизации.

Обычно вы создаете приложение VCL с помощью TApplication и TApplication.Run() для создания основного цикла вашей программы. Основной цикл обрабатывает сообщения Windows и другие материалы, но также вызывает CheckSynchronize(), тогда как CheckSynchronize() проверяет, есть ли в очереди вызовы, которые следует синхронизировать (это вызов, который добавляется в очередь с помощью TThread.Синхронизация()). Итак, когда вы создаете поток, я одновременно запускаю основной цикл, и вот тут-то и начинается ваша проблема.

Вам следует либо переместить код назначения изображения в отдельный метод в TFormUploader и вызвать этот метод с помощью TThread.Синхронизировать () или использовать другие механизмы синхронизации, такие как объекты событий (TEvent / CreateEvent()).

Ответ №4:

У меня была похожая проблема при попытке обновить TToolButton значок в главном EXE-файле из функции обратного вызова, вызываемой DLL. DLL вызывает функцию обратного вызова в ответ на широковещательное сообщение канала, отправленное через реализацию DataSnap, я думаю, в дочернем потоке.

Доступ к TToolButton непосредственно из функции обратного вызова EXE приводит к мерцанию TToolBar и исчезновению значков.

Я создал TThread объект, и взаимодействие с TToolButton которым управляется с помощью TThread.Synchonize() функции в главном потоке: это решило проблему для меня.

 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;