Jump to content

Recommended Posts

Posted (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 :D
Thanks for your help!

Edited by sl23
Posted

Using a tray menu:
 

; 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

 

Posted (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 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

 

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...