2013-09-21 3 views
3

У меня сейчас довольно редкая ситуация. У меня есть приложение, которое напрямую взаимодействует с очередью сообщений Windows. Это приложение также запускает внешние сценарии Lua с LuaJIT. Я хотел иметь средство отладки для этих сценариев, поэтому я создал простое приложение VCL, а затем преобразовал его в библиотеку DLL. Когда первое приложение запускает сеанс отладки с библиотекой, эта DLL создает отдельный поток, где инициализируется и запускается весь объект VCL.Запуск VCL в отдельной теме

procedure TDebuggerThread.Execute; 
begin 
    Application.Initialize; 
    Application.MainFormOnTaskbar := True; 
    Application.CreateForm (TMainForm, MainForm); 
    Application.Run; 
end; 

Поддерживает ли VCL этот способ таким образом? К какому потоку будет TThread.Synchronize (Proc: TThreadProc) отправить свое сообщение?

Inb4 "сообщения в VCL и в основное приложение будут беспорядочно" - они не будут, потому что каждый поток имеет свою собственную очередь сообщений.

Также вы можете ознакомиться с источниками here. (Возможно) проблемная библиотека называется LuaDebugger. Вместо надлежащего клиента (Core, Engine, Client) В настоящее время я использую LuaDefaultHost, что довольно простое консольное приложение, требующее отладчика и ведущее в основном как lua.exe. С консольным клиентом отладчик работает удивительно гладко - единственная проблема, с которой я столкнулся, заключается в том, что если я закрываю окно консоли во время использования библиотеки, VCL бросает «Обработчик окон уже недействителен» (на русском языке: /). Если я позволю клиенту завершить взаимодействие с отладчиком так, как он должен, все будет хорошо. Вероятно, вызов Windows.TerminateThread во время завершения модуля должен исправить это.

+0

Либо я что-то упускаю, либо вы получаете доступ и выполняете методы и запускаете очередь сообщений на объекте приложения, созданного в другом потоке. Я бы подумал, что он должен был разбиться раньше .. –

+0

@SertacAkyuz Я тоже так думал. Видимо, VCL более гибкий, чем мы рассматривали.: O Хотя я все еще не пытался использовать библиотеку с клиентом GUI, только с консольным. Вероятно, мне нужно запустить специальный эксперимент, чтобы определить, будет ли VCL по-прежнему использовать очередь основного потока. – Delfigamer

+0

@ Delfigamer Как это сделать VCL? Как VCL может вставить себя в поток, принадлежащий исполняемому файлу? Это не может сделать силой. Это требует сотрудничества от исполняемого файла. У VCL нет концепции основного потока процесса. Существует только поток VCL, этот поток, который инициализировал VCL. –

ответ

5

Ваша единственная надежда - создать поток, а затем загрузить DLL из этого потока. Итак, чтобы быть как можно более ясным, вы создаете поток, а затем из кода, выполняемого в этом потоке, вы вызываете LoadLibrary для загрузки DLL.

VCL должен быть запущен из потока, который загружает DLL. Инициализация VCL происходит во время инициализации библиотеки DLL и определяет, какой поток является основным потоком VCL. Основной поток VCL - это поток, который инициализирует VCL, поток, который загружает DLL.

Вы, вероятно, должны будете иметь четкую главу с этим целым подходом, потому что в одном процессе вы будете иметь два потока графического интерфейса, два насоса сообщений. Отображение модального окна включает в себя отключение окон в обоих потоках графического интерфейса.

Я не могу быть уверен, что этот общий подход (два потока GUI в одном процессе, один из которых является потоком VCL) будет работать, никогда не сделав этого. Однако я думаю, что есть хороший шанс, что он полетит.


Вы также задать вполне конкретный вопрос:

К какой нить TThread.Synchronize (Proc: TThreadProc) отправить свое сообщение?

Ответ - это всегда поток, который инициализировал модуль. Таким образом, для исполняемого файла это основной поток процесса. Для DLL нить, которая инициализировала модуль, представляет собой поток, который называется LoadLibrary, поток, который выполняет начальный вызов DllMain, поток, который выполняет код инициализации модулей DLL. Это известно в RTL/VCL в качестве основного потока модуля. Это поток, идентификатор которого задан System.MainThreadID.

Чтобы доказать это, на случай, если вы не примете мое слово для этого, вот небольшая демонстрация.

Исполняемые

program DllThreading; 

{$APPTYPE CONSOLE} 

uses 
    Classes, Windows; 

type 
    TMyThread = class(TThread) 
    protected 
    procedure Execute; override; 
    end; 

procedure TMyThread.Execute; 
var 
    lib: HMODULE; 
    proc: procedure; stdcall; 
