|
РАССЫЛКА САЙТА
RSDN.RU |
Здравствуйте! Message Box и немного фантазии Автор: Paul Bludov
|
СОВЕТ Подробнее о хуках можно прочитать на http://www.rsdn.ru/article/?baseserv/winhooks.xml |
Все, что нужно – это установить локальный хук, вызвать ::MessageBox(), выполнить в обработчике хука все необходимые действия и снять хук по завершении ::MessageBox().
Тут имеется небольшая проблема: стандартное окно сообщения использует локальный цикл обработки сообщений (message pump), и окон, появившихся в результате вызова ::MessageBox(), может быть несколько. На самом деле все не так плохо: первое оповещение типа HCBT_CREATEWND, пришедшее в наш обработчик, даст нам HWND окна сообщения, которое мы и будем использовать в дальнейшем.
Листинг 2. Код, добавляющий 'галочку' в стандартное окно сообщения
class CMessageBoxPatcher : public CThunk<CMessageBoxPatcher, HOOKPROC> { BOOL CalcCheckBoxRect ( RECT *prectCheckBox , int *nGap ) { HWND hwndTextOrIcon; RECT rectTmp; // Ищем иконку или текст, если иконки нет hwndTextOrIcon = ::FindWindowEx(m_hwndMessageBox, NULL, _T("STATIC"), NULL); if (!hwndTextOrIcon) return FALSE; if (!::GetWindowRect(hwndTextOrIcon, &rectTmp)) return FALSE; // Тут мы получили .left, отступ по вертикали, и, возможно, .bottom prectCheckBox->left = rectTmp.left; ::MapWindowPoints(NULL, m_hwndMessageBox, (LPPOINT)&rectTmp, 1); *nGap = rectTmp.top; prectCheckBox->bottom = rectTmp.bottom; // Ищем текст (если до этого нашли иконку) hwndTextOrIcon = ::FindWindowEx(m_hwndMessageBox, hwndTextOrIcon , _T("STATIC"), NULL); if (hwndTextOrIcon && !::GetWindowRect(hwndTextOrIcon, &rectTmp)) return FALSE; // получили .right && .bottom prectCheckBox->right = rectTmp.right; if (rectTmp.bottom > prectCheckBox->bottom) prectCheckBox->bottom = rectTmp.bottom; // Теперь нужно рассчитать размер текста и галочки HDC hdcMessageBox = ::GetWindowDC(m_hwndMessageBox); if (!hdcMessageBox) return FALSE; rectTmp.left = ::GetSystemMetrics(SM_CXMENUCHECK); rectTmp.right -= prectCheckBox->left; rectTmp.top = 0; rectTmp.bottom = 0x4000; ::DrawText(hdcMessageBox, m_lpCheckBoxString, -1, &rectTmp, DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX); ::ReleaseDC(m_hwndMessageBox, hdcMessageBox); // Получили .top prectCheckBox->top = prectCheckBox->bottom - rectTmp.bottom; return ::MapWindowPoints(NULL, m_hwndMessageBox, (LPPOINT)prectCheckBox, 2); } HWND InsetCheckBox() { RECT rectCheckBox; RECT rectWindow; int nHeightGrow; HWND hwndCheckBox = NULL; if (!CalcCheckBoxRect(&rectCheckBox, &nHeightGrow)) return NULL; // Создаем галочку hwndCheckBox = ::CreateWindowEx(WS_EX_NOPARENTNOTIFY, _T("BUTTON"), m_lpCheckBoxString, BS_LEFT | BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP | WS_CHILD | WS_VISIBLE, rectCheckBox.left, rectCheckBox.top, rectCheckBox.right - rectCheckBox.left, rectCheckBox.bottom - rectCheckBox.top, m_hwndMessageBox, NULL, NULL, 0); if (hwndCheckBox) { // Устанавливаем нужный шрифт ::SendMessage(hwndCheckBox, WM_SETFONT, ::SendMessage(m_hwndMessageBox, WM_GETFONT, 0, 0), FALSE); // Выставляем начальное состояние if (m_bNoMore) ::SendMessage(hwndCheckBox, BM_SETCHECK, BST_CHECKED, 0); } // Увеличиваем окно и сдвигаем все кнопки вниз if (::GetWindowRect(m_hwndMessageBox, &rectWindow)) { nHeightGrow += (rectCheckBox.bottom - rectCheckBox.top); ::SetWindowPos(m_hwndMessageBox, NULL, 0, 0, rectWindow.right - rectWindow.left, rectWindow.bottom - rectWindow.top + nHeightGrow, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); MoveButtonsDown(nHeightGrow); } return m_hwndCheckBox = hwndCheckBox; } void MoveButtonsDown ( int nDistance ) { HWND hwndButton = NULL; RECT rectButton; while (hwndButton = ::FindWindowEx(m_hwndMessageBox, hwndButton, _T("BUTTON"), NULL), hwndButton) { ::GetWindowRect(hwndButton, &rectButton); ::MapWindowPoints(NULL, m_hwndMessageBox, (LPPOINT)&rectButton, 2); ::SetWindowPos(hwndButton, NULL, rectButton.left, rectButton.top + nDistance, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); } } bool IsOurWindow ( HWND hwnd ) const { ATLASSERT(m_hwndMessageBox); return m_hwndMessageBox == hwnd; } LRESULT CBTProc ( int nCode, WPARAM wParam, LPARAM lParam ) { HWND hwnd = (HWND)wParam; if (HCBT_CREATEWND == nCode && !m_hwndMessageBox) m_hwndMessageBox = hwnd; else if (HCBT_ACTIVATE == nCode && !m_hwndCheckBox && IsOurWindow(hwnd)) InsetCheckBox(); else if (HCBT_DESTROYWND == nCode && IsOurWindow(hwnd)) m_bNoMore = (BST_CHECKED == ::SendMessage(m_hwndCheckBox, BM_GETCHECK, 0, 0)); return ::CallNextHookEx(m_hHook, nCode, wParam, lParam); } public: CMessageBoxPatcher ( LPCTSTR lpCheckBoxString, bool bNoMoreByDefault = false ) : CThunk<CMessageBoxPatcher, HOOKPROC>((TMFP)CBTProc, this), m_bNoMore(bNoMoreByDefault), m_lpCheckBoxString(lpCheckBoxString), m_hwndCheckBox(NULL), m_hwndMessageBox(NULL) { m_hHook = ::SetWindowsHookEx(WH_CBT, GetThunk(), NULL, ::GetCurrentThreadId()); } ~CMessageBoxPatcher() { if (m_hHook) ::UnhookWindowsHookEx(m_hHook); } bool GetBoxState() const { return m_bNoMore; } private: HHOOK m_hHook; HWND m_hwndCheckBox; HWND m_hwndMessageBox; bool m_bNoMore; LPCTSTR m_lpCheckBoxString; }; inline int WINAPI MessageBox ( IN HWND hwnd, IN LPCTSTR lpText, IN LPCTSTR lpCaption, IN UINT uType, IN LPCTSTR lpCheckBoxString, IN OUT PBOOL pbNoMore ) { CMessageBoxPatcher patcher(lpCheckBoxString, !!*pbNoMore); int nRet; nRet = ::MessageBox(hwnd, lpText, lpCaption, uType); *pbNoMore = patcher.GetBoxState(); return nRet; }
ПРИМЕЧАНИЕ
Чтобы "превратить" обработчик хука в функцию-член класса, в данном примере используется механизм переходников, thunks.
100% гарантии не дает и этот способ: он рассчитан на то, что у окна сообщения в следующей версии Windows не будет, например, двух иконок, или кнопок сверху.
Ведущий рассылки: Алекс Jenter jenter@rsdn.ru
Публикуемые в рассылке материалы принадлежат сайту RSDN.