Re: How to correctly pop a modeless dialog from console using MFC
Just another technical point.
There are other ways to break out of this.
For most of my console applications, I prepare a Console Control
Handler with the WIN32 API function:
SetConsoleCtrlHandler((PHANDLER_ROUTINE)&ConsoleHandler,TRUE);
where ConsoleHandler is a call back proc that will detect various
signals, like control-c, log off, close from the SysMenu pull down
window, etc, allowing to create a graceful shutdown in a console
application.
Here is a ConsoleHandler call back:
HANDLE hExitEvent = NULL; // <<---- SEE BELOW
BOOL ConsoleHandler(DWORD dwCtrlType)
{
switch (dwCtrlType) {
case CTRL_C_EVENT:
PRINTF("CTRL_C_EVENT\n");
return TRUE;
case CTRL_CLOSE_EVENT:
PRINTF("CTRL_CLOSE_EVENT\n");
SetEvent(hExitEvent);
return FALSE; // TRUE for Windows END TASK popup
case CTRL_BREAK_EVENT:
PRINTF("CTRL_BREAK_EVENT\n");
SetEvent(hExitEvent);
return TRUE;
case CTRL_LOGOFF_EVENT:
PRINTF("CTRL_LOGOFF_EVENT\n");
SetEvent(hExitEvent);
return FALSE;
case CTRL_SHUTDOWN_EVENT:
PRINTF("CTRL_SHUTDOWN_EVENT\n");
SetEvent(hExitEvent);
return FALSE;
default:
PRINTF("default\n");
SetEvent(hExitEvent);
return FALSE;
}
}
The hExitEvent is useful to create a good break out *Kernel Object*
signal.
So in the LOOP I provided, it will change to to this:
hExitEvent = CreateEvent(0, TRUE, FALSE, 0);
SetConsoleCtrlHandler((PHANDLER_ROUTINE)&ConsoleHandler,TRUE);
_cprint("* Press ESC to exit\n");
while (WaitForSingleObject(hExitEvent,100) != WAIT_OBJECT_0) {
if (_kbhit() && _getch() == 27) break;
MSG msg;
while (::PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
Now you don't need the Sleep(75) because call to:
WaitForSingleObject(hExitEvent,100)
is the most *efficient* form of synchronization and waiting for events
to occur in Windows! The 100 says wait for 100ms for the hExitEvent
to be signal which is done by the SetEvent() in the control handler.
When its not signaled, windows will return with a timeout result
(WAIT_TIMEOUT) and the loop continues allowing you to check for the
keyboard and do the messages pumping for the GUI.
So while Sleep(75) works, using a Kernel Object to wait on something
is better under windows. Its like saying "Sleep Until the time expires
or until something Happens."
--
HLS
Hector Santos wrote:
Vertilka wrote:
I need to create a console application that has a main() function and
pop a modeless dialog, so the console can still work in parallel to
the modeless dialog (do other work, like communicating with the
modeless dialog).
void main()
{
AfxWinMain(GetModuleHandle(NULL), NULL, GetCommandLine(),
SW_SHOW);
// just to see if the modeless dialog responses
Sleep(10000);
}
In all cases the modeless dialog freeze.
I believe this is a one line solution.
Well, not one line :)
A GUI requires a message pump. All GUI applications start with 1 thread
called the main process thread and the pump is assigned to this thread.
A console application also has 1 thread, the main thread so when you do
the SLEEP() it is blocked and nothing else happens.
So you have two solutions:
1) You can create a 2nd thread with a window and give it is own message
pump. The thread will run on its own independent on the main thread.
2) Instead of blocking in the main thread, you yield with a message pump
dispatch, like so:
while (1) {
MSG msg;
while (::PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
However, there is one problem with that.
You are a console, the keyboard is your input. So unless the Thread
with the WINDOW has a BUTTON to send the EXIT message or set a global
flag, where in the loop you will detect the message or global flag, and
break out of the loop, without that, the only way to break out is to put
a keyboard monitor, like so:
#include <conio.h>
....
_cprint("* Press ESC to exit\n");
while (1) {
if (_kbhit() && _getch() == 27) break;
Sleep(75); // <<-- BE FRIENDLY WITH WINDOWS!
MSG msg;
while (::PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
The low Sleep() is good because without it, the loop is very fast and
there is a lot of "context switching" (look up what that means). It
otherwise it is CPU expensive. You might see a few CPU % without the
sleep because the kbhit checking actually promotes an interrupt so there
is some CPU context switching with it. But with the Sleep(75), you will
see 0% CPU!!!!!!! You can probably go as high with good sensitivity
around 200-400 ms. DO NOT USE SLEEP(0).
Have fun.
--
HLS