LoadCursor and Mixed DPI Multiple Monitors - windows

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?

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.

Related

AccessibleObjectFromPoint and per-monitor DPI

I'm using accessibility with the AccessibleObjectFromPoint function, and I'd like it to work correctly on a per-monitor DPI environment. Unfortunately, I can't get it to work. I tried many things, and the situation for now is:
My app is marked as per-monitor-DPI-aware in the manifest. (True/PM)
I use GetCursorPos and then AccessibleObjectFromPoint.
How can the problem be reproduced:
Have two monitors, one with 100% DPI, the other with 125%.
Run Chrome on the 125% monitor.
Use AccessibleObjectFromPoint on one of the tab names, it won't work.
It works with some apps (DPI-aware, it seems, like explorer), but doesn't work with others. I tried several relevant functions, such as GetPhysicalCursorPos and PhysicalToLogicalPointForPerMonitorDPI, but nothing works.
It's worth noting that Microsoft's inspect.exe works as expected.
I’ve been struggling with this exact same problem for several weeks and can now tell you my findings. Unfortunately I can’t give you more than a hint of code, because the project I am working on, is proprietary.
The issue started at Windows 8.1. The problem did not exist on Windows 7 or Vista, because AccessibleObjectFromPoint always used raw physical coordinates, as documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317984(v=vs.85).aspx .
“Microsoft Active Accessibility does not use logical coordinates. The following methods and functions either return physical coordinates or take them as parameters.” This has not been true since Windows 8.1.
AccessibleObjectFromPoint now uses a flawed calculation that cannot always find the correct window for reasons similar to my question here: High DPI scaling, mouse hooks and WindowFromPoint .
My findings lead me to one conclusion: The API is broken. This does not mean it is not possible though.
Possible solutions I have partially tested that seem to work follow.
Prerequisites are that you
1/. Make your process per monitor DPI aware, NOT USING THE MANIFEST (more on that later).
2/. Determine the hWnd of the window you want to query (WindowFromPoint() variants)
3/. Determine the monitor DPI of the queried hWnd
4/. Determine the DPI of your process
5/. Determine the DPI of the queried hWnd
6/. Determine the monitor origin and offset for the queried hWnd (MonitorFromWindow() and GetMonitorInfo() )
Next, depends on your platform
Windows 10.0.14393+
Write a function that finds the IAccessible (AccessibleObjectFromWindow() ) from the top level window, and then recursively call IAccessible::accHitTest until you reach the bottom-most IAccessible and perhaps ChildID data. Return that as if you would call AccessibleObjectFromPoint.
To call it successfully, you will need to scale the (x,y) co-ordinates into the scale system of the queried hWnd, using the DPIs and co-ordinates fetched in the list above. Watch out for systems where monitors are not the same size or if monitors are partially offset, or above and below.
And now for the important part for 10.0.14393 – Set your thread to the same DPI_AWARENESS_CONTEXT of the hWnd you are querying. Now call your new function. Now revert your thread to monitor DPI aware, and voila, it works, even if the window is not maximised. This is why you must not use the manifest.
If you are on Windows 8.1 to 10.0.10586 you have a tougher task.
Instead of calling accHitTest, as above, you have to recursively call AccessibleChildren and iterate the call IAccessible::accLocation to determine if your test point is within each child. This is tricky and starts to get really messy when you get to e.g. combo boxes in products like Office, which is only system DPI aware.
That’s all I can give you for now.
To do it successfully on multi-platform (mine has to work from Vista to Windows-Current) the only really safe bet is to write a wrapper DLL in C++ that can determine at runtime which OS it is on and change code path accordingly. The reason you want to do it in C++ is to avoid passing IAccessible objects across the .Net/unmanaged marshalling boundary. You can call IUnknown::Release on objects you don’t need to return n the unmanaged side. You can do it all in .Net, but it will be slow.
P.S. also watch out for Chrome returning infinite trees where parents are children of their parents, some snity checks are required. Also, Chrome does not return accRole correctly, and will give you HTML tags instead of VT_I4.
Good luck
A fairly workable solution is as follows, in your IAccessible recursive function:
Use getwindowrect to capture the physical right on main window
Use accChild.accLocation in loop to capture left and Width on each Object
Add this simple test
If l > rct2r.Right And l > arrIACC.x2 Then
arrIACC.x2 = l + w
End If
if dpi = 100 then no Object is furter out than physical right
if dpi > 100 then closebutton is...x pix offset
Use the difference to rescale all values you are in use of Width
arrIACC.w1 = CInt(((-rct2r.Left + arrIACC.w1) / arrIACC.x2) * rct2r.Right)
This solution is from an Excel plugin I have developed, I was testing the Width of the quick access toolbar qat and my result was +- 5 pixels regardless of any DPI.

What does this bit of MSDN documentation mean?

