Running VCL in a separate thread

Now I have a rather rare situation. I have an application that interacts directly with the Windows message queue. This application also runs external Lua scripts with LuaJIT. I wanted to have a debugging tool for these scenarios, so I created a simple VCL application and then converted it to a DLL. When the first application starts a debugging session with the library, this DLL creates a separate thread where the entire VCL object is initialized and launched.

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

Is VCL fully supported this way? What thread will TThread.Synchronize (Proc: TThreadProc) send its message to?

Inb4 "messages in VCL and in the main application will be messy" - they will not, because each thread has its own message queue.

In addition, you can see the sources here . (Maybe) the problematic library is called LuaDebugger . Instead of a proper client ( Core , Engine , Client ) I currently use LuaDefaultHost , which is a fairly simple console application that calls the debugger and lua.exe mostly like lua.exe . With the console client, the debugger works surprisingly smoothly - the only problem I encountered is that if I close the console window while the library is still in use, VCL throws "The window handler is already invalid" (in Russian: /). If I let the client complete the interaction with the debugger as it should, everything will be fine. Probably calling Windows.TerminateThread during finalization should fix this.

+3
multithreading delphi vcl c ++ builder
source share
3 answers

Your only hope is to create a stream and then load the DLL from that stream. So, to be as clear as possible, you create a stream, and then from the code that runs in that stream, you call LoadLibrary to load the DLL.

VCL must be started from the thread that loads the DLL. VCL initialization occurs during the initialization of the DLL and determines which thread is the main VCL thread. The main VCL thread is the thread that initializes the VCL, the thread that loads the DLL.

You will probably have to keep a clear chapter with all this approach, because in one process you will have two GUI threads, two message pumps. The display of a modal window is associated with the disabling of windows in both threads of the graphical interface.

I cannot be sure that this general approach (two GUI threads in one process, one of which is a VCL thread) will work without ever doing this. However, I think there are good chances that he will fly.


You also ask a rather specific question:

To which thread will TThread.Synchronize (Proc: TThreadProc) send its message?

The answer is always the thread that initialized the module. Thus, for the executable, this is the main thread of the process. For a DLL, the thread that initialized the module is the thread that called LoadLibrary , the thread that makes the initial DllMain call, the thread that executes the DLL module initialization code. This is known in RTL / VCL as the main module stream. This is the thread whose identifier is specified by System.MainThreadID .

To prove this point, if you do not take my word for it, here is a small demonstration.

Executable

 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. 

Exit

 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
+6
source share

Reply from EDN Remy Lebo:

The DLL has its own stand-alone copy of VCL and RTL, which are separate from the main copy of the application. For the most part, this kind of threaded use inside a DLL is generally fine, but thread-sensitive functionality like TThread.Synchronize() and TThread.Queue() will not work if you manually update the System.MainThreadID variable with ThreadID of your "main" thread, if your thread does not call CheckSynchronize() periodically (which is usually called automatically when TThread wakes up the "main" thread when the Synchronize / Queue operation is performed).

I don’t know if it is safe to manually configure System.MainThreadID , but the answer to the main question is "in general."

+1
source share

Oh cool, I answer my question.

So,

Is VCL fully supported this way?

It seems so. From what we have, VCL code runs only in the "current" thread and does not know if there are others, or this "current" thread is the main process or is created directly in the same binary file. Until this stream fusses with others, everything goes well.

To which thread will TThread.Synchronize (Proc: TThreadProc) send its message?

The experiment says that the message will be sent to the main thread, and not to the VCL thread (naturally, it works where Application.Run ). To synchronize with the VCL stream, I have to explicitly send messages to the VCL form, here ( LuaDebugger / USyncForm.pas ), this is a custom UM_MethodCall with a pointer to WParam for the synchronized code and LParam containing the optional void* argument.

-one
source share

All Articles