Incorrect hotspot for I-Beam cursor in Windows 7?

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.)

I-beam misaligned with cursor I-beam misaligned with 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.

Not my I-beam

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!

Regedit

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.

Resource Hacker

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).

Easy!

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.