1

I used AutoIt's _ScreenCapture_Capture() before without any trouble. But on Windows 10 with a 4k resolution screen I'm using DPI scaling at 200%. _ScreenCapture_capture() now gets something 2x zoomed in and half the location I asked for.

For instance :

 _ScreenCapture_Capture("c:\a.bmp", 100, 100, 200, 200)
;                             Path,  X1,  Y1,  X2,  Y2

Does not return a 100x100 pixel square of the screen (at 100,100 to 200,200). It returns a 100x100 pixel square (at 50,50 to 100,100) instead.

I did find a solution :

DllCall("User32.dll", "bool", "SetProcessDPIAware")

However this screws up the GUI looks. So I then found this code:

GUISetFont(8.5 * _GDIPlus_GraphicsGetDPIRatio()[0])
Func _GDIPlus_GraphicsGetDPIRatio($iDPIDef = 96)
  Local $aResults[2] = [1, 1]
  _GDIPlus_Startup()
  Local $hGfx = _GDIPlus_GraphicsCreateFromHWND(0)
  If @error Then Return SetError(1, @extended, $aResults)
  #forcedef $__g_hGDIPDll, $ghGDIPDll
  $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetDpiX", "handle", $hGfx, "float*", 0)
  If @error Then Return SetError(2, @extended, $aResults)
  Local $iDPI = $aResult[2]
  Local $aresults[2] = [$iDPIDef / $iDPI, $iDPI / $iDPIDef]
  _GDIPlus_GraphicsDispose($hGfx)
  _GDIPlus_Shutdown()
  Return $aresults
EndFunc   ;==>_GDIPlus_GraphicsGetDPIRatio

Which works great for the GUI but doesn't help for _ScreenCapture_Capture() calls. Seems I can either have a good looking- or a functioning program, not both.

How could I combine these two solutions to get both a good GUI and a functioning program? If I knew how to do the opposite of :

DllCall("User32.dll", "bool", "SetProcessDPIAware")

Then when I need to capture a screen portion I could turn on compatibility, then immediately turn it off when done capturing. But I don't know how to 'UNsetprocessdpiaware'.

user4157124
  • 2,809
  • 13
  • 27
  • 42
MetaStack
  • 3,266
  • 4
  • 30
  • 67

1 Answers1

2

If you are using Windows 10 A update (10.0.14393) you can spin up a separate thread and make the thread alone Per Monitor DPI aware, and leave your main GUI as dpi unaware.

See

SetThreadDpiAwarenessContext function

If you are building using a version of visual studio before 2015 you can declare a pointer to the function prototype

DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext(
  _In_  DPI_AWARENESS_CONTEXT  dpiContext
);

and use GetProcAddress to test for the presence of the function before you call it. Anything in that thread will give you raw physical information if you used

DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE

Note that this function is not available on Windows 10 10.0.10586. You must test for its availability.

birdwes
  • 127
  • 10
  • This is a better solution than the work around I eventually ended up with: https://www.autoitscript.com/forum/topic/185045-screencapture-4k-res-how-to-get-dpi-awareness/#comment-1329168 Thanks, much! – MetaStack Oct 18 '16 at 19:44
  • Be sure to call SetThreadDpiAwareness again, after you call the API that you're interested in, and set your thread's DPI awareness back to the default process awareness, otherwise the awareness mode may screw something else up later on... unless you've got a dedicated thread that calls this API. – peterfelts Mar 16 '17 at 20:03