Is there any function called after the OnInitDialog function in MFC? - c++

I want to create a thread after the creation of a dialog box in MFC. Is there any function that Windows has provided and is automatically called after OnInitDialog so that I can create my thread inside it?

You can simply create your thread in the OnInitDialog function. There's no reason to overcomplicate things by going and searching for a different function, or splitting your initialization code up in two pieces. (There also isn't any such function, because there's no corresponding Windows message that is sent.)
If you want to get your dialog box on the screen before you create the thread, you can just show it manually using the ShowWindow function. For example:
ShowWindow(SW_SHOW);
RedrawWindow();
Also see this post by Raymond Chen: Waiting until the dialog box is displayed before doing something

OnInitDialog() is the main function called upon initialization (in reaction to WM_CREATE).
Why can't you create your thread in there?

I have changed the thread priority to below normal and when the thread executes for the first time I set the thread to normal priory. This works fine. Thanks for your response.

After many years of feeling unsatisifed with the OnTimer solution to draw first view graphics in an MFC dialog app (a favorite playground), this seemed like a nice simple solution:-
Add a WM_HSCROLL handler with class wizard.
At the end of OnInitDialog post a hscroll message with a NULL LPARAM
In handler detect the NULL, draw graphics.
a timer meant that the app was alive for some time before graphics happened, and apparently hscroll is prioritized to happen just after the WM_PAINT message which would erase a picture element to its blank state, deleting anything that was drawn during initdialog.
BOOL CSpecDlg::OnInitDialog()
{
...
PostMessage(WM_HSCROLL,0, (LPARAM)NULL);
return TRUE; // return TRUE unless you set the focus to a control
}
void CSpecDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
if (pScrollBar==NULL)
{
plot();
}
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}

Related

changing mouse cursor to wait cursor then starting worker thread and changing back on thread completion

In an older MFC application I have to perform a network connect request to another computer which may take a number of seconds in the case of incorrect computer name before the request times out. So I am starting up a worker thread to make the initial connection so that the user interface is still responsive.
The network connect request is triggered by the user selecting a menu item which brings up a dialog to fill in the target computer information. When the user clicks the Ok button on the dialog, the network connection request is processed using the worker thread.
What I want to do is to change the mouse cursor to a wait indicator and then remove the wait indicator once the connection is actually made or the attempt times out.
What I am running into is that the mouse cursor is remaining a pointer and the mouse cursor is not changing to a wait indicator.
What I originally thought was that I could just change the mouse cursor using the BeginWaitCursor() function. However that has no effect that I can see.
Further reading indicates that I also need to have an override of the afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) method of the CScrollView class however I can't seem to find anything helpful that describes what I need to do in that method. The OnSetCursor() method seems to be called for a variety of reasons and just moving the mouse causes a breakpoint in that method to be triggered.
It looks like that in the OnSetCursor() method I should detect the current application state and based on that use the SetCursor() function to set one of the possible mouse cursor styles which have been previously loaded with LoadCursor(). See Prevent MFC application to change cursor back to default icon as well as Change cursor for the duration of a thread
However I am unsure at to whether that is how it is actually done and what the parameters that are provided with the OnSetCursor() actually mean and how to use them.
In the second of the two above SO postings it appears a global is being used to decide if the default CView::OnSetCursor() method is being called or not.
First declare the following global variables:
BOOL bConnecting = FALSE; // TRUE if connecting, set by your application
HCURSOR hOldCursor = NULL; // Cursor backup
When you need to display the hourglass cursor call:
bConnecting = TRUE;
hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
Once the connection is established (or failed) call:
bConnecting = FALSE;
SetCursor(hOldCursor);
// Alternatively you can call SetCursor(LoadCursor(NULL, IDC_ARROW)); - no need to backup the cursor then
// Or even not restore the cursor at all, it will be reset on the first WM_MOUSEMOVE message (after bConnecting is set to FALSE)
You also need to override OnSetCursor():
BOOL CMainFrame::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (bConnecting) return TRUE; // Prevent MFC changing the cursor
// else call the default
return CFrameWndEx::OnSetCursor(pWnd, nHitTest, message);
}
And add the ON_WM_SETCURSOR() directive to the message map for CMainFrame in order to enable the OnSetCursor() message handler.
The "main-frame" is the parent of all windows in an MFC Application, and that's why we override OnSetCursor() for it. It affects all other windows.
In the MFC environment you can also use the BeginWaitCursor(), RestoreWaitCursor(), and EndWaitCursor() functions. These are CCmdTarget methods and can be accessed using AfxGetApp() as well as any CWnd derived class.
Note that using a global variable in a multi-threaded environment with both UI thread and worker threads, depending on how the global is used and accessed by the threads, you may create a race condition.
#Constantine's OnSetCursor implementation didn't work for me (VC++ 2013; Win 10) - wait cursor still went back to arrow after starting a thread. But I resolved my issue with following code.
BOOL CMainFrame::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (bConnecting) {
SetCursor(LoadCursor(NULL, IDC_WAIT));
return TRUE; // Prevent MFC changing the cursor
}
// else call the default
return CFrameWndEx::OnSetCursor(pWnd, nHitTest, message);
}
Note that that all opened dialogs or views should have the OnSetCursor if you want to display wait cursor when hovering on the views.

