I've found people asking this question on some forums, but no solutions. This is a small but annoying cosmetic problem many people know.
Some full screen programs disable the shadow under the cursor in Windows. The shadow usually comes back, but when is doesn't (for example the program didn't close normally) the mouse appears without shadow, and you have to go and manually enable it back.
The solution I'd like to do is a .bat or .vbs to enable the shadow, only I haven't figured how.
What I did find is that it's a registry value, and there is also something to do to "refresh" the cursor and make the shadow appear. Can anyone help?
I'd like to add that I have very small experience in .bat or .vbs writing, so if you know what to do and how, please post the how too.
Using the API makes it take effect immediately.
#include <Windows.h>
int main() {
BOOL didSucceed = SystemParametersInfo(
SPI_SETCURSORSHADOW,
0,
(PVOID) TRUE,
SPIF_UPDATEINIFILE + SPIF_SENDCHANGE);
return didSucceed ? EXIT_SUCCESS : EXIT_FAILURE;
}
It's located in the famous (yet undocumented :-) "UserPreferencesMask" registry key.
Here are some pointers:
A first general link with information on this key, and how the mouse shadow setting is defined: HKCU\Control Panel\Desktop\UserPreferencesMask
And a sample that explain how to code it using VBSCript (it's another key, but the principle is the same): Set UserPreferencesMask Binary Registry Key
Related
I need to perform several operations on a list of windows (minimize some of them, restore others) in order to switch between two or more set of windows at once.
The problem with this are those animations you can see when minimizing and restoring a window. The whole process look terrible with all those animations going in and out, up and down.
I cannot, however, disable those animations because this is for other computers and i dont want to change other people's settings, plus those animations are actually useful when you minimize/restore one window only (i.e. when YOU do it manually) because you can see what is happening, but for doing it programmatically on several windows at a time, it's not nice.
I'm currenlty using the SendMessage function to send the WM_SYSCOMMAND message with params SC_MINIMIZE/SC_RESTORE. I dont know whether there is another way.
So, the question:
How can I minimize/restore a window programatically without the animation effect??
PS: The programming language is not important. I can use any language that's nessesary for accomplishing this.
SetWindowPlacement with SW_SHOWMINIMIZED or SW_RESTORE as appropriate for showCmd in WINDOWPLACEMENT seems to bypass window animation. I'd keep an eye on the functionality for future versions of the OS though since documentation does not mention anything about animation.
How about Hide > Minimize > Show ?
You could temporarily disable the animations and then restore the user's original setting.
class WindowsAnimationSuppressor {
public:
WindowsAnimationSuppressor() : m_suppressed(false) {
m_original_settings.cbSize = sizeof(m_original_settings);
if (::SystemParametersInfo(SPI_GETANIMATION,
sizeof(m_original_settings),
&m_original_settings, 0)) {
ANIMATIONINFO no_animation = { sizeof(no_animation), 0 };
::SystemParametersInfo(SPI_SETANIMATION,
sizeof(no_animation), &no_animation,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
m_suppressed = true;
}
}
~WindowsAnimationSuppressor() {
if (m_suppressed) {
::SystemParametersInfo(SPI_SETANIMATION,
sizeof(m_original_settings),
&m_original_settings,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
private:
bool m_suppressed;
ANIMATIONINFO m_original_settings;
};
void RearrangeWindows() {
WindowsAnimationSuppressor suppressor;
// Rearrange the windows here ...
}
When the suppressor is constructed, it remembers the user's original setting and turns off the animation. The destructor restores the original settings. By using a c'tor/d'tor, you ensure that the user's settings are restored if your rearranging code throws an exception.
There is a small window of vulnerability here. In theory, the user could change the setting during the operation, and then you'll slam the original setting back. That's extremely rare and not really that bad.
The Problem
On windows, the coordinates returned for a "mouse button down" event seem to be slightly wrong for an I-Beam cursor. Basically, the x-coordinate is always two pixels left of where it should be.
I've written a very simple win32 program to demonstrate the problem. All it does is turn the cursor into an IBeam and render a vertical red line where the last mouse down event was. I would expect the red line to match up exactly with the vertical part of the I-Beam, but this is not the case.
Here's a screenshot of what happens.
As you can see, the red line is two pixels to the left of where it should be (the behaviour is correct for the standard arrow pointer), so it appears that the hotspot for the I-Beam cursor is wrong.
I've had someone else running Windows 7 64 bit confirm that they experience the same problem, but another tester on Vista does not have the problem.
Some information about my environment
Windows 7 64 bit. Completely default configuration (i.e. no DPI scaling, no weird themes etc)
Visual Studio Express 2010
NVidia graphics card with latest drivers (v270.61)
Switching aero on or off makes no difference. Choosing different cursors in display preferences makes no difference
The Relevant Bits Of Code
My test project is basically the "Win32 Project" template in Visual C++ 2010, with the changes outlined below.
Here's the code where I register the window class and set the cursor to an I Beam
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CURSOR_TEST));
wcex.hCursor = LoadCursor(NULL, IDC_IBEAM); // this is the only line I changed in this function
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CURSOR_TEST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
Here are the relevant parts from my main message loop:
case WM_LBUTTONDOWN:
// record position of mouse down.
// xPos and yPos are just declared as
// global ints for the purpose of this test
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
// cause redraw
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;
case WM_PAINT:
// paint vertical red line at position of last click
hdc = BeginPaint(hWnd, &ps);
RECT rcClient;
GetClientRect(hWnd, &rcClient);
hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
SelectObject(hdc, hPen);
MoveToEx(hdc, xPos, 0, NULL);
LineTo(hdc, xPos, rcClient.bottom);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
break;
The Summary
I've done loads of googling for answers, but can't find anything relevant. Am I doing something wrong with the way that I'm handling the incoming cursor coordinates?
Thanks!
EDIT: More Information After Insightful Questions in the Comments
As guided by #Mark Ransom in the comments, I've used the GetIconInfo function to get more information about the I-Beam cursor. The ICONINFO struct for the cursor indicates that the x coord for the cursor hotspot is at x=8. However, when I dump the bitmap for the cursor (the hbmMask member of the ICONINFO struct, as it is a monochrome cursor), the vertical bar is 10 pixels from the left of the image, not 8 pixels. As Mark points out this is likely the cause of the visual discrepency, but why has this occurred, and how might I fix it?
(I also noticed that the answer to this other question has some interesting information about the different way that I-Beam cursors are handled. I wonder if this is relevant)
This was plaguing me for many years, and evidently many other Windows users? Do you ever just click between two characters, but the text caret ends up too far to the left? Your cursor was clearly between the other two!
If you open Notepad, and then move your cursor to the bottom edge, out of the text region, and observe the change between I-beam and pointer, you can see that the pointer begins two pixels to the left of the I-beam. OP has thoroughly observed this by even writing a program to test just where that misbehaved I-beam cursor is clicking. It must be that the hotspot is set incorrectly. (Never imagined I’d be one of those people who take screenshots with their phone, but in this case, it was actually the easiest method I had in mind to capture the mouse cursor.)
All right, so how do we fix it?
Well, any sane Windows user would open their Mouse settings in the Control Panel, and then very simply change the I-beam cursor to a different version that has a correct hotspot. I could’ve sworn that I’ve done this before, having downloaded a corrected I-beam cursor (that looked just the same) from somewhere, but I can’t seem to find the link where I got it from – but yes, this approach will most assuredly give you a correct hotspot for text selection.
Would it truly fix the problem, though? Or would it leave you sleepless, wondering – knowing – you covered it up...
Doesn’t seem so hard to really fix, anyway...right? We’ll just go and adjust the original cursor file. So, I open Mouse settings in the Control Panel, and then click Browse..., search through the list in Windows/Cursors, but... it’s not there? I looked twice, and then thrice – definitely missing. There weren’t any that resembled what I was using.
So, I peer into the Windows Registry via regedit – I figured that I could find the file path to it in there. It was simple enough to find where the keys were via Google: HKEY_CURRENT_USER/Control Panel/Cursors. But wait – it’s not there either?! I see Arrow, Hand, and other cursors, but no “Ibeam” or “TextSelection” entry anywhere!
You smart people are probably laughing at my bewilderment, knowing fully just where Windows keeps its secret cursors, but alas, my ignorance tormented me. I continued to dig fruitlessly through the other keys trying to find it – maybe text selection was special and had cursor information elsewhere under a related key?
Soon I came to the reasonable assumption that Windows uses a default cursor file if the key isn’t set – but where would that be? Fortunately, I have a modicum of Windows programming experience and know that things can come from embedded resources rather than a standalone .cur file. With some deeper digging, someone named Herby had the answer for me:
They are located in user32.dll [%WinDir%/system32].
(via https://www.neowin.net/forum/topic/374461-default-xp-cursor-location/)
Light at the end of the tunnel. I already had Resource Hacker installed on my computer for some reason, probably because I was doing something deranged before, but I peered inside user32.dll and, sure enough, found the default cursor resources. There was the I-beam under resource ID 73.
I exported it and had a look with a hex editor while referencing the ICO file format. Byte offset 10 has the horizontal pixel coordinate of the hotspot, which was 8. I could just change that byte from 0x08 to 0x0A and then import the modified file back into user32.dll with Resource Hacker, and my problem would be solved (permission issues aside).
That’s simple enough, but do we really want simple? We’ve come this far, so may as well waste the rest of our day. Let’s write a C++ program to do it! Of course, being the totally good engineers we are, we must find a method to do this safe and proper...
Thus began my journey into the deepest depths of programmer hell, suffering the documents of yore which describe obscure WinAPI methods, like those pertaining to updating a DLL resource. First big obstacle here was that magic number, the ID of the cursor resource we want to modify, “73.” What did it mean? Where did it come from?
Well, to me it’s obvious that it’s a generated ID and can’t be trusted to be the de-facto constant that represents the I-beam cursor. So, we need to find some way to find that magic number reliably. Seems simple on paper, no? Well, it isn’t.
Closest I had gotten to tracking down the elusive magic number was the string “USER32” identifying the module, from GetIconInfoEx. Nothing that was actually useful. (Oh, and by the way, fair warning to anyone who wants to figure out .cur files and their butchered BMP format.) If you can find a way to turn IDC_IBEAM into a key name in the user32.dll resources, hats off to you, but after slamming my head against the wall for the better part of this project I decided to go with a dumber approach.
I just copied the original data, exporting it directly from the cursor resource to be used as a signature. I could then LoadLibrary user32.dll, enumerate through the cursors, and then check if they exactly match the signature file. If I find a match, I’ve found the ID I want to modify. I also learned that the second magic number I saw in Resource Hacker was a language code – “1033” (English US). I had to annoyingly do another enumeration to find that number, too.
All’s well and good, several hours later from digging through documentation, I have my solution. There are functions in the Windows API to update resources in DLL files. All I had to do was change the first byte of the signature file (which was the horizontal hotspot offset), and update the resource.
With permission, of course
It was about halfway through this project that I reminded myself it’s a horrible idea to modify system files (especially if it makes the system think something is wrong/out-of-date), let alone what measures the system will take to try and stop you from doing that, but I just couldn’t live without the sweet satisfaction knowing that I completed the solution. And it did work – I made a copy of user32.dll, ran the code, and sure enough, the cursor hotspot was corrected.
RESULT: https://github.com/mukunda-/IBeamFix
System files are system files though, and not even with Administrator access will the system let you mess with them. I’m not going to bother with figuring out how to circumvent that.
A better approach may have been to simply (and by simply, I mean dealing with CUR/BMP hell) export the cursor from user32.dll, checking its characteristics to make sure that it still has the hotspot flaw, modifying the hotspot coordinate, and then updating the registry to use that cursor.
Or, even better, not even bothering with any of this utter madness and just using a replacement cursor. I should have stopped at paragraph four. Look, I made one. https://mukunda.com/stuff/IBeamFixed.cur Problem solved.
I need to create a SDI form with a formview that has two tabs, which encapsulate multiple dialogs as the tab content. But the form has to have a colored background.
And things like these makes me hate programming.
First, I tried CTabControl, via resource editor, tried different things, but the undocumented behavior and the quirks with no answers led me into a roadblock.
After many hours of searching, I found that there is a control called property sheet, which is actually what I need.
Some more searching later, I found that property sheet can even be actually embedded onto CFormView like so: http://www.codeguru.com/Cpp/controls/propertysheet/article.php/c591
And that the dialog classes derived from CPropertyPage can be directly added as pages via AddPage method of CPropertySheet.
Great! Not quite so... Some of the controls didn't worked, and were not created, ran into weird asserts. Turns out the DS_CONTROL style was missing from the dialogs. Found it completely accidentaly on Link, no word about that on MSDN!!!! Property page must have: DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_TABSTOP, and can have: DS_SHELLFONT | DS_LOCALEDIT | WS_CLIPCHILDREN styles! Not any other, which are created by default with resource editor. Sweet, super hidden information for software developers!
The quote in comments on that page: "OMG. That's where that behavior came from...
It turns out that the PlaySound API relied on that behavior when playing sounds on 64bit machines." by Larry Osterman, who as I understand works for Microsoft for 20 years, got me laughing out loud.
Anyway, fixed that, the dialog-controls(CPropertyPages) are created as expected now, and that part looks something remotely promising, but the next part with color is dead end again!
Normally you override WM_CTLCOLOR, check for control ID or hwnd and supply the necessary brush to set the color you need. Not quite so with CPropertySheet, the whole top row stays gray! For CTabCtrl it somehow works, for CPropertySheet it doesn't.
Why? Seems that the CPropertySheet is kinda embedded inside CTabControl or something, because if I override WM_ERASEBKGND, only the internal part changes the color.
Now it seems that there is a GetTabControl() method in the CPropertySheet, that returns the actual CTabCtrl* of the CPropertySheet. But since it's constructed internally, I can't find how to override it's WM_CTLCOLOR message processing.
There seems to be a way to subclass the windowproc, but after multiple tries I can't find any good source on how to do it. SubclassWindow doc on MSDN says: "The window must not already be attached to an MFC object when this function is called."?! What's that?
I tried creating a custom CCustomTabCtrl class based on CTabCtrl via MFC wizard, created an instance of it, called SubclassWindow from one of the CCustomPropertySheet handlers to override the internal CTabCtrl, but nothing works, mystical crashes deep inside MFC.
Tried setting WindowLong with GCL_HBRBACKGROUND for the internal CTabCtrl, nothing changed.
And worst of all, I can't find any sort of useful documentation or tutorials on the topic.
Most I can find is how to ownerdraw the tab control, but this is seriously wrong on so many ways, I want a standard control behavior minus background color, I don't want to support different color schemes, windows versions, IAccesible interfaces and all this stuff, and none of the ownerdraw samples I've seen can get even 10% of all the standard control behavior right. I have no illusion that I will create something better, I wont with the resources at hand.
I stumbled upon this thread, and I can't agree with the author more: http://arstechnica.com/civis/viewtopic.php?f=20&t=169886&sid=aad002424e80121e514548d428cf09c6 owner draw controls are undocumented PITA, that are impossible to do right, and there is NULL information on MSDN to help.
So is there anything I have missed or haven't tried yet? How to change the top strip background color of the CPropertySheet? Anyone?
Your only option is to ownerdraw the tab control. It's not that hard. Well, it is frustrating because MFC doesn't tell you how to make the necessary Win32 calls.
In your CPropertySheet-derived class, overwrite OnInitDialog() and add:
GetTabControl()->ModifyStyle(0,TCS_OWNERDRAWFIXED);
This puts your CPropertySheet-derived class in charge of drawing the tab control. Add a handler for WM_DRAWITEM (OnDrawItem) and change backgroundColor and textColor to match whatever colors you wanted. Code for OnDrawItem follows:
void CPropSht::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (ODT_TAB != lpDrawItemStruct->CtlType)
{
CPropertySheet::OnDrawItem(nIDCtl, lpDrawItemStruct);
return;
}
// prepare to draw the tab control
COLORREF backgroundColor = RGB(0,255,0);
COLORREF textColor = RGB(0,0,255);
CTabCtrl *c_Tab = GetTabControl();
// Get the current tab item text.
TCHAR buffer[256] = {0};
TC_ITEM tcItem;
tcItem.pszText = buffer;
tcItem.cchTextMax = 256;
tcItem.mask = TCIF_TEXT;
if (!c_Tab->GetItem(c_Tab->GetCurSel(), &tcItem )) return;
// draw it
CDC aDC;
aDC.Attach(lpDrawItemStruct->hDC);
int nSavedDC = aDC.SaveDC();
CBrush newBrush;
newBrush.CreateSolidBrush(backgroundColor);
aDC.SelectObject(&newBrush);
aDC.FillRect(&lpDrawItemStruct->rcItem, &newBrush);
aDC.SetBkMode(TRANSPARENT);
aDC.SetTextColor(textColor);
aDC.DrawText(tcItem.pszText, &lpDrawItemStruct->rcItem, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
aDC.RestoreDC(nSavedDC);
aDC.Detach();
}
Thank you for this solution but...
The above solution works well with one tab, but when you have multiple tabs it seems rename the wrong tabs. I needed to change the if statement for GetItem to:
if (!c_Tab->GetItem(lpDrawItemStruct->itemID, &tcItem )) return;
Needed lpDrawItemStruct->itemID to get the tabs named correctly
I'm looking for a way to check to see that a window I am creating is visible and entirely on one monitor. I have seen too many programs do nasty things when they try to restore their position and the place no longer exists and I don't want my program to be vulnerable to this.
How do I find the information on the actual layout of the monitors?
The Screen class contains a lot of functionality for this.
You should check for yourself if a form is outside the Bounds of the Screen, but this is pretty straightforward:
if (!Screen.GetWorkingArea(myWindow).Bounds.Contains(myWindow.Bounds)) {
// Adjust location
}
Just a small syntax correction, or perhaps an update in Visual Studio 2012:
if (!Screen.GetWorkingArea(myWindow).Contains(myWindow.Bounds))
{
//Adjust location
}
Is there a way to change the colors used by plain Win32 menus (background, text, and highlight) for a single process, without using SetSysColors?
(SetSysColors does a global change, which is bad, and if you crash or forget to set the colors back with SetSysColors again before exiting, they will not be restored until you logout.)
The SetMenuInfo() API is your friend. It lets you apply any brush to paint your menu's background.
Something along these lines should solve your problem:
MENUINFO mi = { 0 };
mi.cbSize = sizeof(mi);
mi.fMask = MIM_BACKGROUND|MIM_APPLYTOSUBMENUS;
mi.hbrBack = hBrush;
HMENU hMenu = ::GetMenu(hWnd);
SetMenuInfo(hMenu, &mi);
If I believe your comment to Rob, it is for a skinned application, with special look and feel. So the way to go is probably indeed, as ferek points out (in an unfriendly way...) to use owner-drawn menus: you will be able to define precisely their look.
I have to ask, why? Adopting the regular Windows look-and-feel is good; it means users can be confident that there are consistent elements in your user interface, onto which they can map their experience using other software for the platform.
[I'm probably preaching to the converted, of course, but I thought I'd make the point so anyone who reads an answer for this doesn't start making all their menus sky-blue-pink 'cause it looks pretty.]