MFC main UI thread workings and modal dialogs - c++

First let me say that I have roughly searched stackoverflow for this, but couldn't find a specific answer.
My question is rather theoretical and there is no problem with the running of any code. Please consider a simple MFC application with a timer event and a button (attached to an OnClick event).
void SampleDlg::OnTimer(UINT_PTR nIDEvent)
{
CString msg;
msg.Format("time: %lld, tid: %d", (int64_t)time(0), GetCurrentThreadId());
SetWindowText(msg);
}
My intuition suggests that if I sleep inside the OnClick event, main UI thread should hang and timer events shouldn't kick in.
void SampleDlg::OnClick()
{
Sleep(10000);
}
That is fine, however if I show a new modal dialog inside the OnClick, the timer events still happen. What is different here?
void SampleDlg::OnClick()
{
CString msg;
msg.Format("tid: %d is waiting...", GetCurrentThreadId());
::MessageBox(GetSafeHwnd(), msg, "Msg", 0);
// at this point msgbox tells us that thread with tid is waiting
// thread with tid wont reach this line until msgbox is closed
}
Edit: I have included GetCurrentThreadId() calls to make what i want to ask more clear.
When I run the code above, both msgbox and the window title gives me the same thread-id: 22012 (for example). My question is, what is the value of PC/IP (program counter or instruction pointer) of thread 22012 when msgbox is being shown?

The MessageBox has it's own message loop, like all modal dialogs.
So it uses PeekMessage/GetMessage. WM_TIMER and WM_PAINT messages are pseudo messages that are generated when the message loop executes.
This means that MessageBox internally calls GetMessage or PeekMessage and this causes the effect that the thread still executes MessageBox, but new messages are delivered to you windows.

Related

Progress Bar doesn't move

I need such logic:
ShowProgressBar();
Sleep(1000); //---> do here some work
HideProgressBar();
MessageBoxW(_T("Wait"), _T("Title"), MB_OK);
ShowProgressBar();
Sleep(1000); //---> do here some work
HideProgressBar();
I call this code in separate thread (not main GUI thread). I got normal dialog window but it's progress bar is immobilized:
Then:
And then:
This is ShowProgressBar function:
progressBarWindow = new Progress(this);
if (!progressBarWindow->Create(IDD_PROGRESSBAR, this))
{
AfxMessageBox(TEXT("BAD PROGRESS BAR"));
return;
}
if(!progressBarWindow->ShowWindow(SW_SHOWNA))
{
AfxMessageBox(TEXT("BAD PROGRESS BAR"));
return;
}
this->BeginModalState();
This is HideProgressBar implementation:
progressBarWindow->EndDialog(0);
this->EndModalState();
Progress - is a class of Progress Bar dialog window. It is absolutely standart, has progress bar element with marque setting:
m_ProgressBar.SetMarquee(TRUE, 10)
in OnInitDialog function
Windows have thread affinity.
You’re calling progressBarWindow->Create() on a background thread. The progress bar now lives in your background thread, and expects the thread to have a message pump.
It doesn’t matter it’s parent window belongs to another thread, Windows is OK doing multi-threaded GUI. However you’re not OK. You’re sleeping with Sleep(1000), so the thread ain’t processing windows messages. I think that’s why no GUI updates in your app.
For most applications, the right way to do threading — do all the GUI in the main thread. Even progress bars logically related to the background threads should still be created in the main GUI thread.
To fix, create all your GUI in the main GUI thread, and only use your separate thread for computations, or IO, or whatever else your work is.
P.S. If your next question going to be “But how to notify the GUI thread when my background thread needs to change progress, or completes its job?” — one method is custom windows messages.
For example, you can send WM_USER + 11 to some window (e.g. to your Progress dialog) when your thread finishes the processing, and WM_USER + 12 when your thread needs to update the progress bar position, passing progress position in lParam or wParam. The message handler will be called in the main thread, where you can update progress, or close the progress popup, or do anything else with your GUI.