How to use a handle as a function parameter?

I was writing code for a win32 application when I came across a problem, how to use a handle as a function parameter. For instance whit this function:
void refreshWindow (HWND myWNDhandle)
{
InvalidateRect(myWNDhandle, NULL, FALSE);
}
If I would pass in "hwnd" as the parameter and run the code, like this:
refreshWindow (hwnd);
I would assume my window will be painted again, unfortunately my window won't.
What did i do wrong?
Mechanically your call is perfect. So either the HWND itself is invalid and, as other commenters suggest, you should assert on IsWindow() to validate that, or you are falling victim to the asynchronous nature of window repainting:
Calling InvalidateRect will merely mark the window as in need of painting, and a subsequent call to GetMessage will generate a paint message to paint the window if there are no other higher priority events or messages to process.
Typically then, methods that want to refresh the contents of a window immediately, follow the call to InvalidateRect with a call to UpdateWindow - which will ensure the window is repainted before returning.

I want to print a text on MFC Dialog

I made a thread to set some text on EditBox on Lobby Dialog. Below is my code.
And MainLobby is the Dialog Class. This project is "Dialog Based MFC Project".
MainLobby Lobby;
_beginthreadex(NULL, 0, ReceiveMessage, (void *)Lobby.GetSafeHwnd(), 0, NULL);
Lobby.DoModal();
But it doesn't work. Where is wrong? I thought hard but I couldn't find the answer.
I tried not Lobby.GetSafeHwnd() but Lobby.m_hwnd
unsigned WINAPI ReceiveMessage(void *arg)
{
HWND hDlg = (HWND)arg;
char msg[BUF_SIZE];
int msgLen;
while( (msgLen = recv(CClientApp::hSocket, msg, BUF_SIZE, 0)) != 0 )
{
SetDlgItemText(hDlg, IDC_LOBBY_CBOX, msg);
}
}
I am using MFC now. I will be glad a good idea.
The device context has to be updated.
Try:
UpdateData(true);
SetDlgItemText(hDlg, IDC_LOBBY_CBOX, msg);
UpdateData(false);
Or try it with a CString member variable for IDC LOBBY CBOX.
It is clear from your code that you are not using MFC properly. A Windows dialog based application does not require the programmer to explicitly create message handlers or receivers, that is what the MFC framework itself is doing for you. There is no reason to call beginthreadex before starting the dialog message loop as part of the DoModal method. When you invoke DoModal on any class descended From Dialog or DialogEX, the window is constructed, the member objects are constructed, and the message loop is started before the WM_INITDIALOG message is sent to the dialog for any other initialization before the dialog window is displayed. Once the window is displayed, the message loop is running, and text will display automatically in an edit control when you send it correctly. Normally one would declare a CEdit object with some name using the Class Wizard in visual studio then map the MFC object to the dialog object. Rather than type out all the details I will refer to this article: http://msdn.microsoft.com/en-us/library/6d1asasd.aspx
The dialog's HWND has not been initialized before DoModal is called. So your thread does not receive the proper HWND. The first place that you can access the correct HWND value is inside the dialog's OnInitDialog member function.
I see two problems in your code.
First you are calling Lobby.GetSafeHwnd before DoModal, so before the window is constructed. You should move the thread creation to the WM_INITDIALOG handler of MainLobby to create the thread there. Or you could create a modeless dialog.
Second you are using _beginthreadex in your MFC code. If you are starting a thread that uses MFC, then you need to start that thread with AfxBeginThread. If you only use CRT, then you use _beginthreadex. If you use neither, then use Windows CreateThread. The reason for this is that each layer (CRT, MFC) needs to do some housekeeping of thread specific information. This can only be done if you call the proper thread creation functions.
Third, you might consider using the MFC socket objects like CAsyncSocket. This object can send windows messages to your dialog if some data is available on the socket. This perfectly fits into your scenario with an MFC dialog that should handle socket data.
Another thing, but you already got that right: to access an MFC GUI object you need to be in the thread of that GUI object. So calling the Windows SetDlgItemText with the raw handle hDlg needs to be done, you cannot call an MFC function here.
BTW, are you sure your overall strategy is right? I don't see how you handle dialog life time, socket life time and how you put both together.

