diff --git a/Host Monitor.ahk b/Host Monitor.ahk
index 37e02ae..b878f42 100644
--- a/Host Monitor.ahk
+++ b/Host Monitor.ahk
@@ -1,799 +1,812 @@
-/*
- Host Monitor.ahk
- Authors: Elesar (Daniel Thomas) & Grendahl
-
- Uses functions by SKAN, just me, Uberi & jNizM
-
- Monitors a set of hosts periodically and gives visual representation of their status.
- Includes several tools for troubleshooting.
-
- Some functionality depends on putty.exe being in the inc folder.
-
-*/
-;<===== System Settings =====================================================>
-#SingleInstance, Force
-#NoEnv
-SetBatchLines, -1
-
-;<===== FileInstalls ========================================================>
-;Images
-IfNotExist, % A_ScriptDir . "\img"
- FileCreateDir, % A_ScriptDir . "\img"
-;FileInstall, img\Green.jpg, img\Green.jpg, 1
-;FileInstall, img\Yellow.jpg, img\Yellow.jpg, 1
-;FileInstall, img\Red.jpg, img\Red.jpg, 1
-FileInstall, img\Host Monitor.ico, img\Host Monitor.ico, 1
-
-;Tools
-IfNotExist, % A_ScriptDir . "\inc"
- FileCreateDir, % A_ScriptDir . "\inc"
-FileInstall, temp\pingGraph.exe, inc\pingGraph.exe, 1
-FileInstall, temp\pingMsg.exe, inc\pingMsg.exe, 1
-FileInstall, temp\traceRt.exe, inc\traceRt.exe, 1
-
-;Settings & Misc
-FileInstall, inc\defaultSettings.xml, inc\settings.xml, 0
-FileInstall, inc\transform.xslt, inc\transform.xslt, 1
-FileInstall, hosts.txt, hosts.txt, 0
-
-;<===== Startup =============================================================>
-;Receive messages from slave scripts and Windows
-OnMessage(0x4a, "Receive_WM_COPYDATA")
-OnMessage(0x200, "WM_MOUSEMOVE") ; Tooltips
-
-;Allcate console so we don't have constant flashing windows while pinging
-DllCall("AllocConsole")
-WinHide % "ahk_id " DllCall("GetConsoleWindow", "ptr")
-
-;Make sure we are exiting nicely
-OnExit, Exit
-
-;<===== Settings ============================================================>
-;Static Settings - May mess stuff up visually if changed
-boxWidth := 150
-boxHeight := 45
-
-;Read in settings.xml
-settings := loadXML(A_ScriptDir . "\inc\settings.xml")
-
-;Read in transform.xslt
-tdoc := loadXML(A_ScriptDir . "\inc\transform.xslt")
-
-;Get monitor working area
-SysGet, Mon, MonitorWorkArea
-maxRows := Floor(MonBottom / boxHeight) - 2
-
-;Adjust guiRows setting if higher than maxRows
-if (settings.selectSingleNode("/hostMonitor/settings/guiRows").text > maxRows) {
- node := settings.selectSingleNode("/hostMonitor/settings/guiRows")
- node.text := maxRows
- SaveSettings(settings, tdoc)
-}
-
-;<===== Read in hostList ====================================================>
-;Prompt for host file if not saved.
-while !settings.selectSingleNode("/hostMonitor/settings/hostPath").text
-{
- FileSelectFile, path,,, Select host file, Text (*.txt)
- if !path
- {
- MsgBox, 4,, No file selected.`nDo you want to exit?
- IfMsgBox, Yes
- ExitApp
- } else {
- node := settings.selectSingleNode("/hostMonitor/settings/hostPath")
- node.text := path
- SaveSettings(settings, tdoc)
- }
-}
-
-hostsFile := fileOpen(settings.selectSingleNode("/hostMonitor/settings/hostPath").text, "r")
-hostsData := hostsFile.Read()
-hostsFile.Close()
-
-hosts := Object()
-Loop, Parse, hostsData, `n, `r
-{
- if (A_LoopField != "")
- {
- hosts[A_Index] := Object()
- aliasPos := RegExMatch(A_LoopField, "\[(.*?)\]", alias)
- alias := strReplace(alias, "[")
- alias := strReplace(alias, "]")
- if alias
- {
- hosts[A_Index, "name"] := subStr(A_LoopField, 1, aliasPos - 1)
- hosts[A_Index, "alias"] := alias
- }
- else
- hosts[A_Index, "name"] := A_LoopField
- hosts[A_Index, "ip"] := ""
- hosts[A_Index, "lastSeen"] := "Never"
- hosts[A_Index, "bgImageID"] := ""
- hosts[A_Index, "nameTextID"] := ""
- hosts[A_Index, "statusTextID"] := ""
- hosts[A_Index, "threadID"] := ""
- hosts[A_Index].pingArray := Object()
- }
-}
-
-;<===== Menus ===============================================================>
-;Context Menu
-Menu, ContextMenu, Add, Trace Route, ContextMenuHandler
-Menu, ContextMenu, Add, Ping Graph, ContextMenuHandler
-Menu, ContextMenu, Add
-Menu, ContextMenu, Add, RDP, ContextMenuHandler
-Menu, ContextMenu, Add
-Menu, ContextMenu, Add, Explore C:, ContextMenuHandler
-Menu, ContextMenu, Add
-Menu, ContextMenu, Add, Browse, ContextMenuHandler
-Menu, ContextMenu, Add, Browse (https), ContextMenuHandler
-Menu, ContextMenu, Add
-Menu, ContextMenu, Add, SSH, ContextMenuHandler
-Menu, ContextMenu, Add, Telnet, ContextMenuHandler
-
-;File Menu
-Menu, FileMenu, Add, &Scan Now, MenuHandler
-Menu, FileMenu, Add, Open &Log Folder, MenuHandler
-Menu, FileMenu, Add
-Menu, FileMenu, Add, &Open, MenuHandler
-Menu, FileMenu, Add, &Reload, MenuHandler
-Menu, FileMenu, Add
-Menu, FileMenu, Add, E&xit, MenuHandler
-
-;Help Menu
-Menu, HelpMenu, Add, &Debug, MenuHandler
-Menu, HelpMenu, Add
-Menu, HelpMenu, Add, &About, MenuHandler
-
-;Settings Menu
-Menu, SettingsMenu, Add, &Flush DNS, SettingsMenuHandler
-Menu, SettingsMenu, Add
-Menu, SettingsMenu, Add, &Logging, SettingsMenuHandler
-if settings.selectSingleNode("/hostMonitor/settings/logging").text
- Menu, SettingsMenu, Check, &Logging
-Menu, SettingsMenu, Add, Auto &TraceRt, SettingsMenuHandler
-if settings.selectSingleNode("/hostMonitor/settings/autoTraceRt").text
- Menu, SettingsMenu, Check, Auto &TraceRt
-Menu, SettingsMenu, Add, Remember &Window Pos, SettingsMenuHandler
-if settings.selectSingleNode("/hostMonitor/settings/rememberPos").text
- Menu, SettingsMenu, Check, Remember &Window Pos
-Menu, SettingsMenu, Add
-Menu, SettingsMenu, Add, Change GUI &Rows, SettingsMenuHandler
-Menu, SettingsMenu, Add, Change Max &Threads, SettingsMenuHandler
-Menu, SettingsMenu, Add, Change Check &Interval, SettingsMenuHandler
-Menu, SettingsMenu, Add, Change Ping &Average Count, SettingsMenuHandler
-Menu, SettingsMenu, Add, Change &Warning Latency, SettingsMenuHandler
-
-;Menu Bar
-Menu, MenuBar, Add, &File, :FileMenu
-Menu, MenuBar, Add, &Settings, :SettingsMenu
-Menu, MenuBar, Add, &Help, :HelpMenu
-
-;Tray Menu
-Menu, Tray, Icon, % A_ScriptDir . "\img\Host Monitor.ico"
-
-;<===== Load Images =========================================================>
-;hGreen := LoadPicture(A_ScriptDir . "\img\Green.jpg")
-;hYellow := LoadPicture(A_ScriptDir . "\img\Yellow.jpg")
-;hRed := LoadPicture(A_ScriptDir . "\img\Red.jpg")
-
-;<===== GUI =================================================================>
-Gui, Margin, 5, 5
-Gui, Menu, MenuBar
-RowX := 5
-RowY := 5
-Loop % hosts.MaxIndex() {
- if (A_Index != 1) && !Mod(A_Index - 1, (settings.selectSingleNode("/hostMonitor/settings/guiRows").text
- ?settings.selectSingleNode("/hostMonitor/settings/guiRows").text
- :maxRows)) {
- RowX += 155
- RowY := 5
- }
-
- ;Gui, Add, Picture, % "x" RowX " y" RowY " w" boxWidth " 0x4000000 gDummyLabel vi"
- ; . A_Index, % "HBITMAP:*" hYellow
-
- Gui, Add, Progress, % "x" RowX " y" RowY " w" boxWidth " h" boxHeight " vi"
- . A_Index " cYellow", 100
-
- if hosts[A_Index, "alias"] {
- Gui, Add, Text, % "xp+5 yp+5 w140 h15 gDummyLabel +0x200 +BackgroundTrans vt"
- . A_Index . " +Center", % subStr(hosts[A_Index, "alias"], 1, 25)
- }
- else {
- Gui, Add, Text, % "xp+5 yp+5 w140 h15 gDummyLabel +0x200 +BackgroundTrans vt"
- . A_Index . " +Center", % subStr(hosts[A_Index, "name"], 1, 25)
- }
- Gui, Add, Text, % "xp yp+15 w140 h15 gDummyLabel +0x200 +BackgroundTrans vs"
- . A_Index . " +Center", % hosts[A_Index, "lastSeen"]
- hosts[A_Index, "bgImageID"] := "i" . A_Index
- hosts[A_Index, "nameTextID"] := "t" . A_Index
- hosts[A_Index, "statusTextID"] := "s" . A_Index
- RowY += boxHeight
-}
-Gui, Add, StatusBar
-SB_SetParts(100,100)
-if (RowX < 100) {
- if settings.selectSingleNode("/hostMonitor/settings/rememberPos").text {
- Gui, Show, % "x" . settings.selectSingleNode("/hostMonitor/settings/winX").text
- . " y" . settings.selectSingleNode("/hostMonitor/settings/winY").text . " w320", Host Monitor
- }
- else
- Gui, Show, % "w320", Host Monitor
-}
-else {
- if settings.selectSingleNode("/hostMonitor/settings/rememberPos").text {
- Gui, Show, % "x" . settings.selectSingleNode("/hostMonitor/settings/winX").text
- . " y" . settings.selectSingleNode("/hostMonitor/settings/winY").text, Host Monitor
- }
- else
- Gui, Show, Center, Host Monitor
-}
-
-;<===== Timers ==============================================================>
-;Check hosts based off of settings.xml
-SetTimer, CheckHosts, % (settings.selectSingleNode("/hostMonitor/settings/checkInterval").text * 1000)
-
-;Update status bar every second
-scanTimer := settings.selectSingleNode("/hostMonitor/settings/checkInterval").text
-SetTimer, UpdateSB, 1000
-
-;Clock time based events
-SetTimer, CheckTime, 60000
-
-;<===== Main ================================================================>
-if settings.selectSingleNode("/hostMonitor/settings/logging").Text
- logFile := LogStart(settings)
-CheckHosts()
-
-;<===== End AutoExecute =====================================================>
-return
-
-;<===== Subs ================================================================>
-About:
- aboutText =
- ( LTrim
- Written by Elesar (Daniel Thomas)
- In colaberation with Grendahl
- and others from the AutoHotkey community.
- )
- MsgBox, % aboutText
- return
-
-CheckTime:
- ;Start a new log file if midnight
- if ((A_Hour == 00) && (A_Min == 00) && (settings.selectSingleNode("/hostMonitor/settings/logging").text))
- {
- logStop(logFile)
- logFile := logStart(settings)
- }
- return
-
-ContextMenuHandler:
- if (A_ThisMenuItem == "Trace Route")
- StartTrace(host)
- else if (A_ThisMenuItem == "Ping Graph")
- StartPingGraph(host)
- else if (A_ThisMenuItem == "RDP")
- Run, % "mstsc /v:" . host
- else if (A_ThisMenuItem == "Explore C:")
- Run, % "explore \\" . host . "\c$"
- else if (A_ThisMenuItem == "Browse")
- Run, % "http://" . host
- else if (A_ThisMenuItem == "Browse (https)")
- Run, % "https://" . host
- else if (A_ThisMenuItem == "SSH")
- StartPutty(host, "ssh")
- else if (A_ThisMenuItem == "Telnet")
- StartPutty(host, "telnet")
- else
- MsgBox, % "Context menu broke :("
- return
-
-Debug:
- clipboard := enumObj(hosts) . "`n`n" . enumObj(threads)
- MsgBox, % "Hosts and Threads arrays copied to clipboard."
- Return
-
-DummyLabel:
- ;Needed to get tooltips/menus on text controls...
- Return
-
-Exit:
-GuiClose:
- WinGetPos, winX, winY,,, Host Monitor
- node := settings.selectSingleNode("/hostMonitor/settings/winX")
- node.text := winX
- node := settings.selectSingleNode("/hostMonitor/settings/winY")
- node.text := winY
- SaveSettings(settings, tdoc)
- if settings.selectSingleNode("/hostMonitor/settings/logging").text
- LogStop(logFile)
- ExitApp
- return
-
-GuiContextMenu:
- host := hosts[subStr(A_GuiControl, 2), "ip"]
- if host
- Menu, ContextMenu, Show
- return
-
-MenuButton:
- host := hosts[subStr(A_GuiControl, 2), "name"]
- Menu, ContextMenu, Show
- return
-
-MenuHandler:
- if (A_ThisMenuItem == "&Scan Now")
- CheckHosts()
- else if (A_ThisMenuItem == "Open &Log Folder")
- Run, % "explore " . A_ScriptDir . "\Logs"
- else if (A_ThisMenuItem == "&Open"){
- FileSelectFile, path,,, Select host file, Text (*.txt)
- if path
- {
- node := settings.selectSingleNode("/hostMonitor/settings/hostPath")
- node.text := path
- SaveSettings(settings, tdoc)
- Reload
- }
- }
- else if (A_ThisMenuItem == "&Reload")
- Reload
- else if (A_ThisMenuItem == "E&xit")
- GoSub, Exit
- else if (A_ThisMenuItem == "&Debug")
- GoSub, Debug
- else if (A_ThisMenuItem == "&About")
- GoSub, About
- else
- MsgBox, % "Menu system broke :("
- return
-
-SettingsMenuHandler:
- reloadScript := false
- if (A_ThisMenuItem == "&Flush DNS")
- MsgBox, % ((FlushDNS() == 1)?"DNS cache flushed.":"Failed to flush DNS cache.")
- else if (A_ThisMenuItem == "&Logging"){
- Menu, SettingsMenu, ToggleCheck, &Logging
- node := settings.selectSingleNode("/hostMonitor/settings/logging")
- node.text := !node.text
- if node.text
- logFile := LogStart(settings)
- else
- LogStop(logFile)
- }
- else if (A_ThisMenuItem == "Auto &TraceRt"){
- Menu, SettingsMenu, ToggleCheck, Auto &TraceRt
- node := settings.selectSingleNode("/hostMonitor/settings/autoTraceRt")
- node.text := !node.text
- }
- else if (A_ThisMenuItem == "Remember &Window Pos"){
- Menu, SettingsMenu, ToggleCheck, Remember &Window Pos
- node := settings.selectSingleNode("/hostMonitor/settings/rememberPos")
- node.text := !node.text
- }
- else if (A_ThisMenuItem == "Change GUI &Rows"){
- InputBox, userInput, Change GUI Rows, % "Enter a row count between 1 and "
- . maxRows . ".`nSet to 0 for AutoSize based on monitor work area and host count."
- . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/guiRows").text
- if !userInput
- return
- if userInput is not integer
- {
- MsgBox, This setting can only take an integer.
- return
- }
- if (userInput > maxRows)
- userInput := maxRows
- node := settings.selectSingleNode("/hostMonitor/settings/guiRows")
- node.text := userInput
- MsgBox, 4,,% "Setting changed. This setting requires a script reload.`nReload now?"
- ifMsgBox, Yes
- reloadScript := true
- }
- else if (A_ThisMenuItem == "Change Max &Threads"){
- InputBox, userInput, Change Max Threads, % "Enter a thread count greater than 1."
- . "`nWarning: May load CPU or cause script issues if too many threads are allowed."
- . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/maxThreads").text
- if !userInput
- return
- if userInput is not integer
- {
- MsgBox, This setting can only take an integer.
- return
- }
- if (userInput < 1)
- userInput := 1
- node := settings.selectSingleNode("/hostMonitor/settings/maxThreads")
- node.text := userInput
- }
- else if (A_ThisMenuItem == "Change Check &Interval"){
- InputBox, userInput, Change Check Interval, % "Enter a check interval in seconds."
- . "`nSuggest 30 or higher."
- . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/checkInterval").text
- if !userInput
- return
- if userInput is not integer
- {
- MsgBox, This setting can only take an integer.
- return
- }
- if (userInput < 1)
- userInput := 1
- node := settings.selectSingleNode("/hostMonitor/settings/checkInterval")
- node.text := userInput
- }
- else if (A_ThisMenuItem == "Change Ping &Average Count"){
- InputBox, userInput, Change Ping Average Count, % "Enter an averaging count."
- . "`nSuggest 10 - 50."
- . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/pingAvgCount").text
- if !userInput
- return
- if userInput is not integer
- {
- MsgBox, This setting can only take an integer.
- return
- }
- if (userInput < 2)
- userInput := 2
- node := settings.selectSingleNode("/hostMonitor/settings/pingAvgCount")
- node.text := userInput
- MsgBox, 4,,% "Setting changed. This setting requires a script reload.`nReload now?"
- ifMsgBox, Yes
- reloadScript := true
- }
- else if (A_ThisMenuItem == "Change &Warning Latency"){
- InputBox, userInput, Change Ping Average Count, % "Enter a high latency warning level."
- . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/warnLatency").text
- if !userInput
- return
- if userInput is not integer
- {
- MsgBox, This setting can only take an integer.
- return
- }
- node := settings.selectSingleNode("/hostMonitor/settings/warnLatency")
- node.text := userInput
- }
- else
- MsgBox, % "Settings menu system broke :("
- SaveSettings(settings, tdoc)
- if reloadScript
- Reload
- return
-
-UpdateSB:
- scanTimer--
- SB_SetText("Online: " . onlineCount, 1)
- SB_SetText("Offline: " . offlineCount, 2)
- SB_SetText("Next scan in " . scantimer, 3)
- return
-
-;<===== Functions ===========================================================>
-CheckHosts(){
- Global
- SetTimer, UpdateSB, Off
- SetTimer, CheckHosts, Off
- SB_SetText("Online: 0", 1)
- SB_SetText("Offline: 0", 2)
- SB_SetText("Scan in progress...", 3)
- onlineCount := 0
- offlineCount := 0
- threads := Object()
- Loop, % hosts.MaxIndex()
- {
- ;Wait for free thread
- while (threads.MaxIndex() >= settings.selectSingleNode("/hostMonitor/settings/maxThreads").text)
- sleep, 250
-
- ;Start thread to check host
- if A_IsCompiled {
- Run, % A_ScriptDir . "\inc\PingMsg.exe """ . A_ScriptName . """ "
- . hosts[A_Index, "name"] . " " . A_Index,, Hide, threadID
- }
- else {
- Run, % A_ScriptDir . "\inc\PingMsg.ahk """ . A_ScriptName . """ "
- . hosts[A_Index, "name"] . " " . A_Index,, Hide, threadID
- }
-
- hosts[A_Index, "threadID"] := threadID
- threads.Push(threadID)
-
- GuiControl,, % hosts[A_Index, "statusTextID"], % "Checking host ("
- . hosts[A_Index, "threadID"] . ")"
-
- ;Update status bar
- SB_SetText(threads.MaxIndex() . " threads active", 3)
- }
- scanTimer := settings.selectSingleNode("/hostMonitor/settings/checkInterval").text
- SetTimer, CheckHosts, % (settings.selectSingleNode("/hostMonitor/settings/checkInterval").text * 1000)
- SetTimer, UpdateSB, On
- SetTimer, StaleThreads, % (((settings.selectSingleNode("/hostMonitor/settings/checkInterval").text) / 2) * 1000)
-}
-
-;FlushDNS by jNizM
-FlushDNS(){
- if !(DllCall("dnsapi.dll\DnsFlushResolverCache"))
- {
- throw Exception("DnsFlushResolverCache", -1)
- return 0
- }
- return 1
-}
-
-LoadXML(file){
- xmlFile := fileOpen(file, "r")
- xml := xmlFile.read()
- xmlFile.Close()
-
- doc := ComObjCreate("MSXML2.DOMdocument.6.0")
- doc.async := false
- if !doc.loadXML(xml)
- {
- MsgBox, % "Could not load" . file . "!"
- }
-
- return doc
-}
-
-Log(fileName, text){
- FormatTime, TimeStamp, A_Now, [dd/MMM/yyyy HH:mm:ss]
- logFile := fileOpen(fileName, "a")
- logFile.Write(TimeStamp . " " . text . "`r`n")
- logFile.Close()
- return 1
-}
-
-LogStart(settings){
- FormatTime, TimeStamp, A_Now, dd-MMM-yyyy_HH-mm-ss
- IfNotExist, % A_ScriptDir . "\Logs\"
- FileCreateDir, % A_ScriptDir . "\Logs"
- fileName := A_ScriptDir . "\Logs\" . TimeStamp . ".txt"
- Try {
- logFile := fileOpen(fileName, "w")
- }
- catch e {
- MsgBox, Failed to open file for logging!`n%A_LastError%
- }
- logFile.Write("[" . TimeStamp . "] Logging started. Using hosts file " . settings.selectSingleNode("/hostMonitor/settings/hostPath").text . "`r`n")
- logFile.Close()
- return fileName
-}
-
-LogStop(fileName){
- FormatTime, TimeStamp, A_Now, [dd/MMM/yyyy HH:mm:ss]
- logFile := fileOpen(fileName, "a")
- logFile.Write(TimeStamp . " Logging finished.`r`n")
- logFile.Close()
- return 1
-}
-
-Receive_WM_COPYDATA(wParam, lParam){
- Global
- Critical
- StringAddress := NumGet(lParam + 2*A_PtrSize)
- CopyOfData := StrGet(StringAddress)
-
- ;hostID|HostName|PingTime|IP
- reply := StrSplit(CopyOfData, "|")
-
- ;Clear thread from threads array
- Loop, % threads.MaxIndex()
- {
- if (threads[A_Index] == hosts[reply[1], "threadID"])
- threads.removeAt(A_Index)
- }
-
- ;Process reply
- if (reply[3] != "TIMEOUT")
- {
- ;Good return
- ;Check if host's ping array is full, remove oldest element if so
- if (hosts[reply[1]].pingArray.getCapacity() == settings.selectSingleNode("/hostMonitor/settings/pingAvgCount").text)
- hosts[reply[1]].pingArray.RemoveAt(1)
-
- ;Add new ping time to array
- hosts[reply[1]].pingArray.Push(reply[3])
-
- ;Update host's name element if its an IP & update GUI
- if (RegExMatch(hosts[reply[1], "name"], "^((|\.)\d{1,3}){4}$") && !hosts[reply[1], "alias"])
- {
- hosts[reply[1], "name"] := reply[2]
- GuiControl,, % hosts[reply[1], "nameTextID"], % subStr(hosts[reply[1], "name"], 1, 25)
- }
-
- ;Update host's ip element if different
- if (reply[4] != hosts[reply[1], "ip"])
- hosts[reply[1], "ip"] := reply[4]
-
- ;Update host's lastSeen element
- hosts[reply[1], "lastSeen"] := A_Hour . ":" . A_Min
-
- ;Calculate average ping time from array
- sum := 0
- for each, ping in hosts[reply[1]].pingArray
- sum += ping
- avg := floor(sum/hosts[reply[1]].pingArray.getCapacity())
- SetFormat, FloatFast, 0.0
-
- ;Update BGImage. Yellow if above warnLatency, green otherwise
- if (strReplace(reply[3], "ms") >= settings.selectSingleNode("/hostMonitor/settings/warnLatency").text)
- {
- ;GuiControl,, % hosts[reply[1], "bgImageID"], % "HBITMAP:*" hYellow
- GuiControl, +cYellow, % hosts[reply[1], "bgImageID"],
-
- ;Log warnings to file if enabled.
- if settings.selectSingleNode("/hostMonitor/settings/logging").text
- Log(logFile, hosts[reply[1], "name"] . " latency high: " . reply[3] . "ms (" settings.selectSingleNode("/hostMonitor/settings/warnLatency").text . "ms threshold)")
- }
- else
- {
- ;GuiControl,, % hosts[reply[1], "bgImageID"], % "HBITMAP:*" hGreen
- GuiControl, +cGreen, % hosts[reply[1], "bgImageID"],
- }
-
- ;Refresh host text
- if hosts[reply[1], "alias"] {
- GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "alias"]
- } else {
- GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "name"]
- }
-
- ;Update status text
- GuiControl,, % hosts[reply[1], "statusTextID"], % reply[3] . "ms (" . avg . "ms)"
-
- ;Increment onlineCount variable
- onlineCount++
- } else {
- ;Timeout
- ;Check if host's ping array is full, remove oldest element if so
- if (hosts[reply[1]].pingArray.getCapacity() == settings.selectSingleNode("/hostMonitor/settings/pingAvgCount").text)
- hosts[reply[1]].pingArray.RemoveAt(1)
-
- ;Add new ping time to array
- hosts[reply[1]].pingArray.Push(9999)
-
- ;Update BGImage to red
- ;GuiControl,, % hosts[reply[1], "bgImageID"], % "HBITMAP:*" hRed
- GuiControl, +cRed, % hosts[reply[1], "bgImageID"],
-
- ;Refresh host text
- if hosts[reply[1], "alias"] {
- GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "alias"]
- } else {
- GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "name"]
- }
-
- ;Update status text
- GuiControl,, % hosts[reply[1], "statusTextID"], % "Last Seen: " . hosts[reply[1], "lastSeen"]
-
- ;Log failures to file if enabled
- if settings.selectSingleNode("/hostMonitor/settings/logging").text
- Log(logFile, hosts[reply[1], "name"] . " TIMEOUT")
-
- ;Increment offlineCount variable
- offlineCount++
-
- ;Start tracert if autoTraceRt is enabled in settings
- if settings.selectSingleNode("/hostMonitor/settings/autoTraceRt").text
- StartTrace(hosts[reply[1], "name"])
- }
-
- ;Update status bar
- if !threads.MaxIndex()
- SetTimer, UpdateSB, On
- else
- SB_SetText(threads.MaxIndex() . " threads active", 3)
-
- ;Done...
- return true
-}
-
-SaveSettings(settings, tdoc){
- try {
- resultDoc := ComObjCreate("MSXML2.DOMdocument.6.0")
- settings.transformNodeToObject(tdoc, resultDoc)
- resultDoc.save(A_ScriptDir . "\inc\settings.xml")
- ObjRelease(Object(resultDoc))
- } catch e {
- MsgBox, % "Could not save settings.`n" . e . "`n" . e.description
- return 0
- }
- return 1
-}
-
-StaleThreads(){
- Global
- ;Loop hosts array
- for host in hosts
- {
- ;Store index in hostID for use in nested loop
- hostID := A_Index
-
- ;Get host thread ID
- hostThreadID := hosts[hostID, "threadID"]
-
- ;Check if hosts threadID is still in the threads array
- for each, tid in threads
- {
- ;if found, color the BG yellow & set text
- if (hostThreadID == tid)
- {
- ;GuiControl,, % hosts[hostID, "bgImageID"], % "HBITMAP:*" hYellow
- GuiControl, +cYellow, % hosts[hostID, "bgImageID"],
-
- ;Refresh host text
- if hosts[hostID, "alias"] {
- GuiControl,, % hosts[hostID, "nameTextID"], % hosts[reply[1], "alias"]
- } else {
- GuiControl,, % hosts[hostID, "nameTextID"], % hosts[reply[1], "name"]
- }
-
- ;Update status text
- GuiControl,, % hosts[hostID, "statusTextID"], % "Thread lost..."
- threads.RemoveAt(A_Index)
- }
- }
- }
- ;Release threads array
- ObjRelease(threads)
-}
-
-StartTrace(host){
- if A_IsCompiled
- Run, % A_ScriptDir . "\inc\tracert.exe " . host
- else
- Run, % A_ScriptDir . "\inc\tracert.ahk " . host
-}
-
-StartPingGraph(host){
- if A_IsCompiled
- Run, % A_ScriptDir . "\inc\pingGraph.exe " . host
- else
- Run, % A_ScriptDir . "\inc\pingGraph.ahk " . host
-}
-
-StartPutty(host, mode){
- Global
- IfNotExist, % A_ScriptDir . ".\inc\putty.exe"
- {
- MsgBox, 4,, % "This function requires putty.exe to be in the inc folder.`nGo to the putty download page?"
- IfMsgBox, Yes
- Run, http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
- else
- return
- }
- objShell := ComObjCreate("WScript.Shell")
- objExec := objShell.Exec(ComSpec . " /c .\inc\putty.exe -" . mode . " "
- . host . " " . ((mode == "ssh")?22:(mode == "telnet")?23:""))
-}
-
-WM_MOUSEMOVE() {
- global hosts
- static CurrControl, PrevControl
- CurrControl := A_GuiControl
- If (CurrControl <> PrevControl)
- {
- ToolTip ; Turn off any previous tooltip.
- SetTimer, DisplayToolTip, 1000
- PrevControl := CurrControl
- }
- return
-
- DisplayToolTip:
- SetTimer, DisplayToolTip, Off
- ToolTip, % hosts[subStr(CurrControl, 2), "ip"]
- SetTimer, RemoveToolTip, 3500
- return
-
- RemoveToolTip:
- SetTimer, RemoveToolTip, Off
- ToolTip
- return
-}
-
-;<===== Includes ============================================================>
-#Include %A_ScriptDir%\inc
-#Include Common Functions.ahk
+/*
+ Host Monitor.ahk
+ Authors: Elesar (Daniel Thomas) & Grendahl
+
+ Uses functions by SKAN, just me, Uberi & jNizM
+
+ Monitors a set of hosts periodically and gives visual representation of their status.
+ Includes several tools for troubleshooting.
+
+ Some functionality depends on putty.exe being in the inc folder.
+
+*/
+;<===== System Settings =====================================================>
+#SingleInstance, Force
+#NoEnv
+SetBatchLines, -1
+
+;<===== FileInstalls ========================================================>
+;Images
+IfNotExist, % A_ScriptDir . "\img"
+ FileCreateDir, % A_ScriptDir . "\img"
+;FileInstall, img\Green.jpg, img\Green.jpg, 1
+;FileInstall, img\Yellow.jpg, img\Yellow.jpg, 1
+;FileInstall, img\Red.jpg, img\Red.jpg, 1
+FileInstall, img\Host Monitor.ico, img\Host Monitor.ico, 1
+
+;Tools
+IfNotExist, % A_ScriptDir . "\inc"
+ FileCreateDir, % A_ScriptDir . "\inc"
+FileInstall, temp\pingGraph.exe, inc\pingGraph.exe, 1
+FileInstall, temp\pingMsg.exe, inc\pingMsg.exe, 1
+FileInstall, temp\traceRt.exe, inc\traceRt.exe, 1
+
+;Settings & Misc
+FileInstall, inc\defaultSettings.xml, inc\settings.xml, 0
+FileInstall, inc\transform.xslt, inc\transform.xslt, 1
+FileInstall, hosts.txt, hosts.txt, 0
+
+;<===== Startup =============================================================>
+;Receive messages from slave scripts and Windows
+OnMessage(0x4a, "Receive_WM_COPYDATA")
+OnMessage(0x200, "WM_MOUSEMOVE") ; Tooltips
+
+;Allcate console so we don't have constant flashing windows while pinging
+DllCall("AllocConsole")
+WinHide % "ahk_id " DllCall("GetConsoleWindow", "ptr")
+
+;Make sure we are exiting nicely
+OnExit, Exit
+
+;<===== Settings ============================================================>
+;Static Settings - May mess stuff up visually if changed
+boxWidth := 150
+boxHeight := 45
+
+;Read in settings.xml
+settings := loadXML(A_ScriptDir . "\inc\settings.xml")
+
+;Read in transform.xslt
+tdoc := loadXML(A_ScriptDir . "\inc\transform.xslt")
+
+;Get monitor working area
+SysGet, Mon, MonitorWorkArea
+maxRows := Floor(MonBottom / boxHeight) - 2
+
+;Adjust guiRows setting if higher than maxRows
+if (settings.selectSingleNode("/hostMonitor/settings/guiRows").text > maxRows) {
+ node := settings.selectSingleNode("/hostMonitor/settings/guiRows")
+ node.text := maxRows
+ SaveSettings(settings, tdoc)
+}
+
+;<===== Read in hostList ====================================================>
+;Prompt for host file if not saved.
+while !settings.selectSingleNode("/hostMonitor/settings/hostPath").text
+{
+ FileSelectFile, path,,, Select host file, Text (*.txt)
+ if !path
+ {
+ MsgBox, 4,, No file selected.`nDo you want to exit?
+ IfMsgBox, Yes
+ ExitApp
+ } else {
+ node := settings.selectSingleNode("/hostMonitor/settings/hostPath")
+ node.text := path
+ SaveSettings(settings, tdoc)
+ }
+}
+
+hostsFile := fileOpen(settings.selectSingleNode("/hostMonitor/settings/hostPath").text, "r")
+hostsData := hostsFile.Read()
+hostsFile.Close()
+
+hosts := Object()
+Loop, Parse, hostsData, `n, `r
+{
+ if (A_LoopField != "")
+ {
+ hosts[A_Index] := Object()
+ aliasPos := RegExMatch(A_LoopField, "\[(.*?)\]", alias)
+ alias := strReplace(alias, "[")
+ alias := strReplace(alias, "]")
+ if alias
+ {
+ hosts[A_Index, "name"] := subStr(A_LoopField, 1, aliasPos - 1)
+ hosts[A_Index, "alias"] := alias
+ }
+ else
+ hosts[A_Index, "name"] := A_LoopField
+ hosts[A_Index, "ip"] := ""
+ hosts[A_Index, "lastSeen"] := "Never"
+ hosts[A_Index, "bgImageID"] := ""
+ hosts[A_Index, "nameTextID"] := ""
+ hosts[A_Index, "statusTextID"] := ""
+ hosts[A_Index, "threadID"] := ""
+ hosts[A_Index].pingArray := Object()
+ }
+}
+
+;<===== Menus ===============================================================>
+;Context Menu
+Menu, ContextMenu, Add, Trace Route, ContextMenuHandler
+Menu, ContextMenu, Add, Ping Graph, ContextMenuHandler
+Menu, ContextMenu, Add
+Menu, ContextMenu, Add, RDP, ContextMenuHandler
+Menu, ContextMenu, Add
+Menu, ContextMenu, Add, Explore Shares, ContextMenuHandler
+Menu, ContextMenu, Add
+Menu, ContextMenu, Add, Browse, ContextMenuHandler
+Menu, ContextMenu, Add, Browse (https), ContextMenuHandler
+Menu, ContextMenu, Add
+Menu, ContextMenu, Add, SSH, ContextMenuHandler
+Menu, ContextMenu, Add, Telnet, ContextMenuHandler
+
+;File Menu
+Menu, FileMenu, Add, &Scan Now, MenuHandler
+Menu, FileMenu, Add, Open &Log Folder, MenuHandler
+Menu, FileMenu, Add
+Menu, FileMenu, Add, &Open, MenuHandler
+Menu, FileMenu, Add, &Reload, MenuHandler
+Menu, FileMenu, Add
+Menu, FileMenu, Add, E&xit, MenuHandler
+
+;Help Menu
+Menu, HelpMenu, Add, &Debug, MenuHandler
+Menu, HelpMenu, Add
+Menu, HelpMenu, Add, &About, MenuHandler
+
+;Settings Menu
+Menu, SettingsMenu, Add, &Flush DNS, SettingsMenuHandler
+Menu, SettingsMenu, Add
+Menu, SettingsMenu, Add, &Logging, SettingsMenuHandler
+if settings.selectSingleNode("/hostMonitor/settings/logging").text
+ Menu, SettingsMenu, Check, &Logging
+Menu, SettingsMenu, Add, Auto &TraceRt, SettingsMenuHandler
+if settings.selectSingleNode("/hostMonitor/settings/autoTraceRt").text
+ Menu, SettingsMenu, Check, Auto &TraceRt
+Menu, SettingsMenu, Add, Remember &Window Pos, SettingsMenuHandler
+if settings.selectSingleNode("/hostMonitor/settings/rememberPos").text
+ Menu, SettingsMenu, Check, Remember &Window Pos
+Menu, SettingsMenu, Add, Pass &Credentials to Shares, SettingsMenuHandler
+if settings.selectSingleNode("/hostMonitor/settings/useCreds").text
+ Menu, SettingsMenu, Check, Pass &Credentials to Shares
+Menu, SettingsMenu, Add
+Menu, SettingsMenu, Add, Change GUI &Rows, SettingsMenuHandler
+Menu, SettingsMenu, Add, Change Max &Threads, SettingsMenuHandler
+Menu, SettingsMenu, Add, Change Check &Interval, SettingsMenuHandler
+Menu, SettingsMenu, Add, Change Ping &Average Count, SettingsMenuHandler
+Menu, SettingsMenu, Add, Change &Warning Latency, SettingsMenuHandler
+
+;Menu Bar
+Menu, MenuBar, Add, &File, :FileMenu
+Menu, MenuBar, Add, &Settings, :SettingsMenu
+Menu, MenuBar, Add, &Help, :HelpMenu
+
+;Tray Menu
+Menu, Tray, Icon, % A_ScriptDir . "\img\Host Monitor.ico"
+
+;<===== Load Images =========================================================>
+;hGreen := LoadPicture(A_ScriptDir . "\img\Green.jpg")
+;hYellow := LoadPicture(A_ScriptDir . "\img\Yellow.jpg")
+;hRed := LoadPicture(A_ScriptDir . "\img\Red.jpg")
+
+;<===== GUI =================================================================>
+Gui, Margin, 5, 5
+Gui, Menu, MenuBar
+RowX := 5
+RowY := 5
+Loop % hosts.MaxIndex() {
+ if (A_Index != 1) && !Mod(A_Index - 1, (settings.selectSingleNode("/hostMonitor/settings/guiRows").text
+ ?settings.selectSingleNode("/hostMonitor/settings/guiRows").text
+ :maxRows)) {
+ RowX += 155
+ RowY := 5
+ }
+
+ ;Gui, Add, Picture, % "x" RowX " y" RowY " w" boxWidth " 0x4000000 gDummyLabel vi"
+ ; . A_Index, % "HBITMAP:*" hYellow
+
+ Gui, Add, Progress, % "x" RowX " y" RowY " w" boxWidth " h" boxHeight " vi"
+ . A_Index " cYellow", 100
+
+ if hosts[A_Index, "alias"] {
+ Gui, Add, Text, % "xp+5 yp+5 w140 h15 gDummyLabel +0x200 +BackgroundTrans vt"
+ . A_Index . " +Center" , % subStr(hosts[A_Index, "alias"], 1, 25)
+ }
+ else {
+ Gui, Add, Text, % "xp+5 yp+5 w140 h15 gDummyLabel +0x200 +BackgroundTrans vt"
+ . A_Index . " +Center", % subStr(hosts[A_Index, "name"], 1, 25)
+ }
+ Gui, Add, Text, % "xp yp+15 w140 h15 gDummyLabel +0x200 +BackgroundTrans vs"
+ . A_Index . " +Center", % hosts[A_Index, "lastSeen"]
+ hosts[A_Index, "bgImageID"] := "i" . A_Index
+ hosts[A_Index, "nameTextID"] := "t" . A_Index
+ hosts[A_Index, "statusTextID"] := "s" . A_Index
+ RowY += boxHeight
+}
+Gui, Add, StatusBar
+SB_SetParts(100,100)
+if (RowX < 100) {
+ if settings.selectSingleNode("/hostMonitor/settings/rememberPos").text {
+ Gui, Show, % "x" . settings.selectSingleNode("/hostMonitor/settings/winX").text
+ . " y" . settings.selectSingleNode("/hostMonitor/settings/winY").text . " w320", Host Monitor
+ }
+ else
+ Gui, Show, % "w320", Host Monitor
+}
+else {
+ if settings.selectSingleNode("/hostMonitor/settings/rememberPos").text {
+ Gui, Show, % "x" . settings.selectSingleNode("/hostMonitor/settings/winX").text
+ . " y" . settings.selectSingleNode("/hostMonitor/settings/winY").text, Host Monitor
+ }
+ else
+ Gui, Show, Center, Host Monitor
+}
+
+;<===== Timers ==============================================================>
+;Check hosts based off of settings.xml
+SetTimer, CheckHosts, % (settings.selectSingleNode("/hostMonitor/settings/checkInterval").text * 1000)
+
+;Update status bar every second
+scanTimer := settings.selectSingleNode("/hostMonitor/settings/checkInterval").text
+SetTimer, UpdateSB, 1000
+
+;Clock time based events
+SetTimer, CheckTime, 60000
+
+;<===== Main ================================================================>
+if settings.selectSingleNode("/hostMonitor/settings/logging").Text
+ logFile := LogStart(settings)
+CheckHosts()
+
+;<===== End AutoExecute =====================================================>
+return
+
+;<===== Subs ================================================================>
+About:
+ aboutText =
+ ( LTrim
+ Written by Elesar (Daniel Thomas)
+ In colaberation with Grendahl
+ and others from the AutoHotkey community.
+ )
+ MsgBox, % aboutText
+ return
+
+CheckTime:
+ ;Start a new log file if midnight
+ if ((A_Hour == 00) && (A_Min == 00) && (settings.selectSingleNode("/hostMonitor/settings/logging").text))
+ {
+ logStop(logFile)
+ logFile := logStart(settings)
+ }
+ return
+
+ContextMenuHandler:
+ if (A_ThisMenuItem == "Trace Route")
+ StartTrace(host)
+ else if (A_ThisMenuItem == "Ping Graph")
+ StartPingGraph(host)
+ else if (A_ThisMenuItem == "RDP")
+ Run, % "mstsc /v:" . host
+ else if (A_ThisMenuItem == "Explore Shares") {
+ hostName := IPHelper.ReverseLookup(host)
+ StringSplit, hostName, hostName,.
+ Run, % A_ScriptDir "\inc\Shares.ahk " . hostName1
+ }
+ else if (A_ThisMenuItem == "Browse")
+ Run, % "http://" . hosts[reply[1], "name"], % subStr(hosts[reply[1], "name"], 1, 25)
+ else if (A_ThisMenuItem == "Browse (https)")
+ Run, % "https://" . hosts[reply[1], "name"], % subStr(hosts[reply[1], "name"], 1, 25)
+ else if (A_ThisMenuItem == "SSH")
+ StartPutty(host, "ssh")
+ else if (A_ThisMenuItem == "Telnet")
+ StartPutty(host, "telnet")
+ else
+ MsgBox, % "Context menu broke :("
+ return
+
+Debug:
+ clipboard := enumObj(hosts) . "`n`n" . enumObj(threads)
+ MsgBox, % "Hosts and Threads arrays copied to clipboard."
+ Return
+
+DummyLabel:
+ ;Needed to get tooltips/menus on text controls...
+ Return
+
+Exit:
+GuiClose:
+ WinGetPos, winX, winY,,, Host Monitor
+ node := settings.selectSingleNode("/hostMonitor/settings/winX")
+ node.text := winX
+ node := settings.selectSingleNode("/hostMonitor/settings/winY")
+ node.text := winY
+ SaveSettings(settings, tdoc)
+ if settings.selectSingleNode("/hostMonitor/settings/logging").text
+ LogStop(logFile)
+ ExitApp
+ return
+
+GuiContextMenu:
+ host := hosts[subStr(A_GuiControl, 2), "ip"]
+ if host
+ Menu, ContextMenu, Show
+ return
+
+MenuButton:
+ host := hosts[subStr(A_GuiControl, 2), "name"]
+ Menu, ContextMenu, Show
+ return
+
+MenuHandler:
+ if (A_ThisMenuItem == "&Scan Now")
+ CheckHosts()
+ else if (A_ThisMenuItem == "Open &Log Folder")
+ Run, % "explore " . A_ScriptDir . "\Logs"
+ else if (A_ThisMenuItem == "&Open"){
+ FileSelectFile, path,,, Select host file, Text (*.txt)
+ if path
+ {
+ node := settings.selectSingleNode("/hostMonitor/settings/hostPath")
+ node.text := path
+ SaveSettings(settings, tdoc)
+ Reload
+ }
+ }
+ else if (A_ThisMenuItem == "&Reload")
+ Reload
+ else if (A_ThisMenuItem == "E&xit")
+ GoSub, Exit
+ else if (A_ThisMenuItem == "&Debug")
+ GoSub, Debug
+ else if (A_ThisMenuItem == "&About")
+ GoSub, About
+ else
+ MsgBox, % "Menu system broke :("
+ return
+
+SettingsMenuHandler:
+ reloadScript := false
+ if (A_ThisMenuItem == "&Flush DNS")
+ MsgBox, % ((FlushDNS() == 1)?"DNS cache flushed.":"Failed to flush DNS cache.")
+ else if (A_ThisMenuItem == "&Logging"){
+ Menu, SettingsMenu, ToggleCheck, &Logging
+ node := settings.selectSingleNode("/hostMonitor/settings/logging")
+ node.text := !node.text
+ if node.text
+ logFile := LogStart(settings)
+ else
+ LogStop(logFile)
+ }
+ else if (A_ThisMenuItem == "Auto &TraceRt"){
+ Menu, SettingsMenu, ToggleCheck, Auto &TraceRt
+ node := settings.selectSingleNode("/hostMonitor/settings/autoTraceRt")
+ node.text := !node.text
+ }
+ else if (A_ThisMenuItem == "Remember &Window Pos"){
+ Menu, SettingsMenu, ToggleCheck, Remember &Window Pos
+ node := settings.selectSingleNode("/hostMonitor/settings/rememberPos")
+ node.text := !node.text
+ }
+ else if (A_ThisMenuItem == "Pass &Credentials to Shares"){
+ Menu, SettingsMenu, ToggleCheck, Pass &Credentials to Shares
+ node := settings.selectSingleNode("/hostMonitor/settings/useCreds")
+ node.text := !node.text
+ }
+ else if (A_ThisMenuItem == "Change GUI &Rows"){
+ InputBox, userInput, Change GUI Rows, % "Enter a row count between 1 and "
+ . maxRows . ".`nSet to 0 for AutoSize based on monitor work area and host count."
+ . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/guiRows").text
+ if !userInput
+ return
+ if userInput is not integer
+ {
+ MsgBox, This setting can only take an integer.
+ return
+ }
+ if (userInput > maxRows)
+ userInput := maxRows
+ node := settings.selectSingleNode("/hostMonitor/settings/guiRows")
+ node.text := userInput
+ MsgBox, 4,,% "Setting changed. This setting requires a script reload.`nReload now?"
+ ifMsgBox, Yes
+ reloadScript := true
+ }
+ else if (A_ThisMenuItem == "Change Max &Threads"){
+ InputBox, userInput, Change Max Threads, % "Enter a thread count greater than 1."
+ . "`nWarning: May load CPU or cause script issues if too many threads are allowed."
+ . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/maxThreads").text
+ if !userInput
+ return
+ if userInput is not integer
+ {
+ MsgBox, This setting can only take an integer.
+ return
+ }
+ if (userInput < 1)
+ userInput := 1
+ node := settings.selectSingleNode("/hostMonitor/settings/maxThreads")
+ node.text := userInput
+ }
+ else if (A_ThisMenuItem == "Change Check &Interval"){
+ InputBox, userInput, Change Check Interval, % "Enter a check interval in seconds."
+ . "`nSuggest 30 or higher."
+ . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/checkInterval").text
+ if !userInput
+ return
+ if userInput is not integer
+ {
+ MsgBox, This setting can only take an integer.
+ return
+ }
+ if (userInput < 1)
+ userInput := 1
+ node := settings.selectSingleNode("/hostMonitor/settings/checkInterval")
+ node.text := userInput
+ }
+ else if (A_ThisMenuItem == "Change Ping &Average Count"){
+ InputBox, userInput, Change Ping Average Count, % "Enter an averaging count."
+ . "`nSuggest 10 - 50."
+ . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/pingAvgCount").text
+ if !userInput
+ return
+ if userInput is not integer
+ {
+ MsgBox, This setting can only take an integer.
+ return
+ }
+ if (userInput < 2)
+ userInput := 2
+ node := settings.selectSingleNode("/hostMonitor/settings/pingAvgCount")
+ node.text := userInput
+ MsgBox, 4,,% "Setting changed. This setting requires a script reload.`nReload now?"
+ ifMsgBox, Yes
+ reloadScript := true
+ }
+ else if (A_ThisMenuItem == "Change &Warning Latency"){
+ InputBox, userInput, Change Ping Average Count, % "Enter a high latency warning level."
+ . "`nCurrent setting is " . settings.selectSingleNode("/hostMonitor/settings/warnLatency").text
+ if !userInput
+ return
+ if userInput is not integer
+ {
+ MsgBox, This setting can only take an integer.
+ return
+ }
+ node := settings.selectSingleNode("/hostMonitor/settings/warnLatency")
+ node.text := userInput
+ }
+ else
+ MsgBox, % "Settings menu system broke :("
+ SaveSettings(settings, tdoc)
+ if reloadScript
+ Reload
+ return
+
+UpdateSB:
+ scanTimer--
+ SB_SetText("Online: " . onlineCount, 1)
+ SB_SetText("Offline: " . offlineCount, 2)
+ SB_SetText("Next scan in " . scantimer, 3)
+ return
+
+;<===== Functions ===========================================================>
+CheckHosts(){
+ Global
+ SetTimer, UpdateSB, Off
+ SetTimer, CheckHosts, Off
+ SB_SetText("Online: 0", 1)
+ SB_SetText("Offline: 0", 2)
+ SB_SetText("Scan in progress...", 3)
+ onlineCount := 0
+ offlineCount := 0
+ threads := Object()
+ Loop, % hosts.MaxIndex()
+ {
+ ;Wait for free thread
+ while (threads.MaxIndex() >= settings.selectSingleNode("/hostMonitor/settings/maxThreads").text)
+ sleep, 250
+
+ ;Start thread to check host
+ if A_IsCompiled {
+ Run, % A_ScriptDir . "\inc\PingMsg.exe """ . A_ScriptName . """ "
+ . hosts[A_Index, "name"] . " " . A_Index,, Hide, threadID
+ }
+ else {
+ Run, % A_ScriptDir . "\inc\PingMsg.ahk """ . A_ScriptName . """ "
+ . hosts[A_Index, "name"] . " " . A_Index,, Hide, threadID
+ }
+
+ hosts[A_Index, "threadID"] := threadID
+ threads.Push(threadID)
+
+ GuiControl,, % hosts[A_Index, "statusTextID"], % "Checking host ("
+ . hosts[A_Index, "threadID"] . ")"
+
+ ;Update status bar
+ SB_SetText(threads.MaxIndex() . " threads active", 3)
+ }
+ scanTimer := settings.selectSingleNode("/hostMonitor/settings/checkInterval").text
+ SetTimer, CheckHosts, % (settings.selectSingleNode("/hostMonitor/settings/checkInterval").text * 1000)
+ SetTimer, UpdateSB, On
+ SetTimer, StaleThreads, % (((settings.selectSingleNode("/hostMonitor/settings/checkInterval").text) / 2) * 1000)
+}
+
+;FlushDNS by jNizM
+FlushDNS(){
+ if !(DllCall("dnsapi.dll\DnsFlushResolverCache"))
+ {
+ throw Exception("DnsFlushResolverCache", -1)
+ return 0
+ }
+ return 1
+}
+
+LoadXML(file){
+ xmlFile := fileOpen(file, "r")
+ xml := xmlFile.read()
+ xmlFile.Close()
+
+ doc := ComObjCreate("MSXML2.DOMdocument.6.0")
+ doc.async := false
+ if !doc.loadXML(xml)
+ {
+ MsgBox, % "Could not load" . file . "!"
+ }
+
+ return doc
+}
+
+Log(fileName, text){
+ FormatTime, TimeStamp, A_Now, [dd/MMM/yyyy HH:mm:ss]
+ logFile := fileOpen(fileName, "a")
+ logFile.Write(TimeStamp . " " . text . "`r`n")
+ logFile.Close()
+ return 1
+}
+
+LogStart(settings){
+ FormatTime, TimeStamp, A_Now, dd-MMM-yyyy_HH-mm-ss
+ IfNotExist, % A_ScriptDir . "\Logs\"
+ FileCreateDir, % A_ScriptDir . "\Logs"
+ fileName := A_ScriptDir . "\Logs\" . TimeStamp . ".txt"
+ Try {
+ logFile := fileOpen(fileName, "w")
+ }
+ catch e {
+ MsgBox, Failed to open file for logging!`n%A_LastError%
+ }
+ logFile.Write("[" . TimeStamp . "] Logging started. Using hosts file " . settings.selectSingleNode("/hostMonitor/settings/hostPath").text . "`r`n")
+ logFile.Close()
+ return fileName
+}
+
+LogStop(fileName){
+ FormatTime, TimeStamp, A_Now, [dd/MMM/yyyy HH:mm:ss]
+ logFile := fileOpen(fileName, "a")
+ logFile.Write(TimeStamp . " Logging finished.`r`n")
+ logFile.Close()
+ return 1
+}
+
+Receive_WM_COPYDATA(wParam, lParam){
+ Global
+ Critical
+ StringAddress := NumGet(lParam + 2*A_PtrSize)
+ CopyOfData := StrGet(StringAddress)
+
+ ;hostID|HostName|PingTime|IP
+ reply := StrSplit(CopyOfData, "|")
+
+ ;Clear thread from threads array
+ Loop, % threads.MaxIndex()
+ {
+ if (threads[A_Index] == hosts[reply[1], "threadID"])
+ threads.removeAt(A_Index)
+ }
+
+ ;Process reply
+ if (reply[3] != "TIMEOUT")
+ {
+ ;Good return
+ ;Check if host's ping array is full, remove oldest element if so
+ if (hosts[reply[1]].pingArray.getCapacity() == settings.selectSingleNode("/hostMonitor/settings/pingAvgCount").text)
+ hosts[reply[1]].pingArray.RemoveAt(1)
+
+ ;Add new ping time to array
+ hosts[reply[1]].pingArray.Push(reply[3])
+
+ ;Update host's name element if its an IP & update GUI
+ if (RegExMatch(hosts[reply[1], "name"], "^((|\.)\d{1,3}){4}$") && !hosts[reply[1], "alias"])
+ {
+ hosts[reply[1], "name"] := reply[2]
+ GuiControl,, % hosts[reply[1], "nameTextID"], % subStr(hosts[reply[1], "name"], 1, 25)
+ }
+
+ ;Update host's ip element if different
+ if (reply[4] != hosts[reply[1], "ip"])
+ hosts[reply[1], "ip"] := reply[4]
+
+ ;Update host's lastSeen element
+ hosts[reply[1], "lastSeen"] := A_Hour . ":" . A_Min
+
+ ;Calculate average ping time from array
+ sum := 0
+ for each, ping in hosts[reply[1]].pingArray
+ sum += ping
+ avg := floor(sum/hosts[reply[1]].pingArray.getCapacity())
+ SetFormat, FloatFast, 0.0
+
+ ;Update BGImage. Yellow if above warnLatency, green otherwise
+ if (strReplace(reply[3], "ms") >= settings.selectSingleNode("/hostMonitor/settings/warnLatency").text)
+ {
+ ;GuiControl,, % hosts[reply[1], "bgImageID"], % "HBITMAP:*" hYellow
+ GuiControl, +cYellow, % hosts[reply[1], "bgImageID"],
+
+ ;Log warnings to file if enabled.
+ if settings.selectSingleNode("/hostMonitor/settings/logging").text
+ Log(logFile, hosts[reply[1], "name"] . " latency high: " . reply[3] . "ms (" settings.selectSingleNode("/hostMonitor/settings/warnLatency").text . "ms threshold)")
+ }
+ else
+ {
+ ;GuiControl,, % hosts[reply[1], "bgImageID"], % "HBITMAP:*" hGreen
+ GuiControl, +cLime, % hosts[reply[1], "bgImageID"],
+ }
+
+ ;Refresh host text
+ if hosts[reply[1], "alias"] {
+ GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "alias"]
+ } else {
+ GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "name"]
+ }
+
+ ;Update status text
+ GuiControl,, % hosts[reply[1], "statusTextID"], % reply[3] . "ms (" . avg . "ms)"
+
+ ;Increment onlineCount variable
+ onlineCount++
+ } else {
+ ;Timeout
+ ;Check if host's ping array is full, remove oldest element if so
+ if (hosts[reply[1]].pingArray.getCapacity() == settings.selectSingleNode("/hostMonitor/settings/pingAvgCount").text)
+ hosts[reply[1]].pingArray.RemoveAt(1)
+
+ ;Add new ping time to array
+ hosts[reply[1]].pingArray.Push(9999)
+
+ ;Update BGImage to red
+ ;GuiControl,, % hosts[reply[1], "bgImageID"], % "HBITMAP:*" hRed
+ GuiControl, +cRed, % hosts[reply[1], "bgImageID"],
+
+ ;Refresh host text
+ if hosts[reply[1], "alias"] {
+ GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "alias"]
+ } else {
+ GuiControl,, % hosts[reply[1], "nameTextID"], % hosts[reply[1], "name"]
+ }
+
+ ;Update status text
+ GuiControl,, % hosts[reply[1], "statusTextID"], % "Last Seen: " . hosts[reply[1], "lastSeen"]
+
+ ;Log failures to file if enabled
+ if settings.selectSingleNode("/hostMonitor/settings/logging").text
+ Log(logFile, hosts[reply[1], "name"] . " TIMEOUT")
+
+ ;Increment offlineCount variable
+ offlineCount++
+
+ ;Start tracert if autoTraceRt is enabled in settings
+ if settings.selectSingleNode("/hostMonitor/settings/autoTraceRt").text
+ StartTrace(hosts[reply[1], "name"])
+ }
+
+ ;Update status bar
+ if !threads.MaxIndex()
+ SetTimer, UpdateSB, On
+ else
+ SB_SetText(threads.MaxIndex() . " threads active", 3)
+
+ ;Done...
+ return true
+}
+
+SaveSettings(settings, tdoc){
+ try {
+ resultDoc := ComObjCreate("MSXML2.DOMdocument.6.0")
+ settings.transformNodeToObject(tdoc, resultDoc)
+ resultDoc.save(A_ScriptDir . "\inc\settings.xml")
+ ObjRelease(Object(resultDoc))
+ } catch e {
+ MsgBox, % "Could not save settings.`n" . e . "`n" . e.description
+ return 0
+ }
+ return 1
+}
+
+StaleThreads(){
+ Global
+ ;Loop hosts array
+ for host in hosts
+ {
+ ;Store index in hostID for use in nested loop
+ hostID := A_Index
+
+ ;Get host thread ID
+ hostThreadID := hosts[hostID, "threadID"]
+
+ ;Check if hosts threadID is still in the threads array
+ for each, tid in threads
+ {
+ ;if found, color the BG yellow & set text
+ if (hostThreadID == tid)
+ {
+ ;GuiControl,, % hosts[hostID, "bgImageID"], % "HBITMAP:*" hYellow
+ GuiControl, +cYellow, % hosts[hostID, "bgImageID"],
+
+ ;Refresh host text
+ if hosts[hostID, "alias"] {
+ GuiControl,, % hosts[hostID, "nameTextID"], % hosts[reply[1], "alias"]
+ } else {
+ GuiControl,, % hosts[hostID, "nameTextID"], % hosts[reply[1], "name"]
+ }
+
+ ;Update status text
+ GuiControl,, % hosts[hostID, "statusTextID"], % "Thread lost..."
+ threads.RemoveAt(A_Index)
+ }
+ }
+ }
+ ;Release threads array
+ ObjRelease(threads)
+}
+
+StartTrace(host){
+ if A_IsCompiled
+ Run, % A_ScriptDir . "\inc\tracert.exe " . host
+ else
+ Run, % A_ScriptDir . "\inc\tracert.ahk " . host
+}
+
+StartPingGraph(host){
+ if A_IsCompiled
+ Run, % A_ScriptDir . "\inc\pingGraph.exe " . host
+ else
+ Run, % A_ScriptDir . "\inc\pingGraph.ahk " . host
+}
+
+StartPutty(host, mode){
+ Global
+ IfNotExist, % A_ScriptDir . ".\inc\putty.exe"
+ {
+ MsgBox, 4,, % "This function requires putty.exe to be in the inc folder.`nGo to the putty download page?"
+ IfMsgBox, Yes
+ Run, http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
+ else
+ return
+ }
+ objShell := ComObjCreate("WScript.Shell")
+ objExec := objShell.Exec(ComSpec . " /c .\inc\putty.exe -" . mode . " "
+ . host . " " . ((mode == "ssh")?22:(mode == "telnet")?23:""))
+}
+
+WM_MOUSEMOVE() {
+ global hosts
+ static CurrControl, PrevControl
+ CurrControl := A_GuiControl
+ If (CurrControl <> PrevControl)
+ {
+ ToolTip ; Turn off any previous tooltip.
+ SetTimer, DisplayToolTip, 1000
+ PrevControl := CurrControl
+ }
+ return
+
+ DisplayToolTip:
+ SetTimer, DisplayToolTip, Off
+ ToolTip, % hosts[subStr(CurrControl, 2), "ip"]
+ SetTimer, RemoveToolTip, 3500
+ return
+
+ RemoveToolTip:
+ SetTimer, RemoveToolTip, Off
+ ToolTip
+ return
+}
+
+;<===== Includes ============================================================>
+#Include %A_ScriptDir%\inc
+#Include Common Functions.ahk
+#Include IPHelper.ahk
+
diff --git a/inc/Shares.ahk b/inc/Shares.ahk
new file mode 100644
index 0000000..bde1c0e
--- /dev/null
+++ b/inc/Shares.ahk
@@ -0,0 +1,191 @@
+/*
+ Shares.ahk
+ Author: Grendahl
+
+ This script takes host name, host login name, and host password as
+ parameters and provides the user with a list of shares on the host.
+ The user can then select a share and open it using the passed in credentials.
+*/
+;<===== System Settings =====================================================>
+#SingleInstance Off
+#NoEnv
+#NoTrayIcon
+SendMode Input
+
+;<===== Startup ==========================================================>
+;Set Encryption seed
+Seed = C0mpl3xPas5word!
+
+;Read in settings.xml and transform.xslt
+settings := loadXML(A_ScriptDir . "\settings.xml")
+tdoc := loadXML(A_ScriptDir . "\transform.xslt")
+
+;Set host to passed in parameter
+Host = %1%
+;Ensure Host is actually passed in
+If(!Host) {
+ Msgbox Invalid Input - Must pass Host as command line parameter
+ ExitApp
+}
+;********NEED TO CONVERT IP BACK TO NAME HERE! (or pass the name and not the IP from the context menu handler)********
+
+StringUpper, Host, Host
+
+;Read settings.xml
+encryptedUser := settings.selectSingleNode("/hostMonitor/settings/userName").text
+encryptedPass := settings.selectSingleNode("/hostMonitor/settings/userPass").text
+useCreds := settings.selectSingleNode("/hostMonitor/settings/useCreds").text
+
+;If there userName or userPass settings don't exist, go make some
+If(!encryptedUser || !encryptedPass)
+ gosub SaveCreds
+
+;If User/Pass are not encrypted, send user to set them.
+If encryptedUser is not integer
+ notEncrypted = 1
+If encryptedPass is not integer
+ notEncrypted = 1
+if(notEncrypted == 1)
+ gosub SaveCreds
+
+;If User/Pass are encrypted, decrypt them for use in this script.
+If encryptedUser is integer
+ User := Uncode(settings.selectSingleNode("/hostMonitor/settings/userName").text,Seed)
+If encryptedPass is integer
+ Pass := Uncode(settings.selectSingleNode("/hostMonitor/settings/userPass").text,Seed)
+
+;Enumerate shares on host
+Drives := ListShares(Host, User, Pass)
+
+;<===== GUI ==============================================================>
+Menu, Tray, Icon, % A_ScriptDir . "\..\img\Host Monitor.ico"
+Gui, Add, Text,,Choose a share:
+Gui, Add, ListBox, vShareSelection gListBox r%Count%, % Drives
+Gui, Add, Button, gSaveCreds, Credentials
+Gui, Add, Button, xp+75 yp gExplore +default, Explore
+Gui, +OwnDialogs +ToolWindow
+Gui, Show, ,%Host%
+return
+
+;<===== Labels ==============================================================>
+ListBox:
+ if A_GuiEvent <> DoubleClick
+ return
+Explore:
+ GuiControlGet, Share,,ListBox1
+ Share := " \\" . Host . "\" . Share
+ Gui, Destroy
+ Run, explorer %Share%
+ If(useCreds=1) {
+ Loop 20
+ {
+ Sleep 500
+ IfWinExist, Windows Security
+ {
+ WinActivate, Windows Security
+ WinWaitActive, Windows Security
+ Sleep 500
+ SendRaw %User%
+ Send {TAB}
+ SendRaw %Pass%
+ Send {ENTER}
+ Break
+ }
+ }
+ }
+ExitApp
+
+SaveCreds:
+ Gui, Destroy
+ InputBox, User, Password, Enter username to encrypt:
+ User := Code(User,Seed)
+ InputBox, Pass, Password, Enter password to encrypt:, Hide
+ Pass := Code(Pass,Seed)
+ node := settings.selectSingleNode("/hostMonitor/settings/userName")
+ node.text := User
+ SaveSettings(settings, tdoc)
+ node := settings.selectSingleNode("/hostMonitor/settings/userPass")
+ node.text := Pass
+ SaveSettings(settings, tdoc)
+ notEncrypted = 0
+ Run, % A_ScriptFullPath . " " . Host
+ExitApp
+
+GuiClose:
+ ExitApp
+
+ESC::ExitApp
+
+;<===== Functions ==============================================================>
+ListShares(Server, User, Pass) { ;Enumerate shares on a remote host
+ Global Count = 0
+ PropertyList := "Name"
+ wmiLocator := ComObjCreate("WbemScripting.SWbemLocator")
+ try
+ objWMIService := wmiLocator.ConnectServer(Server, "root\cimv2", User, Pass)
+ catch e
+ {
+ MsgBox % "Unable to connect to " Server "`,`n" User " does not have permission.`n`n" e.Extra " Error Message:`n" e.Message
+ ExitApp
+ }
+ WQLQuery = Select * From Win32_Share
+ colDiskDrive := objWMIService.ExecQuery(WQLQuery)._NewEnum
+ While colDiskDrive[objDiskDrive]
+ Loop, Parse, PropertyList, `,
+ {
+ Count ++
+ Shares .= A_index = 1 ? objDiskDrive[A_LoopField] . "|" : . A_LoopField
+ }
+ StringTrimRight, Shares, Shares, 1
+ Return, Shares
+}
+
+Code(String, Seed) { ;Encrypt a string using Mersenne Twister
+ Random,, Seed
+ Loop, Parse, String
+ {
+ Random x, 1, 1000000
+ Random y, 1, 1000000
+ newString .= (Asc(A_loopfield)+x) y
+ }
+ return newString
+}
+
+Uncode(String, Seed) { ;Decrept a Mersenne Twister encrypted string
+ Random,, Seed
+ while StrLen(String)>0
+ {
+ Random x, 1, 1000000
+ Random y, 1, 1000000
+ Pos := InStr(String, y)
+ oldString .= Chr(SubStr(String, 1, Pos-1)-x)
+ String := SubStr(String, Pos+StrLen(y))
+ }
+ return oldString
+}
+
+LoadXML(file){
+ xmlFile := fileOpen(file, "r")
+ xml := xmlFile.read()
+ xmlFile.Close()
+ doc := ComObjCreate("MSXML2.DOMdocument.6.0")
+ doc.async := false
+ if !doc.loadXML(xml)
+ {
+ MsgBox, % "Could not load" . file . "!"
+ }
+ return doc
+}
+
+SaveSettings(settings, tdoc){
+ try {
+ resultDoc := ComObjCreate("MSXML2.DOMdocument.6.0")
+ settings.transformNodeToObject(tdoc, resultDoc)
+ resultDoc.save(A_ScriptDir . "\settings.xml")
+ ObjRelease(Object(resultDoc))
+ } catch e {
+ MsgBox, % "Could not save settings.`n" . e . "`n" . e.description
+ return 0
+ }
+ return 1
+}
diff --git a/inc/defaultSettings.xml b/inc/defaultSettings.xml
index dd5822d..b7a807e 100644
--- a/inc/defaultSettings.xml
+++ b/inc/defaultSettings.xml
@@ -12,5 +12,8 @@
10
60
+ 1
+
+
diff --git a/inc/settings.xml b/inc/settings.xml
index bf2a7bf..921b59a 100644
--- a/inc/settings.xml
+++ b/inc/settings.xml
@@ -1,16 +1,19 @@
-
-
-
- 10
- 250
- 60
- 10
- 0
- 1
- 1
- 10
- 10
- 30
-
-
-
+
+
+
+ 10
+ 250
+ 60
+ 10
+ 0
+ 0
+ 1
+ 10
+ 10
+ 50
+
+ 1
+
+
+
+