Displaying window from ui-thread sometimes blocks the main ui-thread

I am not an MFC expert, and definately not a multithread expert but still, I want to understand this strange behavior. At some places when I create an MFC ui thread in my application it is started and can display a messagebox no problem, with the main ui thread blocked. However sometimes if I create the thread, then the thread execution stops until the main ui thread non-blocked again.
Of course the situation is made up. The main ui thread's blocking, symbolizes some kind of synchronisation, while the ui thread's messagebox symbolizes some more complex ui dialog.
This is more of a theoretical question. I am primary interested in the reason why this could happen? Here is an example :
class MessageBoxThread : public CWinThread
{
DECLARE_DYNCREATE( MessageBoxThread );
public:
virtual BOOL InitInstance()
{
CWinThread::InitInstance();
AfxMessageBox(_T("Some text"));
return TRUE;
}
};
IMPLEMENT_DYNCREATE( MessageBoxThread , CWinThread );
void testing()
{
MessageBoxThread* pMessageBoxThread = new MessageBoxThread ();
pMessageBoxThread->CreateThread();
Sleep(10000);
AfxMessageBox(_T("It Worked!"));
}
So in terms of the example, if I put 'testing' function into CWinApp::InitInstance it is working as expected (showing 'Some text' first then after 10 sec showing 'It Worked!'). However if I put it into a random place deep inside my application, it might happen that the two messageboxes appear at the same time after the 10 sec sleep. What could cause such behavior?
I am not an MFC fan, but knowing Win32 I can see what is happening. By putting it "deep" you probably mean calling the testing() function in some window message processing code - a code that runs on the main UI thread and processes pumped messages. So you are putting your UI thread to sleep which in this case is similar to what a message box or a modal dialog would do. So, your message pump (implemented in CWinApp I think) is blocked by this Sleep() waiting you to complete processing the message in response to which your code is executed (does not matter what message).
Okay, I finally found the answer. When someone creates a ui thread, that person must initialize the m_pMainWnd member which contains the ui threads base window. If you fail to do so, the framework will use the application provided main window, which is the one in the main ui thread. Since that one is blocked because of the Sleep() method, the other ui thread will only respond when the main thread is not blocked.

DLL crashes app after reuse

I use an application that uses mdi and a script can be attached to, and detached from, a mdi window to be run/stopped on demand; this script loads my dll that does some work; it does fine so; however, when I detach the script still all is fine and the application should unload the dll (and it calls dllmain with the appropriate thread_attach/detach and process_attach/detach operations). Now if I try to re-attach the script to the winow, or to attach it to another window, after the dll has been in use once - the main application crashes. I have isloated the problem to a thread that is created by the dll; the tread crates a window; so, I create the thread like so:
if (!hThread) hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
and, when the script is detached it shuts down the thread like so (no matter if the commented-out lines are uncommented-out):
SendMessage(hWnd, WM_DESTROY, 0, 0);
//TerminateThread(hThread, 0);
//WaitForSingleObject(hWndThread, INFINITE);
CloseHandle(hThread);
hThread = NULL;
I'm at a loss here as to why the main app crashes. A different thread (i.e. one that would simply sleep for a second and loop, will do no harm. What gives?
Ok, here are some ideas:
You said your thread opens a window. Do you run a message loop in the thread function, or you expect your window to be serviced by some other message loop?
If you are running your own message loop in the thread, then exiting the loop may or may not happen, depending on how you have written it. If you use something like:
while(GetMessage(&msg, ...) // msg loop in the thread function
{
....
}
DestroyWindow(hWnd); // see comment below
then this requires a WM_QUIT and not WM_DESTROY to exit. Anyway, the best is to send your window a WM_QUIT and after exiting the message loop then call DestroyWindow() to destroy it properly.
Quoting from MSDN:
DestroyWindow function
Destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it. The function also destroys the window's menu, flushes the thread message queue, destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain (if the window is at the top of the viewer chain
After posting WM_QUIT message to your window, your main thread should wait for the window thread to exit. Here is some relevant code:
SendMessage(hWnd, WM_QUIT, 0, 0); // send your quit message to exit the msg loop
if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) // wait up to 5 seconds
{
TerminateThread(hThread, -1); // bad! try to never end here
}
I hope this helps. I use this in a threaded log viewer that uses a window to display log messages.

