How to Launch a Program at Windows Startup with Visible GUI When User Logs On


1 views

When configuring a Windows service or scheduled task to run at system startup, we often encounter the limitation that GUI applications started before user login won't automatically display their interface upon subsequent user login. This creates usability issues for administrative tools that require interactive interfaces.

The standard Task Scheduler configuration using "Run whether user is logged on or not" executes the process in session 0, making the GUI inaccessible to user sessions. While service-based solutions exist, they're often overkill for simple GUI applications.

Here's a PowerShell script solution that monitors session changes and shows the window when a user logs in:


# Save as ShowOnLogon.ps1
$programPath = "C:\Path\To\Your\App.exe"
$processName = [System.IO.Path]::GetFileNameWithoutExtension($programPath)

# Check if process already running in session 0
$existingProcess = Get-Process -Name $processName -ErrorAction SilentlyContinue
if ($existingProcess) {
    $sessionId = $existingProcess.SessionId
    if ($sessionId -eq 0) {
        # Move to current session when user logs in
        $newSessionId = [System.Diagnostics.Process]::GetCurrentProcess().SessionId
        $handle = [System.Diagnostics.Process]::GetProcessById($existingProcess.Id).Handle
        $result = [Win32]::WTSEnumerateSessions([IntPtr]::Zero, 0, 1, [ref]$sessions, [ref]$count)
        # Window display logic would go here
    }
} else {
    Start-Process -FilePath $programPath
}

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
    [DllImport("wtsapi32.dll", SetLastError=true)]
    public static extern bool WTSEnumerateSessions(
        IntPtr hServer,
        int Reserved,
        int Version,
        ref IntPtr ppSessionInfo,
        ref int pCount);
}
"@

For more robust solutions, consider creating a Windows service that communicates with a GUI component via named pipes:


// Service Code (C#)
using (var pipeServer = new NamedPipeServerStream("MyAppPipe", PipeDirection.InOut))
{
    pipeServer.WaitForConnection();
    // Communication logic here
}

// GUI Client Code
using (var pipeClient = new NamedPipeClientStream(".", "MyAppPipe", PipeDirection.InOut))
{
    pipeClient.Connect();
    // Window display logic
}

For simpler cases, you can combine registry startup entries with session detection:


Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"MyApp"="\"C:\\Path\\To\\Your\\App.exe\" /minimized"

Then in your application code, detect session changes and show the window when appropriate.

Remember that each approach has security implications - especially when dealing with cross-session operations. The PowerShell solution offers the best balance of simplicity and functionality for most use cases, while the service-based approach provides more robustness for critical applications.


When configuring a program to launch at system startup on Windows Server 2012 using Task Scheduler with the "Run whether user is logged on or not" option, administrators face a significant limitation: the program's GUI remains hidden even when a user subsequently logs in via Remote Desktop Connection.

The fundamental issue stems from how Windows handles session isolation. Programs launched under the "Run whether user is logged on or not" setting execute in session 0 (non-interactive session), while user logins occur in session 1 (or higher). This architecture was implemented for security reasons but creates visibility problems for GUI applications.

Here are three practical approaches to resolve this:

Method 1: Registry Run Key with Session Awareness

Create a startup script that checks for user sessions:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"MyAppLauncher"="wscript.exe \"C:\\scripts\\launchapp.vbs\""

Then create launchapp.vbs:

Set WshShell = WScript.CreateObject("WScript.Shell")
appPath = "C:\path\to\your\app.exe"

' Check if any user is logged in
While True
    Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
    Set colItems = objWMIService.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
    
    If colItems.Count > 0 Then
        ' User is logged in, launch visible
        WshShell.Run appPath, 1, False
        Exit While
    Else
        ' No user logged in, wait and check again
        WScript.Sleep 10000
    End If
Wend

Method 2: Hybrid Task Scheduler Approach

Combine Task Scheduler with a session detection mechanism:

# PowerShell script (Save as Start-MyApp.ps1)
$appPath = "C:\path\to\your\app.exe"
$taskName = "MyApp Startup Trigger"

# Create scheduled task for system startup
$action = New-ScheduledTaskAction -Execute $appPath
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries

Register-ScheduledTask -Action $action -Trigger $trigger -Settings $settings 
    -TaskName $taskName -User "SYSTEM" -RunLevel Highest -Force

# Add logon trigger to show window if hidden
$logonTrigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -Action $action -Trigger $logonTrigger 
    -TaskName "$taskName - Logon" -User $env:USERNAME -RunLevel Highest

Method 3: Windows Service with GUI

For .NET applications, consider creating a Windows Service that can interact with the desktop:

using System;
using System.ServiceProcess;
using System.Diagnostics;

namespace MyAppService
{
    public partial class MyAppService : ServiceBase
    {
        private Process _appProcess;
        
        public MyAppService()
        {
            InitializeComponent();
            this.CanHandleSessionChangeEvent = true;
        }
        
        protected override void OnStart(string[] args)
        {
            StartApplication();
        }
        
        protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            if (changeDescription.Reason == SessionChangeReason.SessionLogon)
            {
                if (_appProcess != null && !_appProcess.HasExited)
                {
                    // Bring window to foreground (requires additional P/Invoke)
                    ShowWindow(_appProcess.MainWindowHandle, SW_RESTORE);
                }
            }
        }
        
        private void StartApplication()
        {
            _appProcess = new Process();
            _appProcess.StartInfo.FileName = @"C:\path\to\your\app.exe";
            _appProcess.Start();
        }
        
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        
        private const int SW_RESTORE = 9;
    }
}

Regardless of which method you choose, be aware of these critical factors:

  • Session 0 Isolation: Modern Windows versions strictly separate services and user applications
  • UAC Implications: Elevated privileges may be required for certain operations
  • Remote Desktop Behavior: The solution must account for both console and RDP sessions
  • Security Context: Ensure the solution doesn't create security vulnerabilities

For most scenarios, Method 2 (Hybrid Task Scheduler Approach) provides the best balance of reliability and maintainability. It leverages built-in Windows mechanisms while providing the required GUI visibility upon user login.