From: Vertilka on
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).

Whatever i tried, i could only pop a modal dialog. (where the console
is in hold till the modal dialog close itself).

When switching to modeless dialog using Create() and ShowWindow() the
dialog is displayed without its controls and it freeze / block (you
can see the hourglass cursor).

1) I tried to pop the modeless dialog from the main() function:

void main()
{
AfxWinInit(GetModuleHandle(NULL), NULL, GetCommandLine(),
SW_SHOW);

TestGUI * gui;
gui = new TestGUI();
gui->Create(TestGUI::IDD);
gui->ShowWindow(SW_SHOW);

// just to see if the modeless dialog responses
Sleep(10000);
}

2) I tried to pop the modeless dialog from the InitInstance() of a
CWinApp derived class:

extern int AFXAPI AfxWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow);

class MyApp : public CWinApp
{
public:
virtual BOOL InitInstance()
{
gui = new TestGUI();
gui->Create(TestGUI::IDD);
gui->ShowWindow(SW_SHOW);

return TRUE;
}

private:
TestGUI * gui;
};

MyApp my_app;

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.
Please help.

TNX,
Vertilka
From: Hector Santos on
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
From: Hector Santos on
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
From: Joseph M. Newcomer on
I'm curious why you need a "console application" AND a modeless dialog. You have not
really stated the rationale for this.

Key here is that you have no message pump. To be honest, this is one of the very few
weird and exotic situations where I would consider launching a secondary thread, using
AfxBeginThread for a UI thread, and let the UI thread launch the modeless dialog. Any UI
components would be in this one-and-only window-owning UI thread.

It is not clear what you gain by building a console app.
joe

On Sat, 27 Mar 2010 14:28:52 -0700 (PDT), Vertilka <vertilka(a)gmail.com> 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).
>
>Whatever i tried, i could only pop a modal dialog. (where the console
>is in hold till the modal dialog close itself).
>
>When switching to modeless dialog using Create() and ShowWindow() the
>dialog is displayed without its controls and it freeze / block (you
>can see the hourglass cursor).
>
>1) I tried to pop the modeless dialog from the main() function:
>
>void main()
>{
> AfxWinInit(GetModuleHandle(NULL), NULL, GetCommandLine(),
>SW_SHOW);
>
> TestGUI * gui;
> gui = new TestGUI();
> gui->Create(TestGUI::IDD);
> gui->ShowWindow(SW_SHOW);
>
> // just to see if the modeless dialog responses
> Sleep(10000);
>}
>
>2) I tried to pop the modeless dialog from the InitInstance() of a
>CWinApp derived class:
>
>extern int AFXAPI AfxWinMain(HINSTANCE hInstance,
> HINSTANCE hPrevInstance,
> LPTSTR lpCmdLine, int nCmdShow);
>
>class MyApp : public CWinApp
>{
>public:
> virtual BOOL InitInstance()
> {
> gui = new TestGUI();
> gui->Create(TestGUI::IDD);
> gui->ShowWindow(SW_SHOW);
>
> return TRUE;
> }
>
>private:
> TestGUI * gui;
>};
>
>MyApp my_app;
>
>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.
>Please help.
>
>TNX,
>Vertilka
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Vertilka on
Well,
This is a realtime software, it works againt an hardware.
There is a special mode where you can debug the hardware. a GUI is a good
way to interact with the user, in contrary to using a command line.
Developing the GUI is easy with .NET but the marsheling is a quite an
overhead, thats why i tried MFC.