Callback implementation using Windows message loop

I have C++ library (Win32 Console) where I have implemented an asynchronous function using a timer. The asynchronous method returns device information.
I have created a separate thread, "Timer Thread", to create a hidden window and then I call SetTimer() and then the implemented message loop.
When the timer expires, it enables a callback.
When I use the library in a console application, it is working fine.
In an MFC application I am doing a post message to update the user interface when the callback triggers. The post message is not working.
If I remove the message loop in the library, it is working fine in the MFC application.
I came to conclusion that:
I guess the problem is due to two message loops, one MFC (main thread) and the TimerThread message loop. So when the callback is called and the subsequent PostMessage results in the TimerThread message loop and not reported in the MFC (main thread) message loop.
If I remove the TimerThread message loop then it works fine in the MFC application, but it fails to work in the console application.
How do I overcome this problem?
class IDeviceEnumerationCallback
{
public:
virtual void onDeviceDiscovered(DeviceInfo* pDeviceInfo,unsigned short nNoOfDevice) = 0;
};
class IDeviceDiscovery
{
public:
virtual int InitialiseDiscovery(IDeviceEnumerationCallback*) = 0;
virtual void UnInitialiseDiscovery() = 0;
virtual int EnumerateDevice() = 0;
};
class CDeviceDiscovery:IDeviceDiscovery
{
//Implementation
}
In the MFC/console application I am implementing IDeviceEnumerationCallback to get a callback.
I am using Bonjour API to enumerate the device and all the methods in Bonjour API are callbacks.
I am wating for some time interval to enumerate a device using Bonjour API and then say after 400 ms I am calling a callback to return the result. In the MFC application when the callback is called, I am doing a PostMessage() to update the user interface.
Earlier I tried without Windows message pump. I had a SetTimer function, and it is working with the MFC application, but for the console application, the callback never gets called, so I implemented a message pump here. Now it is not working for MFC application.
First, there's no reason to do what you did: creating a separate threads, then creating a window in it, set the window timer, run the message loop, respond to the WM_TIMER message and invoke the callback.
If you create "your own" thread - you don't actually need all this. You could just implement a simple loop with either Sleep (or WaitForXXXX if you want an abort option), and invoke your callback.
Usually one creates a hidden window with a timer to avoid creating an additional thread. That is, within a thread that operates GUI (and hence - runs the message loop) you create a window, and it will be served by the message loop. Actually this is would you could do in your MFC app.
However, as you said, you want a generic code for both MFC and console apps.
In MFC application I am doing post message to update UI when the
callback triggers.the post message is Not wokring.
What exactly do you mean by "diung post message"? The message should be posted either to a specific window, or to the thread. In the first case it's dispatched to the window procedure, and in the second case the message loop implementation is responsible for handling the message.
If you post your message to a specific window - how do you get its handle (HWND)? Is it your app's main window (AfxGetMainWnd)? What does your thread start working, after the MFC has created the main window, or earlier?
I ask all those question because you seem to be a newbie (no offences), and those are typical mistakes.
The problem is that you should not be creating a hidden window and using SetTimer instead you should be using the MFC worker thread functionality for background work.
//You create a thread like so.
// you need a CMyObject only if you need to pass any information
//to the thread function.
CMyObject *pNewObject = new CMyObject;
AfxBeginThread(MyThreadProc, pNewObject);
//This function will be run in separate thread
UINT MyThreadProc( LPVOID pParam )
{
//The parameter that was passed to this function
CMyObject* pObject = (CMyObject*)pParam;
while( 1 )
{
//add your code to do stuff.
Sleep(5000); //or whatever your SetTimer interval was
}
return 0; // thread completed successfully
}

