summaryrefslogtreecommitdiff
path: root/plugins/ContextHelp/src/dlgboxsubclass.cpp
diff options
context:
space:
mode:
authorGoraf <goraf@miranda-ng.org>2016-02-05 22:40:46 +0000
committerGoraf <goraf@miranda-ng.org>2016-02-05 22:40:46 +0000
commitbacf5e0272c566c30e591152084daf2f684aebe8 (patch)
tree99a7100a05892d1a7c39782f91039dc9a37df7f3 /plugins/ContextHelp/src/dlgboxsubclass.cpp
parent1d862b873baa48f43b4698fb95640f20dab3d4f8 (diff)
ContextHelp: initial commit (adopted)
* working * 32/64-bit compilable git-svn-id: http://svn.miranda-ng.org/main/trunk@16225 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/ContextHelp/src/dlgboxsubclass.cpp')
-rw-r--r--plugins/ContextHelp/src/dlgboxsubclass.cpp492
1 files changed, 492 insertions, 0 deletions
diff --git a/plugins/ContextHelp/src/dlgboxsubclass.cpp b/plugins/ContextHelp/src/dlgboxsubclass.cpp
new file mode 100644
index 0000000000..ab0a63f4dd
--- /dev/null
+++ b/plugins/ContextHelp/src/dlgboxsubclass.cpp
@@ -0,0 +1,492 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#define SC_CONTEXTHELP_SEPARATOR SC_SEPARATOR+1
+#define SC_CONTEXTHELP_DIALOG SC_CONTEXTHELP+1
+
+#define PROP_CONTEXTSTATE _T("HelpPlugin_ContextState")
+#define PROPF_MENUFORCED 0x01 // always show help context menu for ctl (override default)
+#define PROPF_MENUDISABLED 0x02 // never show help context menu for ctl
+#define PROPF_AUTOTIPFORCED 0x04 // always show autotip for ctl (override default)
+#define PROPF_AUTOTIPDISABLED 0x08 // never show autotip for ctl
+
+extern HINSTANCE hInst;
+extern HWND hwndHelpDlg;
+extern WORD settingAutoTipDelay;
+static HHOOK hMessageHook, hKeyboardHook, hEatNextMouseHook = NULL;
+static HANDLE hServiceShowHelp, hServiceSetContext;
+
+struct DlgBoxSubclassData {
+ HWND hwndDlg;
+ WNDPROC pfnOldWndProc;
+ DWORD flags;
+} static *dlgBoxSubclass = NULL;
+static int dlgBoxSubclassCount = 0;
+static CRITICAL_SECTION csDlgBoxSubclass;
+
+#define DBSDF_MINIMIZABLE 0x01 // WS_MINIMIZEBOX style was set on hooked window
+#define DBSDF_MAXIMIZABLE 0x02 // WS_MAXIMIZEBOX style was set on hooked window
+
+struct FindChildAtPointData {
+ HWND hwnd;
+ POINT pt;
+ int bestArea;
+};
+
+// ChildWindowFromPoint() messes up with group boxes
+static INT_PTR CALLBACK FindChildAtPointEnumProc(HWND hwnd, LPARAM lParam)
+{
+ if (IsWindowVisible(hwnd)) {
+ struct FindChildAtPointData *fcap = (struct FindChildAtPointData*)lParam;
+ RECT rcVisible, rc, rcParent;
+ GetWindowRect(hwnd, &rc);
+ GetWindowRect(GetParent(hwnd), &rcParent);
+ IntersectRect(&rcVisible, &rcParent, &rc);
+ if (PtInRect(&rcVisible, fcap->pt)) {
+ int thisArea = (rc.bottom - rc.top)*(rc.right - rc.left);
+ if (thisArea && (thisArea<fcap->bestArea || fcap->bestArea == 0)) {
+ fcap->bestArea = thisArea;
+ fcap->hwnd = hwnd;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// IsChild() messes up with owned windows
+int IsRealChild(HWND hwndParent, HWND hwnd)
+{
+ while (hwnd != NULL) {
+ if (hwnd == hwndParent)
+ return 1;
+ if (hwndParent == GetWindow(hwnd, GW_OWNER))
+ return 0;
+ hwnd = GetParent(hwnd);
+ }
+
+ return 0;
+}
+
+static BOOL CALLBACK RemovePropForAllChildsEnumProc(HWND hwnd, LPARAM lParam)
+{
+ RemoveProp(hwnd, (TCHAR*)lParam);
+
+ return TRUE;
+}
+
+static HWND hwndMouseMoveDlg = NULL;
+static UINT idMouseMoveTimer = 0;
+static LONG cursorPos = MAKELONG(-1, -1);
+static int openedAutoTip = 0;
+
+static void CALLBACK NoMouseMoveForDelayTimerProc(HWND hwnd, UINT msg, UINT idTimer, DWORD time)
+{
+ POINT pt;
+ HWND hwndCtl;
+ struct FindChildAtPointData fcap;
+ UNREFERENCED_PARAMETER(msg);
+ UNREFERENCED_PARAMETER(time);
+
+ KillTimer(hwnd, idTimer);
+ if (idMouseMoveTimer != idTimer)
+ return;
+ idMouseMoveTimer = 0;
+ if (!settingAutoTipDelay || !IsWindow(hwndMouseMoveDlg))
+ return;
+
+ ZeroMemory(&fcap, sizeof(fcap));
+ if (!GetCursorPos(&pt))
+ return;
+ // ChildWindowFromPoint() messes up with group boxes
+ fcap.hwnd = NULL;
+ fcap.pt = pt;
+ EnumChildWindows(hwndMouseMoveDlg, (WNDENUMPROC)FindChildAtPointEnumProc, (LPARAM)&fcap);
+ hwndCtl = fcap.hwnd;
+ if (hwndCtl == NULL) {
+ ScreenToClient(hwndMouseMoveDlg, &pt);
+ hwndCtl = ChildWindowFromPointEx(hwndMouseMoveDlg, pt, CWP_SKIPINVISIBLE);
+ if (hwndCtl == NULL)
+ return;
+ }
+
+ LONG flags = (LONG)GetProp(hwndCtl, PROP_CONTEXTSTATE);
+ if (flags&PROPF_AUTOTIPDISABLED)
+ return;
+ flags = SendMessage(hwndCtl, WM_GETDLGCODE, (WPARAM)VK_RETURN, (LPARAM)NULL);
+ if (flags&DLGC_HASSETSEL || flags&DLGC_WANTALLKEYS)
+ return; // autotips on edits are annoying
+
+ CURSORINFO ci;
+ BOOL(WINAPI *pfnGetCursorInfo)(CURSORINFO*);
+ ci.cbSize = sizeof(ci);
+ *(FARPROC*)&pfnGetCursorInfo = GetProcAddress(GetModuleHandleA("USER32"), "GetCursorInfo");
+ // if(pfnGetCursorInfo && IsWinVer2000Plus()) // call not safe for WinNT4
+ if (pfnGetCursorInfo(&ci) && !(ci.flags&CURSOR_SHOWING))
+ return;
+
+ if (IsRealChild(hwndMouseMoveDlg, hwndCtl) && hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ return;
+ openedAutoTip = 1;
+ PostMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndCtl);
+ }
+}
+
+static LRESULT CALLBACK KeyboardInputHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code == HC_ACTION && idMouseMoveTimer != 0) {
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ }
+ return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
+}
+
+// workaround for WM_HELP (SC_CONTEXTHELP causes an additional WM_LBUTTONUP when selecting)
+static LRESULT CALLBACK EatNextMouseButtonUpHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0 && wParam == WM_LBUTTONUP) {
+ UnhookWindowsHookEx(hEatNextMouseHook); // unhook ourselves
+ hEatNextMouseHook = NULL;
+ return -1;
+ }
+ return CallNextHookEx(hEatNextMouseHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK DialogBoxSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC pfnWndProc;
+ DWORD flags;
+ int i;
+
+ EnterCriticalSection(&csDlgBoxSubclass);
+ for (i = 0; i<dlgBoxSubclassCount; i++)
+ if (dlgBoxSubclass[i].hwndDlg == hwndDlg)
+ break;
+ if (i == dlgBoxSubclassCount) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ return 0;
+ }
+ pfnWndProc = dlgBoxSubclass[i].pfnOldWndProc;
+ flags = dlgBoxSubclass[i].flags;
+ if (msg == WM_NCDESTROY) {
+ struct DlgBoxSubclassData *buf;
+ MoveMemory(dlgBoxSubclass + i, dlgBoxSubclass + i + 1, sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount - i - 1));
+ dlgBoxSubclassCount--;
+ buf = (struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass, sizeof(struct DlgBoxSubclassData)*dlgBoxSubclassCount);
+ if (buf != NULL)
+ dlgBoxSubclass = buf;
+ else if (!dlgBoxSubclassCount)
+ dlgBoxSubclass = NULL;
+ }
+ LeaveCriticalSection(&csDlgBoxSubclass);
+
+ switch (msg) {
+ case WM_INITMENUPOPUP:
+ if (flags&DBSDF_MINIMIZABLE || flags&DBSDF_MAXIMIZABLE) {
+ HMENU hMenu = GetSystemMenu(hwndDlg, FALSE);
+ if ((HMENU)wParam != hMenu)
+ break;
+ int isMin = IsIconic(hwndDlg);
+ int isMax = IsZoomed(hwndDlg);
+ EnableMenuItem(hMenu, SC_RESTORE, MF_BYCOMMAND | (isMin || isMax) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_MINIMIZE, MF_BYCOMMAND | (flags&DBSDF_MINIMIZABLE && !isMin) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | (flags&DBSDF_MAXIMIZABLE && !isMax) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_SIZE, MF_BYCOMMAND | (GetWindowLongPtr(hwndDlg, GWL_STYLE)&WS_THICKFRAME && !isMin && !isMax) ? MF_ENABLED : MF_GRAYED);
+ }
+ break;
+ case WM_MOUSEMOVE: // TrackMouseEvent() would disturb too much
+ if (!settingAutoTipDelay)
+ break;
+ if (cursorPos == lParam)
+ break;
+ cursorPos = lParam;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MOUSEWHEEL:
+ if (!settingAutoTipDelay)
+ break;
+ if (msg != WM_MOUSEMOVE && !idMouseMoveTimer)
+ break;
+ if (openedAutoTip && IsWindow(hwndHelpDlg))
+ DestroyWindow(hwndHelpDlg);
+ openedAutoTip = 0;
+ hwndMouseMoveDlg = hwndDlg;
+ if (hwndHelpDlg == NULL)
+ idMouseMoveTimer = SetTimer(NULL, idMouseMoveTimer, settingAutoTipDelay, (TIMERPROC)NoMouseMoveForDelayTimerProc);
+ break;
+ case WM_CAPTURECHANGED:
+ if ((HWND)lParam == hwndDlg)
+ break;
+ case WM_SHOWWINDOW:
+ case WM_WINDOWPOSCHANGING:
+ case WM_MOVING:
+ case WM_SIZING:
+ case WM_CANCELMODE:
+ case WM_CHILDACTIVATE:
+ case WM_MOUSEACTIVATE:
+ case WM_ACTIVATEAPP:
+ case WM_ACTIVATE:
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ break;
+ case WM_SYSCOMMAND:
+ if ((UINT)wParam == SC_CONTEXTHELP_DIALOG) { // alt. "What's this Dialog?"
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ break;
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+ case WM_CONTEXTMENU:
+ {
+ POINT pt;
+ HWND hwndCtl;
+ struct FindChildAtPointData fcap;
+
+ // workaround for badly coded plugins that do display a context menu
+ // and pass the message to DefWindowProc afterwards (doing a "break;").
+ if (GetTickCount() - GetMessageTime()>10)
+ return 0;
+
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+
+ ZeroMemory(&fcap, sizeof(fcap));
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ // ChildWindowFromPoint() messes up with group boxes
+ fcap.hwnd = NULL;
+ fcap.pt = pt;
+ EnumChildWindows(hwndDlg, (WNDENUMPROC)FindChildAtPointEnumProc, (LPARAM)&fcap);
+ hwndCtl = fcap.hwnd;
+ if (hwndCtl == NULL) {
+ ScreenToClient(hwndDlg, &pt);
+ hwndCtl = ChildWindowFromPointEx(hwndDlg, pt, CWP_SKIPINVISIBLE);
+ if (hwndCtl == NULL)
+ break;
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ }
+ {
+ LONG flags = (LONG)GetProp(hwndCtl, PROP_CONTEXTSTATE);
+ if (flags&PROPF_MENUDISABLED)
+ break;
+ else if (!(flags&PROPF_MENUFORCED)) {
+ int type = GetControlType(hwndCtl);
+ // showing a context menu on these looks silly (multi components)
+ if (type == CTLTYPE_TOOLBAR || type == CTLTYPE_LISTVIEW || type == CTLTYPE_TREEVIEW || type == CTLTYPE_STATUSBAR || type == CTLTYPE_CLC)
+ break;
+ }
+ }
+ if (IsRealChild(hwndDlg, hwndCtl)) {
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP, (hwndCtl == hwndDlg) ? TranslateT("&What's this Dialog?") : TranslateT("&What's this?"));
+ if (TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, hwndDlg, NULL)) {
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL) {
+ DestroyMenu(hMenu);
+ break;
+ }
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndCtl);
+ }
+ DestroyMenu(hMenu);
+ }
+ return 0;
+ }
+ case WM_HELP:
+ {
+ HELPINFO *hi = (HELPINFO*)lParam;
+ if (hi->iContextType != HELPINFO_WINDOW) break;
+ // fix for SHBrowseForFolder() dialog, which sends unhandled help to parent
+ if (!IsRealChild(hwndDlg, (HWND)hi->hItemHandle))
+ break;
+
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+
+ if (!IsWindow(hwndHelpDlg)) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ break;
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hi->hItemHandle);
+ // we need to eat the next WM_LBUTTONDOWN (if invoked by mouse)
+ if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON) & 0x8000 && hEatNextMouseHook == NULL)
+ hEatNextMouseHook = SetWindowsHookEx(WH_MOUSE, EatNextMouseButtonUpHookProc, NULL, GetCurrentThreadId());
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ EnumChildWindows(hwndDlg, RemovePropForAllChildsEnumProc, (LPARAM)PROP_CONTEXTSTATE);
+ {
+ TCHAR text[64];
+ mir_sntprintf(text, sizeof(text), _T("unhooked window 0x%X for context help\n"), hwndDlg);
+ OutputDebugString(text);
+ }
+ SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)pfnWndProc);
+ break;
+ }
+ return CallWindowProc(pfnWndProc, hwndDlg, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK HelpSendMessageHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0) {
+ CWPSTRUCT *msg = (CWPSTRUCT*)lParam;
+ switch (msg->message) {
+ case WM_INITDIALOG: // dialogs and message boxes
+ if (GetClassLong(msg->hwnd, GCW_ATOM) != 32770) // class="#32770"
+ break;
+ if (msg->hwnd == hwndHelpDlg || (DLGPROC)GetWindowLongPtr(msg->hwnd, DWLP_DLGPROC) == HelpDlgProc)
+ break;
+#ifndef EDITOR
+ if ((DLGPROC)GetWindowLongPtr(msg->hwnd, DWLP_DLGPROC) == ShadowDlgProc)
+ break;
+#endif
+ {
+ LONG_PTR style, exStyle;
+ struct DlgBoxSubclassData *buf;
+
+ exStyle = GetWindowLongPtr(msg->hwnd, GWL_EXSTYLE);
+ if (exStyle&WS_EX_CONTEXTHELP)
+ break;
+ style = GetWindowLongPtr(msg->hwnd, GWL_STYLE);
+
+ EnterCriticalSection(&csDlgBoxSubclass);
+ buf = (struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass, sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ break;
+ }
+ dlgBoxSubclass = buf;
+ dlgBoxSubclass[dlgBoxSubclassCount].hwndDlg = msg->hwnd;
+ dlgBoxSubclass[dlgBoxSubclassCount].pfnOldWndProc = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)DialogBoxSubclassProc);
+ dlgBoxSubclass[dlgBoxSubclassCount].flags = 0;
+
+ // WS_EX_CONTEXTHELP cannot be used in conjunction WS_MINIMIZEBOX or WS_MAXIMIZEBOX
+ // solution: switch off WS_MINIMIZEBOX or WS_MAXIMIZEBOX when only one of them is present
+ if (!(style & WS_MINIMIZEBOX) || !(style & WS_MAXIMIZEBOX)) {
+ if (style & WS_MINIMIZEBOX)
+ dlgBoxSubclass[dlgBoxSubclassCount].flags |= DBSDF_MINIMIZABLE;
+ if (style & WS_MAXIMIZEBOX)
+ dlgBoxSubclass[dlgBoxSubclassCount].flags |= DBSDF_MAXIMIZABLE;
+ SetWindowLongPtr(msg->hwnd, GWL_STYLE, style&(~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX)));
+ SetWindowLongPtr(msg->hwnd, GWL_EXSTYLE, (LONG_PTR)exStyle | WS_EX_CONTEXTHELP);
+ }
+ dlgBoxSubclassCount++;
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ }
+ {
+ HMENU hMenu;
+ hMenu = GetSystemMenu(msg->hwnd, FALSE);
+ if (hMenu != NULL && AppendMenu(hMenu, MF_SEPARATOR, SC_CONTEXTHELP_SEPARATOR, NULL)) {
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP, TranslateT("&What's this?"));
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP_DIALOG, TranslateT("&What's this Dialog?"));
+ }
+ }
+ {
+ TCHAR text[64];
+ mir_sntprintf(text, sizeof(text), _T("hooked window 0x%X for context help\n"), msg->hwnd);
+ OutputDebugString(text);
+ }
+ break;
+ }
+ }
+ return CallNextHookEx(hMessageHook, code, wParam, lParam);
+}
+
+static INT_PTR ServiceShowHelp(WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ if (!IsWindow((HWND)wParam))
+ return 1;
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ return 2;
+ }
+ PostMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)wParam);
+
+ return 0;
+}
+
+static INT_PTR ServiceSetContextState(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ LONG flags;
+ EnterCriticalSection(&csDlgBoxSubclass);
+ for (i = 0; i < dlgBoxSubclassCount; i++)
+ if (IsRealChild(dlgBoxSubclass[i].hwndDlg, (HWND)wParam))
+ break;
+ if (i == dlgBoxSubclassCount) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+
+ return 2;
+ }
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ flags = (lParam&HCSF_CONTEXTMENU) ? PROPF_MENUFORCED : PROPF_MENUDISABLED;
+ flags |= (lParam&HCSF_AUTOTIP) ? PROPF_AUTOTIPFORCED : PROPF_AUTOTIPDISABLED;
+
+ return !SetProp((HWND)wParam, PROP_CONTEXTSTATE, (HANDLE)flags);
+}
+
+int InstallDialogBoxHook(void)
+{
+ InitializeCriticalSection(&csDlgBoxSubclass);
+ hServiceShowHelp = CreateServiceFunction(MS_HELP_SHOWHELP, ServiceShowHelp);
+ hServiceSetContext = CreateServiceFunction(MS_HELP_SETCONTEXTSTATE, ServiceSetContextState);
+ hMessageHook = SetWindowsHookEx(WH_CALLWNDPROC, HelpSendMessageHookProc, NULL, GetCurrentThreadId()); // main thread
+ hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardInputHookProc, NULL, GetCurrentThreadId()); // main thread
+
+ return hMessageHook == NULL;
+}
+
+int RemoveDialogBoxHook(void)
+{
+ DestroyServiceFunction(hServiceShowHelp); // does NULL check
+ DestroyServiceFunction(hServiceSetContext); // does NULL check
+ UnhookWindowsHookEx(hMessageHook);
+ if (hKeyboardHook)
+ UnhookWindowsHookEx(hMessageHook);
+ if (hEatNextMouseHook)
+ UnhookWindowsHookEx(hEatNextMouseHook);
+ DeleteCriticalSection(&csDlgBoxSubclass);
+ for (int i = 0; i<dlgBoxSubclassCount; i++)
+ SetWindowLongPtr(dlgBoxSubclass[i].hwndDlg, GWLP_WNDPROC, (LONG_PTR)dlgBoxSubclass[i].pfnOldWndProc);
+ mir_free(dlgBoxSubclass); // does NULL check
+
+ return 0;
+}