LoadCursor and Mixed DPI Multiple Monitors

519 Views Asked by At

When the LoadCursor function is used to load a cursor from a resource, the resulting HCURSOR can be used across different monitors and always appears at the correct size.

ie: typically:

  • on a 96 dpi monitor the 32x32 resource is used,
  • on a 192 dpi monitor the 64x64 resource is used.

However, when a cursor is programmatically created from memory (say using LookupIconIdFromDirectoryEx and CreateIconFromResourceEx) the resulting cursor has a fixed resolution. This means it appears at the wrong size on at least one monitor in a mixed DPI multi-monitor setup.

I also checked out the LoadCursorFromFile and it too provides this dynamic resolution behaviour like LoadCursor.

Is there a way to programmatically create a cursor that dynamically switches depending which monitor it's shown on? What magic is going on behind the scenes for cursors loaded with LoadCursor to work differently?

3

There are 3 best solutions below

0
Brad Robinson On

After much experimentation I finally discovered that WPF can load cursors from resource and memory streams and get the correct DPI behaviour if the scaleWithDpi option is used:

public Cursor(Stream cursorStream, bool scaleWithDpi)

Looking at the reference source it ends up in the function LoadFromStream which loads the stream by writing it to a temporary file and the loading from the file. See source

To sum up:

  • It seems the only way to get a dynamic DPI cursor like this is with the Win32 native resource loading functions and by loading from a file. It doesn't seem like you can load a cursor directly from memory with this behavior.
  • The "dynamic dpi" part of this behavior seems to be related to the LR_DEFAULTSIZE flag passed to the LoadImage function.
1
DJm00n On

What magic is going on behind the scenes for cursors loaded with LoadCursor to work differently?

LoadImage with provided hInst/MAKEINTRESOURCE(resId) or LR_LOADFROMFILE flag and LoadCursorFromFile APIs are indeed saving additional information in returned HCURSOR/HICON.

DrawIconEx with DI_DEFAULTSIZE and CopyImage with LR_COPYFROMRESOURCE and maybe other APIs are using this information to get best fit icon image from resource source for DPI-dependant size for current monitor/window.

This icon source information can be acquired manually with GetIconInfoEx in szModName/wResID/szResName member of ICONINFOEX structure that is available since Windows Vista.

CreateIconFromResourceEx does not set this info since it just receives memory pointer to the icon/cursor bits. In this case simple image scaling is applied on DrawIconEx call.

It seems there is no other way to use this DPI-aware draw mechanism other than to use a load from file/exe/dll.

Some info on this topic in Raymond Chen blog:

PS: As a workaround I think it is possible to handle WM_SETCURSOR window message, calculate a DPI-aware cursor size for your window (use wParam with GetDpiForWindow and GetSystemMetricsForDpi with SM_CXCURSOR/SM_CYCURSOR) load corresponding HCURSOR and call SetCursor function with it.

Also there is a new user cursor size setting in Windows 10 that may be accounted too. It is saved in CursorBaseSize under HKEY_CURRENT_USER\Control Panel\Cursors registry. Here is some Qt code that deals with it and returns proper cursor size.

1
lb90 On

There's a new API as of Windows 10 1607 that might help: SetThreadCursorCreationScaling