Using modal progress bar dialogs in Windows CE?

I'm writing a Windows CE application in C++ directly applying the WINAPI. In this application I parse a text file, which may or may not be large and thus may or may not take a while to load; and as I will add functionality to load files across the wireless network, I figured it would be best to add a progress bar.
My aim is to display the progress bar in a modal dialog, thereby preventing the user from interacting with the main window. A thread is then created to perform the loading in the background, leaving the main thread to update the GUI.
However, using EndDialog() prevents me to return to the code which loads the file until the dialog has been closed. Obviously I want to show the dialog and then load the load, periodically updating the progress from the background thread. At this point I only know of two options to circumvent this:
Create the dialog using CreateDialog, modify the message handler to accommodate messages designated to the dialog, disable the main window and lastly create the background thread.
Create the background thread in a suspended initial state, create the dialog using DialogBoxParam passing along the thread ID, and when capturing the WM_INITDIALOG resume the thread.
Although any of these two would probably work (I'm leaning towards the second option), I'm curious about whether this is the way progress bars are supposed to be handled in a Windows environment -- or if there is a leaner, more clever way of doing it.
You don't have to do anything particularly tricky or unusual. Just create the modal dialog box with DialogBox(). In the WM_INITDIALOG handler of your dialog box procedure, create the background thread to load the file. As the loading progresses, send the PBM_SETPOS message to the progress bar control to update it.
When the loading completes, call EndDialog() to close the dialog box. However, EndDialog() must be called from within your dialog procedure. So to do that, you need to send a dummy message (e.g. WM_APP):
DialogBox(..., DlgProc);
// File loading is done and dialog box is gone now
...
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, LPARAM lparam, WPARAM wparam)
{
switch(msg)
{
case WM_INITDIALOG:
CreateThread(..., LoadingThread, ...);
return TRUE;
case WM_APP:
EndDialog(hwnd);
return TRUE;
...
}
return FALSE:
}
DWORD WINAPI LoadingThread(LPVOID param)
{
// Load the file
while(!done)
{
...
SendMessage(hwndProgressBar, PBM_SETPOS, progress, 0);
}
SendMessage(hwndDialogBox, WM_APP, 0, 0);
return 0;
}

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.