The first parameter to the EnumFontFamiliesEx function, according to the MSDN documentation, is described as:
hdc [in]
A handle to the device context from which to enumerate the fonts.
What exactly does it mean?
What does device context mean?
Why should a device context be related to fonts?
Question (3) is a legitimately difficult thing to find an explanation for, but the reason is simple enough:
Some devices provide their own font support. For example, a PostScript printer will allow you to use PostScript fonts. But those same fonts won't be usable when rendering on-screen, or to another printer without PostScript support. Another example would be that a plotter (which is a motorized pen) requires vector fonts with a fixed stroke thickness, so raster fonts can't be used with such a device.
If you're interested in device-specific font support, you'll want to know about the GetDeviceCaps function.
The windows API uses the concept of handles extensively. A handle is an integer value that you can use as a token to access an API resource. You can think of it as a kind of "this" pointer, although it is definitely not a pointer.
A device context is an object within the windows API that represents a something that you can draw on or display graphics on. It might be a printer, a bitmap, or a screen, or some other context in which creating graphics makes sense. In Windows, fonts must be selected into device contexts before they can be used. In order to find out what fonts are currently available in any given device context, you can enumerate them. That's where EnumFontFamiliesEx comes in.
Microsoft has other articles on device context,
https://learn.microsoft.com/en-us/windows/win32/gdi/about-device-contexts
An application must inform GDI to load a particular device driver and,
once the driver is loaded, to prepare the device for drawing
operations (such as selecting a line color and width, a brush pattern
and color, a font typeface, a clipping region, and so on). These tasks
are accomplished by creating and maintaining a device context (DC). A
DC is a structure that defines a set of graphic objects and their
associated attributes, and the graphic modes that affect output. The
graphic objects include a pen for line drawing, a brush for painting
and filling, a bitmap for copying or scrolling parts of the screen, a
palette for defining the set of available colors, a region for
clipping and other operations, and a path for painting and drawing
operations. Unlike most of the structures, an application never has
direct access to the DC; instead, it operates on the structure
indirectly by calling various functions.
Obviously font is a kind of drawing.

How to measure static size beforehand? WINAPI

I am creating a widow with static text, and because of the all 96/120/180 DPI stuff, I need to create a layouting mini-engine.
The dialog is created in code, statics are created in code, fonts are created in code, everything, mostly because resources in .rc have their share of DPI related problems as well and I want a total control.
The problem with all this is that I don't know how to find the length of the text in statics. I need to calculate the initial size of the static control, and also, I need to calculate a padding between different statics in font unit sizes, but since I don't know the size of the previous static, I can't offset the next one.
The biggest problem is that static does the word wrapping, therefore I can't find a text measuring function that would calculate that and a correction for a custom font, italic, bold, oversize...
Anyone have any ideas?
The static control styles (ENDELLIPSIS,PATHELLIPSIS and LEFTNOWORDWRAP) seem to map to the DrawText flags, so calling DrawText with DT_WORDBREAK|DT_CALCRECT will probably be as close as you can get...
I can't think of any compelling reason to do this any differently then the way all other GUI class libraries do it. Just scale window sizes between the 'design' DPI setting and the target machine DPI setting. Using DPI-independent constants is pretty painful in MFC since everything is pixel based. So keep your workstation at the common 96 DPI setting, scale from there on the target machine. You do have to keep a bit of slack because of TrueType hinting.

The biggest size of Windows Cursor

I have a cursor what the size size 128x128, but when i used LoadCursor to load and show it, it only has 32x32. Which API can make it correctly? It seems MS resize it. Thanks.
Windows XP does not include any system cursors that are larger than 32x32. (If larger cursors were included, they would be stretched down to 32x32 when the standard APIs load the cursors.)
For high-DPI systems, Windows XP has adjusted the SM_CXCURSOR and SM_CYCURSOR values to be 64x64 pixels. This size adjustment is to prevent the mouse pointer from virtually disappearing because it is too small to be effectively used. Although the other aspects of the system scale with DPI, the mouse pointer does not scale. Microsoft does not try to enforce a DPI-independent size for the mouse pointer.
The system also provides the SetSystemCursor API function that you can use to change the system cursor for specific categories. You can use this function to set a cursor of any size. However, you must call the function programmatically, and you can only use it to set a cursor for a specific category. You cannot use it to make all cursors on the system the same size.
http://support.microsoft.com/kb/307213
Don't use LoadCursor, use LoadImage() instead.
SM_CXCURSOR by SM_CYCURSOR is the only cursor size the system can currently use.
Use GetSystemMetrics to find out those values.

any quick and easy way to capture a part of the screen? getPixel was slow and GetDIBits seemed a bit complcated as a start

I was trying some code to capture part of the screen using getPixel on Windows, with the device context being null (to capture screen instead of window), but it was really slow. It seems that GetDIBits() can be fast, but it seems a bit complcated... I wonder if there is a library that can put the whole region into an array, and pixel[x][y] will return the 24 bit color code of the pixel?
Or does such library exist on the Mac? Or if Ruby or Python already has such a library that can do that?
I've never done this but I'd try to:
Create a memory Device Context (DC)
using CreateCompatibleDC passing it
the Device Context of the desktop
(GetDC(NULL)).
Create a Bitmap (using
CreateCompatibleBitmap) the same
size as the region your capturing.
Select the bitmap into the DC you
created (using SelectObject).
Do a BitBlt from the
desktop DC to the DC you created (using SRCCOPY flag).
Working with Device Contexts can cause GDI leaks if you do things in the wrong order so make sure that you read the documentation on all the GDI functions you use (e.g. SelectObject, GetDC, etc.).

Resources