MFC application gui hangs when some data processing - user-interface

I have application with GUI but it hangs, when i'am clicking the button that actually does all work. What to do to make it run normally and displaying gui changes "online"?

If you don't want to work with background threads and it's a loop of some kind taking so long, you could add this member function to your UI code and call it in the loop:
void CMyDlg::PumpMessages()
{
// Must call Create() before using the dialog
ASSERT(m_hWnd!=NULL);
MSG msg;
// Handle dialog messages
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(!IsDialogMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

Related

PollEvent() in Win32

I'm trying to mimic SFML's PollEvent(Event &event) function in Windows. It seems far more complicated that I imagined. Note that I already encapsulated the window procedure function in my class.
There could be many "window events" in my program - WindowMoved, WindowResized etc.
My first attempt was to have a private data member in the class, defined as WindowEvent *_lastWindowEvent. This variable will be set if PeekMessage() returns a non-zero value, just before DispatchMessage() is called. Then, winProc() will edit _lastWindowEvent, depending on the message it will receive.
The drawback here is that I noticed that winProc() may be called with a MSG parameter regardless of DispatchMessage(), like with the WM_SETCURSOR message.
Then I thought about having instead a std::queue<WindowEvent> in my class, when winProc() continuously pushes WindowEvents to it. The problem here is that sometimes the window procedure function keeps getting messages and won't return. This happens when I drag-move the window (then the WM_MOVING message is continuously called, along with other messages). The code after DispatchMessage() will not run until I release my mouse. This also happens when resizing the window.
Did I grasp anything wrong? How do you think such PollEvent function can be implemented?
Given that PollEvent is primarily for a game loop style design, you can probably poll for what you need while simultaneously servicing the Windows event loop:
class Window
{
HWND _hwnd; // Win32 handle to the window
RECT _lastWindowSize; // last known window size
POINT _lastMousePos; // last known mouse position on window
BYTE _lastKeyboardState[256]; // last known key state
std::list<Event> _events; // unprocessed events
public:
bool PollEvent(Event* pEvent);
};
bool Window::PollEvent(Event* pEvent)
{
// return any previously queued events
if (_events.size() > 0)
{
*pEvent = _events.pop_front();
return true;
}
// process 1 windows event message
if (PeekMessage(&msg, _hWnd, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.msg == WM_QUIT)
{
*pEvent = EXIT_EVENT; // special handling for WM_QUIT
return true;
}
}
// -----------------------------------------
// poll keyboard state
BYTE kbState[256];
GetKeyboardState(kbState);
bool isKeyboardEvent = false;
if (memcmp(_lastKeyboardState, kbState, 256) != 0)
{
// not shown
// compute diff of kbState and _lastKeyboardState
// generate a keyboard event and add to the queue
Event kbevt;
kbevt.type = KeyEvent;
kbevt.code = <computed code based on diff above>
_events.push_back(kbevt);
}
memcpy(_lastKeyboardState, kbState, 256);
// -----------------------------------------
// -----------------------------------------
// poll window size changes
RECT rectWindowSize;
int width, height, oldwidth, oldheight;
GetClientRect(&rectWindowSize);
width = rectWindowSize.right - rectWindowSize.left;
height = rectWindowSize.bottom - rectWindowSize.top;
oldwidth = _lastWindowSize.right - _lastWindowSize.left;
oldheight = _lastWindowSize.bottom - _lastWindowSize.top;
if ((width != oldwidth) || (height != oldheight))
{
Event sizeEvent;
sizeEvent.type = SizeEvent;
sizeEvent.width = width;
sizeEvent.height = height;
_events.push_back(kbevt);
}
_lastWindowSize = rectWindowSize;
// -----------------------------------------
// not shown - computing mouse position, joystick position, text stuff
// if at least one event was queued - return it now
if (_events.size() > 0)
{
*pEvent = _events.pop_front();
return true;
}
return false;
}

Two dialogs in one MFC application

I am trying to make a dialog based MFC application , where two dialogs needs to be shown sequentially.
What that means is , once the first dialog(modal) is shown and dismissed (by pressing OK), the second dialog needs to be brought up.My requirement is the second dialog should be modeless.
But what I observe is the second dialog is shown but none of the message handling function are being called in response of user messages.I think the message map itself is not working, while the overridden functions(like OnInitdialog) are being called. I tried replacing this modeless dialog with a modal one , and alas, the doModal() itself fails.
Here is the little code:
CFirstDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
m_pMainWnd = dlgModeLess;
dlgModeLess->ShowWindow(SW_SHOW);
dlgModeLess->UpdateWindow();
}
Here is the message map of the second dialog:
BEGIN_MESSAGE_MAP(CSecondDlg, CDialog)
ON_MESSAGE(TRAY_MESSAGE,OnTrayMessage)
ON_BN_CLICKED(IDOK, &CSecongDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_RADIO1, &CSecondDlg::OnBnClickedRadio1)
END_MESSAGE_MAP()
I think I am doing something conceptually wrong. Kindly share your thoughts on what need to be done to tackle such a scenario.
As I mentioned in a previous post, it is not necessary that the second dialog be non modal.
Just do something like this:
BOOL CMyTestApp::InitInstance()
{
CMyTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
COtherDlg Dlg ;
m_pMainWnd = &dlg;
if (Dlg.DoModal() == IDCANCEL)
{
...
}
}
else if (nResponse == IDCANCEL)
{
...
}
return FALSE;
}
When you create a modeless dialog box, control will return to your calling function right away, so you need to declare variable dlgModeLess at a global scope and make sure your program/scope is still active until the dialog box finishes
I have solved this problem and this turned out to be interesting.
It seems that Cdialog::Create() itself is not sufficient for creating a fully operative modeless dialog box. We have to supply a win32 style message loop to it.
So this effectively makes two message loops in the program, one provide my MFC framework and the one that I wrote after returning from IDOK. Here is the modified code.
CSecondDlg *dlgModeLess = new CSecondDlg();
dlgModeLess->Create(CSecondDlg::IDD,NULL);
CTrayIconDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MSG leftmsg;
PeekMessage(&leftmsg,m_pMainWnd->m_hWnd,0,0,PM_REMOVE);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, dlgModeLess->m_hWnd, 0, 0)) != 0)
{
if (bRet == -1)
{
// Handle the error and possibly exit
}
else if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
This code works as expected, The interesting thing to note here is the PeekMessage function which removes a WM_QUIT message that is inserted in the thread's message queue once the first dialog is dismissed as we do not want to quite at that point of time. This i believe is done by the MFC framework.

How do I get MessageBox icon using WinAPI

I have a WH_CALLWNDPROC hook code which handles WM_INITDIALOG message to get information about message boxes. I could get "Message", "Title", "Buttons" but I couldnt get "icon" information. I'm trying to use a function like below:
long getIcon(HWND hwnd) { // handle of messagebox dialog
HWND hlbl = GetDlgItem(hwnd,20);
wcout << "LABEL HWND: " << hlbl << endl;
if (hlbl != NULL) {
LRESULT r = SendMessage(hlbl,WM_GETICON,0,0);
return (long)r;
}
return 0;
}
function always returns 0. I have checked by MS Spy++ and I saw that icon handle is 0.
What is the correct way to get icon?
The icon that is displayed on the message box dialog is implemented using a STATIC control with SS_ICON style. You can obtain the icon handle by sending that control the STM_GETICON message.
In the code in your question, the variable named hlbl is actually the window handle of the STATIC control that contains the icon. I'd name it hIconWnd. With that name change, the code to obtain the icon would look like this:
HICON getIcon(HWND hwnd) { // handle of messagebox dialog
HWND hIconWnd = GetDlgItem(hwnd, 20);
if (hIconWnd != NULL) {
return (HICON)SendMessage(hIconWnd, STM_GETICON, 0, 0);
}
return NULL;
}

what the pattern here in the event loop?

In the following code
Draw();
while (WM_QUIT != msg.message)
{
msg = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);
if (msg)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Draw();
}
why draw was called inside and outside the event loop?
That's a typical game loop. It constantly draw frames, the PeekMessage() call ensures Windows messages are dispatched as normal, typically mouse and keyboard input to make the game interactive.
It looks like it's called to do an initial draw before it starts the message pump. If there are no messages, it will have drawn something.

