#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;