begin 
    lib := LoadLibrary('dll.dll'); 
    proc := GetProcAddress(lib, 'foo'); 
    proc(); 
    Sleep(INFINITE); 
end; 

begin 
    Writeln('This is the process main thread: ', GetCurrentThreadId); 
    TMyThread.Create; 
    Readln; 
end. 

DLL

library Dll; 

uses 
    Classes, Windows; 

type 
    TMyThread = class(TThread) 
    private 
    procedure DoStuff; 
    protected 
    procedure Execute; override; 
    end; 

procedure TMyThread.DoStuff; 
begin 
    Writeln('This is the thread which executes synchronized methods in the DLL: ', GetCurrentThreadId); 
end; 

procedure TMyThread.Execute; 
begin 
    Writeln('This is the thread created in the DLL: ', GetCurrentThreadId); 
    Synchronize(DoStuff); 
end; 

procedure foo; stdcall; 
begin 
    TMyThread.Create; 
    CheckSynchronize(1000); 
end; 

exports 
    foo; 

begin 
    Writeln('This is the initialization thread of the DLL: ', GetCurrentThreadId); 
end. 

Выход

 
This is the process main thread: 2788 
This is the initialization thread of the DLL: 5752 
This is the thread created in the DLL: 6232 
This is the thread which executes synchronized methods in the DLL: 5752 
+0

. Если вам нужно больше проработать, как только вы напишете« Application: = .. »(использует« формы »), у вас уже есть экземпляр приложения. Включая «формы» в разделе uses, pulls «graphics», «controls» и т. Д., Который устанавливает VCL в своих разделах инициализации, который вызывается при запуске Dll. Обратите внимание, что в источнике проекта приложения VCL Forms нет «Приложения: =». –

+0

@SertacAkyuz Считаете ли вы, что я опишу, будет работать? –

+0

Я не вижу никаких проблем. Однако я бы не подумал о проблеме модальности либо до тех пор, пока не столкнулся с этим. –

-2

О круто Я отвечаю на мой собственный вопрос.

Так,

ли VCL полностью поддерживает выполняется таким образом?

Кажется, что именно. Из того, что у нас здесь, код VCL запускается только в текущем потоке и не знает, есть ли другие, или этот «текущий» поток является основным процессом или создается прямо в том же двоичном файле. Пока эта нить не путается с другими, все идет хорошо.

Кому какой поток будет TThread.Synchronize (Proc: TThreadProc) отправить сообщение?

Эксперимент говорит, сообщение будет отправлено на основной поток процесса, не VCL нити (естественно, это тот, где Application.Run работает). Чтобы синхронизировать с потоком VCL, я должен явно отправлять сообщения в форму VCL, здесь (LuaDebugger/USyncForm.pas) это пользовательский UM_MethodCall с WParam, удерживающим указатель на синхронизированный код и LParam, содержащий необязательный аргумент void*.

+0

* Код VCL запускается только в текущем потоке. * Ну что вы ожидали ?! Вы вызываете 'Application.Run' и ожидаете, что код волшебным образом перенесется в другой поток! Что касается 'Synchronize', то код, который я просматриваю, довольно ясен. Он запускает код в потоке VCL, поток, инициализирующий модуль. Это поток, идентификатор которого можно найти в 'System.MainThreadID'. –

+0

Я проигнорировал, потому что ваш ответ явно неверен. Синхронизация не выполняет код в основном потоке процесса. Он выполняет его в потоке инициализации модуля. Это подтверждается обновлением моего ответа. –

+1

'Synchronize()' не отправляет сообщение в поток, инициализирующий модуль, он помещает в любой поток, указанный в настоящее время 'MainThreadID'. Он может указывать поток, который инициализировал модуль по умолчанию, но вы можете изменить его на любой требуемый идентификатор потока. Или вы даже можете назначить свой собственный ответ «WakeMainThread», чтобы делать все, что захотите, и не нужно публиковать его вообще. –

1

Ответ от EDN Реми Лебо:

Библиотека DLL имеет свой собственный автономный экземпляр VCL и RTL, которые являются отдельными из копии главного приложения. По большей части, этот вид древовидного использования внутри DLL, как правило, хорошо, но mainthread чувствительных функциональных возможностей, как TThread.Synchronize() и TThread.Queue(), не будет работать, если вы не вручную обновить System.MainThreadID переменные с ThreadID из ваших «главного ", если ваш поток не называет CheckSynchronize() периодически (который обычно вызывается автоматически, когда TThread« просыпается »« основная »нить , когда выполняется операция Synchronize/Queue).

Не знаю, является ли настройка System.MainThreadID вручную, но ответ на главный вопрос здесь «в общем-то».

Смежные вопросы