winapi - prevent a context menu from closing - winapi

I have a scenario where a context menu should be keep open. I have used an WH_MSGFILTER hook for receving a context menu messages. When an user clicks on the context menu item a dialog window for choosing color is opened:
case WM_LBUTTONUP:
{
if (colorSelected)
{
//pMSG->message = WM_NULL; doesn't prevent context menu from closing when a dialog box is opened below
currentColor = chooseColor(selectedMenuItemPos); //call ChooseColor winapi
}
break;
}
Here is an example how to prevent a context menu from closing where an user clicks on the context menu item. As you can see in the above code I have also tried that method but it doesn't work when a dialog box is opened. Is it possible to keep a context menu open in such scenario ? Here is a similar question but it can be resolved using a method described above. I'm not interested in other solution, for example a fake menu window. I'm just curious if it is technically possible in winapi.

WH_CBT hook can prevent menu from closing via handling HCBT_DESTROYWND.
Register the hook: SetWindowsHookEx(WH_CBT, CBTProc, NULL, GetCurrentThreadId());
Hook procedure:
HRESULT CALLBACK CBTProc(
int nCode, WPARAM wParam, LPARAM lParam
)
{
switch (nCode)
{
case HCBT_DESTROYWND:
{
if((HWND)wParam == gMenuWindowHandle)
{
return 1; // Return 1 to prevents closing window.
}
}
break;
}
return ::CallNextHookEx(NULL, nCode, wParam, lParam);
}
Result (About is an menu item and About WindowsProject21 is a dialog box):

Related

Hot tracking list item selection in a combo box

