Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / MFC
Alternative
Article

Tutorial - Modeless Dialogs with MFC

Rate me:
Please Sign up or sign in to vote.
3.00/5 (4 votes)
5 Jan 2014CPOL5 min read 17.6K   10   1
This is an alternative for "Tutorial - Modeless Dialogs with MFC"

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

  1. 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.
  2. 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

  1. 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:
    C++
    //BirthdayReminderDlg.cpp:
    
    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;            
        }
        //else: Apply the handle for controlling the dialog window;
    } 
  2. 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:
    C++
    //1. BirthdayReminder.h:
    #pragma once
    #include "resource.h"
    //Added User Code:
    UINT WindowControlByParallelThread (LPVOID pParam);
    //Further Microsoft Code:
    class CBirthdayReminderApp : public CWinApp {...}
    
    //2. BirthdayReminderDlg.h: nothing
    
    //3. BirthdayReminderDlg.cpp: 
    #defines  ... 
    #includes ... 
    //Added User Code:
    extern  int    iDialogWindowOptions; //data exchange dialog window <-> parallel thread
    extern  CEvent g_eventKillParallelProcess; //for killing the parallel thread at the end
    //Further Microsoft Code ... 
    
    //4. BirthdayReminder.cpp:
    #includes ... 
    //Added User Code:
    CEvent    g_eventKillParallelProcess;
    int     iDialogWindowOptions    = 0;
    UINT    WindowControlByParallelThread (LPVOID pParam) {for code see 5.}
    
    BOOL CBirthdayReminderApp::InitInstance()
    {
       //Microsoft Code ...
    
       //User Code - Start parallel Thread before the modal Window Creation:
       AfxBeginThread(WindowControlByParallelThread, NULL);
       //Further Microsoft Code:
       CBirthdayReminderDlg  dlg;    
       CWnd* m_pMainWnd    = &dlg;        
       INT_PTR nResponse    = dlg.DoModal();
       .....
    
       //Again User Code at the Bottom of the File:
       g_eventKillParallelProcess.SetEvent();
       return true;
    } 
     
    //5. Parallel Thread:
    UINT WindowControlByParallelThread (LPVOID pParam)
    {    
      //Definition of some Variables:
      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)    //2 = flashing;
            {
              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
  3. 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionModeless MFC CDialog Pin
geoyar6-Jan-14 9:11
professionalgeoyar6-Jan-14 9:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.