Rewrote my version of the OP, it took more code than the 50 lines i was hoping for the rewrite, and even ended up being more than the original code, to make it easier to maintain, more efficient & easier to modify for anyone who wants to modify to their whims. It now uses ProcessName,ClassName,WindowStyle to uniquely identify windows, uses PID for window management sessions(move window on first run only), only saves position data from windows that are niether maximised nor minimised and idles until active window belongs to a managed process.
Script to gather info about active window,to verfiy window identifiers in INI are the correct ones.
Code: Select all
; Automatically Restore Previous Window Size/Pos
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#Persistent
#SingleInstance, Force
#NoTrayIcon
Process, Priority, , Normal
;get monitor count
SysGet, numberOfMonitors, MonitorCount
;==================================================== CONFIGURATION SECTION =====================================================================================================
;list of processes to resize & reposition automatically - note the main class of a process listed here will be the class name registered when a listed process window is first encountered.
managedProcesses = xulrunner.exe,firefox.exe,notepad.exe,chrome.exe,notepad++.exe,ProcessHacker.exe,Cmd.exe,mintty.exe,FoxitReader.exe,taskmgr.exe
;class names that prevent an active window with a listed class from being managed, seprate with commas.
excludedClassList =
;titles that prevent an active window with a listed title & belonging to a managed processes from being managed, add titles on newlines.
excludedTitlesList =
(
any window title delimited by lines will exclude that window from being managed - every title should be on a new line
)
;==================================================== END OF CONFIGURATION SECTION ==============================================================================================
;append dynamicallyManagedProcesses to managedProcesses list
dynamicallyManagedProcesses := INI("r", "dynamically managed processes", "dynamicallyManagedProcessesList")
managedProcesses .= "," dynamicallyManagedProcesses ;update managedProcesses with dynamicallyManagedProcesses on init.
Loop ;main execution loop
Main()
;to add or remove additional windows as managed processes with a single hotkey,NOTE:this hotkey CAN NOT remove managed processes defined above,only those added by hotkey
^+Ins::Add_RemoveDynamicallyManagedProcess()
Main(){
Global
ActiveWinStats()
;sessionIDlist references PID of windows that have already been restored using data from previous process sessions,it ensures a window with it's pid in the list isn't moved again until new process.
If (activeWinIsManaged AND !InStr(sessionIDlist, activeWindowID)){
SetWinPos()
}
Else If(activeWinIsManaged AND InStr(sessionIDlist, activeWindowID)){
SaveWinPos()
}
;wait until active window is a managed process
While !IsContainedIn(managedProcesses, activeProcessName){
WinGet, activeProcessName, ProcessName, A
Sleep, 100
}
}
;=================================================================================================================================================
ActiveWinStats(){ ;returns active window title, position, class, style, PID & parent exe, by populating global variables
Global
WinGetActiveStats, activeTitle, activeWidth, activeHeight, active_X, active_Y
WinGetClass, activeClass, A
WinGet, activeProcessName, ProcessName, A
WinGet, activeStyle, Style, A
WinGet, activeProcessPID, PID, A ;to track active process session,so that this process is only acted upon if it's PID isn't listed as being processed,it's reset when process dies.
WinGet, activeWindowID, ID, A ;to track active process session,so that this process is only acted upon if it's PID isn't listed as being processed,it's reset when process dies.
WinGet, activeWinIsMaxOrMin, MinMax, A ;returns false/0 if active win is niether maximised nor minimised,i.e 'candidate be repositioned & resized'
statsDebug:= activeTitle "`n" activeWidth "`n" activeHeight "`n" active_X "`n" active_Y "`n" activeClass "`n" activeProcessName "`n" activeWindowID "`n" activeStyle "`n" activeWinIsMaxOrMin
; ToolTip, %statsDebug%
If ( IsContainedIn(managedProcesses, activeProcessName) AND !IsContainedIn(excludedClassList, activeClass) AND !IsContainedIn(excludedTitlesList, activeTitle) AND !activeWinIsMaxOrMin){
activeWinIsManaged := true
}
Else activeWinIsManaged := false
}
;==========================================================================================================================================================================
;===================================== Functions to resize & reposition managed windows according to exclusion rules ====================================================
;==========================================================================================================================================================================
SetWinPos(){ ;saves position of window after moving it to previous session size and position
Global
GetWinStats()
;verify window is valid window and not a child window of a mananged processes
If AllPairsMatch(activeProcessName "|" thisProcessName, activeClass "|" thisProcClass, activeStyle "|" thisProcStyle){
;if activee window's position data doesn't match stored position data move it
If !AllPairsMatch(activeHeight "|" thisProcHeight, activeWidth "|" thisProcWidth, active_X "|" thisProcX, active_Y "|" thisProcY){
sessionIDlist .= activeWindowID "`, " ;register active window ID so that it's no longer moved again,but it's position is updated for future sessions
;if active window attributes are not out of bounds proceed
If(active_X > -1 AND active_Y > -1 AND activeWidth < A_ScreenWidth AND activeHeight < A_ScreenHeight AND activeWidth > 0 AND activeHeight > 0 AND numberOfMonitors = 1){
MoveWin()
}
Else If(active_X AND active_Y AND activeWidth AND activeHeight AND numberOfMonitors > 1){ ;If morethan one monitor !unable to test reliability due to single monitor setup.
MoveWin()
}
}
Else ;if AllPairsMatch...
sessionIDlist .= activeWindowID "`, " ;register active window ID so that it's no longer moved again,if it was repositioned to previous position by itself or other means.
}
}
;=================================================================================================================================================
SaveWinPos(){ ;saves position of a window that has already been moved to position & size of previous session for future sessions
Global
GetWinStats()
;verify window is valid window and not a child window of a mananged processes
If AllPairsMatch(activeProcessName "|" thisProcessName, activeClass "|" thisProcClass, activeStyle "|" thisProcStyle){
;if activee window's position data changes from stored data, save it to update saved data
If !AllPairsMatch(activeHeight "|" thisProcHeight, activeWidth "|" thisProcWidth, active_X "|" thisProcX, active_Y "|" thisProcY){
;if active window attributes are not out of bounds, proceed with saving data
If(active_X > -1 AND active_Y > -1 AND activeWidth < A_ScreenWidth AND activeHeight < A_ScreenHeight AND activeWidth > 0 AND activeHeight > 0 AND numberOfMonitors = 1){
SaveWinStats()
}
Else If(active_X AND active_Y AND activeWidth AND activeHeight AND numberOfMonitors > 1){ ;If morethan one monitor !unable to test reliability due to single monitor setup.
SaveWinStats()
}
}
}
}
;==========================================================================================================================================================================
;==========================================================================================================================================================================
;==========================================================================================================================================================================
MoveWin(){
Global
WinActivate, ahk_pid %activeProcessPID% ;For those times windows fail to get resized because another window took focus. Yea even with PID based move, that is an issue!
WinMove, ahk_pid %activeProcessPID%,, %thisProcX%, %thisProcY%, %thisProcWidth%, %thisProcHeight% ;SetPos of identified managed window based on it's PID.
activeProcessStats := activeProcessName "`," thisProcWidth "`," thisProcHeight "`," thisProcX "`," thisProcY "`," activeClass "`," activeStyle
INI("w", "managed processes",activeProcessName, activeProcessStats)
Sleep, 1000 ;safeguard against write & repositioning delay
}
;=================================================================================================================================================
SaveWinStats(){
Global
activeProcessStats := activeProcessName "`," activeWidth "`," activeHeight "`," active_X "`," active_Y "`," activeClass "`," activeStyle
INI("w", "managed processes",activeProcessName, activeProcessStats)
Sleep, 100 ;safeguard against write delay
}
;=================================================================================================================================================
GetWinStats(){ ;gets previously recorded stats for active process as well as refreshing active window stats
Global
ActiveWinStats()
thisProcessStats := INI("r", "managed processes", activeProcessName)
Sleep, 100 ;safeguard against read delay
StringSplit, statArray, thisProcessStats, `,
thisProcessName:=statArray1, thisProcWidth:=statArray2, thisProcHeight:=statArray3, thisProcX:=statArray4, thisProcY:=statArray5, thisProcClass:=statArray6, thisProcStyle:=statArray7
;if managedProcesses is detected for first time, set thisProc* attribs to current to allow it to be saved to INI
If activeWinIsManaged ;safeguard against unmanaged windows being registered
IfNotInString, thisINI, %activeProcessName%
thisProcessName:=activeProcessName, thisProcClass:=activeClass, thisProcStyle:=activeStyle
}
;=================================================================================================================================================
IsContainedIn(haystack, needle){ ;if Needle is contained in Haystack it returns true - Haystack must be comma delimited or line delimited
IfNotInString, haystack, `n ;if not line delimited
{
Loop, Parse, haystack, `,
IfEqual, needle, %A_LoopField%
Return True
}
Else
{
Loop, Parse, haystack, `n
IfEqual, needle, %A_LoopField%
Return True
}
}
;=================================================================================================================================================
; MsgBox, % AllPairsMatch("do|doo", x "|" y)
AllPairsMatch(pairs*){ ;each pair must be a single function parameter delimited by '|', ex. AllPairsMatch(varA "|" varB)
for index,pair in pairs
{
numberOfPairs++
StringSplit, pairArray, pair, |
IfEqual, pairArray1, %pairArray2%
pairsMatched++ ;increment for every pair matched
}
IfEqual, pairsMatched, %numberOfPairs% ;all pairs match
Return true
}
;=================================================================================================================================================
INI(rw, ini_section, ini_key, key_value:="", key_default_value:=" ", ini_file:="WindowSizePosLog.ini"){ ;r/w - 'r' to read & 'w' to write to ini | 'd' delete INI key
Global thisINI
FileRead, thisINI, %ini_file% ;global var with INI file contents
If (rw = "r"){ ;read from ini
IniRead, thisKeyVal, %ini_file%, %ini_section%, %ini_key%, %key_default_value%
; FileAppend, [%A_Now%] [READ] %ini_section% --> %ini_key% --> %key_value%`n, %ini_file%.LOG.log ;log INI reads
Return, thisKeyVal
}
Else If (rw = "w"){ ;write to ini
IniWrite, %key_value%, %ini_file%, %ini_section%, %ini_key%
FileAppend, [%A_Now%] [WRITE] %ini_section% --> %ini_key% --> %key_value%`n, %ini_file%.LOG.log ;log INI writes
}
Else If (rw = "d"){ ;delete key from ini
IniDelete, %ini_file%, %ini_section%, %ini_key%
FileAppend, [%A_Now%] [DELETE] %ini_section% --> %ini_key% `n, %ini_file%.LOG.log ;log INI writes
}
}
;=================================================================================================================================================
Add_RemoveDynamicallyManagedProcess(){ ;if active process is managed process it prompts to remove it and if it's not it prompts to add it to managedProcesses list.
Global
ActiveWinStats()
dynamicallyManagedProcesses := INI("r", "dynamically managed processes", "dynamicallyManagedProcessesList")
this_dynamicallyManagedProcesses := ""
If !InStr(dynamicallyManagedProcesses , activeProcessName){ ; if active window is not managed,add it.
MsgBox, 0x40041, %A_ScriptName%, ADD Active Window as a Managed Process? `n`n %activeProcessName% `n %activeClass% `n %activeStyle%
IfMsgBox, OK
{
INI("w", "dynamically managed processes","dynamicallyManagedProcessesList", dynamicallyManagedProcesses "`," activeProcessName)
SaveWinStats()
}
}
Else If InStr(dynamicallyManagedProcesses , activeProcessName){ ;if active window is already managed remove it as managedProcesses by rebuilding dynamicallyManagedProcessesList with out it.
MsgBox, 0x40031, %A_ScriptName%, Remove Window as a Managed Process? `n`n %activeProcessName% `n %activeClass% `n %activeStyle%
IfMsgBox, OK
{
Loop, Parse, dynamicallyManagedProcesses, `, ;rebuild list minus removed process
IfNotInString, A_LoopField, %activeProcessName%
If A_LoopField ;if loop field contains anything
this_dynamicallyManagedProcesses .= A_LoopField ","
dynamicallyManagedProcesses := this_dynamicallyManagedProcesses
INI("w", "dynamically managed processes","dynamicallyManagedProcessesList", dynamicallyManagedProcesses)
StringReplace, managedProcesses, managedProcesses, %activeProcessName%, , All ;remove process from primary managedProcesses list
INI("d", "managed processes", activeProcessName) ;remove process pos data from INI
}
}
;update managedProcesses with dynamicallyManagedProcesses
dynamicallyManagedProcesses := INI("r", "dynamically managed processes", "dynamicallyManagedProcessesList")
IfNotInString, managedProcesses, %dynamicallyManagedProcesses%
managedProcesses .= "," dynamicallyManagedProcesses
}