sl23 Posted August 30 Posted August 30 (edited) I set up a script to create symlinks on launching a specified app and removing the symlink on app closure. The trouble is that it freezes the entire AutoIT program so I can't set that app to only run as single instance or even launch any other apps, while that one is running. I have found a workaround by using ProcessExists, but that doesn't seem to work for monitoring if the app is open or not. Btw, my AutoIT program is an app launcher, hence the issue. If anyone could point out some way to be able to monitor the app to remove the symlink without freezing the AutoIT program, that would be great Thanks for your help! Edited August 30 by sl23
Gianni Posted August 30 Posted August 30 How do you launch that app? Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
sl23 Posted August 30 Author Posted August 30 Using a tray menu: expandcollapse popup; TrayMenu.au3 ; Builds and manages tray menu structure, refresh logic, supports cross-INI variable expansion in Arguments/SetEnvN ; Now supports absolute and relative paths directly, no Utils.au3 needed for path logic. #include-once #include "Sandboxie.au3" #include "Favorites.au3" #include "ScanFolders.au3" #include "SymLinks.au3" #include "Updates.au3" Global $g_TrayMenuTitle, $g_TrayReload, $g_TraySettings, $g_TrayGenLinks, $g_TraySeparator1, $g_TraySeparator2, $g_TraySeparator3, $g_TrayExit Global $g_CategoryMenus = ObjCreate("Scripting.Dictionary") Global $g_TrayItemMap = ObjCreate("Scripting.Dictionary") Global $g_TrayScan Global $g_TrayCreateGlobalLinks, $g_TrayRemoveGlobalLinks Global $g_TrayCheckUpdates ; <-- ADDED: Menu item for update check Local $globalIni = _ResolvePath("App\Settings.ini", @ScriptDir) ; --- Path Handling Functions (embedded here) --- Func _PathIsAbsolute($path) ; Returns True if path is absolute (starts with drive letter or UNC) Return StringRegExp($path, "^[A-Za-z]:\\|^\\\\") EndFunc Func _ResolvePath($path, $baseDir) ; Accepts both absolute and relative paths If _PathIsAbsolute($path) Then Return $path Else Return $baseDir & "\" & $path EndIf EndFunc Func _GetFileName($path) Local $parts = StringSplit($path, "\", 2) Return $parts[UBound($parts)-1] EndFunc Func _GetAppCategory($appName, $categories) For $catName In $categories.Keys Local $catApps = $categories.Item($catName) If $catApps.Exists($appName) Then Return $catName EndIf Next Return "" EndFunc Func _GetFolderName($path) Local $parts = StringSplit($path, "\", 2) If UBound($parts) > 0 Then Return $parts[UBound($parts)-1] Else Return $path EndIf EndFunc ; --- Helper: Resolve cross-INI variable value --- Func _TrayMenu_ResolveEnv($envValue) If StringInStr($envValue, "|") Then Local $parts = StringSplit($envValue, "|") If $parts[0] = 3 Then Local $iniFile = _ResolvePath("App\" & $parts[1] & ".ini", @ScriptDir) Local $section = $parts[2] Local $key = $parts[3] If FileExists($iniFile) Then Local $val = IniRead($iniFile, $section, $key, "__NOT_FOUND__") If $val <> "__NOT_FOUND__" Then Return $val Else MsgBox(16, "Tray Launcher Error", _ "INI found: " & $iniFile & @CRLF & _ "Section or key NOT found: [" & $section & "] " & $key) EndIf Else MsgBox(16, "Tray Launcher Error", _ "INI file NOT found: " & $iniFile) EndIf Return "" EndIf MsgBox(16, "Tray Launcher Error", "Malformed SetEnv value: " & $envValue) Return "" Else Return $envValue EndIf EndFunc ; Create global symlinks on startup --- _SymLink_CreateGlobalSymlinks($globalIni) Func _TrayMenu_Build(ByRef $categories, ByRef $apps, ByRef $settings) TrayItemDelete(0) $g_TrayItemMap.RemoveAll() $g_CategoryMenus.RemoveAll() ; 1. Utility submenu $g_TrayMenuTitle = TrayCreateMenu("Axiom Menu") $g_TrayReload = TrayCreateItem("🔄 Reload Menu", $g_TrayMenuTitle) $g_TraySettings = TrayCreateItem("⚙️ Settings", $g_TrayMenuTitle) $g_TrayGenLinks = TrayCreateItem("🔗 Generate Links", $g_TrayMenuTitle) $g_TrayScan = TrayCreateItem("🧐 Scan", $g_TrayMenuTitle) ; --- Add buttons for global symlink management --- $g_TrayCreateGlobalLinks = TrayCreateItem("➕ Create Global Symlinks", $g_TrayMenuTitle) $g_TrayRemoveGlobalLinks = TrayCreateItem("➖ Remove Global Symlinks", $g_TrayMenuTitle) ; --- Add Check for Updates button --- $g_TrayCheckUpdates = TrayCreateItem("🔍 Check for Updates", $g_TrayMenuTitle) ; ADDED ; --- Determine what to show --- Local $hasCategories = False For $catName In $categories If $catName = "Fave" Then ContinueLoop If $catName = "Other" Then If Not $apps.Exists("Other") Or UBound($apps.Item("Other")) = 0 Then ContinueLoop EndIf If $apps.Exists($catName) Then Local $arr = $apps.Item($catName) If IsArray($arr) And UBound($arr) > 0 Then $hasCategories = True ExitLoop EndIf EndIf Next Local $hasFaves = False If $apps.Exists("Fave") Then Local $faveArr = $apps.Item("Fave") If IsArray($faveArr) And UBound($faveArr) > 0 Then $hasFaves = True EndIf EndIf ; --- Separator1: only if categories --- If $hasCategories Then $g_TraySeparator1 = TrayCreateItem("") EndIf ; 2. Category submenus (skip Fave and empty Other) For $catName In $categories If $catName = "Fave" Then ContinueLoop If $catName = "Other" Then If Not $apps.Exists("Other") Or UBound($apps.Item("Other")) = 0 Then ContinueLoop EndIf If $apps.Exists($catName) Then Local $arr = $apps.Item($catName) If IsArray($arr) And UBound($arr) > 0 Then Local $catMenu = TrayCreateMenu("📁 " & $catName) $g_CategoryMenus.Item($catName) = $catMenu For $i = 0 To UBound($arr) - 1 Local $appName = $arr[$i][0] Local $buttonText = $arr[$i][1] Local $appItem = TrayCreateItem($buttonText, $catMenu) $g_TrayItemMap.Item($appName) = $appItem Next EndIf EndIf Next ; --- Separator2 logic --- If $hasFaves Then $g_TraySeparator2 = TrayCreateItem("") EndIf ; 3. Fave flat list If $hasFaves Then Local $faveArr = $apps.Item("Fave") For $i = 0 To UBound($faveArr) - 1 Local $appName = $faveArr[$i] Local $catName = _GetAppCategory($appName, $categories) Local $catIni = _ResolvePath("App\" & $catName & ".ini", @ScriptDir) Local $buttonText = IniRead($catIni, $appName, "ButtonText", $appName) Local $faveItem = TrayCreateItem($buttonText) $g_TrayItemMap.Item($appName) = $faveItem Next EndIf ; --- Separator3: ALWAYS before Exit --- $g_TraySeparator3 = TrayCreateItem("") $g_TrayExit = TrayCreateItem("❌ Exit") EndFunc Func _TrayMenu_RefreshFull() If @Compiled Then Run(@ScriptFullPath) Else Run(@AutoItExe & ' "' & @ScriptFullPath & '"') EndIf Exit EndFunc Func TrayUI_Destroy() For $appName In $g_TrayItemMap.Keys TrayItemDelete($g_TrayItemMap.Item($appName)) Next For $catName In $g_CategoryMenus.Keys TrayItemDelete($g_CategoryMenus.Item($catName)) Next If IsDeclared("g_TrayMenuTitle") And $g_TrayMenuTitle Then TrayItemDelete($g_TrayMenuTitle) If IsDeclared("g_TrayReload") And $g_TrayReload Then TrayItemDelete($g_TrayReload) If IsDeclared("g_TraySettings") And $g_TraySettings Then TrayItemDelete($g_TraySettings) If IsDeclared("g_TrayGenLinks") And $g_TrayGenLinks Then TrayItemDelete($g_TrayGenLinks) If IsDeclared("g_TrayScan") And $g_TrayScan Then TrayItemDelete($g_TrayScan) If IsDeclared("g_TrayCreateGlobalLinks") And $g_TrayCreateGlobalLinks Then TrayItemDelete($g_TrayCreateGlobalLinks) If IsDeclared("g_TrayRemoveGlobalLinks") And $g_TrayRemoveGlobalLinks Then TrayItemDelete($g_TrayRemoveGlobalLinks) If IsDeclared("g_TraySeparator1") And $g_TraySeparator1 Then TrayItemDelete($g_TraySeparator1) If IsDeclared("g_TraySeparator2") And $g_TraySeparator2 Then TrayItemDelete($g_TraySeparator2) If IsDeclared("g_TraySeparator3") And $g_TraySeparator3 Then TrayItemDelete($g_TraySeparator3) If IsDeclared("g_TrayExit") And $g_TrayExit Then TrayItemDelete($g_TrayExit) If IsDeclared("g_TrayCheckUpdates") And $g_TrayCheckUpdates Then TrayItemDelete($g_TrayCheckUpdates) ; ADDED $g_TrayItemMap.RemoveAll() $g_CategoryMenus.RemoveAll() EndFunc Func _TrayMenu_AppLauncher($appName, $catIni) ; --- Read all app variables --- Local $keys = IniReadSection($catIni, $appName) Local $vars = ObjCreate("Scripting.Dictionary") If IsArray($keys) Then For $i = 1 To UBound($keys) - 1 Local $k = $keys[$i][0] Local $v = $keys[$i][1] If StringRegExp($k, "^SetEnv\d+$") Then $v = _TrayMenu_ResolveEnv($v) EndIf $vars.Item($k) = $v Next EndIf ; --- Get Arguments and substitute variables --- Local $argsRaw = "" If $vars.Exists("Arguments") Then $argsRaw = $vars.Item("Arguments") Local $args = $argsRaw Local $matches = StringRegExp($argsRaw, "%([^%]+)%", 3) If IsArray($matches) Then For $i = 0 To UBound($matches) - 1 Local $vn = $matches[$i] If $vars.Exists($vn) Then $args = StringReplace($args, "%" & $vn & "%", $vars.Item($vn)) EndIf Next EndIf ; --- Get executable (prefer SetEnv1, fallback to RunFile) --- Local $exe = "" If $vars.Exists("SetEnv1") And $vars.Item("SetEnv1") <> "" Then $exe = $vars.Item("SetEnv1") ElseIf $vars.Exists("RunFile") And $vars.Item("RunFile") <> "" Then $exe = $vars.Item("RunFile") EndIf $exe = _ResolvePath($exe, @ScriptDir) ; --- Determine workDir --- Local $workDir = "" If $vars.Exists("WorkDir") And $vars.Item("WorkDir") <> "" Then $workDir = _ResolvePath($vars.Item("WorkDir"), @ScriptDir) Else Local $lastSlash = StringInStr($exe, "\", 0, -1) If $lastSlash > 0 Then $workDir = StringLeft($exe, $lastSlash - 1) Else $workDir = @ScriptDir EndIf EndIf ; --- Check exe is not a folder and is a valid executable --- Local $ext = StringLower(StringRight($exe, 4)) If $exe = "" _ Or Not FileExists($exe) _ Or ($ext <> ".exe" And $ext <> ".bat" And $ext <> ".cmd") Then MsgBox(16, $appName & " Launch Error", _ "Executable not found or not a valid exe/batch/cmd file:" & @CRLF & $exe) Return EndIf ; --- SingleInstance logic (per app via INI) --- Local $exeName = StringRegExpReplace($exe, "^.*\\", "") Local $singleInstance = 0 If $vars.Exists("SingleInstance") Then $singleInstance = Number($vars.Item("SingleInstance")) EndIf If $singleInstance = 1 Then Local $isRunning = ProcessExists($exeName) If $isRunning Then MsgBox(64, $appName & " Already Running", _ "There is an instance of this app/file open already.") Return EndIf EndIf ; --- Create per-app/category symlinks BEFORE launch --- _SymLink_CreateAppSymlinks($catIni, $appName, $globalIni) ; --- RunAsAdmin logic (per app via INI) --- Local $runAsAdmin = "0" If $vars.Exists("RunAsAdmin") Then $runAsAdmin = $vars.Item("RunAsAdmin") EndIf ; --- Sandboxie logic (per app via INI) --- Local $sandboxie = "0", $sandboxName = "" If $vars.Exists("Sandboxie") Then $sandboxie = $vars.Item("Sandboxie") If $vars.Exists("SandboxName") Then $sandboxName = $vars.Item("SandboxName") Local $settingsIni = @ScriptDir & "\App\Settings.ini" ; --- Strip off executable path if present at start of Arguments --- If StringLeft($args, StringLen($exe)) = $exe Then $args = StringTrimLeft($args, StringLen($exe)) $args = StringStripWS($args, 1) Else Local $exeQuoted = '"' & $exe & '"' If StringLeft($args, StringLen($exeQuoted)) = $exeQuoted Then $args = StringTrimLeft($args, StringLen($exeQuoted)) $args = StringStripWS($args, 1) EndIf EndIf ; --- Launch --- Local $appExited = False If $sandboxie = "1" And $sandboxName <> "" Then Local $pid = _RunWithSandboxie($exe, $args, $workDir, $sandboxName, $settingsIni) If $pid <> 0 Then ProcessWaitClose($pid) $appExited = True EndIf ElseIf $runAsAdmin = "1" Then ShellExecute($exe, $args, $workDir, "runas") ; Can't reliably detect process exit for ShellExecute/runas $appExited = False Else Local $pid = Run('"' & $exe & '" ' & $args, $workDir) ; Do NOT block with ProcessWaitClose, tray must stay responsive! ; $appExited = False ; If you need to do cleanup, handle it elsewhere (e.g., tray exit or background watcher) EndIf ; --- Remove per-app/category symlinks after app closes --- If $appExited Then _SymLink_RemoveAppSymlinks($catIni, $appName, $globalIni) EndIf EndFunc Func _TrayMenu_HandleEvents(ByRef $settings, ByRef $categories, ByRef $apps) Local $msg = TrayGetMsg() Switch $msg Case $g_TrayReload _TrayMenu_RefreshFull() Case $g_TrayGenLinks _Shortcuts_GenerateLinks($categories, $apps, $settings) Case $g_TraySettings ShowSettingsGUI() Case $g_TrayScan _ScanFolders_AndShowResults($settings) Case $g_TrayCreateGlobalLinks Local $globalIni = _ResolvePath("App\Settings.ini", @ScriptDir) _SymLink_ManualCreateGlobalSymlinks($globalIni) MsgBox(64, "Global Symlinks", "Global symlinks created.") Case $g_TrayRemoveGlobalLinks Local $globalIni = _ResolvePath("App\Settings.ini", @ScriptDir) _SymLink_ManualRemoveGlobalSymlinks($globalIni) MsgBox(64, "Global Symlinks", "Global symlinks removed.") Case $g_TrayCheckUpdates Updates_Check(True) Case $g_TrayExit _SymLink_TrayExitCleanup() _SymLink_RemoveGlobalSymlinks($globalIni) Exit Case Else For $appName In $g_TrayItemMap.Keys If $msg = $g_TrayItemMap.Item($appName) Then Local $catName = _GetAppCategory($appName, $categories) Local $catIni = _ResolvePath("App\" & $catName & ".ini", @ScriptDir) If Not FileExists($catIni) Then MsgBox(16, "INI Error", "INI file not found: " & $catIni) ExitLoop EndIf Local $keys = IniReadSection($catIni, $appName) If Not IsArray($keys) Then MsgBox(16, "INI Error", "Section [" & $appName & "] not found in " & $catIni) ExitLoop EndIf _TrayMenu_AppLauncher($appName, $catIni) ExitLoop EndIf Next EndSwitch EndFunc
orbs Posted August 30 Posted August 30 (edited) 10 hours ago, sl23 said: ProcessExists, but that doesn't seem to work for monitoring if the app is open or not. you'd have to troubleshoot that. ProcessExists() or WinExists() are your options. obviously you would not want to use any of the *Wait* functions, because, well, they wait. you do use ProcessExists() in your singleton check, i assume that part works correctly? if so, why does it not work - and what do you mean by not work - for the quote above? EDIT: i see your script has the function to restart itself, and while doing so, lose any prior indication of an app being launched or not. therefore using the PID with ProcessExists() is moot. you have to either positively identify the process by other means - executable name and command line arguments combination come to mind - or use WinExists() to positively identify the window, probably by title. i don't know how your apps look like or behave, but in general i'd go first with WinExists(). Edited August 30 by orbs Signature - my forum contributions: Spoiler UDF: LFN - support for long file names (over 260 characters) InputImpose - impose valid characters in an input control TimeConvert - convert UTC to/from local time and/or reformat the string representation AMF - accept multiple files from Windows Explorer context menu DateDuration - literal description of the difference between given dates WinPose - simultaneous fluent move and resize Apps: Touch - set the "modified" timestamp of a file to current time Show For Files - tray menu to show/hide files extensions, hidden & system files, and selection checkboxes SPDiff - Single-Pane Text Diff Magic Math - a math puzzle Demos: Title Bar Menu - click the window title to pop-up a menu
sl23 Posted Sunday at 03:35 PM Author Posted Sunday at 03:35 PM ok, so I tried several different ways of doing this but I can't get it to work. It's just becoming a mess! Rather than post my messy code. I may well start again with this if I can get some foundation in. But to do that, I need to know how would I go about monitoring an app without the monitoring blocking other parts of the script. The idea is that, I use a tray menu to launch an app. When the app is launched, it creates symlinks. The app is monitored in some way, so that when closed, it then removes those symlinks, but while the app is open, it is blocking usage of the tray menu. So could someone tell me, how do I achieve that without it blocking the tray menu? Thanks for your help.
mlibre2 Posted Sunday at 04:28 PM Posted Sunday at 04:28 PM To prevent the main function from blocking "pause", an asynchronous method should be used, since when calling a second function, the first one waits for the second to finish. myLogin 🛡️
argumentum Posted Sunday at 07:41 PM Posted Sunday at 07:41 PM 3 hours ago, sl23 said: So could someone tell me, how do I achieve that without it blocking the tray menu? I told you about IPC already... . Oh, I've got it !. Check for process exist every so often, and if is not there, process closed. Keep the processes and steps ( do this(1), then that (2) ) in an array. PS: ...when my grandparents used to tell me that I was a genius for setting their VCR for the show, they may have been right ... do miss the cookies ... Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
ioa747 Posted Monday at 08:40 AM Posted Monday at 08:40 AM On 8/30/2025 at 9:31 AM, sl23 said: I set up a script to create symlinks on launching a specified app and removing the symlink on app closure. with _Timer_SetTimer() that will check every e.g.: 1000ms , if the application is running. If it is running it will do nothing, if it is not running it will remove the links example: expandcollapse popup#include <Timers.au3> #include <GUIConstantsEx.au3> #include <Array.au3> HotKeySet("{ESC}", "_Exit") ; exit HotKeySet("{HOME}", "_Display") ; Display $aApps array HotKeySet("{END}", "_NewApp") ; Run one more notepad (as new example app) Global $MyAppsDir = @ScriptDir & "\MyApps" Global $aApps[0][2] ; We need a GUI to make AutoIt timers work, as the Timers.au3 library is linked to GUI messages. Global $hGUI = GUICreate("My Timer need one GUI", 100, 100) GUISetState(@SW_HIDE) ; set timer to check every nnn Local $i_TimerInterval = 1000 _Timer_SetTimer($hGUI, $i_TimerInterval, "_TimerCheck") ; Run some example apps _RunApp("notepad") _RunApp("notepad") _RunApp("notepad") ; show result of fake symlink ShellExecute($MyAppsDir) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd _Exit() Func _RunApp($sName) ShellExecute($sName) Sleep(200) Local $hWnd = WinGetHandle("[ACTIVE]") ConsoleWrite("$hWnd=" & $hWnd & @CRLF) If Not FileExists($MyAppsDir) Then DirCreate($MyAppsDir) Local $sPath = $MyAppsDir & "\" & $hWnd & ".txt" Local $sFill = $hWnd & "|" & $sPath _ArrayAdd($aApps, $sFill) ; fake symlink FileWrite($sPath, $hWnd) EndFunc ;==>_RunApp ; Timer call back function Func _TimerCheck($hWnd, $iMsg, $iIDTimer, $iTime) #forceref $hWnd, $iMsg, $iIDTimer, $iTime If IsArray($aApps) Then Local $sRange = "" For $i = 0 To UBound($aApps) - 1 If Not WinExists(HWnd($aApps[$i][0])) Then ConsoleWrite("found:" & $aApps[$i][0] & @CRLF) FileDelete($aApps[$i][1]) $sRange &= $i & ";" EndIf Next If $sRange <> "" Then $sRange = StringTrimRight($sRange, 1) ConsoleWrite("$sRange=" & $sRange & @CRLF) _ArrayDelete($aApps, $sRange) If @error Then ConsoleWrite("_ArrayDelete @error:" & @error & @CRLF) EndIf EndIf EndFunc ;==>_TimerCheck ; --- HotKeySet functions --- Func _NewApp() _RunApp("notepad") EndFunc ;==>_NewApp Func _Exit() ConsoleWrite("Killed All Timers? " & _Timer_KillAllTimers($hGUI) & @CRLF) GUIDelete($hGUI) DirRemove($MyAppsDir, 1) Exit EndFunc ;==>_Exit Func _Display() If WinExists("$aApps") Then WinClose("$aApps") Else _ArrayDisplay($aApps, "$aApps") EndIf EndFunc ;==>_Display I know that I know nothing
sl23 Posted Monday at 08:59 AM Author Posted Monday at 08:59 AM Wow, thanks for all the replies. i will look into those, when I get time! Appreciate the help.😉
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now