Help
  • Explore Community
  • Get Started
  • Ask the Community
  • How-To & Best Practices
  • Contact Support
Notifications
Login / Register
Community
Community
Notifications
close
  • Forums
  • Knowledge Center
  • Events & Webinars
  • Ideas
  • Blogs
Help
Help
  • Explore Community
  • Get Started
  • Ask the Community
  • How-To & Best Practices
  • Contact Support
Login / Register
Sustainability
Sustainability

We Value Your Feedback!
Could you please spare a few minutes to share your thoughts on Cloud Connected vs On-Premise Services. Your feedback can help us shape the future of services.
Learn more about the survey or Click here to Launch the survey
Schneider Electric Services Innovation Team!

[FYI] - How to run executable in user session from service session 0

APC UPS Data Center & Enterprise Solutions Forum

Schneider, APC support forum to share knowledge about installation and configuration for Data Center and Business Power UPSs, Accessories, Software, Services.

cancel
Turn on suggestions
Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.
Showing results for 
Show  only  | Search instead for 
Did you mean: 
  • Home
  • Schneider Electric Community
  • APC UPS, Critical Power, Cooling and Racks
  • APC UPS Data Center & Enterprise Solutions Forum
  • [FYI] - How to run executable in user session from service session 0
Options
  • Mark Topic as New
  • Mark Topic as Read
  • Float this Topic for Current User
  • Bookmark
  • Subscribe
  • Mute
  • Printer Friendly Page
Invite a Co-worker
Send a co-worker an invite to the portal.Just enter their email address and we'll connect them to register. After joining, they will belong to the same company.
You have entered an invalid email address. Please re-enter the email address.
This co-worker has already been invited to the Exchange portal. Please invite another co-worker.
Please enter email address
Send Invite Cancel
Invitation Sent
Your invitation was sent.Thanks for sharing Exchange with your co-worker.
Send New Invite Close
Top Experts
User Count
BillP
Administrator BillP Administrator
5060
voidstar_apc
Janeway voidstar_apc
196
Erasmus_apc
Sisko Erasmus_apc
112
Teken
Spock Teken
111
View All

Invite a Colleague

Found this content useful? Share it with a Colleague!

Invite a Colleague Invite
Back to APC UPS Data Center & Enterprise Solutions Forum
Anonymous user
Not applicable

Posted: ‎2021-06-29 05:56 AM . Last Modified: ‎2024-03-13 12:16 AM

0 Likes
0
4269
  • Mark as New
  • Bookmark
  • Subscribe
  • Mute
  • Permalink
  • Print
  • Email to a Friend
  • Report Inappropriate Content

Link copied. Please paste this link to share this article on your social media post.

Posted: ‎2021-06-29 05:56 AM . Last Modified: ‎2024-03-13 12:16 AM

[FYI] - How to run executable in user session from service session 0

Ladies and Gentleman,

I hereby post a solution that will make it possible to run a program in a user session. This might come in handy when you want to interact with your desktop before shutting down your computer by the UPS.

There are 2 files required, which are a .cmd file and a .ps1 file. (.ps1 = powershell script file)(In this example I will use the names calculator.cmd and calculator.ps1)

1. CMD file

The Powerchute software supports the functionality to execute a .cmd file before shutting down the computer. From this file we are going to execute the Powershell script file.

Type/Copy the following code:

@echo OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%calculator.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%PowerShellScriptPath%""' -Verb RunAs}";

Store and save the file at the cmdfiles location.

2. Powershell script file

The powershell script is going to execute the calculator program with the current session ID of the logged in user.

Side note: Microsoft prevented interaction from a service with programs running on the desktop of a user. This has been done from Windows Vista and higher. When shutting down your PC you want to shutdown certain programs, due to session 0 isolation this has be come impossible (well nearly :P).

What is this file going to do? It is going to check if the 'explorer.exe' is running and if so it is going to retrieve its token. Then the script is going to execute your command in the user session. (I know there is more to it but I don't want to explain every little detail because I want you to read/study *cough* copy the script).

$source = @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;

public class ApplicationLoader
{
    #region Structures

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int Length;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct STARTUPINFO
    {
        public int cb;
        public String lpReserved;
        public String lpDesktop;
        public String lpTitle;
        public uint dwX;
        public uint dwY;
        public uint dwXSize;
        public uint dwYSize;
        public uint dwXCountChars;
        public uint dwYCountChars;
        public uint dwFillAttribute;
        public uint dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;
    }

    enum TOKEN_TYPE : int
    {
        TokenPrimary = 1,
        TokenImpersonation = 2
    }

    enum SECURITY_IMPERSONATION_LEVEL : int
    {
        SecurityAnonymous = 0,
        SecurityIdentification = 1,
        SecurityImpersonation = 2,
        SecurityDelegation = 3,
    }

    public const int TOKEN_DUPLICATE = 0x0002;
    public const uint MAXIMUM_ALLOWED = 0x2000000;
    public const int CREATE_NEW_CONSOLE = 0x00000010;

    public const int IDLE_PRIORITY_CLASS = 0x40;
    public const int NORMAL_PRIORITY_CLASS = 0x20;
    public const int HIGH_PRIORITY_CLASS = 0x80;
    public const int REALTIME_PRIORITY_CLASS = 0x100;

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hSnapshot);

    [DllImport("kernel32.dll")]
    static extern uint WTSGetActiveConsoleSessionId();

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
        ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
        String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("kernel32.dll")]
    static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
    public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
        int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

    [DllImport("kernel32.dll")]
    static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

    [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity]
    static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

    ///


    /// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
    ///

    ///