I have a combo box and I need to intercept the changement of the selection while the user changes the selection by just hovering with the mouse without clicking. This is for displaying complementary information about the item the user is hovering over.
CBN_SELCHANGE won't do the job, because this message gets fired only when the user has actually changed the selection by clicking on one of the combo box items or when the up/down keys are pressed.
Apparently no message is fired while the user is just hovering over the the combobox.
Illustration
E.g: I need to know when the user moves the mouse from the entry 2 to the entry 33.
This is c++ subclass based on c# article which you mentioned:
LRESULT CALLBACK ComboProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubClass, DWORD_PTR)
{
if (msg == WM_CTLCOLORLISTBOX)
{
COMBOBOXINFO ci = { sizeof(COMBOBOXINFO) };
GetComboBoxInfo(hwnd, &ci);
if (HWND(lParam) == ci.hwndList)
{
int pos = SendMessage(ci.hwndList, LB_GETCURSEL, 0, 0);
OutputDebugStringA(std::to_string(pos).c_str());
OutputDebugStringA("\n");
}
}
if (msg == WM_NCDESTROY)
{
RemoveWindowSubclass(hwnd, ComboProc, uIdSubClass);
}
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
...
SetWindowSubclass(hComboBox, ComboProc, 0, 0);
This was tested on Windows 10.
This can only report the hover selection in drop down list, it can't change the selection.

How To Detect Pushbutton pressing?

Im trying to build simple application (Using Win32 API) which shows a black window within a button which should close the application, The problem is that I cant figure out how detect a PushBotton click.
Little peace of my code for example:
HWND hButton = CreateWindow(TEXT("Button"),TEXT("Exit"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,100,100,50,50,hWnd,0,hInstance,0);
Briefly, you need to give the button an ID and then handle WM_COMMAND messages from that button in your window proc. The article at http://www.infernodevelopment.com/c-win32-api-tutorial gives a decent example.
You need to analyze WM_COMMAND
message in main window procedure:
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
if ((uMsg == WM_COMMAND) && ((HWND)lParam == hButton)) //check MSDN for WM_COMMAND and BN_CLICKED notifications
{
//button was pressed
}
.......
}

Win32 WH_CBT Hook - close window before it is created

I´m a currently running a global hook that watches for a certain window and then closes it with PostMessage(hWnd,WM_CLOSE,0,0);
The DLL containing the hook:
LRESULT CALLBACK MyFunc(int code, WPARAM wParam, LPARAM lParam)
{
switch(code)
{
case HCBT_CREATEWND:
{
HWND hWnd = (HWND)wParam;
//GetWindowText(hWnd, wintext, 80) returns nothing
//if ( strcmp(wintext, ("Kaspersky Internet Security: license notification")) == 0 )
// {
// Beep(70,100);
// PostMessage(hWnd,WM_CLOSE,0,0);
// }
break;
}
case HCBT_ACTIVATE:
{
HWND hWnd = (HWND)wParam;
GetWindowText(hWnd, wintext, 80);
if ( strcmp(wintext, ("Kaspersky Internet Security: license notification")) == 0 )
{
Beep(70,100);
PostMessage(hWnd,WM_CLOSE,0,0);
}
break;
}
}
...
As you can see i´m using GetWindowText(hWnd, wintext, 80) to determine by window title if the currently activated window is the one to be closed. i´m
closing the window when it is being activated and not when it is created. I would like to close the window when it is being created, that is when HCBT_CREATEWND is true.
The thing is that I can´t use GetWindowText(hWnd, wintext, 80) to get the window title and thereby determine if the window should be closed because when HCBT_CREATEWND is called the window hasn't been fully created and has no title, GetWindowText(hWnd, wintext, 80) returns nothing.
To summarize my question: is it possible to get the title of the window or in any other way determine what kind of window it is, when HCBT_CREATEWND is true?
Could you try GetClassName instead of reading the window text?
It's probably more reliable the reading the window text (internationalization). An easy way to discover a window's class name is to use the spy++ utility - a handy tutorial is described here.
The window isn't created yet. Which means you won't be able to call ::GetWindowText(). Instead use the lParam as an LPCBT_CREATEWND. This gives you access to the CREATESTRUCT via a pointer. You can use the lpszName to get access to the window name.
Something like this --
CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch(nCode)
{
case HCBT_CREATEWND:
{
LPCBT_CREATEWND lpCreate = (LPCBT_CREATEWND)lParam;
lpCreate->lpcs->lpszName; // this is the name
break;
}
}
}

Overlay Window not drawing correctly when hooking

The requirement is to draw my information in side of another application's window.
To take care of z order and so forth hooking WH_GETMESSAGE and draw on WM_PAINT seem good.
However some WM_PAINT are intended for the window area of my concern, but other WM_PAINT are for something completely different, like a context menu or button.
Example Notepad is hooked with an overlay writing "Hello" into the Notepad screen. This works fine. However when right clicking Notepad the context menu gets overlay with Hello. Basically the context menu is destroyed.
Is there an elegant way of determining what WM_PAINT is context menu?
LRESULT CALLBACK overlayHook(int code, WPARAM wParam, LPARAM lParam) {
//Try and be the LAST responder to WM_PAINT messages;
LRESULT retCode = CallNextHookEx(hhk, code, wParam, lParam);
//Per GetMsgProc documentation, don't do anything fancy
if(code < 0) {
return retCode;
}
//Assumes that target application only draws when WM_PAINT message is
//removed from input queue.
if(wParam == PM_NOREMOVE) {
return retCode;
}
MSG* message = (MSG*)lParam;
if(message->message != WM_PAINT) {
//Ignore everything that isn't a paint request
return retCode;
}
PAINTSTRUCT psPaint;
BeginPaint(message->hwnd, &psPaint);
HDC hdc = psPaint.hdc;
RECT r = psPaint.rcPaint;
TextOut(hdc, 10, 10, "Hello", 4);
EndPaint(message->hwnd, &psPaint);
return retCode;
}
It is not enough to test for the draw update region, because the context menu could be anywhere and contain the area of my concern.
I don't know of any elegant way to do this, but you could use GetWindowLong() to get the window's style or GetClassName() to get its class name, and then base your filtering decisions on those values.

How to set the text on the "save" button in Windows' file dialog?

I'm trying to set the text on the "save" button of the Windows "Save file as..." dialog.
I've set up the hook, received the message, found the button (nb. If I call "GetWindowText()" I see "&Save" so I know it's the right button).
Next I changed the text using "SetWindowText()" (and called "GetWindowText()" to check it - the text is correct).
But ... the button still says "Save".
I can change the "Cancel" button using the exact same code - no problem. What's so special about the "Save" button? How can I change it.
Code (for what it's worth):
static UINT_PTR CALLBACK myHook(HWND hwnd, UINT msg, WPARAM, LPARAM)
{
if (msg == WM_INITDIALOG) {
wchar_t temp[100];
HWND h = GetDlgItem(GetParent(hwnd),IDOK);
GetWindowTextW(h,temp,100); // temp=="&Save"
SetWindowTextW(h,L"Testing");
GetWindowTextW(h,temp,100); // temp=="Testing"
}
}
I finally made it work....
I'm pretty sure there's something funny going on with the "Save" button but this code will wrestle it into submission:
// I replace the dialog's WindowProc with this
static WNDPROC oldProc = NULL;
static BOOL CALLBACK buttonSetter(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Set the button text on every window redraw....
if (msg == WM_ERASEBKGND) {
SetDlgItemTextW(hwnd,IDOK,L"OK");
}
return oldProc(hwnd, msg, wParam, lParam);
};
// This is the callback for the GetWriteName hook
static UINT_PTR CALLBACK GWNcallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND dlg = GetParent(hwnd);
if (msg == WM_INITDIALOG) {
oldProc = (WNDPROC)GetWindowLongPtr(dlg, GWL_WNDPROC);
if (oldProc !=0) {
SetWindowLongPtr(dlg, GWL_WNDPROC, (LONG)buttonSetter);
}
}
// We need extra redraws to make our text appear...
InvalidateRect(dlg,0,1);
}
You probably need to redraw the window after setting the text.
Try calling UpdateWindow() after setting the text.
Use CDM_SETCONTROLTEXT message to set the text rather than mess with SetWindowText directly, i.e.
SendMessage(hwnd, CDM_SETCONTROLTEXT, IDOK, L"Testing");
http://msdn.microsoft.com/en-us/library/ms646960(VS.85).aspx has more on customizing open/save dialogs

Resources