This is my startup script that I put a shortcut to run in my startup folder to load my standard scripts on computer bootup.
It basically Runs a list of scripts.
This list can include a folder which will run all files in that folder and subfolders. Wildcards * and ? can also be used.
Relative path can also be used by using .\ at the beginning of a file path. One dot is the folder this script is located in. Each additional dot steps back one folder.
It also looks for a txt file with the same name as the script and includes the files listed there.
It then creates a tooltip that list all the scripts that it started.
It then removes the tray icon of all the scripts it started leaving only the "AHK Startup" tray icon.
When AHK Startup is exited or stopped all the scripts it started will also be exited and stopped.
All the lines between:
(Join,
)]
Are example paths only to show different syntax and formatting of how paths can be done. These lines MUST BE EDITED to the path and scripts that the user wants AHK Startup to run.
In the list of scripts, the flag "/noload" can be used after a script name to not run the script but include it in the "Load" submenu. When a running script is exited through the menu then it is also moved to the "Load" submenu which means scripts can be loaded and unloaded through the menu by using "Load" and "Exit". I use "/noload" on scripts that I want easy access to but do not actually want to have running all the time. A script is only moved to the "Load" submenu if exited through the menu. If a script is closed directly or closes itself, then it is not added to the "Load" submenu.
I use this script for startup but AHK Startup does not really even have to be done at bootup, that is just how I use it. You could make three copies of AHK Startup and rename them "Work Scripts", "Web Scripts", and "Game Scripts". Then define within each of those which scripts it starts. Then run them as you need them, one or all three and have three tray icons that each start and stop related scripts together. AHK Startup is really about just consolidating multiply scripts to start and stop together like suites of scripts that you want to run together.
You lose some of the functionality of right mouse clicking on a scripts tray icon as the tray icons for the scripts are removed. So, there are no individual script tray icons to click on to get the default tray menu items like pause and suspend. Those are the only two I have not currently implemented. I have another one of my scripts always running that gives me a different way to access those abilities other than right clicking a tray icon.
Hotkey Help - Help Dialog for Currently Running AHK Scripts
Update: Added a custom right click menu to the AHK Startup tray icon to allow access to some individual script controls.
Update: Added the ability to Reload, Load, and Exit scripts.
Update: Added flags for running scripts with v1 or v2 versions of AutoHotkey.exe
Update: Added additional cleanup of tray icons on screen resolution change and anytime the AHK Startup tray icon is moused over
Update: Converted from TrayIcon library to KillTrayIcon function
Code: Select all
; AHK Startup
; Fanatic Guru
;
; Version: 2023 03 16
;
; Startup Script for Startup Folder to Run on Bootup.
;{-----------------------------------------------
; Runs the Scripts Defined in the Files Array
; Removes the Scripts' Tray Icons leaving only AHK Startup
; Creates a ToolTip for the One Tray Icon Showing the Startup Scripts
; If AHK Startup is Exited All Startup Scripts are Exited
; Includes a "Load" menu for a list of scripts that are not currently loaded
; Includes flags to allow for running of both v1 and v2 scripts
;}
; INITIALIZATION - ENVIROMENT
;{-----------------------------------------------
;
#Requires AutoHotkey v1.1.33
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance force ; Ensures that only the last executed instance of script is running
DetectHiddenWindows, On
;}
; INITIALIZATION - VARIABLES
;{-----------------------------------------------
; Folder: all files in that folder and subfolders
; Relative Paths: .\ at beginning is the folder of the script, each additional . steps back one folder
; Wildcards: * and ? can be used
; Flags to the right are used to indicate additional instructions, can use tabs for readability
; "/noload" indicates to not load the script initially but add to Load submenu
; "/v1" or no version flag indicates to run with AutoHotkey v1 exe
; "/v2" indicates to run with AutoHotkey v2 exe
Files := [ ; Additional Startup Files and Folders Can Be Added Between the ( Continuations ) Below
(Join,
"C:\Users\Guru\Documents\AutoHotkey\Startup\"
"C:\Users\Guru\Documents\AutoHotkey\Compiled Scripts\*.exe"
A_MyDocuments "\AutoHotkey\My Scripts\Hotstring Helper.ahk"
"C:\Users\Guru\Documents\AutoHotkey\My Scripts\Calculator.ahk" "/v2"
".\Web\Google Search.ahk" "/v1"
"..\Dictionary.ahk"
"Hotkey Help.ahk"
"MediaMonkey.ahk" "/noload"
)]
; Define Path to AutoHotkey.exe for v1 and v2
RunPathV1 := A_AhkPath
RunPathV2 := A_ProgramFiles "\AutoHotkey\v2\AutoHotkey.exe"
;}
; AUTO-EXECUTE
;{-----------------------------------------------
;
if FileExist(RegExReplace(A_ScriptName,"(.*)\..*","$1.txt")) ; Look for text file with same name as script
Loop, Read, % RegExReplace(A_ScriptName,"(.*)\..*","$1.txt")
if A_LoopReadLine
Files.Insert(A_LoopReadLine)
Scripts := {}
For index, File in Files
{
if File ~= "/noload"
Status := false
else
Status := true
if File ~= "/v2"
RunPath := RunPathV2
else
RunPath := RunPathV1
File := RegExReplace(File, "/noload|/v1|/v2")
RegExMatch(File,"^(\.*)\\",Match), R := StrLen(Match1) ; Look for relative pathing
if (R=1)
File := A_ScriptDir SubStr(File,R+1)
else if (R>1)
File := SubStr(A_ScriptDir,1,InStr(A_ScriptDir,"\",,0,R-1)) SubStr(File,R+2)
if RegExMatch(File,"\\$") ; If File ends in \ assume it is a folder
Loop, %File%*.*,,1 ; Get full path of all files in folder and subfolders
{
SplitPath, % A_LoopFileFullPath,,,, Script_Name
Scripts[Script_Name, "Path"] := A_LoopFileFullPath
Scripts[Script_Name, "Status"] := Status
Scripts[Script_Name, "RunPath"] := RunPath
}
else
if RegExMatch(File,"\*|\?") ; If File contains wildcard
Loop, %File%,,1 ; Get full path of all matching files in folder and subfolders
{
SplitPath, % A_LoopFileFullPath,,,, Script_Name
Scripts[Script_Name, "Path"] := A_LoopFileFullPath
Scripts[Script_Name, "Status"] := Status
Scripts[Script_Name, "RunPath"] := RunPath
}
else
{
SplitPath, % File,,,, Script_Name
Scripts[Script_Name, "Path"] := File
Scripts[Script_Name, "Status"] := Status
Scripts[Script_Name, "RunPath"] := RunPath
}
}
; Run All the Scripts with Status true, Keep Their Pid
for Script_Name, Script in Scripts
{
if !Script.Status
continue
; Use same AutoHotkey version to run scripts as this current script is using
; Required to deal with 'launcher' that was introduced when Autohotkey v2 is installed
; Requires literal quotes around variables to handle spaces in file paths/names
Run, % """" Script.RunPath """ """ Script.Path """",,, Pid ; specify Autohotkey version
Scripts[Script_Name,"Pid"] := Pid
}
OnExit, ExitSub ; Gosub to ExitSub when this Script Exits
; Build Menu and TrayTip then Remove Tray Icons
gosub TrayTipBuild
gosub MenuBuild
OnMessage(0x404, "AHK_NOTIFYICON") ; Hook Events for Tray Icon (used for Tray Icon cleanup on mouseover)
OnMessage(0x7E, "AHK_DISPLAYCHANGE") ; Hook Events for Display Change (used for Tray Icon cleanup on resolution change)
TrayIconRemove(10)
;
;}-----------------------------------------------
; END OF AUTO-EXECUTE
; HOTKEYS
;{-----------------------------------------------
;
~#^!Escape::ExitApp ; <-- Terminate Script
;}
; SUBROUTINES
;{-----------------------------------------------
;
TrayTipBuild:
Tip_Text := ""
for Script_Name, Script in Scripts
if Script.Status
Tip_Text .= Script_Name "`n"
Sort, Tip_Text
Tip_Text := TrimAtDelim(Trim(Tip_Text, " `n"))
Menu, Tray, Tip, %Tip_Text% ; Tooltip is limited to first 127 characters
return
; Stop All the Scripts with Status true (Called When this Scripts Exits)
ExitSub:
for Script_Name, Script in Scripts
{
WinGet, hWnds, List, % "ahk_pid " Script.Pid
Loop % hWnds
{
hWnd := hWnds%A_Index%
WinKill, % "ahk_id " hWnd
}
}
ExitApp
return
;}
; SUBROUTINES - GUI
;{-----------------------------------------------
;
MenuBuild:
try Menu, SubMenu_Load, DeleteAll ; SubMenu_Load does not always exist
Menu, Tray, DeleteAll
for Script_Name, Script in Scripts
if Script.Status
{
PID := Script.PID
try Menu, SubMenu_%PID%, DeleteAll
Menu, SubMenu_%PID%, Add, View Lines, ScriptCommand
Menu, SubMenu_%PID%, Add, View Variables, ScriptCommand
Menu, SubMenu_%PID%, Add, View Hotkeys, ScriptCommand
Menu, SubMenu_%PID%, Add, View Key History, ScriptCommand
Menu, SubMenu_%PID%, Add
Menu, SubMenu_%PID%, Add, &Open, ScriptCommand
Menu, SubMenu_%PID%, Add, &Edit, ScriptCommand
Menu, SubMenu_%PID%, Add, &Pause, ScriptCommand
Menu, SubMenu_%PID%, Add, &Suspend, ScriptCommand
Menu, SubMenu_%PID%, Add, &Reload, ScriptCommand
Menu, SubMenu_%PID%, Add, &Exit, ScriptCommand
Menu, Tray, Add, %Script_Name%, :SubMenu_%PID%
}
else
Menu, SubMenu_Load, Add, % Script_Name, ScriptCommand_Load
Menu, Tray, NoStandard
Menu, Tray, Add
try Menu, Tray, Add, Load, :SubMenu_Load ; SubMenu_Load does not always exist
Menu, Tray, Standard
try Menu, Tray, Default, Load ; SubMenu_Load does not always exist
return
ScriptCommand:
Cmd_Open = 65300
Cmd_Reload = 65400
Cmd_Edit = 65401
Cmd_Pause = 65403
Cmd_Suspend = 65404
Cmd_Exit = 65405
Cmd_ViewLines = 65406
Cmd_ViewVariables = 65407
Cmd_ViewHotkeys = 65408
Cmd_ViewKeyHistory = 65409
Pid := RegExReplace(A_ThisMenu,"SubMenu_(\d*)$","$1") ; each SubMenu name included Pid
cmd := RegExReplace(A_ThisMenuItem, "[^\w#@$?\[\]]") ; strip invalid chars
; if Cmd_Reload, simulate by exiting and running again with captured Pid
if (cmd = "Reload")
{
for Script_Name, Script in Scripts ; find Script by Pid
if (Script.Pid = Pid)
break
Menu, SubMenu_%PID%, DeleteAll ; delete Tray SubMenu of old Pid (use 'try' just in case)
PostMessage, 0x111, Cmd_Exit,,,ahk_pid %Pid%
Run, % """" Script.RunPath """ """ Script.Path """",,, Pid ; specify Autohotkey version
Scripts[Script_Name,"Pid"] := Pid
gosub MenuBuild ; need to rebuild menu because changed Pid is used in menu names
TrayIconRemove(8) ; need to remove new icon
}
else
{
if (cmd = "Pause" or cmd = "Suspend")
Menu, SubMenu_%PID%, ToggleCheck, %A_ThisMenuItem%
cmd := Cmd_%cmd%
PostMessage, 0x111, %cmd%,,,ahk_pid %Pid%
}
; If Cmd_Exit then Set Status to false
if (cmd = 65405)
{
for Script_Name, Script in Scripts
if (Script.Pid = Pid)
break
Scripts[Script_Name, "Status"] := false
; Rebuild Menu and TrayTip
gosub MenuBuild
gosub TrayTipBuild
}
return
ScriptCommand_Load:
; Run Script and Keep Info
Run, % """" Scripts[A_ThisMenuItem].RunPath """ """ Scripts[A_ThisMenuItem].Path """",,, Pid ; specify Autohotkey version
Scripts[A_ThisMenuItem, "Pid"] := Pid
Scripts[A_ThisMenuItem, "Status"] := true
; Rebuild Menu and TrayTip then Remove Tray Icon
gosub MenuBuild
gosub TrayTipBuild
TrayIconRemove(8)
return
;}
; FUNCTIONS
;{-----------------------------------------------
;
TrayIconRemove(Attempts)
{
global Scripts
Loop, % Attempts ; Try To Remove Over Time Because Icons May Lag Especially During Bootup
{
for Script_Name, Script in Scripts
if Script.Status
{
WinGet, hWnds, List, % "ahk_pid " Script.Pid
Loop % hWnds
{
hWnd := hWnds%A_Index%
KillTrayIcon(hWnd)
}
}
Sleep A_index**2 * 200
}
return
}
; Lexikos
KillTrayIcon(scriptHwnd) {
static NIM_DELETE := 2, AHK_NOTIFYICON := 1028
VarSetCapacity(nic, size := 936+4*A_PtrSize)
NumPut(size, nic, 0, "uint")
NumPut(scriptHwnd, nic, A_PtrSize)
NumPut(AHK_NOTIFYICON, nic, A_PtrSize*2, "uint")
return DllCall("Shell32\Shell_NotifyIcon", "uint", NIM_DELETE, "ptr", &nic)
}
TrimAtDelim(String,Length:=124,Delim:="`n",Tail:="...")
{
if (StrLen(String)>Length)
RegExMatch(SubStr(String, 1, Length+1),"(.*)" Delim, Match), Result := Match Tail
else
Result := String
return Result
}
AHK_NOTIFYICON(wParam, lParam, uMsg, hWnd) ; OnMessage(0x404, "AHK_NOTIFYICON")
{
; Cleanup Tray Icons on MouseOver
if (lParam = 0x200) ; WM_MOUSEMOVE := 0x200
TrayIconRemove(1)
}
AHK_DISPLAYCHANGE(wParam, lParam) ; OnMessage(0x7E, "AHK_DISPLAYCHANGE")
{
; Cleanup Tray Icons on Resolution Change
TrayIconRemove(8) ; Resolution Change can take a moment so try over time
}
;}
I #include [Library] TrayIcon.ahk in my script as I use these functions in other scripts but have manually placed the three required function at the end of AutoHotkey Startup above for convenience.
Here is a link to my (outdated) version of the entire updated TrayIcon Library:
Updated TrayIcon Library
FG