Reset cursor in WM_SETCURSOR handler properly - c++

INTRODUCTION AND RELEVANT INFORMATION:
I have made an application that needs to change the look of a cursor into hand when mouse hovers above the static control, but resets it to a normal cursor otherwise.
My initial application was in full screen mode, but recently terms have changed and it must have a resizable window.
This means that my handler for WM_SETCURSOR must be rewritten to reflect newly introduced changes.
Cursors are loaded in WM_CREATE, and I have defined class cursor, like this:
// cursors
case WM_CREATE:
hCursorHand = LoadCursor( NULL, IDC_HAND );
hCursorArrow = LoadCursor( NULL, IDC_ARROW );
// other stuff
In my class:
WNDCLASSEX wc;
// ...
wc.hCursor = hCursorArrow;
//...
This is my old WM_CURSOR handler ( code is simplified for clarity purposes ):
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
SetCursor(hCursorHand);
else
SetCursor(hCursorArrow);
return TRUE;
If cursor hovers above static control, then my handler changes it to hand, else sets it to default cursor ( arrow ).
Bellow is the picture I have sketched in Paint that displays the desired look of the cursor when it hovers above static control, it is on the client area, and when user resizes window.
If additional code snippets are required, ask and I will edit my post, but for now, they are omitted to keep the post short and concise.
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
MY EFFORTS TO SOLVE PROBLEM:
Bellow are the code snippets that I have tried, but they all failed:
First snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
return DefWindowProc( hWnd, msg, lParam, wParam );
Second Snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
break; // based on MSDN example
Third snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
return FALSE;
These set cursor to hand no matter where it is.
If I leave my WM_SETCURSOR handler unchanged, the only problem I get is that instead of sizing arrows, I get regular arrow ( as the cursor’s look ) when I hover over the border, but window can be sized.
If I comment out my WM_SETCURSOR handler, sizing arrows and cursor arrow appear properly, but cursor doesn’t change into hand when hovers above static control ( which is logical, since there is no WM_SETCURSOR handler to change it ).
I have browsed through SO archive, looked on MSDN, CodeProject , DaniWeb, Cprogramming and CodeGuru, but had no success.
Looking through those, I have found an example where people compare low word of the lParam against hit test code.
Looking through MSDN I have found link for hit test values ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx ) and I have found link for cursor types (http://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx ).
Currently I am reading them, because I think that I will have to load additional cursor resources, take several comparisons of hit test values, and then use those resources to set cursor look accordingly.
QUESTION:
I really would like my WM_SETCURSOR handler to look like this:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
// reset cursor's look to default
so I ask the community to instruct me on how to do this.
If this isn’t possible, then I will consider using multiple if statements to check the hit test code, and set cursor’s look accordingly.
Of course, if there is better solution for my problem, please suggest it, I will consider it as well.
Thank you.
Regards.

In general, if you handle the WM_SETCURSOR message you must either
Call SetCursor() to set the cursor, and return TRUE, or
If the message came from a child window, return FALSE for default processing, or
If the message is from your own window, pass the message through to DefWindowProc()
I think the last two points aren't made quite clear by the MSDN docs.
The window under the mouse pointer gets the first WM_SETCURSOR message. If it handles it and returns at that point, nothing else happens. If however it calls DefWindowProc(), then DWP forwards the message to the window's parent to handle. If the parent chooses not to handle it, it can return FALSE and the DefWindowProc processing will continue.
But this only applies if the message came from a previous call to DWP. If the message originated with the window itself, rather than a child, returning TRUE or FALSE without setting the cursor means the cursor won't be set at all.
Another thing: although your question didn't specify, I'm assuming from your use of GetDlgItem() that your top-level window is a dialog. If that's true, you can't just return TRUE or FALSE for a message - you need to return the value using SetWindowLongPtr() and store the return value in DWLP_MSGRESULT. Returning FALSE from a dialog procedure indicates that you didn't handle the message at all - this is equivalent to passing a message through to DefWindowProc().
So I think the proper handling for your situation is, in your top-level window:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
return TRUE;
}
return FALSE;
If your top-level window isn't in fact a dialog, you would do this:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);

Here's my first example, if cursor go to menubar, cursor changes to cursor hand:
HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
if(LOWORD(lParam) == HTMENU)
{
SetCursor(cursorHand);
}
break;
Here's my second example, if cursor goes to button, cursor changes to cursorHand:
HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
if(LOWORD(lParam) == buttonId)//declare you
{
SetCursor(cursorHand);
}
break;
Warning:menubar and button is not created! Create you and please, check my answer example.

