why the application hangs when Executing Thread and that thread have ShowMessage or MessageDlg
but when using MessageBox everything is working normally.
all this happened if the application Appearance not the defualt one >> "Windows"
if the selected Appearance "Windows" it will never hangs even with the ShowMessage and MessageDlg
ShowMessage() and MessageDlg() are not thread-safe. They display VCL Forms, which must only be used in the context of the main UI thread.
Windows.MessageBox() is generally thread-safe, if you specify a nil owner window when calling it from a worker thread. It creates and displays its own dialog window, and runs its own modal message loop, within the context of the calling thread, so there are usually no threading issues. But there are some gotchas (see this article: MessageBoxes and worker threads).
TApplication.MessageBox() calls Windows.MessageBox() internally, but is not thread-safe because it does things invoking the RTL and MainForm that are not thread-safe, and thus should only be used in the context of the main UI thread as well.
In short, don't use VCL popup messages in worker threads - period. Use Windows.MessageBox(), or to be on the safe side, delegate you popup messages to the main UI thread.
Related
I was wondering if it's safe to read values from a VCL control outside the main thread.
Let's say I read line after line (or it's caption property) from a TMemo/TEdit control from a thread (without TThread.Synchronize), but also made sure the TMemo/TEdit control has been disabled and/or it's read only flag has been enabled.
Would it be safe?
No this is not safe. The content from memo and edit controls is read by calling the window procedure (using the Perform method), passing WM_GETTEXTLENGTH and WM_GETTEXT. Such code is required by Win32 to be executed on the thread that created the window, in this case the main thread. So when you read the Text property from a worker thread, you break that rule, because you call Perform on the worker thread, and then execute the window procedure on the wrong thread.
You might instead think that you could use SendMessage passing WM_GETTEXTLENGTH/WM_GETTEXT to arrange that the window procedure was executed on the main thread. But that is subject to a nasty race condition. You need a window handle to use SendMessage, but accessing the Handle property on the worker thread is not threadsafe. That must be done on the main thread, otherwise VCL window re-creation can lead to the window being created by your worker thread. And GUI windows must be created by the main thread.
I am working on a VCL (Delphi Win32) forms application platform with embedded scripting and would like to add debugging support. Scripting executes in the main VCL thread - scripts do direct UI manipulation and has some other legacy constraints keeping it in the UI thread.
The debugger UI needs to run in its own thread since the main UI thread will block on script breakpoints. It still needs to be in the same process for the thread-safe debugging component to work.
I tried to follow Blorgbeard's comment on https://stackoverflow.com/a/12505959/243144, but I am not sure if this is even possible with Delphi's VCL. (.NET creates a new ApplicationContext when passing a form to Application.Run) With the following Delphi, blocking of the main UI thread stops message processing on the second thread (and vice versa).
procedure TDebuggerThread.Execute;
begin
CoInitialize(nil);
FForm := TForm2.Create(nil);
FForm.Show;
Application.Run;
end;
Delphi forms are single-thread only. Any descendant of TControl, including TForm, must only be accessed from the main UI thread. Never call any TApplication method on anything but the main thread.
You're of course allowed to create other windows that are tied to different threads. You just can't use the VCL UI elements in those threads. Instead, you'll use CreateWindow to create the main window and any controls on it. You'll write a window procedure to handle any messages sent to those windows. You'll handle control-notification messages in the parent's window procedure rather than the child's.
One of the constraints of the VCL is that all interaction with GUI controls must be performed on the main thread. There's no way to circumvent that.
If you want to show debugger GUI in a separate thread, using VCL, your only option is to use an out-of-process solution. In other words, run your debugger in a different process, and use IPC to communicate between the two processes.
I killed off all my threads and the dialog box disappears, but it still runs... I can see it as a Process still running. So I have to then kill the process.
exit(0) is probably not the best way to close out of an app. Is there another way, or is this the best? thx
The following code helped the debugger, otherwise it crashed OnClose.
void CServerDlg::OnClose()
{
TerminateThread(this->hThread_TcpIp, 0);
TerminateThread(this->hThread_ReadData, 0);
TerminateThread(this->hThread_ReadSetup, 0);
//exit(0); //<-- not good to use
CDialog::OnClose();
}
If CCatsServerDlg is the main dialog of the application, simply calling OnOK() or OnCancel() will terminate the application. There is no need to terminate each thread. They will be automatically terminated when the process exits.
You can also try PostQuitMessage.
The no nos:
You should never use exit() to terminate Windows application.
You should not call message handler directly, since most of the messages (including WM_CLOSE) are issued by the system after performing some steps.
You should never use TerminateThread according to MSDN:
TerminateThread is a dangerous function that should only be used in the most extreme cases.
You should never close an app without terminating all threads, since each thread must perform some cleaning. For example free memory and Windows handles. This is not a good programming practice, since terminating process may release memory but most likely all Windows resources (user and system) would not be released.
What you should do:
Use synchronization mechanism to terminate threads if you are using worker threads. For this I would suggest using an event object. You can use PostQuitMessage only for the threads that have a message queue, usually GUI threads.
After requesting threads termination you should use WaitForMultipleObjects function and after threads are terminated, terminate the main thread.
If your dialog is a modal main application window, call EndDialog. After dialog terminates, you should return FALSE from the application InitInstance.
If you dialog is not a modal dialog, after terminating threads, you should call DestroyWindow.
Is there any analog for Delphi that specify that some thread is background thread? As for .NET I can say SomeThread.IsBackground = true; and this thread will become background.
Thanks in advance!
The .net documentation describes the IsBackground property like this:
A thread is either a background thread or a foreground thread. Background threads are identical to foreground threads, except that background threads do not prevent a process from terminating. Once all foreground threads belonging to a process have terminated, the common language runtime ends the process. Any remaining background threads are stopped and do not complete.
A Delphi process terminates when the main function in the .dpr file completes. This main function always runs in the context of the main process thread, that is the thread that is automatically created by the system when the process starts.
So, in Delphi there is no equivalent property. There is a single foreground thread, the main thread, and all other threads are background thread, using the .net terminology. A thread cannot, at runtime, change state from foreground to background, or vice versa.
To verify that the current thread is the main VCL thread, check TThread.CurrentThread.ThreadID = MainThreadID(*). The main VCL thread is supposed to stay the foreground thread, and is the only thread where the GUI should be updated, so the answer to your question is "no".
If you are using a recent version of Delphi you can however make use of TThread.CreateAnonymousThread and TThread.Synchronize in order to have anonymous methods executed in either a background thread or in the main VCL thread, respectively.
*) Please note that the CurrentThread class property was added only a few versions back. If you are using an old Delphi version, such as Borland Delphi 7, you can only perform this check from within the execute method of the thread (or from any method that is called by Execute etc).
In Delphi, every thread except the main thread is a background thread. That's why you can only update the GUI from the main thread.
Is it possible to use TrackPopupMenu from a secondary thread? I'm trying to use it with TPM_NONOTIFY and TPM_RETURNCMD flags.
In our code, the call to TrackPopupMenu returns immediately without displaying the menu, indicating that the user cancelled the menu.
The same code, when called from the main/gui thread works fine.
You need to run this from the same thread that owns the window to which the menu is attached.
The threading rule in Windows is that windows have affinity to the thread that creates the window. Since TrackPopupMenu receives a window handle, you can assume that it must be called from that window's thread.
In practice on Windows (and all GUI frameworks that I have ever come across), everything related to the GUI should happen in the main thread.