The name of the application to launch
    /// Process information regarding the launched application that gets returned to the caller
    ///
    public static bool StartProcessAndBypassUAC(String applicationName, string startingDir, out PROCESS_INFORMATION procInfo)
    {
        uint winlogonPid = 0;
        IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
        procInfo = new PROCESS_INFORMATION();

        // obtain the currently active session id; every logged on user in the system has a unique session id
        uint dwSessionId = WTSGetActiveConsoleSessionId();

        // obtain the process id of the winlogon process that is running within the currently active session
        // -- chaged by ty
        // Process[] processes = Process.GetProcessesByName("winlogon");
        Process[] processes = Process.GetProcessesByName("explorer");
        foreach (Process p in processes)
        {
            if ((uint)p.SessionId == dwSessionId)
            {
                winlogonPid = (uint)p.Id;
            }
        }

        // obtain a handle to the winlogon process
        hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

        // obtain a handle to the access token of the winlogon process
        if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
        {
            CloseHandle(hProcess);
            return false;
        }

        // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
        // I would prefer to not have to use a security attribute variable and to just
        // simply pass null and inherit (by default) the security attributes
        // of the existing token. However, in C# structures are value types and therefore
        // cannot be assigned the null value.
        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
        sa.Length = Marshal.SizeOf(sa);

        // copy the access token of the winlogon process; the newly created token will be a primary token
        if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
        {
            CloseHandle(hProcess);
            CloseHandle(hPToken);
            return false;
        }

        // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
        // the window station has a desktop that is invisible and the process is incapable of receiving
        // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
        // interaction with the new process.
        STARTUPINFO si = new STARTUPINFO();
        si.cb = (int)Marshal.SizeOf(si);
        si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

        // flags that specify the priority and creation method of the process
        int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

        // create a new process in the current user's logon session
        bool result = CreateProcessAsUser(hUserTokenDup,        // client's access token
                                        null,                   // file to execute
                                        applicationName,        // command line
                                        ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                        ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                        false,                  // handles are not inheritable
                                        dwCreationFlags,        // creation flags
                                        IntPtr.Zero,            // pointer to new environment block
                                        startingDir,            // name of current directory
                                        ref si,                 // pointer to STARTUPINFO structure
                                        out procInfo            // receives information about new process
                                        );

        // invalidate the handles
        CloseHandle(hProcess);
        CloseHandle(hPToken);
        CloseHandle(hUserTokenDup);

        return result; // return the result
    }
    
    public void Execute(string applicationName)
    {
        ApplicationLoader.PROCESS_INFORMATION procInfo;
    
        ApplicationLoader.StartProcessAndBypassUAC(applicationName, null, out procInfo);
    }

}
"@

Add-Type -TypeDefinition $source
$obj = New-Object ApplicationLoader
$obj.Execute("calc.exe"); 
#<- Commands like C:\Utilities\program.exe Flag are also possible.

# If running in the console, wait for input before closing.
#if ($Host.Name -eq "ConsoleHost")
#{
#    Write-Host "Press any key to continue..."
#    $Host.UI.RawUI.FlushInputBuffer()   # Make sure buffered input doesn't "press a key" and skip the ReadKey().
#    $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
#

Save and store the file in the same directory as the .cmd file.

Hopefully, this helps you out. We contacted the tech support of APC for help and they told us they did not support the feature to shutdown programs. (This is in my opinion a HUGE FAILURE).

Good luck!!

Greetings,

Phanvi

Labels
  • Labels:
  • UPS Management Devices & PowerChute Software
  • Tags:
  • .cmd
  • apc
  • cmdlet
  • desktop
  • interaction
  • mylittlepony
  • PowerChute
  • programs
  • service
  • session0
  • shutdown
  • windows
Reply

Link copied. Please paste this link to share this article on your social media post.

  • All forum topics
  • Previous Topic
  • Next Topic
Replies 0
To The Top!

Forums

  • APC UPS Data Center Backup Solutions
  • EcoStruxure IT
  • EcoStruxure Geo SCADA Expert
  • Metering & Power Quality
  • Schneider Electric Wiser

Knowledge Center

Events & webinars

Ideas

Blogs

Get Started

  • Ask the Community
  • Community Guidelines
  • Community User Guide
  • How-To & Best Practice
  • Experts Leaderboard
  • Contact Support
Brand-Logo
Subscribing is a smart move!
You can subscribe to this board after you log in or create your free account.
Forum-Icon

Create your free account or log in to subscribe to the board - and gain access to more than 10,000+ support articles along with insights from experts and peers.

Register today for FREE

Register Now

Already have an account? Login

Terms & Conditions Privacy Notice Change your Cookie Settings © 2025 Schneider Electric

This is a heading

With achievable small steps, users progress and continually feel satisfaction in task accomplishment.

Usetiful Onboarding Checklist remembers the progress of every user, allowing them to take bite-sized journeys and continue where they left.

of