How can I show a modeless dialog and display information in it immediately?

I want to show a modeless dialog on the screen and display some information in it.
However if I use it the following way, it has some problems:
function()
{
showdialog(XXX).
//heavy work.
update the dialog..
//heavy work.
update the dialog...
}
It seems the dialog displayed, but it does not draw any information in it. It only draw all information when the function is over.
How can I modify the modeless dialog so it will display the information immediately?
There are a few things you can do.
(1) You could post the dialog a message from inside the CDialog::OnInitDialog method and then handle the long function in the message handler of that posted message. That way the dialog will first be displayed and then later the long function will get run.
(2) The second option is to make sure the message loop gets some processing time. So if your long function is some sort of loop just add the occasional call to the ProcessMessages to make sure the message queue is kept empty:
void ProcessMessages()
{
MSG msg;
CWinApp* pApp = AfxGetApp();
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
pApp->PumpMessage();
}
}
Edit: It certainly is possible to use threads is such a situation, but doing so is not always without risk and complexity.
Using threads with a GUI means having to deal with multiple message queues which then means using API's like PostThreadMessage and that introduces a new set of issues to be wary of.
For an example of one such issue refer to this link:
http://msdn.microsoft.com/en-us/library/ms644946(VS.85).aspx
where is says:
Messages sent by PostThreadMessage are
not associated with a window. As a
general rule, messages that are not
associated with a window cannot be
dispatched by the DispatchMessage
function. Therefore, if the recipient
thread is in a modal loop (as used by
MessageBox or DialogBox), the
messages will be lost. To intercept
thread messages while in a modal
loop, use a thread-specific hook.
I use the process message approach in the Zeus IDE and it works very well at making sure the GUI remains responsive to the user. It is also has the advantage of being very easy to implement.
In OnInitDialog, start a worker thread to perform the computations. Post a user message from the worker thread to update the dialog.
This is superior to the ProcessMessages implementation for several reasons:
The code for doing the calculations can be separated out of the UI code, where it does not belong.
The UI remains responsive while the actual calculations are being performed. ProcessMessages allows multiple UI updates during the single calculation function, but the UI will still be blocked during the actual calculations.
Dialog code:
#define WM_NEW_COUNT (WM_USER + 0x101)
BEGIN_MESSAGE_MAP()
ON_MESSAGE(WM_NEW_COUNT, OnNewCount)
END_MESSAGE_MAP()
BOOL CMyDialog::OnInitDialog()
{
CWinThread* pThread = AfxBeginThread(MyCountFunc, this->GetSafeHwnd());
return TRUE;
}
LRESULT CMyDialog::OnNewCount(WPARAM wParam, LPARAM)
{
int newCount = (int)wParam;
// m_count is DDX member, e.g. for label
m_count = newCount;
UpdateData(FALSE);
return 0;
}
The worker thread:
UINT MyCountFunc(LPVOID lParam)
{
HWND hDialogWnd = (HWND)lParam;
for (int i=0; i < 100; ++i)
{
PostMessage(hDialogWnd, WM_NEW_COUNT, i, NULL);
}
}
As a rule of thumb, heavy computations should never be placed in the GUI thread. Since it is a modeless dialog, the dialog will not own the message loop. The ProcessMessage() solution will work, but is IMO not the right way. My suggestion is:
1) Spawn a new thread in OnInitDialog()
2) Have the separate thread post messages to the dialog when something interesting happens. One of these interesting things is that the work is done.
Note, however, that this will mean that you need to perform proper synchronization.
Don't try to do your heavy work all at once. Have the dialog post itself a message in the WM_APP range in OnInitDialog. The WM_APP handler can do part of the heavy work, then do another PostMessage and return. In this way, you allow the message pump to process window messages in between your chunks of processing.