Displaying progressbar using threading in win 32 applicaition!

In my application I have a simple module were I will read files for some process that will take
few seconds..so I thought of displaying a progress bar(using worker thread) while the files are in progress.I have created a thread (code shown below) and also I designed a dialog window with progress control.I used the function MyThreadFunction below to display the progressbar but it just shows only one time and disappears,I am not sure how to make it work.I tried my best inspite of the fact that I am new to threading.
reading files
void ReadMyFiles()
{
for(int i = 0; i < fileCount ; fileCount++)
{
CWinThread* myThread = AfxBeginThread((AFX_THREADPROC)MyThreadFunction,NULL);
tempState = *(checkState + index);
if(tempCheckState == NOCHECKBOX)
{
//my operations
}
else//CHECKED or UNCHECKED
{
//myoperation
}
myThread->PostThreadMessage(WM_QUIT,NULL,NULL);
}
}
thread functions
UINT MyThreadFunction(LPARAM lparam)
{
HWND dialogWnd = CreateWindowEx(0,WC_DIALOG,L"Proccessing...",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
600,300,280,120,NULL,NULL,NULL,NULL);
HWND pBarWnd = CreateWindowEx(NULL,PROGRESS_CLASS,NULL,WS_CHILD|WS_VISIBLE|PBS_MARQUEE,40,20,200,20,
dialogWnd,(HMENU)IDD_PROGRESS,NULL,NULL);
MSG msg;
PostMessage( pBarWnd, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) );
PostMessage(pBarWnd,PBM_SETPOS,0,0);
while(PeekMessage(&msg,NULL,NULL,NULL,PM_NOREMOVE))
{
if(msg.message == WM_QUIT)
{
DestroyWindow(dialogWnd);
return 1;
}
AfxGetThread()->PumpMessage();
Sleep(40);
}
return 1;
}
Do you really want to create a new thread and a progress bar for each individual file? Create the thread outside of the for() loop.
But this is not the right way to do it, your main UI is still dead as a doornail. Windows will turn your main window in a ghost with "Not Responding" in the title bar after a couple of seconds. You want to use a worker thread to do the file manipulation and the main thread to display the progress bar using a dialog that can only be closed when the worker uses PostMessage() to indicate completion.

Resources