Last active
November 20, 2018 12:33
-
-
Save hoppfrosch/9402b065681e8b002f8a to your computer and use it in GitHub Desktop.
With the introduction of the Desktop Window Manager (DWM) and Aero themes in Windows Vista, the size and position of windows has never been the same. The OS says that the window is one size but when displayed, the window can be another (usually larger) size. The WinGetPosEx function is an attempt to help the developer to identify the correct pos…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Credits: jballi - https://autohotkey.com/boards/viewtopic.php?f=6&t=3392 | |
#NoEnv | |
#SingleInstance Force | |
;;;;;ListLines Off | |
;-- Initialize | |
Caption :=True | |
ToolWindow :=False | |
Resize :=False | |
Theme :=True | |
;-- Workaround for AutoHotkey Basic | |
PtrType:=(A_PtrSize=8) ? "Ptr":"UInt" | |
;-- GUI options | |
gui Margin,6,4 | |
;-- Text | |
gui Add,Text,, | |
(ltrim | |
If desired, move and/or resize (if resize has been enabled) this window | |
before running any of the examples below. | |
) | |
gui Add,Text,xm ;-- Spacer | |
gui Add,Text,xm,Results from WinGetPos: | |
gui Font,Bold | |
gui Add,Text,xm y+0 cNavy vWinGetPosResults | |
,X=9999, Y=9999, Width=9999, Height=9999, Offset_X=9, Offset_Y=9 | |
gui Font | |
gui Add,Text,xm,Results from WinGetPosEx: | |
gui Font,Bold | |
gui Add,Text,xm y+0 cBlue vWinGetPosExResults | |
,X=9999, Y=9999, Width=9999, Height=9999, Offset_X=9, Offset_y=9 | |
gui Font | |
gui Add,Text,xm h3 ;-- Spacer | |
gui Add,Button,xm gGetPosSize,%A_Space% Get Window Pos/Size %A_Space% | |
gui Add,Text,xm h3 ;-- Spacer | |
gui Add,Text,xm,Change window attributes: | |
;;;;;gui Add,Button,xm gTogglePolicy,%A_Space%Toggle non-client rendering policy `n (Vista+ only)%A_Space% | |
gui Add,Button,xm gToggleTheme,%A_Space%Toggle Theme `n (Window XP+)%A_Space% | |
gui Add,button,x+0 hp gToggleCaption,%A_Space%Toggle `n Caption%A_Space% | |
gui Add,Button,x+0 hp gToggleResize,Toggle`nResize | |
;;;;;gui Add,button,xm y+0 wp gToggleToolWindow,Toggle ToolWindow | |
gui Add,Text,xm h3 ;-- Spacer | |
gui Add,Text,xm,Move this window to these coordinates: | |
gui Add,Text,xm+20,X: %A_Space% | |
gui Add,Edit,x+0 w60 vXPos,0 | |
gui Add,Text,x+10,Y:%A_Space% | |
gui Add,Edit,x+0 w60 vYPos,0 | |
gui Add,CheckBox,xm+20 y+3 Checked vUseOffsets,Use offsets | |
gui Add,Button,xm+20 y+3 gMoveWindow,%A_Space% Move %A_Space% | |
gui Add,Text,xm h3 ;-- Spacer | |
gui Add,Text,xm,Create a new window and position it relative to this window: | |
gui Add,Text,xm+20,Inside: %A_Space% | |
gui Add,Radio,x+0 Checked vInsideLeft gInsideButtonAction,Left | |
gui Add,Radio,x+2 vInsideRight gInsideButtonAction,Right | |
gui Add,Radio,x+2 vInsideRightTop gInsideButtonAction,Right Top | |
gui Add,Radio,x+2 vInsideTop gInsideButtonAction,Top | |
gui Add,Radio,x+2 vInsideBottom gInsideButtonAction,Bottom | |
gui Add,Text,xm+20 y+2,Outside: %A_Space% | |
gui Add,Radio,x+2 vOutsideLeft gOutsideButtonAction,Left | |
gui Add,Radio,x+2 vOutsideRight gOutsideButtonAction,Right | |
gui Add,Radio,x+2 vOutsideRightTop gOutsideButtonAction,Right Top | |
gui Add,Radio,x+2 vOutsideTop gOutsideButtonAction,Top | |
gui Add,Radio,x+2 vOutsideBottom gOutsideButtonAction,Bottom | |
gui Add,CheckBox,xm+20 y+3 vUseOffsets2 Checked,Use offsets | |
gui Add,Button,xm+20 y+3 gPopupWindow,%A_Space% Create %A_Space% | |
gui Add,Text,xm h3 ;-- Spacer | |
gui Add,Button,xm gReload,%A_Spacd% Reload... %A_Space% | |
;-- Identify window handle | |
gui +LastFound | |
WinGet hParentGUI,ID | |
;-- Render but don't show | |
gui Show,Hide,WinGetPosEx Example | |
;-- Get initial position/size | |
gosub GetPosSize | |
;-- Show | |
gui Show | |
return | |
GUISize: | |
gosub GetPosSize | |
return | |
GUIEscape: | |
GUIClose: | |
ExitApp | |
GetPosSize: | |
SetTimer %A_ThisLabel%,Off | |
WinGetPos X,Y,Width,Height,ahk_id %hParentGUI% | |
GUIControl,,WinGetPosResults,% "X=" . X . ", Y=" . Y . ", Width=" . Width . ", Height=" . Height | |
;;;;;outputdebug % "WingetPos: " . "X=" . X . ", Y=" . Y . ", Width=" . Width . ", Height=" . Height | |
pRP :=WinGetPosEx(hParentGUI,X,Y,Width,Height,Offset_X,Offset_Y) | |
GUIControl,,WinGetPosExResults,% "X=" . X . ", Y=" . Y . ", Width=" . Width . ", Height=" . Height . ", Offset_X=" . Offset_X . ", Offset_Y=" . Offset_Y | |
;;;;;X :=Left :=NumGet(pRP+0,0,"Int") | |
;;;;;Y :=Top :=NumGet(pRP+0,4,"Int") | |
;;;;;Right :=NumGet(pRP+0,8,"Int") | |
;;;;;Bottom :=NumGet(pRP+0,12,"Int") | |
;;;;;Width :=Right-Left | |
;;;;;Height :=Bottom-Top | |
;;;;;Offset_X :=NumGet(pRP+0,16,"Int") | |
;;;;;Offset_Y :=NumGet(pRP+0,20,"Int") | |
;;;;;outputdebug % "WinGetPosEx2: " . "X=" . X . ", Y=" . Y . ", Width=" . Width . ", Height=" . Height . ", Offset_X=" . Offset_X . ", Offset_Y=" . Offset_Y | |
return | |
InsideButtonAction: | |
GUIControl,,OutsideLeft,0 | |
GUIControl,,OutsideRight,0 | |
GUIControl,,OutsideRightTop,0 | |
GUIControl,,OutsideTop,0 | |
GUIControl,,OutsideBottom,0 | |
return | |
OutsideButtonAction: | |
GUIControl,,InsideLeft,0 | |
GUIControl,,InsideRight,0 | |
GUIControl,,InsideRightTop,0 | |
GUIControl,,InsideTop,0 | |
GUIControl,,InsideBottom,0 | |
return | |
MoveWindow: | |
gui Submit,NoHide | |
Offset_X :=Offset_Y:=0 | |
if UseOffsets | |
WinGetPosEx(hParentGUI,X,Y,Width,Height,Offset_X,Offset_Y) | |
XPos+=Offset_X | |
YPos+=Offset_Y | |
gui Show,x%XPos% y%YPos% | |
gosub GetPosSize | |
return | |
;-- Note: Center is not not used as an example because the calculations are the | |
; same regardless of the OS, Windows theme, or the attributes of the window. | |
PopupWindow: | |
gui Submit,NoHide | |
;-- Set GUI default | |
gui 2:Default | |
;-- Disable parent GUI, give ownership to parent GUI | |
gui 1:+Disabled | |
gui +Owner1 | |
;-- Identify window handle | |
gui 2:+LastFound | |
WinGet hChildGUI,ID | |
;-- GUI options | |
gui -MinimizeBox | |
;-- GUI objects | |
gui Add,Text,, | |
(ltrim | |
Just some junk text to let Autohotkey | |
determine the width and height of this | |
popup window. | |
) | |
;-- Render but don't show | |
gui Show,Hide,Popup Window | |
;-- Collect window position, size, and offset | |
WinGetPosEx(hParentGUI,ParentX,ParentY,ParentW,ParentH) | |
WinGetPosEx(hChildGUI,ChildX,ChildY,ChildW,ChildH,ChildOffset_X,ChildOffset_Y) | |
;-- Use offset? | |
Offset_X :=0 | |
Offset_Y :=0 | |
if UseOffsets2 | |
{ | |
Offset_X:=ChildOffset_X | |
Offset_Y:=ChildOffset_Y | |
} | |
;-- Initialize X/Y options | |
XOption :="x" . Round(ParentX+Offset_X+((ParentW-ChildW)/2)) | |
YOption :="y" . Round(ParentY+Offset_Y+((ParentH-ChildH)/2)) | |
;-- Set X/Y options | |
if InsideLeft | |
XOption:="x" . ParentX+Offset_X | |
if InsideRight | |
XOption:="x" . ParentX+ParentW-ChildW+Offset_X | |
if InsideRightTop | |
{ | |
XOption:="x" . ParentX+ParentW-ChildW+Offset_X | |
YOption:="y" . ParentY+Offset_Y | |
} | |
if InsideTop | |
YOption:="y" . ParentY+Offset_Y | |
if InsideBottom | |
YOption:="y" . ParentY+ParentH-ChildH+Offset_Y | |
if OutsideLeft | |
XOption:="x" . ParentX-ChildW+Offset_X | |
if OutsideRight | |
XOption:="x" . ParentX+ParentW+Offset_X | |
if OutsideRightTop | |
{ | |
XOption:="x" . ParentX+ParentW+Offset_X | |
YOption:="y" . ParentY+Offset_Y | |
} | |
if OutsideTop | |
YOption:="y" . ParentY-ChildH+Offset_Y | |
if OutsideBottom | |
YOption:="y" . ParentY+ParentH+Offset_Y | |
;-- Show the window in the requested location | |
gui Show,%XOption% %YOption% | |
return | |
2GUIClose: | |
2GUIEscape: | |
;-- Enable parent window | |
gui 1:-Disabled | |
;-- Destroy window so that it can be used again | |
gui Destroy | |
return | |
TogglePolicy: | |
DWMWA_NCRENDERING_POLICY :=2 | |
DWMNCRP_USEWINDOWSTYLE :=0 | |
DWMNCRP_DISABLED :=1 | |
DWMNCRP_ENABLED :=2 | |
DWMNCRP_LAST :=3 | |
if (Flag="" or Flag=DWMNCRP_USEWINDOWSTYLE) | |
Flag:=DWMNCRP_DISABLED | |
else | |
Flag:=DWMNCRP_USEWINDOWSTYLE | |
DllCall("dwmapi\DwmSetWindowAttribute" | |
,PtrType,hParentGUI | |
,"UInt",DWMWA_NCRENDERING_POLICY | |
,"Int*",Flag,"UInt",4) | |
gosub GetPosSize | |
return | |
ToggleCaption: | |
Caption :=!Caption | |
gui % (Caption ? "+":"-") . "Caption" | |
Sleep 50 | |
gui Show,AutoSize | |
gosub GetPosSize | |
return | |
ToggleReSize: | |
Resize :=!Resize | |
gui % (Resize ? "+":"-") . "Resize" | |
Sleep 50 | |
gui Show,AutoSize | |
gosub GetPosSize | |
return | |
ToggleTheme: | |
Theme :=!Theme | |
if Theme | |
DllCall("uxtheme\SetWindowTheme",PtrType,hParentGUI,Str,"",PtrType,0) | |
;-- This appears to work on all versions of AutoHotkey. Note: Syntax is | |
; critical. Do not make any changes. | |
else | |
DllCall("uxtheme\SetWindowTheme",PtrType,hParentGUI,PtrType,0,"Str","") | |
Sleep 50 | |
gosub GetPosSize | |
return | |
ToggleToolWindow: | |
ToolWindow :=!ToolWindow | |
gui % (ToolWindow ? "+":"-") . "ToolWindow" | |
Sleep 50 | |
gui Show,AutoSize | |
gosub GetPosSize | |
return | |
Reload: | |
Reload | |
return | |
#include WinGetPosEx.ahk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;------------------------------ | |
; | |
; Function: WinGetPosEx | |
; | |
; Description: | |
; | |
; Gets the position, size, and offset of a window. See the *Remarks* section | |
; for more information. | |
; | |
; Parameters: | |
; | |
; hWindow - Handle to the window. | |
; | |
; X, Y, Width, Height - Output variables. [Optional] If defined, these | |
; variables contain the coordinates of the window relative to the | |
; upper-left corner of the screen (X and Y), and the Width and Height of | |
; the window. | |
; | |
; Offset_X, Offset_Y - Output variables. [Optional] Offset, in pixels, of the | |
; actual position of the window versus the position of the window as | |
; reported by GetWindowRect. If moving the window to specific | |
; coordinates, add these offset values to the appropriate coordinate | |
; (X and/or Y) to reflect the true size of the window. | |
; | |
; Returns: | |
; | |
; If successful, the address of a RECTPlus structure is returned. The first | |
; 16 bytes contains a RECT structure that contains the dimensions of the | |
; bounding rectangle of the specified window. The dimensions are given in | |
; screen coordinates that are relative to the upper-left corner of the screen. | |
; The next 8 bytes contain the X and Y offsets (4-byte integer for X and | |
; 4-byte integer for Y). | |
; | |
; Also if successful (and if defined), the output variables (X, Y, Width, | |
; Height, Offset_X, and Offset_Y) are updated. See the *Parameters* section | |
; for more more information. | |
; | |
; If not successful, FALSE is returned. | |
; | |
; Requirement: | |
; | |
; Windows 2000+ | |
; | |
; Remarks, Observations, and Changes: | |
; | |
; * Starting with Windows Vista, Microsoft includes the Desktop Window Manager | |
; (DWM) along with Aero-based themes that use DWM. Aero themes provide new | |
; features like a translucent glass design with subtle window animations. | |
; Unfortunately, the DWM doesn't always conform to the OS rules for size and | |
; positioning of windows. If using an Aero theme, many of the windows are | |
; actually larger than reported by Windows when using standard commands (Ex: | |
; WinGetPos, GetWindowRect, etc.) and because of that, are not positioned | |
; correctly when using standard commands (Ex: gui Show, WinMove, etc.). This | |
; function was created to 1) identify the true position and size of all | |
; windows regardless of the window attributes, desktop theme, or version of | |
; Windows and to 2) identify the appropriate offset that is needed to position | |
; the window if the window is a different size than reported. | |
; | |
; * The true size, position, and offset of a window cannot be determined until | |
; the window has been rendered. See the example script for an example of how | |
; to use this function to position a new window. | |
; | |
; * 20150906: The "dwmapi\DwmGetWindowAttribute" function can return odd errors | |
; if DWM is not enabled. One error I've discovered is a return code of | |
; 0x80070006 with a last error code of 6, i.e. ERROR_INVALID_HANDLE or "The | |
; handle is invalid." To keep the function operational during this types of | |
; conditions, the function has been modified to assume that all unexpected | |
; return codes mean that DWM is not available and continue to process without | |
; it. When DWM is a possibility (i.e. Vista+), a developer-friendly messsage | |
; will be dumped to the debugger when these errors occur. | |
; | |
; Credit: | |
; | |
; Idea and some code from *KaFu* (AutoIt forum) | |
; jballi - https://autohotkey.com/boards/viewtopic.php?f=6&t=3392 | |
; | |
;------------------------------------------------------------------------------- | |
WinGetPosEx(hWindow,ByRef X="",ByRef Y="",ByRef Width="",ByRef Height="",ByRef Offset_X="",ByRef Offset_Y="") | |
{ | |
Static Dummy5693 | |
,RECTPlus | |
,S_OK:=0x0 | |
,DWMWA_EXTENDED_FRAME_BOUNDS:=9 | |
;-- Workaround for AutoHotkey Basic | |
PtrType:=(A_PtrSize=8) ? "Ptr":"UInt" | |
;-- Get the window's dimensions | |
; Note: Only the first 16 bytes of the RECTPlus structure are used by the | |
; DwmGetWindowAttribute and GetWindowRect functions. | |
VarSetCapacity(RECTPlus,24,0) | |
DWMRC:=DllCall("dwmapi\DwmGetWindowAttribute" | |
,PtrType,hWindow ;-- hwnd | |
,"UInt",DWMWA_EXTENDED_FRAME_BOUNDS ;-- dwAttribute | |
,PtrType,&RECTPlus ;-- pvAttribute | |
,"UInt",16) ;-- cbAttribute | |
if (DWMRC<>S_OK) | |
{ | |
if ErrorLevel in -3,-4 ;-- Dll or function not found (older than Vista) | |
{ | |
;-- Do nothing else (for now) | |
} | |
else | |
outputdebug, | |
(ltrim join`s | |
Function: %A_ThisFunc% - | |
Unknown error calling "dwmapi\DwmGetWindowAttribute". | |
RC=%DWMRC%, | |
ErrorLevel=%ErrorLevel%, | |
A_LastError=%A_LastError%. | |
"GetWindowRect" used instead. | |
) | |
;-- Collect the position and size from "GetWindowRect" | |
DllCall("GetWindowRect",PtrType,hWindow,PtrType,&RECTPlus) | |
} | |
;-- Populate the output variables | |
X:=Left :=NumGet(RECTPlus,0,"Int") | |
Y:=Top :=NumGet(RECTPlus,4,"Int") | |
Right :=NumGet(RECTPlus,8,"Int") | |
Bottom :=NumGet(RECTPlus,12,"Int") | |
Width :=Right-Left | |
Height :=Bottom-Top | |
OffSet_X:=0 | |
OffSet_Y:=0 | |
;-- If DWM is not used (older than Vista or DWM not enabled), we're done | |
if (DWMRC<>S_OK) | |
Return &RECTPlus | |
;-- Collect dimensions via GetWindowRect | |
VarSetCapacity(RECT,16,0) | |
DllCall("GetWindowRect",PtrType,hWindow,PtrType,&RECT) | |
GWR_Width :=NumGet(RECT,8,"Int")-NumGet(RECT,0,"Int") | |
;-- Right minus Left | |
GWR_Height:=NumGet(RECT,12,"Int")-NumGet(RECT,4,"Int") | |
;-- Bottom minus Top | |
;-- Adjust width and height for offset calculations if DPI is in play | |
; See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx | |
; The current version of AutoHotkey is PROCESS_SYSTEM_DPI_AWARE (contains "<dpiAware>true</dpiAware>" in its manifest) | |
; DwmGetWindowAttribute returns DPI scaled sizes | |
; GetWindowRect does not | |
; get monitor handle where the window is at so we can get the monitor name | |
hMonitor := DllCall("MonitorFromRect",PtrType,&RECT,UInt,2) ; MONITOR_DEFAULTTONEAREST = 2 (Returns a handle to the display monitor that is nearest to the rectangle) | |
; get monitor name so we can get a handle to the monitor device context | |
VarSetCapacity(MONITORINFOEX,104) | |
NumPut(104,MONITORINFOEX) | |
DllCall("GetMonitorInfo",PtrType,hMonitor,PtrType,&MONITORINFOEX) | |
monitorName := StrGet(&MONITORINFOEX+40) | |
; get handle to monitor device context so we can get the dpi adjusted and actual screen sizes | |
hdc := DllCall("CreateDC",Str,monitorName,PtrType,0,PtrType,0,PtrType,0) | |
; get dpi adjusted and actual screen sizes | |
dpiAdjustedScreenHeight := DllCall("GetDeviceCaps",PtrType,hdc,Int,10) ; VERTRES = 10 (Height, in raster lines, of the screen) | |
actualScreenHeight := DllCall("GetDeviceCaps",PtrType,hdc,Int,117) ; DESKTOPVERTRES = 117 | |
; delete hdc as instructed | |
DllCall("DeleteDC",PtrType,hdc) | |
; calculate dpi adjusted width and height | |
dpiFactor := actualScreenHeight/dpiAdjustedScreenHeight ; this will be 1.0 if DPI is 100% | |
dpiAdjusted_Width := Ceil(Width/dpiFactor) | |
dpiAdjusted_Height := Ceil(Height/dpiFactor) | |
;-- Calculate offsets and update output variables | |
NumPut(Offset_X:=(dpiAdjusted_Width-GWR_Width)//2,RECTPlus,16,"Int") | |
NumPut(Offset_Y:=(dpiAdjusted_Height-GWR_Height)//2,RECTPlus,20,"Int") | |
Return &RECTPlus | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment