Introduction
I am working with Microsoft Visual Studio 2012 and I was programming a "BirthdayReminder
" application for handling of various date types and output pre-trigger possibilities. The application is a MFC C++ dialog and uses a dialog window as its kernel element. After finishing the reminder processing code, I was approaching the aspect how to control the dialog window (minimize, hide, let it flash, bring it into the foreground, etc.) when outputting a reminder. And now my problems began.
Background
When creating a C++ dialog with the Microsoft Visual Studio, the VS wizard generates two classes for you, a window-less application launcher class and a dialog window class. In my example, the files generated by the wizard were named as follows:
- BirtdayReminder.h (launcher class:
class CBirthdayReminderApp : public CWinApp
) - BirtdayReminder.cpp (launcher class:
class CBirthdayReminderApp : public CWinApp
) - BirtdayReminderDlg.h (dialog window class:
class CBirthdayReminderDlg : public CDialogEx
) - BirtdayReminderDlg.cpp (dialog window class:
class CBirthdayReminderDlg : public CDialogEx
)
Unfortunately, the Visual Studio creates a modal dialog window in InitInstance()
without any window control possibilities for the user.
Summary of the Failed Attempts
- An internet search for "Modeless Dialogs with MFC" delivers several results. All of them are stating that a modeless dialog object must be global. I can absolutely confirm that. However, the Visual Studio does not allow to define the dialog object globally. Although the compiler accepts it, program execution after compilation stops right at the beginning by putting out an assertion failure, saying: dialog object must be
NULL
at this position. I have tried several solution proposals out of the web, but all of them with the same result: assertion failure. Microsoft does not allow a modeless window for a dialog in Visual Studio 2012 (and presumably also some versions before). Some hints or links to a C++ class CModeless
can be found in the web. However, the class itself can no longer be found (except on some private computers). This is a clear indication that Microsoft has exterminated the possibility of modeless dialogs. - In order to control a window, the window-less application launcher must somehow gain control over the whole application. Firstly, this control is not foreseen in a Visual Studio standard project. It is a one-way street. Launch the application and do nothing else. And secondly, it is window-less, thus being cut off from Windows messages or timers. This makes a periodical calling of the launcher impossible. The only way - that I know - to overcome this deficiency is to start a parallel thread, containing an endless loop, thus allowing a periodical status checking and performing of associated actions. I have declared and defined a parallel thread. I started it in
InitInstance()
. I moved the dialog object definition to the top (i.e., before the endless loop) of the parallel thread, thus keeping the dialog object alive as long as the parallel thread is alive. Without success. The dialog window pops up and closes at once again. In trial 2b, I moved all the Visual Studio wizard code from InitInstance()
to the top of the parallel thread, except starting of the parallel thread. I put out a message box on top of the parallel thread as well as within the endless loop in order to be sure that it was running. I can confirm that the parallel thread was really active. However, also this approach failed. The dialog window pops up and closes again at once and the whole application is closed after that. I don't know how and why Microsoft does it, but they do. So, finally I gave up.
Summary of the Successful Attempts
- The simplest solution gains full control over the MODAL dialog window, created by the Visual Studio, within the dialog object itself. In my application, I only want to control the dialog window (show, hide, etc.) and I don't care if the window is modal or modeless. If the dialog object already applies a Windows timer function, you only have to get a dialog window handle and afterwards, you can control the modal window. If you don't have a timer function included, please add one. Here is the trivial code:
CWnd *pWndDialog = NULL;
bool bWindowFound = false;
int iLoopCounter = 0;
void CBirthdayReminderDlg::OnTimer(UINT nIDEvent)
{
iLoopCounter++;
if ((iLoopCounter == 20) && (bWindowFound == false))
AfxMessageBox ("No handle received");
if (bWindowFound == false)
{
pWndDialog = CWnd::FindWindow(NULL,_T("BirthdayReminder"));
if (pWndDialog != NULL) bWindowFound = true;
}
}
- The second successful attempt gains full control over the MODAL dialog window and it applies a parallel thread for this. It uses the original VS wizard code and adds only a parallel thread to it. The code is not fully tested, but minimizing, maximizing and hiding the dialog window worked at once. So, I don't have any doubts that also bringing the dialog window into the foreground will work. Here is the code:
#pragma once
#include "resource.h"
UINT WindowControlByParallelThread (LPVOID pParam);
class CBirthdayReminderApp : public CWinApp {...}
#defines ...
#includes ...
extern int iDialogWindowOptions; extern CEvent g_eventKillParallelProcess;
#includes ...
CEvent g_eventKillParallelProcess;
int iDialogWindowOptions = 0;
UINT WindowControlByParallelThread (LPVOID pParam) {for code see 5.}
BOOL CBirthdayReminderApp::InitInstance()
{
AfxBeginThread(WindowControlByParallelThread, NULL);
CBirthdayReminderDlg dlg;
CWnd* m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
.....
g_eventKillParallelProcess.SetEvent();
return true;
}
UINT WindowControlByParallelThread (LPVOID pParam)
{
int iLoopCounter = 0;
bool bWindowFound = false;
CWnd *pWndDialog = NULL;
while (1)
{
iLoopCounter++;
if ((iLoopCounter > 2)&&
(bWindowFound == false)) AfxMessageBox ("No Handle received");
if (bWindowFound == false)
{
pWndDialog = CWnd::FindWindow(NULL,_T("BirthdayReminder"));
if (pWndDialog != NULL) bWindowFound = true;
}
else
{
if (iDialogWindowOptions == 2) {
if (iLoopCounter % 2 == 0) pWndDialog->ShowWindow(SW_HIDE);
else pWndDialog->ShowWindow(SW_SHOWNORMAL);
}
}
if(::WaitForSingleObject(g_eventKillParallelProcess, 1000) == WAIT_OBJECT_0) break;
}
return 0;
}
Explanation of the (trivial) test code in the parallel thread:
- Define some variables in the top, whereas the pointer to the window is the most important one
- Enter the endless loop and output an error message, if no handle was received after two seconds
- Try to get a dialog window handle
- Let the dialog window flash (1 second on, 1 second off), just as a test
- Jump back to the top of the while loop after 1000 msec
- Break the endless loop, if the kill event was set
- Create a DLL through the VS wizard. Add a dialog class manually. The Visual Studio allows a global dialog object definition and execution through a MODELESS dialog window in DLLs. I have programmed a pretty comprehensive DLL project exactly this way (which did, however, not go into "series production"). Export a
GetWindowControlOption
function (exported by the DLL dialog window) and a function SetWindowControlOption
(exported by the window-less DLL class). Create a Windows application that loads the DLL. Let the application run in an endless loop and request permanently the desired dialog window status through GetWindowControlOption
and execute the desired status afterwards through SetWindowControlOption
.
I like, of course, solution number one the most, because it is really simple.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.