The question as I understand it is, given a parent window with one more more child windows (one of which is a static control) how do you set the cursor to the hand when the cursor is over the static control, and the arrow when the cursor is over the client area of the parent window, and let default processing take place when the cursor is over a non-client area of the parent window.
To check things out, I wrote a simple program with a top-level window with a static control as a child window. My first attempt was the following:
1) Set the class cursor of the top-level window to LoadCursor(NULL, IDC_ARROW). This allows the default processing to set the cursor to the arrow when appropriate.
2) Keep track of the position of the mouse cursor by handling the WM_MOUSEMOVE message by calling my HandleWMMouseMove function as follows:
case WM_MOUSEMOVE:
HandleWMMouseMove(lParam);
break;
//ptCurrMousePos is a global variable of type POINT
static void HandleWMMouseMove(LPARAM mousepos)
{
ptCurrMousePos.x = (int)(short)(LOWORD(mousepos));
ptCurrMousePos.y = (int)(short)(HIWORD(mousepos));
}
3) and then all I had to do was handle the WM_SETCURSOR message by calling my HandleWMSetCursor function as follows:
case WM_SETCURSOR:
if (HandleWMSetCursor())
return TRUE;
break;
//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL HandleWMSetCursor(void)
{
if (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic) {
SetCursor(hCursorHand);
return TRUE;
}
return FALSE;
}
this worked fine but I couldn't understand how the code posted in the question was working even partially. So I asked the questioner whether the child window was really a static control. The answer was yes but it was created with the SS_NOTIFY style. So I created my child window with this style and my code stopped working, but the code posted with the question started to work. With the help of the Spy++ program distributed with Visual Studio I learned the following.
Static controls are normally transparent (not in the visual sense, but meaning that even when the mouse is over the transparent window, Windows will consider the mouse to be over the window underneath the transparent window).
When you create a static control with SS_NOTIFY the control is no longer transparent (i.e. it starts to receive an process mouse messages (like WM_MOUSEMOVE), so my code stopped working because it never received WM_MOUSE messages when the cursor was over the static control. On the other hand the code in the question did not work when the static control was created without SS_NOTIFY because without this style the static control was transparent, which meant that the WARAM in the WM_SETCURSOR message was never equal to the static control (in other words Windows never considered the mouse to be over the static control because it was transparent).
It is possible to combine the two approaches by changing the WM_SETCURSOR handler function to the following:
//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL HandleWMSetCursor(WPARAM wParam)
{
if (((HWND)wParam == hwndStatic) || (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic)) {
SetCursor(hCursorHand);
return TRUE;
}
return FALSE;
}

Related

Lock window position on display change

I have an application which has several windows and I want that some of them (which I will call CMyLockedFrameWndEx, because they derive from CFrameWndEx) stay placed where they were after changing system display area.
The Parent of all windows of my application is NULL.
I've managed to already catch the WM_DISPLAYCHANGE message when I drag the position of the 2nd monitor relatively to the first; and I also had come to catch the WM_DEVICECHANGE when I connect or disconnect the 2nd monitor's HDMI cable. I've intercepted them both at CMyLockedFrameWndEx::WindowProc.
The automatic window repositioning occurs after that. I noticed that bacause I've put breakpoints on CMyLockedFrameWndEx::OnWindowPosChanging and CMyLockedFrameWndEx::OnWindowPosChanged and they stop after the events I catched on WindowProc. This workflow seems to be unrelated to the catching of events I described as my WindowProc method is:
LRESULT CMyLockedFrameWndEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_DISPLAYCHANGE)
{
TRACE(_T("DISPLAY CHANGE"));
return 0L;
}
if (message == WM_SYSCOMMAND)
{
TRACE(_T("SYSCOMMAND"));
if (wParam == SC_MOVE)
{
return 0L;
}
}
if (message == WM_WININICHANGE)
{
TRACE(_T("WININICHANGE"));
if (wParam == SPI_SETWORKAREA)
{
return 0L;
}
}
return __super::WindowProc( message, wParam, lParam);
}
and when passing in OnWindowPosChanging or OnWindowPosChanged, the flow doesn't come from the specific cases of WindowProc handled by me. And that is a problem.
I tried to follow the call stack to see what window sent the WM_WINDOWPOSCHANGING or the WM_WINDOWPOSCHANGED message, but I didn't succeed. I have even tried to use Spy++64 to detect who was the the sender of the message, but I didn't succeed. The whole idea of seeing who was the sender was: if it is associated to a system's display change is to detect it beforehand and impeach the auto repositioning to even happen.
As i didn't succeed yet, what can I do to make the window immune to a system's display change?
Thanks.
The good news is: you are, obviously, in control of the placement of your own window.
The bad news - Windows will first move your window to its new position, then send you the WM_DISPLAYCHANGE and WM_SETTINGCHANGE, as seen in this Spy++'s log:
S WM_WINDOWPOSCHANGING lpwp:003CF9AC
S WM_GETMINMAXINFO lpmmi:003CF624
R WM_GETMINMAXINFO lpmmi:003CF624
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:003CF9AC
S WM_MOVE xPos:278 yPos:450
R WM_MOVE
R WM_WINDOWPOSCHANGED
S WM_SETTINGCHANGE wFlag:SPI_ICONVERTICALSPACING pszMetrics:0026E018
R WM_SETTINGCHANGE
S WM_DISPLAYCHANGE cBitsPerPixel:32 cxScreen:2560 cyScreen:1440
R WM_DISPLAYCHANGE
S WM_SETTINGCHANGE wFlag:SPI_SETWORKAREA pszMetrics:0026E018
R WM_SETTINGCHANGE
So - you would have to test the position changing to another screen by yourself. Like in this simplified example, where 2560 is my screen's width:
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS* pWP = (WINDOWPOS*)lParam;
if ((pWP->flags & SWP_NOMOVE) == 0) // it's a move
{
if (pWP->x < 2560)
pWP->flags |= SWP_NOMOVE;
}
return 0;
}
The unhappy news are I can not do this on a preventive way, because the system moves the windows to the primary screen even after sending any useful messages.
The good news are I can react to the move by adding a
ON_WM_WINDOWPOSCHANGED()
line on the class's message map and supply it with its respective handler function:
CMyLockedFrameWndEx::OnWindowPosChanged(WINDOWPOS* lpwndpos)
{
__super::OnWindowPosChanged(lpwndpos);
CFrameWndEx* pFrame=(CFrameWndEx*)::AfxGetMainWnd();
VALIDATE_FPTR(pFrame);
CMyDoc* pDoc=(CMyDoc*)pFrame->GetActiveDocument();
VALIDATE_FPTR(pDoc);
POSITION p= pDoc->GetFirstViewPosition();
while(p)
{
CMyLockedView* pLockedView= dynamic_cast<CLockedView*>(pDoc->GetNextView(p));
if(!pLockedView)
continue;
if(pLockedView->GetParentFrame() == this)
{
this->SetWindowPos(NULL, m_Top, m_Left, 0, 0, SWP_NOSIZE);
break;
}
}
}´
Note, that it helps me that I was persisting the position where the window was in the m_Top and m_Left variables.

