P10: Windows Automation Suite (Capstone)
P10: Windows Automation Suite (Capstone)
Project Overview
What youโll build: A comprehensive automation platform that combines AutoHotkey v2 and PowerShell, featuring a system tray application with hotkeys for quick actions, a PowerShell backend module for complex operations, and seamless integration where AHK triggers PowerShell scripts and displays results through GUI notifications.
| Attribute | Value |
|---|---|
| Difficulty | Expert |
| Time Estimate | 1 month |
| Programming Languages | AutoHotkey v2 + PowerShell |
| Knowledge Area | System Administration, Desktop Automation, Inter-Process Communication |
| Prerequisites | Completed at least 2-3 projects from each category (AHK and PowerShell) |
Learning Objectives
After completing this project, you will be able to:
- Design hybrid automation systems - Architect solutions that leverage both AHK and PowerShell strengths
- Implement inter-process communication - Pass data between AHK and PowerShell reliably
- Build system tray applications - Create professional Windows applications with tray icons and menus
- Create toast notifications - Display modern Windows 10/11 notifications from scripts
- Develop a command palette interface - Build fuzzy-search command execution like VS Codeโs Ctrl+Shift+P
- Handle scheduled automation - Run background tasks on timers with user-friendly alerts
- Manage configuration across tools - Share settings between AHK and PowerShell components
- Deploy multi-component systems - Package and distribute applications with multiple interacting parts
Deep Dive: Theoretical Foundation
Inter-Process Communication (IPC) Strategies
When AutoHotkey and PowerShell need to work together, they must communicate across process boundaries. Understanding IPC options is essential for building robust integrations.
Strategy 1: Command-Line Invocation (Simplest)
AHK launches PowerShell as a child process and captures output:
AHK Script
|
+-- Run("powershell.exe -Command Get-Service")
|
v
PowerShell executes, writes to stdout
|
v
AHK captures output via StdoutRead
|
v
AHK parses and displays results
Pros: Simple, no setup required, works everywhere Cons: Process startup overhead (~200-500ms), limited to text output
Strategy 2: Shared Files (Reliable)
Both tools read/write to shared configuration or data files:
AHK writes command:
C:\Temp\automation_command.json
{ "action": "get-services", "filter": "SQL*" }
|
v
PowerShell watches file, executes, writes result:
C:\Temp\automation_result.json
{ "services": [...], "timestamp": "..." }
|
v
AHK reads result file, displays GUI
Pros: Works across restarts, debuggable (files are visible), supports complex data Cons: File I/O latency, cleanup required, race conditions possible
Strategy 3: Named Pipes (Fast)
Create a named pipe for bidirectional communication:
PowerShell creates pipe server:
\\.\pipe\automation_pipe
|
AHK connects as client <---> PowerShell reads/writes
|
Real-time bidirectional messaging
Pros: Very fast, real-time, no disk I/O Cons: Complex to implement, requires running server, connection management
Strategy 4: COM Objects (Deep Integration)
PowerShell exposes objects that AHK can call directly:
PowerShell registers COM object
|
v
AHK creates: ComObject("Automation.PowerShellBridge")
|
v
AHK calls: obj.GetServices("SQL*")
|
v
Returns actual objects, not just text
Pros: Richest integration, type-safe, fast after initial setup Cons: Requires registration, complex implementation, Windows-specific
Recommended Approach for This Project:
Use a hybrid strategy:
- Quick commands: Direct PowerShell invocation with JSON output
- Configuration: Shared JSON config file
- Background operations: File-based with file-watching
- Real-time monitoring: Named pipes for event streaming
When to Use AHK vs PowerShell: Decision Framework
Making the right tool choice is critical for maintainable automation. Use this framework:
Use AutoHotkey When:
| Requirement | Why AHK? |
|---|---|
| Keyboard shortcuts/hotkeys | Native hotkey engine, sub-millisecond response |
| Mouse automation | Direct cursor control, click simulation |
| GUI popup windows | Built-in GUI system, lightweight windows |
| Clipboard management | Native clipboard monitoring |
| Window manipulation | WinMove, WinActivate, window enumeration |
| System tray icons | Native tray menu support |
| User input blocking | Can intercept and modify keystrokes |
Use PowerShell When:
| Requirement | Why PowerShell? |
|---|---|
| System administration | WMI/CIM queries, service management |
| File operations at scale | Robust file handling, permissions |
| Remote execution | PowerShell Remoting built-in |
| Complex data processing | Pipeline paradigm, object manipulation |
| Windows Event Logs | Native event log cmdlets |
| Scheduled Tasks | Task Scheduler integration |
| Registry operations | Full registry access |
| Active Directory | AD cmdlets for user/group management |
Decision Tree:
Is it user-facing with immediate feedback?
|
+-- Yes: Is it input-related (keyboard/mouse)?
| |
| +-- Yes: Use AHK
| |
| +-- No: Is it a simple popup/notification?
| |
| +-- Yes: Use AHK
| |
| +-- No: AHK for display, PowerShell for logic
|
+-- No: Is it system administration?
|
+-- Yes: Use PowerShell
|
+-- No: Does it need to run in background?
|
+-- Yes: PowerShell (or hybrid)
|
+-- No: Either works, prefer simpler option
System Tray Applications
A system tray (notification area) application provides persistent presence without consuming screen space. Windows users expect professional tools to live in the tray.
Anatomy of a tray application:
System Tray
|
+-- [Icon] <- Your application's icon
|
+-- Left-click: Show main window or primary action
|
+-- Right-click: Context menu
|
+-- Menu item 1
+-- Menu item 2
+-- Separator
+-- Exit
Key considerations:
- Icon design: Use clear, recognizable icons (16x16 and 32x32 pixels)
- Tooltip: Show application name and current status on hover
- Menu organization: Group related items, use separators
- Exit handling: Clean up resources before exiting
- Single instance: Prevent multiple copies running
AutoHotkey tray implementation:
A_TrayMenu.Delete() ; Clear default menu
A_TrayMenu.Add("Command Palette", ShowPalette)
A_TrayMenu.Add("Settings", ShowSettings)
A_TrayMenu.Add() ; Separator
A_TrayMenu.Add("Exit", ExitApp)
A_TrayMenu.Default := "Command Palette" ; Double-click action
TraySetIcon("shell32.dll", 44) ; Set custom icon
Toast Notifications on Windows
Modern Windows (10/11) provides the toast notification system for non-intrusive alerts. These appear in the Action Center and can include buttons, images, and rich text.
Toast notification architecture:
Your Script
|
v
BurntToast PowerShell Module (or WinRT APIs)
|
v
Windows Notification Platform
|
v
Action Center + Desktop Toast
Toast components:
| Component | Description |
|---|---|
| Title | Bold header text |
| Body | Main message content |
| Attribution | Source application text |
| App Logo | Small icon (optional) |
| Hero Image | Large banner image (optional) |
| Buttons | Action buttons (max 5) |
| Progress Bar | For ongoing operations |
PowerShell toast example (using BurntToast):
New-BurntToastNotification -Text "Automation Alert", "Server health check failed on SQL01" `
-AppLogo "C:\Icons\warning.png" `
-Button (New-BTButton -Content "View Details" -Arguments "view:healthcheck")
Fallback for systems without BurntToast:
# Use Windows Forms balloon notification
Add-Type -AssemblyName System.Windows.Forms
$balloon = New-Object System.Windows.Forms.NotifyIcon
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path)
$balloon.BalloonTipTitle = "Automation Alert"
$balloon.BalloonTipText = "Server health check completed"
$balloon.Visible = $true
$balloon.ShowBalloonTip(5000)
Script Integration Patterns
Integrating AHK and PowerShell requires consistent patterns for reliability and maintainability.
Pattern 1: Fire-and-Forget
AHK triggers PowerShell and doesnโt wait for results:
; AHK: Trigger backup without waiting
Run('powershell.exe -WindowStyle Hidden -File "C:\Scripts\Backup.ps1"')
ToolTip("Backup started in background")
Use when: Long-running operations, no immediate feedback needed
Pattern 2: Synchronous Call with Output
AHK runs PowerShell and captures the result:
; AHK: Get service status and show in GUI
result := RunWaitOutput('powershell.exe -Command "Get-Service spooler | ConvertTo-Json"')
data := JSON.Parse(result)
MsgBox("Spooler status: " . data.Status)
Use when: Quick queries, results needed for display
Pattern 3: Async with Callback File
AHK triggers PowerShell, then monitors for result file:
; AHK: Start long operation
Run('powershell.exe -File "C:\Scripts\LongTask.ps1"')
; Monitor for completion
SetTimer(CheckTaskComplete, 1000)
CheckTaskComplete() {
if FileExist("C:\Temp\task_complete.json") {
result := FileRead("C:\Temp\task_complete.json")
; Process result, show notification
SetTimer(CheckTaskComplete, 0)
}
}
Use when: Long operations with result feedback
Pattern 4: Event Streaming
PowerShell runs continuously, AHK reads events:
PowerShell: Watches event log, writes events to file
|
v
C:\Temp\events_stream.log (continuously appended)
|
v
AHK: Tail-follows file, shows notifications for new entries
Use when: Continuous monitoring, real-time alerts
Configuration Management
Shared configuration ensures both AHK and PowerShell components work with the same settings.
Recommended: JSON configuration
{
"version": "1.0.0",
"hotkeys": {
"commandPalette": "#Space",
"quickDeploy": "#^d"
},
"paths": {
"scripts": "C:\\AutomationSuite\\Scripts",
"logs": "C:\\AutomationSuite\\Logs",
"temp": "C:\\AutomationSuite\\Temp"
},
"servers": [
{"name": "SQL01", "address": "192.168.1.10"},
{"name": "SQL02", "address": "192.168.1.11"}
],
"healthCheck": {
"intervalMinutes": 30,
"services": ["MSSQLSERVER", "SQLServerAgent"],
"notifyOnFailure": true
},
"notifications": {
"enabled": true,
"useBurntToast": true,
"soundEnabled": false
}
}
Loading in AutoHotkey:
LoadConfig() {
global Config
configPath := A_ScriptDir . "\config.json"
if FileExist(configPath) {
content := FileRead(configPath)
Config := JSON.Parse(content)
}
}
Loading in PowerShell:
$ConfigPath = Join-Path $PSScriptRoot "config.json"
$Config = Get-Content $ConfigPath | ConvertFrom-Json
Complete Project Specification
Component Overview
| Component | Technology | Purpose |
|---|---|---|
| System Tray Application | AutoHotkey v2 | Main entry point, hotkey registration, GUI |
| Command Palette | AutoHotkey v2 | Fuzzy-search command execution interface |
| PowerShell Backend | PowerShell Module | System operations, server management |
| Notification Engine | PowerShell | Toast notifications via BurntToast |
| Event Monitor | PowerShell | Background log/event watching |
| Configuration | Shared JSON | Settings for all components |
Functional Requirements
| ID | Requirement | Priority | Component |
|---|---|---|---|
| F1 | System tray icon with right-click menu | Must Have | AHK |
| F2 | Hotkey-triggered command palette | Must Have | AHK |
| F3 | Fuzzy search for commands | Must Have | AHK |
| F4 | Execute PowerShell cmdlets from palette | Must Have | AHK + PS |
| F5 | Display results in GUI popup | Must Have | AHK |
| F6 | Scheduled health checks | Must Have | PS |
| F7 | Desktop notifications for alerts | Must Have | PS + AHK |
| F8 | Quick file deployment to servers | Should Have | PS |
| F9 | Event log monitoring | Should Have | PS |
| F10 | Custom command definitions | Should Have | Config |
| F11 | Notification history | Nice to Have | AHK |
| F12 | Remote server selection GUI | Nice to Have | AHK |
Non-Functional Requirements
| ID | Requirement |
|---|---|
| NF1 | Command palette appears within 100ms of hotkey |
| NF2 | PowerShell operations complete within 30 seconds |
| NF3 | Memory usage under 100MB total |
| NF4 | Single-instance enforcement |
| NF5 | Graceful degradation if PowerShell unavailable |
| NF6 | Works without administrator privileges (except remote operations) |
Real World Outcome
When complete, youโll have a professional automation suite:
System Tray Menu Structure
[Tray Icon: Lightning bolt]
|
Right-click menu:
+-- Open Command Palette [Win+Space]
+-- โโโโโโโโโโโโโโโโโโโโโ
+-- Quick Actions >
| +-- Deploy Config Files
| +-- Restart SQL Services
| +-- Collect Server Logs
+-- Server Status >
| +-- SQL01: [Running]
| +-- SQL02: [Running]
| +-- WEB01: [Warning]
+-- โโโโโโโโโโโโโโโโโโโโโ
+-- Settings...
+-- View Logs...
+-- โโโโโโโโโโโโโโโโโโโโโ
+-- Exit
Command Palette Interaction
[Win+Space pressed]
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Automation Suite [X] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ > svc โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ > Get-ServiceStatus Query service status โ
โ > Restart-RemoteService Restart services on servers โ
โ > Stop-RemoteService Stop services on servers โ
โ โ
โ [Enter] Run [Tab] Details [Esc] Close โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
After selection:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Get-ServiceStatus โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Service Name: SQL* โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Server(s): โ
โ [x] SQL01 [x] SQL02 [ ] WEB01 โ
โ โ
โ [Run] [Cancel] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Results display:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Service Status Results [X] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Server Service Status StartType โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ SQL01 MSSQLSERVER Running Automatic โ
โ SQL01 SQLServerAgent Running Automatic โ
โ SQL02 MSSQLSERVER Stopped Automatic โ
โ SQL02 SQLServerAgent Stopped Automatic โ
โ โ
โ [Restart Stopped] [Copy] [Close] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Notification Examples
Health Check Alert:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ [!] Automation Suite โ
โ โ
โ Health Check Failed โ
โ โ
โ SQL02: MSSQLSERVER is Stopped โ
โ 2 services require attention โ
โ โ
โ [View Details] [Restart Services] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Event Log Alert:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ [!] Security Alert โ
โ โ
โ 15 failed login attempts detected โ
โ Source: 192.168.1.105 โ
โ Target: Administrator โ
โ โ
โ [View Event Log] [Block IP] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration File Format
config.json:
{
"$schema": "./config-schema.json",
"version": "1.0.0",
"application": {
"name": "Automation Suite",
"singleInstance": true,
"startWithWindows": false,
"minimizeToTray": true
},
"hotkeys": {
"commandPalette": "#Space",
"quickDeploy": "#^d",
"serverStatus": "#^s",
"emergencyRestart": "#^!r"
},
"paths": {
"root": "C:\\AutomationSuite",
"scripts": "${root}\\Scripts",
"logs": "${root}\\Logs",
"temp": "${root}\\Temp",
"icons": "${root}\\Icons"
},
"servers": [
{
"name": "SQL01",
"address": "sql01.corp.local",
"group": "database",
"enabled": true
},
{
"name": "SQL02",
"address": "sql02.corp.local",
"group": "database",
"enabled": true
},
{
"name": "WEB01",
"address": "web01.corp.local",
"group": "web",
"enabled": true
}
],
"commands": [
{
"id": "get-service-status",
"name": "Get-ServiceStatus",
"description": "Query service status on remote servers",
"script": "Get-ServiceStatus.ps1",
"category": "services",
"parameters": [
{"name": "ServiceName", "type": "string", "default": "*"},
{"name": "ComputerName", "type": "server-select", "required": true}
]
},
{
"id": "restart-service",
"name": "Restart-RemoteService",
"description": "Restart services on remote servers",
"script": "Restart-RemoteService.ps1",
"category": "services",
"confirmRequired": true,
"parameters": [
{"name": "ServiceName", "type": "string", "required": true},
{"name": "ComputerName", "type": "server-select", "required": true}
]
},
{
"id": "deploy-config",
"name": "Deploy-ConfigFile",
"description": "Deploy configuration files to servers",
"script": "Deploy-ConfigFile.ps1",
"category": "deployment",
"confirmRequired": true,
"parameters": [
{"name": "SourcePath", "type": "file-browse", "required": true},
{"name": "DestinationPath", "type": "string", "required": true},
{"name": "ComputerName", "type": "server-select", "required": true},
{"name": "Backup", "type": "boolean", "default": true}
]
}
],
"healthCheck": {
"enabled": true,
"intervalMinutes": 30,
"onStartup": true,
"checks": [
{
"type": "service",
"servers": ["SQL01", "SQL02"],
"services": ["MSSQLSERVER", "SQLServerAgent"],
"severity": "critical"
},
{
"type": "service",
"servers": ["WEB01"],
"services": ["W3SVC"],
"severity": "warning"
}
],
"notifications": {
"onFailure": true,
"onRecovery": true,
"quietHoursStart": "22:00",
"quietHoursEnd": "07:00"
}
},
"eventMonitor": {
"enabled": true,
"rules": [
{
"name": "Failed Logins",
"logName": "Security",
"eventId": [4625],
"threshold": 10,
"windowMinutes": 5,
"severity": "critical"
},
{
"name": "Service Crash",
"logName": "System",
"eventId": [7034],
"threshold": 1,
"windowMinutes": 1,
"severity": "warning"
}
]
},
"notifications": {
"enabled": true,
"provider": "burnttoast",
"fallbackToLegacy": true,
"soundEnabled": true,
"duration": "short"
},
"logging": {
"level": "info",
"maxSizeMB": 10,
"retentionDays": 30
}
}
Solution Architecture
Full System Diagram
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Windows Automation Suite โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ AutoHotkey v2 Frontend โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ System Tray โ โ Command โ โ Result/Notification โ โ โ
โ โ โ Application โ โ Palette โ โ Display Windows โ โ โ
โ โ โ โ โ โ โ โ โ โ
โ โ โ - Icon/Menu โ โ - Fuzzy searchโ โ - Data tables โ โ โ
โ โ โ - Hotkeys โ โ - Param forms โ โ - Error displays โ โ โ
โ โ โ - Quick menu โ โ - Execution โ โ - Confirmation dialogs โ โ โ
โ โ โโโโโโโโโฌโโโโโโโโ โโโโโโโโโฌโโโโโโโโ โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ โ โ
โ โโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ JSON / CLI โ
โ v โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Integration Layer โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ PS Invoker โ โ Config Manager โ โ Event Bridge โ โ โ
โ โ โ โ โ โ โ โ โ โ
โ โ โ - RunWaitOutput โ โ - Load config โ โ - File watcher โ โ โ
โ โ โ - Async execution โ โ - Validate โ โ - Event queue โ โ โ
โ โ โ - JSON parsing โ โ - Hot-reload โ โ - Alert dispatch โ โ โ
โ โ โโโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโโฌโโโโโโโโโโ โ โ
โ โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโ โ
โ โ ^ โ
โ โ PowerShell invocation โ Events/Results โ
โ v โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ PowerShell Backend Module โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Service Mgmt โ โ File Operations โ โ Event Monitor โ โ โ
โ โ โ โ โ โ โ โ โ โ
โ โ โ - Get-Status โ โ - Deploy files โ โ - Watch logs โ โ โ
โ โ โ - Start/Stop โ โ - Collect logs โ โ - Correlate โ โ โ
โ โ โ - Health checks โ โ - Backup/restore โ โ - Generate alerts โ โ โ
โ โ โโโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโโฌโโโโโโโโโโ โ โ
โ โ โ โ โ โ โ
โ โ โโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโ โ โ
โ โ โ PowerShell Remoting โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
v
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Remote Servers โ
โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ SQL01 โ โ SQL02 โ โ WEB01 โ ... โ
โ โ (WinRM) โ โ (WinRM) โ โ (WinRM) โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Component Interaction Flows
Flow 1: Command Palette Execution
1. User presses Win+Space
|
v
2. AHK: ShowCommandPalette()
|
+-- Load commands from config.json
+-- Create searchable list
+-- Show GUI
|
v
3. User types "svc" and selects "Get-ServiceStatus"
|
v
4. AHK: ShowParameterForm("get-service-status")
|
+-- Display parameter inputs
+-- Show server checkboxes
|
v
5. User fills form and clicks [Run]
|
v
6. AHK: ExecutePowerShellCommand()
|
+-- Build command: powershell.exe -File Get-ServiceStatus.ps1 ...
+-- Run with output capture
|
v
7. PowerShell: Get-ServiceStatus -ComputerName SQL01,SQL02 -ServiceName "SQL*"
|
+-- Connect to servers via Remoting
+-- Query services
+-- Format results as JSON
+-- Write to stdout
|
v
8. AHK: ParseResults()
|
+-- Read stdout
+-- Parse JSON
+-- Display in results GUI
Flow 2: Health Check with Notification
1. Timer fires (every 30 minutes)
|
v
2. PowerShell: Invoke-HealthCheck
|
+-- Read health check config
+-- For each check:
| +-- Connect to servers
| +-- Query services
| +-- Compare to expected state
|
v
3. PowerShell: Generate results
|
+-- If failures found:
| +-- Write to alerts file: C:\Temp\health_alerts.json
| +-- Exit with code 1
+-- If all OK:
+-- Exit with code 0
|
v
4. AHK: Check health check completion
|
+-- If exit code > 0:
| +-- Read alerts file
| +-- Parse JSON
| +-- Call: ShowNotification()
|
v
5. AHK: ShowNotification("Health Check Failed", details)
|
+-- If BurntToast available:
| +-- Invoke: New-BurntToastNotification
+-- Else:
+-- Show balloon tip
Flow 3: Event Log Monitoring
1. On startup: AHK triggers background PowerShell process
|
v
2. PowerShell: Start-EventMonitor (runs continuously)
|
+-- Read monitoring rules from config
+-- For each rule:
| +-- Build XPath filter
| +-- Subscribe to event log
|
v
3. Event occurs matching a rule
|
v
4. PowerShell: Event handler fires
|
+-- Evaluate against threshold
+-- If threshold exceeded:
| +-- Append to: C:\Temp\event_alerts.json
|
v
5. AHK: File watcher detects change
|
v
6. AHK: ReadNewAlerts()
|
+-- Parse new alerts
+-- For each alert:
+-- ShowNotification()
File Organization
C:\AutomationSuite\
โ
โโโ AutomationSuite.ahk # Main AHK script (entry point)
โโโ config.json # Shared configuration
โโโ config-schema.json # JSON schema for validation
โ
โโโ AHK\ # AutoHotkey components
โ โโโ Lib\
โ โ โโโ JSON.ahk # JSON parser library
โ โ โโโ GUI.ahk # GUI helper functions
โ โ โโโ Tray.ahk # Tray icon management
โ โ โโโ PSInvoker.ahk # PowerShell invocation helpers
โ โ
โ โโโ CommandPalette.ahk # Command palette implementation
โ โโโ ResultsViewer.ahk # Results display windows
โ โโโ NotificationBridge.ahk # Notification display
โ โโโ EventWatcher.ahk # File/event watching
โ
โโโ PowerShell\ # PowerShell module
โ โโโ AutomationBackend\
โ โ โโโ AutomationBackend.psd1 # Module manifest
โ โ โโโ AutomationBackend.psm1 # Module loader
โ โ โ
โ โ โโโ Public\ # Exported functions
โ โ โ โโโ Get-ServiceStatus.ps1
โ โ โ โโโ Restart-RemoteService.ps1
โ โ โ โโโ Stop-RemoteService.ps1
โ โ โ โโโ Deploy-ConfigFile.ps1
โ โ โ โโโ Get-RemoteLogs.ps1
โ โ โ โโโ Invoke-HealthCheck.ps1
โ โ โ โโโ Start-EventMonitor.ps1
โ โ โ
โ โ โโโ Private\ # Internal functions
โ โ โ โโโ Get-ServerSession.ps1
โ โ โ โโโ Write-Alert.ps1
โ โ โ โโโ ConvertTo-AutomationOutput.ps1
โ โ โ โโโ Test-BurntToast.ps1
โ โ โ
โ โ โโโ Classes\
โ โ โโโ HealthCheckResult.ps1
โ โ โโโ EventAlert.ps1
โ โ
โ โโโ Scripts\ # Standalone scripts (called by AHK)
โ โโโ Run-Command.ps1 # Generic command runner
โ โโโ Quick-HealthCheck.ps1 # Quick status check
โ โโโ Send-Notification.ps1 # Toast notification sender
โ
โโโ Icons\ # Application icons
โ โโโ app.ico
โ โโโ warning.ico
โ โโโ error.ico
โ โโโ success.ico
โ
โโโ Logs\ # Application logs
โ โโโ automation.log
โ โโโ errors.log
โ
โโโ Temp\ # Temporary files for IPC
โโโ health_alerts.json
โโโ event_alerts.json
โโโ command_results.json
Phased Implementation Guide
Phase 1: System Tray Skeleton (2-3 days)
Goal: Create a working system tray application with basic menu structure.
Tasks:
- Create main AHK script structure
- Single-instance enforcement
- Tray icon setup
- Basic menu items
- Implement hotkey registration
- Load hotkeys from config
- Register with error handling
- Test with placeholder actions
- Set up configuration loading
- Create initial config.json
- Implement JSON loading in AHK
- Hot-reload on file change
Deliverables:
- Tray icon visible and clickable
- Menu with placeholder items
- Win+Space shows tooltip โCommand Palette coming soonโ
Code Structure:
; AutomationSuite.ahk
#Requires AutoHotkey v2.0
#SingleInstance Force
Persistent
; Load libraries
#Include "AHK\Lib\JSON.ahk"
#Include "AHK\Lib\Tray.ahk"
; Global state
global Config := {}
global AppRunning := true
; Initialize
LoadConfig()
SetupTray()
RegisterHotkeys()
; Event loop
OnExit(CleanupAndExit)
; Functions
LoadConfig() {
global Config
configPath := A_ScriptDir . "\config.json"
if FileExist(configPath) {
content := FileRead(configPath)
Config := JSON.Parse(content)
}
}
SetupTray() {
A_TrayMenu.Delete()
A_TrayMenu.Add("&Command Palette", ShowCommandPalette)
A_TrayMenu.Add()
A_TrayMenu.Add("&Settings...", ShowSettings)
A_TrayMenu.Add("View &Logs", ViewLogs)
A_TrayMenu.Add()
A_TrayMenu.Add("E&xit", ExitApp)
A_TrayMenu.Default := "&Command Palette"
TraySetIcon(A_ScriptDir . "\Icons\app.ico")
A_IconTip := "Automation Suite"
}
RegisterHotkeys() {
if Config.Has("hotkeys") {
Hotkey(Config["hotkeys"]["commandPalette"], ShowCommandPalette)
}
}
Phase 2: PowerShell Module Backend (3-4 days)
Goal: Create the PowerShell module with core functionality.
Tasks:
- Set up module structure
- Create manifest (psd1)
- Create module loader (psm1)
- Organize public/private functions
- Implement core functions
- Get-ServiceStatus (from P08)
- Invoke-HealthCheck (from P05)
- Session management helpers
- Standardize output format
- JSON output for AHK consumption
- Error handling with structured responses
- Exit codes for status indication
Deliverables:
- PowerShell module loads successfully
- Get-ServiceStatus works standalone
- JSON output is parseable
Example Function:
# Get-ServiceStatus.ps1
function Get-ServiceStatus {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string[]]$ComputerName,
[string]$ServiceName = "*",
[PSCredential]$Credential,
[switch]$AsJson
)
$results = @()
$errors = @()
foreach ($server in $ComputerName) {
try {
$invokeParams = @{
ComputerName = $server
ScriptBlock = { param($svc) Get-Service -Name $svc -EA SilentlyContinue }
ArgumentList = $ServiceName
ErrorAction = 'Stop'
}
if ($Credential) { $invokeParams['Credential'] = $Credential }
$services = Invoke-Command @invokeParams
foreach ($svc in $services) {
$results += [PSCustomObject]@{
ComputerName = $server
ServiceName = $svc.Name
DisplayName = $svc.DisplayName
Status = $svc.Status.ToString()
StartType = $svc.StartType.ToString()
}
}
}
catch {
$errors += [PSCustomObject]@{
ComputerName = $server
Error = $_.Exception.Message
}
}
}
$output = @{
Success = $errors.Count -eq 0
Results = $results
Errors = $errors
Timestamp = (Get-Date -Format "o")
}
if ($AsJson) {
return $output | ConvertTo-Json -Depth 10
}
return $output
}
Phase 3: AHK-PowerShell Integration (3-4 days)
Goal: Enable AHK to invoke PowerShell commands and parse results.
Tasks:
- Create PowerShell invoker class
- Synchronous execution with output capture
- Async execution with file-based results
- Timeout handling
- Implement JSON parsing
- Use/create JSON library for AHK v2
- Handle nested objects and arrays
- Error handling for malformed JSON
- Build result display framework
- Generic data table GUI
- Error display with details
- Copy to clipboard functionality
Deliverables:
- AHK can run PowerShell and get JSON back
- Results display in GUI table
- Errors show meaningful messages
Integration Code:
; PSInvoker.ahk
class PSInvoker {
static powershellPath := "powershell.exe"
static scriptPath := ""
static timeout := 30000 ; 30 seconds
static Init(scriptDir) {
this.scriptPath := scriptDir . "\PowerShell\Scripts"
}
; Synchronous execution with output
static RunCommand(command, args := "") {
fullCmd := this.powershellPath . ' -NoProfile -ExecutionPolicy Bypass '
fullCmd .= '-Command "& {' . command . '} ' . args . ' | ConvertTo-Json -Depth 10"'
shell := ComObject("WScript.Shell")
exec := shell.Exec(fullCmd)
; Wait for completion with timeout
startTime := A_TickCount
while !exec.Status {
if (A_TickCount - startTime > this.timeout) {
exec.Terminate()
return { success: false, error: "Timeout after " . (this.timeout/1000) . " seconds" }
}
Sleep(100)
}
if (exec.ExitCode != 0) {
errOutput := exec.StdErr.ReadAll()
return { success: false, error: errOutput }
}
output := exec.StdOut.ReadAll()
try {
data := JSON.Parse(output)
return { success: true, data: data }
} catch {
return { success: false, error: "Failed to parse JSON: " . output }
}
}
; Run a script file
static RunScript(scriptName, params := "") {
scriptFile := this.scriptPath . "\" . scriptName
if !FileExist(scriptFile) {
return { success: false, error: "Script not found: " . scriptName }
}
fullCmd := this.powershellPath . ' -NoProfile -ExecutionPolicy Bypass '
fullCmd .= '-File "' . scriptFile . '" ' . params
shell := ComObject("WScript.Shell")
exec := shell.Exec(fullCmd)
; Wait and capture output
startTime := A_TickCount
while !exec.Status {
if (A_TickCount - startTime > this.timeout) {
exec.Terminate()
return { success: false, error: "Timeout" }
}
Sleep(100)
}
output := exec.StdOut.ReadAll()
try {
data := JSON.Parse(output)
return { success: true, data: data }
} catch {
return { success: true, data: output } ; Return raw if not JSON
}
}
}
Phase 4: Command Palette (4-5 days)
Goal: Build a VS Code-style command palette with fuzzy search.
Tasks:
- Create command registry
- Load commands from config
- Support categories and keywords
- Enable/disable commands dynamically
- Implement fuzzy search
- Character-by-character matching
- Score-based ranking
- Highlight matching characters
- Build parameter forms
- Dynamic form generation
- Server selection checkboxes
- File browser integration
- Validation before execution
- Connect execution pipeline
- Build PowerShell command from form
- Execute and show progress
- Display results
Deliverables:
- Command palette appears on hotkey
- Fuzzy search filters commands
- Parameters form works
- Commands execute and show results
Command Palette Code:
; CommandPalette.ahk
class CommandPalette {
static gui := ""
static searchEdit := ""
static commandList := ""
static commands := []
static filteredCommands := []
static Show() {
if (this.gui) {
this.gui.Show()
this.searchEdit.Focus()
return
}
this.LoadCommands()
this.CreateGUI()
}
static LoadCommands() {
global Config
this.commands := []
if Config.Has("commands") {
for cmd in Config["commands"] {
this.commands.Push({
id: cmd["id"],
name: cmd["name"],
description: cmd["description"],
script: cmd["script"],
category: cmd["category"],
parameters: cmd.Has("parameters") ? cmd["parameters"] : [],
confirmRequired: cmd.Has("confirmRequired") ? cmd["confirmRequired"] : false
})
}
}
this.filteredCommands := this.commands
}
static CreateGUI() {
this.gui := Gui("+AlwaysOnTop -MinimizeBox", "Automation Suite")
this.gui.SetFont("s10")
this.gui.OnEvent("Close", (*) => this.gui.Hide())
this.gui.OnEvent("Escape", (*) => this.gui.Hide())
; Search box
this.gui.Add("Text", "w400", "Type to search commands:")
this.searchEdit := this.gui.Add("Edit", "w400 vSearchBox")
this.searchEdit.OnEvent("Change", (*) => this.FilterCommands())
; Command list
this.gui.Add("Text", "w400", "")
this.commandList := this.gui.Add("ListView", "w400 h300 vCommandList", ["Command", "Description"])
this.commandList.OnEvent("DoubleClick", (*) => this.ExecuteSelected())
this.PopulateList()
; Buttons
this.gui.Add("Button", "w100", "Run").OnEvent("Click", (*) => this.ExecuteSelected())
this.gui.Add("Button", "x+10 w100", "Cancel").OnEvent("Click", (*) => this.gui.Hide())
this.gui.Show()
this.searchEdit.Focus()
}
static PopulateList() {
this.commandList.Delete()
for cmd in this.filteredCommands {
this.commandList.Add(, cmd.name, cmd.description)
}
if (this.filteredCommands.Length > 0) {
this.commandList.Modify(1, "Select Focus")
}
}
static FilterCommands() {
query := this.searchEdit.Value
if (query = "") {
this.filteredCommands := this.commands
} else {
this.filteredCommands := []
for cmd in this.commands {
if (this.FuzzyMatch(cmd.name . " " . cmd.description, query)) {
this.filteredCommands.Push(cmd)
}
}
}
this.PopulateList()
}
static FuzzyMatch(text, query) {
text := StrLower(text)
query := StrLower(query)
queryPos := 1
for i, char in StrSplit(text) {
if (queryPos > StrLen(query))
return true
if (char = SubStr(query, queryPos, 1))
queryPos++
}
return queryPos > StrLen(query)
}
static ExecuteSelected() {
row := this.commandList.GetNext()
if (row = 0)
return
cmd := this.filteredCommands[row]
this.gui.Hide()
if (cmd.parameters.Length > 0) {
ParameterForm.Show(cmd)
} else {
CommandExecutor.Execute(cmd, {})
}
}
}
Phase 5: Notifications and Polish (4-5 days)
Goal: Implement notifications, health checks, and polish the experience.
Tasks:
- Implement notification bridge
- BurntToast integration
- Fallback to balloon tips
- Action button handling
- Set up scheduled health checks
- Timer-based execution
- Background PowerShell process
- Alert generation
- Build event monitor
- PowerShell event watcher script
- File-based alert communication
- AHK file watcher
- Polish and testing
- Error handling throughout
- Logging implementation
- Performance optimization
- User experience refinements
Deliverables:
- Toast notifications work
- Health checks run on schedule
- Event alerts appear in real-time
- Application feels professional
Notification Bridge:
; NotificationBridge.ahk
class NotificationBridge {
static useBurntToast := false
static initialized := false
static Init() {
if (this.initialized)
return
; Check if BurntToast is available
result := PSInvoker.RunCommand('Get-Module BurntToast -ListAvailable')
this.useBurntToast := (result.success && result.data != "")
this.initialized := true
}
static Show(title, message, severity := "info", buttons := []) {
this.Init()
if (this.useBurntToast) {
this.ShowToast(title, message, severity, buttons)
} else {
this.ShowBalloon(title, message, severity)
}
}
static ShowToast(title, message, severity, buttons) {
icon := this.GetIcon(severity)
buttonParams := ""
if (buttons.Length > 0) {
buttonParams := "-Button "
for btn in buttons {
buttonParams .= '(New-BTButton -Content "' . btn.text . '" -Arguments "' . btn.action . '"),'
}
buttonParams := RTrim(buttonParams, ",")
}
cmd := 'New-BurntToastNotification -Text "' . title . '","' . message . '"'
if (icon != "")
cmd .= ' -AppLogo "' . icon . '"'
if (buttonParams != "")
cmd .= ' ' . buttonParams
PSInvoker.RunCommand(cmd)
}
static ShowBalloon(title, message, severity) {
iconType := severity = "error" ? 3 : (severity = "warning" ? 2 : 1)
TrayTip(message, title, iconType)
}
static GetIcon(severity) {
iconDir := A_ScriptDir . "\Icons\"
switch severity {
case "error": return iconDir . "error.ico"
case "warning": return iconDir . "warning.ico"
case "success": return iconDir . "success.ico"
default: return iconDir . "app.ico"
}
}
}
Health Check Scheduler:
; HealthCheckScheduler.ahk
class HealthCheckScheduler {
static timer := ""
static lastCheck := 0
static checkInProgress := false
static Start() {
global Config
if (!Config.Has("healthCheck") || !Config["healthCheck"]["enabled"])
return
intervalMs := Config["healthCheck"]["intervalMinutes"] * 60 * 1000
; Run on startup if configured
if (Config["healthCheck"]["onStartup"])
this.RunCheck()
; Set up recurring timer
SetTimer(ObjBindMethod(this, "RunCheck"), intervalMs)
}
static Stop() {
SetTimer(ObjBindMethod(this, "RunCheck"), 0)
}
static RunCheck() {
if (this.checkInProgress)
return
this.checkInProgress := true
this.lastCheck := A_Now
; Run health check in background
scriptPath := A_ScriptDir . "\PowerShell\Scripts\Quick-HealthCheck.ps1"
alertPath := A_ScriptDir . "\Temp\health_alerts.json"
; Delete old alerts
if FileExist(alertPath)
FileDelete(alertPath)
; Run async
Run('powershell.exe -WindowStyle Hidden -File "' . scriptPath . '"',, "Hide", &pid)
; Monitor for completion
SetTimer(ObjBindMethod(this, "CheckCompletion", pid, alertPath), 1000)
}
static CheckCompletion(pid, alertPath) {
; Check if process still running
try {
proc := ProcessExist(pid)
if (proc)
return ; Still running
}
; Process completed
SetTimer(, 0) ; Stop this timer
this.checkInProgress := false
; Check for alerts
if FileExist(alertPath) {
content := FileRead(alertPath)
alerts := JSON.Parse(content)
if (alerts.Has("failures") && alerts["failures"].Length > 0) {
this.ShowHealthAlert(alerts)
}
}
}
static ShowHealthAlert(alerts) {
global Config
; Check quiet hours
if (this.InQuietHours())
return
failCount := alerts["failures"].Length
firstFail := alerts["failures"][1]
title := "Health Check Failed"
message := firstFail["server"] . ": " . firstFail["service"] . " is " . firstFail["status"]
if (failCount > 1)
message .= "`n" . (failCount - 1) . " more issue(s) detected"
NotificationBridge.Show(title, message, "warning", [
{ text: "View Details", action: "healthcheck:details" },
{ text: "Restart Services", action: "healthcheck:restart" }
])
}
static InQuietHours() {
global Config
if (!Config["healthCheck"]["notifications"].Has("quietHoursStart"))
return false
; Parse quiet hours
start := Config["healthCheck"]["notifications"]["quietHoursStart"]
end := Config["healthCheck"]["notifications"]["quietHoursEnd"]
; Compare with current time
nowTime := FormatTime(, "HH:mm")
return (nowTime >= start || nowTime < end)
}
}
Testing Strategy
Unit Tests
| Component | Test | Expected Result |
|---|---|---|
| JSON Parser | Parse valid JSON | Object returned |
| JSON Parser | Parse invalid JSON | Error thrown |
| Fuzzy Match | โgssโ matches โGet-ServiceStatusโ | True |
| Fuzzy Match | โxyzโ matches โGet-ServiceStatusโ | False |
| PS Invoker | Run valid command | Success with data |
| PS Invoker | Run with timeout | Error returned |
| Config Loader | Load valid config | Config object populated |
| Config Loader | Load missing file | Defaults applied |
Integration Tests
| Flow | Test | Expected Result |
|---|---|---|
| Tray Application | Start script | Icon appears in tray |
| Tray Application | Right-click icon | Menu displays |
| Command Palette | Press hotkey | Palette appears < 100ms |
| Command Palette | Type and select | Command executes |
| PowerShell | Get-ServiceStatus | JSON results returned |
| Health Check | Trigger check | Alert if failures |
| Notifications | Show toast | Toast appears |
| Notifications | Fallback | Balloon if no BurntToast |
End-to-End Tests
| Scenario | Steps | Expected Outcome |
|---|---|---|
| Fresh Start | Install, launch | Tray icon, menu works |
| Service Query | Palette > โsvcโ > run | Results in table |
| Deploy File | Palette > deploy > select | File on server |
| Health Failure | Stop service, wait | Notification appears |
| Event Alert | Generate 15 failed logins | Security alert shows |
Performance Tests
| Metric | Target | Test Method |
|---|---|---|
| Palette open time | < 100ms | Time from hotkey to visible |
| Service query (3 servers) | < 10s | End-to-end timing |
| Memory usage | < 100MB | Task Manager observation |
| Health check | < 30s | Timer measurement |
Common Pitfalls and Debugging Tips
Pitfall 1: PowerShell execution policy blocks scripts
Symptoms: Scripts fail with โcannot be loaded because running scripts is disabledโ
Solution:
# Always use -ExecutionPolicy Bypass when invoking from AHK
powershell.exe -ExecutionPolicy Bypass -File "script.ps1"
Pitfall 2: JSON parsing fails with special characters
Symptoms: JSON.Parse throws error on valid-looking JSON
Causes:
- Newlines in strings not escaped
- Non-ASCII characters
- BOM (byte order mark) in file
Solution:
; Sanitize JSON before parsing
content := StrReplace(content, "`r`n", "\n")
content := StrReplace(content, "`n", "\n")
content := RegExReplace(content, "^\xEF\xBB\xBF", "") ; Remove BOM
Pitfall 3: WinRM not configured on target servers
Symptoms: โWinRM cannot process the requestโ
Solution:
# Run on each target server as Administrator
Enable-PSRemoting -Force
winrm quickconfig
Pitfall 4: Hotkey conflicts with other applications
Symptoms: Hotkey doesnโt work or triggers wrong action
Solution:
; Check if hotkey is available before registering
try {
Hotkey(hotkeyStr, callback)
} catch Error as e {
MsgBox("Hotkey " . hotkeyStr . " is unavailable: " . e.Message)
}
; Use less common combinations
; Good: #!Space, ^#s
; Bad: ^c, ^v, #v (often taken)
Pitfall 5: File watcher misses changes
Symptoms: Events written but AHK doesnโt see them
Causes:
- File written but not flushed
- Checking too infrequently
- File locked by writer
Solution:
; Use polling with debounce
class FileWatcher {
static lastModified := 0
static debounceMs := 500
static Check(filePath) {
if !FileExist(filePath)
return false
modTime := FileGetTime(filePath, "M")
if (modTime = this.lastModified)
return false
; Debounce: wait for file to settle
Sleep(this.debounceMs)
newModTime := FileGetTime(filePath, "M")
if (newModTime != modTime)
return false ; Still changing
this.lastModified := newModTime
return true
}
}
Pitfall 6: PowerShell output not captured
Symptoms: RunCommand returns empty or null
Causes:
- Output going to stderr
- Non-text output (objects)
- Encoding mismatch
Solution:
; Capture both stdout and stderr
fullCmd := 'powershell.exe -Command "& { ' . command . ' } 2>&1 | Out-String"'
; Ensure JSON output
fullCmd := 'powershell.exe -Command "& { ' . command . ' } | ConvertTo-Json -Depth 10"'
Pitfall 7: Single instance not working
Symptoms: Multiple copies of application running
Solution:
#SingleInstance Force ; Must be at top of script
; Additional check via mutex
if (DllCall("CreateMutex", "Ptr", 0, "Int", 1, "Str", "AutomationSuiteMutex") = 0) {
MsgBox("Another instance is already running")
ExitApp()
}
Debugging Tips
- Enable verbose logging: ```autohotkey global DebugMode := true
Debug(msg) { global DebugMode if (DebugMode) { logPath := A_ScriptDir . โ\Logs\debug.logโ FileAppend(A_Now . โ: โ . msg . โ`nโ, logPath) } }
2. **Test PowerShell commands directly**:
```powershell
# Run command manually to see full output
& "C:\AutomationSuite\PowerShell\Scripts\Get-ServiceStatus.ps1" -ComputerName localhost
- Use ToolTip for quick feedback:
ToolTip("Step 1 complete") Sleep(1000) ToolTip("Step 2: " . variable) - Check process output:
result := PSInvoker.RunCommand("Get-Service") MsgBox("Success: " . result.success . "`nData: " . JSON.Stringify(result.data))
Extensions and Challenges
Easy Extensions
- Command history - Remember recently used commands and show them first
- Quick access favorites - Pin frequently used commands to tray menu
- Custom notification sounds - Different sounds for different alert types
- Keyboard navigation - Full keyboard control in all dialogs
Medium Extensions
- Plugin system - Load additional commands from external files
- Remote server groups - Organize servers by role/location
- Credential manager - Secure storage for multiple credentials
- Report generation - HTML reports for health checks and queries
- Backup/restore config - Export and import settings
Advanced Extensions
- Web dashboard - Simple local web server showing status
- Slack/Teams integration - Send alerts to chat channels
- Scheduled tasks - Define recurring automation jobs
- Multi-machine sync - Share commands across workstations
- RBAC - Role-based access control for sensitive commands
- Audit logging - Track who ran what commands when
Capstone Challenges
- Mobile companion app - Push notifications to phone
- Voice control - Execute commands via speech recognition
- AI assistant - Natural language command interpretation
- Self-healing automation - Auto-remediate common issues
Books That Will Help
| Topic | Book | Chapter/Section |
|---|---|---|
| Windows automation architecture | Windows Internals, Part 1 by Yosifovich et al. | Chapter 1: Concepts and Tools |
| Inter-process communication | Windows System Programming by Johnson M. Hart | Chapter 11: Interprocess Communication |
| Event-driven programming | Game Programming Patterns by Robert Nystrom | Observer Pattern, Event Queue |
| PowerShell remoting | Learn PowerShell in a Month of Lunches by Travis Plunk et al. | Chapters 13-14: Remoting |
| PowerShell module development | PowerShell in Depth by Don Jones et al. | Part 4: PowerShell Modules |
| Configuration management | The Pragmatic Programmer by Hunt & Thomas | Chapter 17: Plain Text Configuration |
| System administration patterns | The Practice of System and Network Administration by Limoncelli et al. | Chapter 4: Automation |
| Notification design | Donโt Make Me Think by Steve Krug | Chapter 6: Alerts and Notifications |
| Error handling | Release It! by Michael Nygard | Chapter 4: Stability Patterns |
| Testing automation | Test-Driven Development by Kent Beck | Chapters 1-5: Test Fundamentals |
Self-Assessment Checklist
Phase 1: System Tray (Foundation)
- Application starts and shows tray icon
- Right-click menu displays with all items
- Single instance enforcement works
- Configuration loads from JSON
- Hotkeys register without errors
- Clean exit removes tray icon
Phase 2: PowerShell Backend (Engine)
- PowerShell module loads successfully
- Get-ServiceStatus works on local machine
- Get-ServiceStatus works on remote servers
- JSON output is valid and complete
- Errors are captured and formatted
- Session management works efficiently
Phase 3: Integration (Bridge)
- AHK can invoke PowerShell commands
- JSON results parse correctly
- Results display in GUI table
- Errors show meaningful messages
- Timeout handling works
- Async execution doesnโt block UI
Phase 4: Command Palette (Interface)
- Palette appears on hotkey < 100ms
- Fuzzy search filters correctly
- Parameter forms generate dynamically
- Server selection works
- Commands execute successfully
- Results display after execution
Phase 5: Notifications (Alerts)
- Toast notifications appear
- Fallback to balloon tips works
- Health checks run on schedule
- Alerts appear for failures
- Quiet hours are respected
- Event monitoring works
Overall Quality
- Memory usage stays under 100MB
- No crashes during 8-hour test
- All components log appropriately
- Configuration changes apply without restart
- Documentation is complete
- Code is organized and readable
Final Notes
This capstone project synthesizes everything youโve learned about Windows automation:
- From AutoHotkey projects: Event-driven programming, GUI creation, hotkey handling, clipboard management
- From PowerShell projects: System administration, remoting, module development, error handling
The key insight is that the best automation combines both tools:
- AHK for what the user touches (keyboard, mouse, screen)
- PowerShell for what the system does (services, files, network)
Building this suite will prepare you for:
- Enterprise automation roles
- DevOps and SRE positions
- Tool development for IT teams
- Personal productivity mastery
Remember: The goal is not just to build the tool, but to understand when and how to combine different automation approaches effectively.
Good luck, and enjoy the journey from beginner to automation expert!