Skip to content

Instantly share code, notes, and snippets.

@hoppfrosch
Last active November 20, 2018 12:33
Show Gist options
  • Save hoppfrosch/9402b065681e8b002f8a to your computer and use it in GitHub Desktop.
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…
; 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
;------------------------------
;
; 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