Reputation: 18507
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
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
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.
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
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
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
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