win32 C++ Custom Color for Trackbar thumb

I have spent today searching everywhere for a concrete explanation or example of coloring the thumb on a trackbar (slider) in win32 C++. Everything I've found has been partially explained, and in trying every conceivable variation I have come up blank.
The control I have been focused on is defined in my rc file as
CONTROL "",IDC_PLAYSLIDER,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP,5,22,187,15
Essentially, my message handling of NM_CUSTOMDRAW comes down to the following. I have no confidence on my color/hdc handling, but the lack of messages is my primary problem.
INT_PTR CALLBACK dialogproc(HWND h, UINT m, WPARAM w, LPARAM l)
{
switch (m) {
case WM_NOTIFY:
{
switch (((LPNMHDR)l)->code) {
case NM_CUSTOMDRAW:
{
LPNMCUSTOMDRAW lpNMCD = (LPNMCUSTOMDRAW)l;
UINT idc = lpNMCD->hdr.idFrom;
switch (lpNMCD->dwDrawStage) {
case CDDS_PREPAINT:
return CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
{
switch (lpNMCD->dwItemSpec)
{
case TBCD_THUMB:
HGDIOBJ old_pen = SelectObject(lpNMCD->hdc, penSlider);
HGDIOBJ old_brush = SelectObject(lpNMCD->hdc, brushSlider);
return CDRF_NEWFONT;
}
}
break;
}
What I am getting at runtime is a CDDS_PREPAINT on the correct control, but no matter what I have tried, I have had no further CDDS_ drawStage messages.
If anyone has done this on a trackbar (most examples are list controls) and is willing to share their message handler code, or can otherwise shed light on my confusion, that would be greatly appreciated.
From the docs for NM_CUSTOMDRAW:
If this message is handled in a dialog procedure, you must set the
return value as part of the window data before returning TRUE. For
more information, see DialogProc.
The DialogProc docs say:
If the dialog box procedure processes a message that requires a
specific return value, the dialog box procedure should set the desired
return value by calling SetWindowLong(hwndDlg, DWL_MSGRESULT, lResult)
immediately before returning TRUE
Note that with the advent of 64-bit windows it is better practice to use SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, lResult).

C++: Trying to hook a message box and change its position

I recently started coding in C++ and I am very new to it. (I code in Javascript, PHP, Java and Obj-C more often)
I'm practicing how to hook a message box and change its position. This is what I have in my .cpp file (after reading this SO post).
#include <iostream>
#pragma comment(lib,"User32.lib")
#include <windows.h>
HHOOK hhookCBTProc = 0;
LRESULT CALLBACK pfnCBTMsgBoxHook(int nCode, WPARAM wParam, LPARAM lParam){
if (nCode == HCBT_CREATEWND)
{
CREATESTRUCT *pcs = ((CBT_CREATEWND *)lParam)->lpcs;
if ((pcs->style & WS_DLGFRAME) || (pcs->style & WS_POPUP))
{
HWND hwnd = (HWND)wParam;
SetWindowPos(hwnd, HWND_TOP,130,122, 0, 0,SWP_NOSIZE);
}
}
return (CallNextHookEx(hhookCBTProc, nCode, wParam, lParam));
}
int main(void)
{
hhookCBTProc = SetWindowsHookEx(WH_CBT,pfnCBTMsgBoxHook,
0, GetCurrentThreadId());
int sResult = MessageBox ( NULL, "Hooked!", "oh my", MB_OK );
UnhookWindowsHookEx(hhookCBTProc);
return 0;
}
For some reason the position of the message box isn't changing. Where did it go wrong?
(I know I can create a customized window or dialog. But I am doing it this way because I want to learn how to hook a message box and where I did wrong.)
Firstly you should check in the debugger that your hook is actually being called, if you haven't already.
Secondly, at the time the HCBT_CREATEWND hook event is triggered, the window has only just been created - the system has yet to size and position it. It will do this with the values in the CREATESTRUCT after the hook returns - overriding your SetWindowPos call.
See the docs from MSDN on the lParam value for this particular hook event:
Specifies a long pointer to a CBT_CREATEWND structure containing
initialization parameters for the window. The parameters include the
coordinates and dimensions of the window. By changing these
parameters, a CBTProc hook procedure can set the initial size and
position of the window.
Therefore, the correct way to use this hook to change a window's position is to modify the values in the CREATESTRUCT directly.
Also note that it's quite possible that the dialog manager sizes and positions the window after creation, so if you find that this still isn't working for you, you may need to try watching for the HCBT_MOVESIZE event instead.
From the docs
At the time of the HCBT_CREATEWND notification, the window has been
created, but its final size and position may not have been determined
and its parent window may not have been established.
Maybe try hooking into CBT_ACTIVATE instead.

C++ window draggable by its menu bar

So I'm using Visual C++, and I have created a draggable, borderless window. Anyway, there is a toolbar along the top, and i want to be able to drag the window by that toolbar. I still want the toolbar to be functional, but i have no earthly idea how to be able to drag the window by it. This is my current window (see the toolbar on the top):
And this is my current code to make it draggable:
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if(hit == HTCLIENT) hit = HTCAPTION;
return hit;
}
break;
You are on the right track with hooking WM_NCHITTEST. Now, you need to make change what constitutes a client hit versus a caption hit. If I understand your code right now, anywhere you click within the client area of the window (everything but the border) will allow you to drag the window elsewhere. This will make interacting with your application very difficult. Instead, you should be returning HTCAPTION only after you have determined that the hit was within the menubar area. Specifically, the area of the menubar that does not hold the File/Edit/Help buttons.
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if (hit == HTCLIENT) { // The hit was somewhere in the client area. Don't know where yet.
// Perform your test for whether the hit was in the region you would like to intercept as a move and set hit only when it is.
// You will have to pay particular attention to whether the user is actually clicking on File/Edit/Help and take care not to intercept this case.
// hit = HTCAPTION;
}
return hit;
break;
}
Some things to keep in mind here:
This can be very confusing to a user that wants to minimize, close, or move your application. Menubars do not convey to the user that you can move the window by dragging them.
If you are concerned with vertical pixels you may consider doing what other applications on Windows are starting to do -- moving the menubar functionality to a single button that is drawn in the titlebar. (See recent versions of Firefox/Opera or Windows explorer in Windows 8 for some idea to move things to the titlebar.
In one of my applications I also wanted to make the window what I call "client area draggable". Unfortunately the mentioned solution (replacing HTCLIENT with HTCAPTION)
does have a serious flaws:
Double-clicking in the client area now shows the same behaviour as
double-clicking the caption (i.e. minimizing/maximizing the window)!
To solve this I did the following in my message handler (excerpt):
case WM_MOUSEMOVE:
// Move window if we are dragging it
if (mIsDragging) // variable: bool mIsDragging;
{
POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
mIsDragging = (ClientToScreen(hWnd, &mousePos) &&
SetWindowPos(hWnd,
NULL,
mDragOrigin.left + mousePos.x - mDragPos.x,
mDragOrigin.top + mousePos.y - mDragPos.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
}
break;
case WM_LBUTTONDOWN:
// Check if we are dragging and safe current cursor position in case
if (wParam == MK_LBUTTON)
{
POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
if (ClientToScreen(hWnd, &mousePos) &&
DragDetect(hWnd, mousePos))
{
// Check if the cursor is pointing to your new caption here!!!!
mIsDragging = true;
mDragPos = mousePos;
GetWindowRect(hWnd, &mDragOrigin);
SetCapture(hWnd);
}
}
break;
// Remove this case when ESC key handling not necessary
case WM_KEYDOWN:
// Restore original window position if ESC is pressed and dragging active
if (!mIsDragging || wParam != VK_ESCAPE)
{
break;
}
// ESC key AND dragging... we restore original position of window
// and fall through to WM_LBUTTONUP as if mouse button was released
// (i.o.w. NO break;)
SetWindowPos(hWnd, NULL, mDragOrigin.left, mDragOrigin.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
case WM_LBUTTONUP:
ReleaseCapture();
break;
case WM_CAPTURECHANGED:
mIsDragging = false;
break;
The (pseudo) code omits the return values (default: 0) and variable definitions but
should make the procedure clear anyway!? (If not drop me a line and I'll add more
or all code).
ps: I just found another comprehensive description which also explains the differences
of these two solutions: http://tinyurl.com/bqtyt3q

Getting handle of child window in C++

I want to get handle of child window, but cannot.
I want to automatically insert text in an Edit control.
Here is what I have:
//global var
int id=0;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
id = GetDlgCtrlID(hwnd);
HWND hwnd_Child;
hwnd_Child = GetDlgItem(hwnd, id);
SendMessage(hwnd_Child, WM_SETTEXT, NULL, (LPARAM)"mYtexttttt");
/* other code */
// id is always 0, why ?
// return FALSE; // stop enumerating
return TRUE; // continue enumeration
}
void MyFunction ()
{
HWND hwnd_Parent;
hwnd_Parent = FindWindow(NULL,"MyTitle"); if(! hwnd)return;
EnumChildWindows(hWnd ,(WNDENUMPROC)EnumWindowsProc, NULL);
}
In WinSpy++, the control IDs of all the components are 000000000. Is it possible that the control ID isn't defined?
How do I find the child handle of some specific (edit box) component if I have the handle of the main window, and the control ID is not defined?
Also, main window is a game application which runs with DirectX, I think.
I want to automatically insert text in a Edit control.
If WinSpy tells you that the control IDs are 0 then getting a 0 back from GetDlgCtrlID() is of course the expected outcome. You'll have to do something else, like counting down the number of EnumWindowsProc() calls and/or calling GetClassName() so you can see it is an edit control. Or GetWindowRect() to go by position.
In your callback method, please do the following:
// get the window text
GetWindowText(hWnd, (LPTSTR)windowText, MAX_PATH);
// get the window info
WINDOWINFO objWinInfo;
GetWindowInfo(hWnd, &objWinInfo)
The above should help you get the child windows.
Also you can do FindWindowEx to the get the child windows.
Control ids are one convenient way to identify child windows; but not the only way. So it is quite possible that control id will be set to arbitrary (or zero) values (in which case the parent window just refers to them directly by their handles.
From your point of view if no control ids exist you must reference them by the position in the tab sequence which should be constant and can be iterated through.