dalle
dalle

Reputation: 18507

Detecting modal dialogs in MFC

How can I programmatically detect if my MFC application currently is displaying a modal dialog or property sheet? Currently I'm using the following, but I feel that the code also triggers for modeless dialogs.

bool HasModalDialog(const CWnd* pWnd)
{
   const CWnd* pChildWnd = pWnd ? pWnd->GetNextWindow(GW_HWNDPREV) : NULL;
   while (pChildWnd)
   {
      if (pWnd == pChildWnd->GetTopLevelParent() &&
         (pChildWnd->IsKindOf(RUNTIME_CLASS(CDialog)) ||
         pChildWnd->IsKindOf(RUNTIME_CLASS(CPropertySheet))))
      {
         return true;
      }

      pChildWnd = pChildWnd->GetNextWindow(GW_HWNDPREV);
   }

   return false;
}

Usage:

HasModalDialog(AfxGetMainWnd())

Anyone got a alternative way of detecting modal dialogs?

Upvotes: 5

Views: 7255

Answers (4)

user13947194
user13947194

Reputation: 402

I can't believe windows doesn't offer a function to do this; as calling EndDialog for a modeless dialog is undefined behaviour. But modal dialog box must use EndDialog. So if I don't want to create two seperate dialog procedures, how the heck will I know the right way to close the dialog.

Anyhow my simple solution is to to create a custom DialogBox style. Following is list of all dialog box styles

  • DS_FIXEDSYS = 0x0008
  • DS_SETFONT = 0x0040
  • DS_ABSALIGN = 0x0001
  • DS_CENTER = 0x0800
  • DS_CENTERMOUSE = 0x1000
  • DS_CONTEXTHELP = 0x2000
  • DS_CONTROL = 0x0400
  • DS_LOCALEDIT = 0x0020
  • DS_MODALFRAME = 0x0080
  • DS_NOFAILCREATE = 0x0010
  • DS_SETFOREGROUND = 0x0200
  • DS_SYSMODAL = 0x0002
  • DS_3DLOOK = 0x0004
  • DS_NOIDLEMSG = 0x0100

Close inspection shows that all dialog box styles reside on there own bits. Note that I ignored DS_SHELLFONT as it is simply a compound style of DS_SETFONT and DS_FIXEDSYS.

Thus our custom style just simply falls on its own bit.

  • JAV_DS_IS_MODELESS = 0x4000

Then when creating a modeless dialog box we set that style into it. Note that using DS_SYSMODAL style instead might be undefined according to MSDN documentation.

struct WindowContent
{
 virtual void onClose(HWND hwnd) { closeWindow(hwnd,0); }
 void closeWindow(HWND hwnd,int result);
}

BOOL CALLBACK dlgProc(HWND hwnd,UINT msg,WPARAM,LPARAM lparam)
{
    WindowContent *content = (WindowContent*)GetWindowLongPtr(hwnd,GWL_USERDATA);

    if(msg == WM_INITDIALOG
    {
     content = (WindowContent*)lparam;
     SetWindowLongPtr(hwnd,GWL_USERDATA,content);
     return 0;
    }
    if(msg == WM_CLOSE)
    {
     content->onClose(hwnd);
     return 0;
    }

    return 0;
}

HWND createDialog(const char *dlg_template_name,WindowContent *content,HWND owner=NULL)
{
 HWND dlg = CreateDialogParamA(NULL,dlg_template_name,owner,dlgProc,(LPARAM)content);
 UINT old_style = GetWindowLong(dlg,GWL_STYLE);
 SetWindowLong(dlg,GWL_STYLE,old_style|JAV_IS_MODELESS);
 return 0;
}

void WindowContent::closeWindow(HWND hwnd,int result)
{
    if( GetClassLong(hwnd,GCW_ATOM) == (int)WC_DIALOG )
    {
     if(GetWindowLong(hwnd,GWL_STYLE) & JAV_DS_IS_MODELESS) DestroyWindow(hwnd);
     else EndDialog(hwnd,result);
    }
    else DestroyWindow(hwnd); 
}

Upvotes: 0

Alan
Alan

Reputation: 13721

Have you tried CWnd::GetLastActivePopup?

I haven't tested this to see if it'll work for modal dialogs only.


Edit 1: According to Raymond Chen, GetLastActivePopup should return the current active modal dialog.

Edit 2: Perhaps another method to retrieve the current modal window would be to modify your code to check for a disabled parent/owner - modal dialogs should always disable their owner before displaying.

Upvotes: 5

ahmedsafan86
ahmedsafan86

Reputation: 1794

I've tried many ways to solve that, why i needed that because i'm dealing with code that declare all the dialog as pointers to allocated in the heapmemory (TDialog* d = new TDialog) this was OWL code I converted it to MFC I want to delete those pointers automatically only if the dialog is modal it is not allocated in the heap, so i need to check for it my solution was easy to override the DoModal in my inherited class and set a flag isModal to true if it is not shown using DoModal the flag isModal will still null_ptr as it was initialized in the constructor

class  : public CDialog
{
    private:
        bool isModal
    public:
        CMyDlg(int id, CWnd* parent = NULL) : CDialog(id, parent), isModal(false)
        {

        }

        virtual INT_PTR DoModal()
        {
            isModal = true;
            return CDialog::DoModal();//return __super::DoModal();
        }

        bool IsModal()
        {
            return isModal;
        }

        virtual void OnCancel()
        {
            if(isModal)
            {
                CDialog::OnCancel();
            }
            else
            {
                DestroyWindow();
            }
        }

        virtual void OnOk()
        {
            if(isModal)
            {
                CDialog::OnCancel();
            }
            else
            {
                DestroyWindow();
            }
        }
        virtual void PostNcDestroy()
        {
            delete this;
        }
}

Upvotes: 1

Goz
Goz

Reputation: 62323

If you are only detecting windows within your application then you could derive your own CDialog and CPropertySheet and put a simple bool in there that keeps track of whether it is modal or not.

bool HasModalDialog(const CWnd* pWnd)
{
   const CWnd* pChildWnd = pWnd ? pWnd->GetNextWindow(GW_HWNDPREV) : NULL;
   while (pChildWnd)
   {
      if (pWnd == pChildWnd->GetTopLevelParent() )
      {
         if ( pChildWnd->IsKindOf(RUNTIME_CLASS(CMyDialog) )
         {
             return ((CMyDialog*)pChildWnd)->IsModal();
         }

         if ( pChildWnd->IsKindOf(RUNTIME_CLASS(CMyPropertySheet) )
         {
             return ((CMyPropertySheet*)pChildWnd)->IsModal();
         }
      }
      pChildWnd = pChildWnd->GetNextWindow(GW_HWNDPREV);
   }

   return false;
}

There must be another way to do it but thats the only way I can think of off the top of my head.

Upvotes: 0

Related Questions