Modal dialogs are nice and easy to use. Problem is that they don't allow me to handle the message loop myself. So I thought I could perhaps use a modeless dialog to emulate a modal one and still be in charge of the message loop myself in order to handle accelerators.
Goal
What I want to achieve in general is the ability to press Ctrl+C (and Ctrl+Ins) while the dialog has the focus and then I want to be able to react to that by copying some information into the clipboard. So if anyone knows a way to do that with modal dialogs in WTL, that also would answer my question.
What I am doing right now
Now what I currently do is deriving my dialog class from CDialogImpl<T>
and CMessageFilter
in order to put me in charge of PreTranslateMessage
. In there I simply use CAccelerator::TranslateAccelerator
and CWindow::IsDialogMessage
to process accelerators and dialog box messages.
In OnInitDialog
I populate the accelerator table and add the message filter to the ("global") message loop. The accelerator table has the same resource ID as the dialog itself:
m_accel.Attach(AtlLoadAccelerators(IDD));
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);
Then I created a surrogate for DoModal
by the name PretendModal
which uses the "global" message loop.
Now the effect (other than the dialog appearing on the task bar) that I am seeing is that the application, once the modal dialog gets closed, cannot be closed anymore. To be precise, the main message loop receives WM_QUIT
(the ATLTRACE2
in WTL::CMessageLoop::Run() gives that away, but it still hangs after this stunt (main frame window gets closed, WM_QUIT gets posted, but the process does not exit). The whole thing behaves the same if I use a separate CMessageLoop
inside PretendModal
(instead of the "global" one).
Even moving another separate new instance of CMessageLoop
into its own thread (after all message loops are thread-local) does not seem to resolve this issue. This leaves me puzzled as to what exactly I am doing wrong here.
NB: The handler for IDCANCEL
and IDOK
removes the dialog class (i.e. the message filter) from the message loop.
Question
What am I doing wrong in my attempt to emulate a modal dialog using a modeless one? Alternatively, how can I catch Ctrl+C (and Ctrl+Ins) when using a modal dialog derived just from CDialogImpl<T>
.
The class
class CAboutDlg :
public CDialogImpl<CAboutDlg>,
public CMessageFilter
{
CAccelerator m_accel;
public:
enum { IDD = IDD_ABOUT };
BEGIN_MSG_MAP(CAboutDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(IDOK, OnCloseCmd)
COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd)
END_MSG_MAP()
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
if (!m_accel.IsNull() && m_accel.TranslateAccelerator(m_hWnd, pMsg))
return TRUE;
return CWindow::IsDialogMessage(pMsg);
}
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
m_accel.Attach(AtlLoadAccelerators(IDD));
if (m_bModal)
{
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);
}
return TRUE;
}
void PretendModal(HWND hwndParent = ::GetActiveWindow())
{
CMessageLoop* pLoop = _Module.GetMessageLoop();
if (pLoop && ::IsWindow(hwndParent))
{
HWND dlg = Create(*this);
if (::IsWindow(dlg))
{
ShowWindow(SW_SHOW);
pLoop->Run();
}
}
}
LRESULT OnCloseCmd(WORD, WORD, HWND, BOOL&)
{
if (m_bModal)
EndDialog(0);
else
{
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->RemoveMessageFilter(this);
::DestroyWindow(*this);
}
return 0;
}
};