system启动普通权限UI应用

注意:启动传参,使用变量; 补充添加,传递上下文环境变量

参考: https://www.cnblogs.com/fatterbetter/p/4203094.html
注意传参的第一个参数为agr[0] 默认为启动路径 agr[1]才是真正参数,C函数
image.png

using System; 
using System.Runtime.InteropServices;

namespace LinseerCopilotServiceImpl.Services
{
    public class ProcessAsUser
    {
        public struct SECURITY_ATTRIBUTES
        {
            public uint nLength;
            public uint lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        public struct STARTUPINFO
        {
            public uint 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 ushort wShowWindow;
            public ushort cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;

        }

        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;

        }

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

        [DllImport("Wtsapi32.dll")]
        private static extern bool WTSQueryUserToken(uint SessionId, out uint hToken);

        [DllImport("Kernel32.dll")]
        private static extern uint GetLastError();

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

        [DllImport("advapi32.dll")]
        public extern static bool CreateProcessAsUser(IntPtr hToken,
            string lpApplicationName,
            string lpCommandLine,
            ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes,
            bool bInheritHandle,
            uint dwCreationFlags,
            uint lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);

        /// <summary>
        /// 从服务启动外部UI应用进程
        /// </summary>
        /// <remarks>服务是System权限,应用是普通权限</remarks>
        /// <param name="exePath"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static PROCESS_INFORMATION StartUIProcessFromService(string exePath)
        {
            //获取Session ID
            var sId = WTSGetActiveConsoleSessionId();
            H3CLog.Info($"WTSGetActiveConsoleSessionId:{sId}");
            if (sId == 0)
            {
                var errorCode = GetLastError();
                throw new Exception($"CreateProcessAsUser failed, WTSGetActiveConsoleSessionId failed with error code:{errorCode}.");
            }

            uint hToken;
            var isOk = WTSQueryUserToken(sId, out hToken);
            H3CLog.Info($"WTSQueryUserToken:{hToken}");
            if (!isOk || hToken == 0)
            {
                var errorCode = GetLastError();
                throw new Exception($"CreateProcessAsUser failed, WTSQueryUserToken failed with error code:{errorCode}.");
            }

            var lpProcessAttr = new SECURITY_ATTRIBUTES();
            lpProcessAttr.nLength = (uint)Marshal.SizeOf(lpProcessAttr);

            var lpThreadAttr = new SECURITY_ATTRIBUTES();
            lpThreadAttr.nLength = (uint)Marshal.SizeOf(lpThreadAttr);

            var lpStratupInfo = new STARTUPINFO();
            lpStratupInfo.cb = (uint)Marshal.SizeOf(lpStratupInfo);
            lpStratupInfo.lpDesktop = @"winsta0\default";

            PROCESS_INFORMATION lpProcessInfo;

            string arguments = "LinseerCopilotService";
            string commandLine = $"\"{exePath}\" {arguments}"; // 创建一个完整的命令行字符串
            isOk = CreateProcessAsUser((IntPtr)hToken, exePath, commandLine, ref lpProcessAttr, ref lpThreadAttr, false, 0, 0, null, ref lpStratupInfo, out lpProcessInfo);
            CloseHandle((IntPtr)hToken);
            if (!isOk)
            {
                var errorCode = GetLastError();
                throw new Exception($"CreateProcessAsUser failed with error code: {errorCode}");
            }

            return lpProcessInfo;
        }
    }
}
using System;
using System.IO;
using System.Diagnostics;
using System.Threading;
using LinseerCopilotServiceImpl.Services;
using Topshelf;
using System.Threading.Tasks;

namespace LinseerCopilotServiceImpl.Business
{

    internal class UiProcessManager : IServiceBusiness
    {
        #region Fields
        AutoResetEvent _resetEvent = new AutoResetEvent(false);
        private bool _isRunning = false;
        private readonly string LinseerCopilotAppName = "LinseerCopilot";

        #endregion

        #region Constructors
        public UiProcessManager()
        {

        }
        #endregion

        #region Private Methods

        private void ProcessAppStarting()
        {
            while (_isRunning)
            {
                _resetEvent.WaitOne();
                if (!_isRunning)
                {
                    break;
                }
                try
                {
                    StopExistApps();
                    string exePath = GetAppEntryPath();
                    var result = ProcessAsUser.StartUIProcessFromService(exePath);
                    H3CLog.Info($"Success to start {LinseerCopilotAppName},Process ID:{result.dwProcessId}");
                }
                catch (Exception ex)
                {
                    H3CLog.Error($"Fail to start {LinseerCopilotAppName}", ex);
                }
            }
        }

        private string GetAppEntryPath()
        {
            string processPath = Process.GetCurrentProcess().MainModule.FileName;
            if (string.IsNullOrEmpty(processPath))
            {
                throw new FileNotFoundException("ProcessPath is null or empty");
            }

            DirectoryInfo dirInfo = new FileInfo(processPath).Directory;
            if (dirInfo == null)
            {
                throw new DirectoryNotFoundException($"ProcessPath is not a directory, path:[{processPath}]");
            }

            var parentDir = dirInfo.Parent;
            if (parentDir == null)
            {
                throw new DirectoryNotFoundException($"ParentDir is null, path:[{processPath}]");
            }

            string exePath = Path.Combine(parentDir.FullName, "LinseerCopilot", "H3C.Entry.exe");
            if (!File.Exists(exePath))
            {
                throw new FileNotFoundException($"{LinseerCopilotAppName} exePath is null or file does not exist, exe path:[${exePath}]");
            }

            H3CLog.Info($"启动外部进程的路径: {exePath}");
            return exePath;
        }

        private void StopExistApps()
        {
            Process[] processes = Process.GetProcessesByName(LinseerCopilotAppName);
            foreach (var process in processes)
            {
                try
                {
                    H3CLog.Info($"Success to close {LinseerCopilotAppName},Process ID:{process.Id}");
                    process.Kill();
                }
                catch (Exception ex)
                {
                    H3CLog.Error($"Fail to stop {LinseerCopilotAppName}", ex);
                }
            }
        }

        private async Task RunAppAsync()
        {
            await Task.Run(() =>
            {
                _resetEvent.Set();
                //try
                //{
                //    StopApp();
                //    string exePath = GetAppEntryPath();
                //    var result = ProcessAsUser.StartUIProcessFromService(exePath);
                //    _systemControlAppProcessId = (int)result.dwProcessId;
                //    H3CLog.Info($"Success to start SystemControl,Process ID:{_systemControlAppProcessId}");
                //}
                //catch (Exception ex)
                //{
                //    H3CLog.Error("Fail to start SystemControl", ex);
                //}
            });
        }

        #endregion

        #region Public Methods

        public async Task StartAsync()
        {
            if (_isRunning) return;
            Thread thread = new Thread(ProcessAppStarting);
            thread.IsBackground = true;
            _isRunning = true;
            thread.Start();
            await RunAppAsync();
        }

        public async Task StopAsync()
        {
            _isRunning = false;
            _resetEvent.Set();
            await Task.CompletedTask;
        }

        public async Task PauseAsync()
        {
            await Task.CompletedTask;
        }

        public async Task ContinueAsync()
        {
            await Task.CompletedTask;
        }

        public async Task OnSessionChangedAsync(SessionChangedArguments session)
        {
            if (session.ReasonCode == SessionChangeReasonCode.SessionUnlock || session.ReasonCode == SessionChangeReasonCode.SessionLogon)
            {
                H3CLog.Info("Session changed, start app");
                await RunAppAsync();
            }
        }
        #endregion
    }
}

** 补充添加,传递上下文环境变量**
7mxgWeybSE.jpg

 public class ProcessAsUser
 {
     public struct SECURITY_ATTRIBUTES
     {
         public uint nLength;
         public IntPtr lpSecurityDescriptor;
         public bool bInheritHandle;
     }

     public struct STARTUPINFO
     {
         public uint 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 ushort wShowWindow;
         public ushort cbReserved2;
         public IntPtr lpReserved2;
         public IntPtr hStdInput;
         public IntPtr hStdOutput;
         public IntPtr hStdError;

     }

     public struct PROCESS_INFORMATION
     {
         public IntPtr hProcess;
         public IntPtr hThread;
         public uint dwProcessId;
         public uint dwThreadId;
     }

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

     [DllImport("Wtsapi32.dll")]
     private static extern bool WTSQueryUserToken(uint SessionId, out IntPtr hToken);

     [DllImport("Kernel32.dll")]
     private static extern uint GetLastError();

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

     [DllImport("advapi32.dll")]
     public extern static bool CreateProcessAsUser(IntPtr hToken,
         string lpApplicationName,
         string lpCommandLine,
         ref SECURITY_ATTRIBUTES lpProcessAttributes,
         ref SECURITY_ATTRIBUTES lpThreadAttributes,
         bool bInheritHandle,
         uint dwCreationFlags,
         IntPtr lpEnvironment,
         string lpCurrentDirectory,
         ref STARTUPINFO lpStartupInfo,
         out PROCESS_INFORMATION lpProcessInformation);


     [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
     private static extern bool CreateEnvironmentBlock(
         out IntPtr lpEnvironment,
         IntPtr hToken,
         bool bInherit);

     [DllImport("userenv.dll", SetLastError = true)]
     [return: MarshalAs(UnmanagedType.Bool)]
     private static extern bool DestroyEnvironmentBlock(
         IntPtr lpEnvironment);

     [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
     private static extern bool LoadUserProfile(
         IntPtr hToken,
         ref PROFILEINFO lpProfileInfo);
     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
     public struct PROFILEINFO
     {
         public int dwSize;
         public int dwFlags;
         [MarshalAs(UnmanagedType.LPTStr)]
         public string lpUserName;
         [MarshalAs(UnmanagedType.LPTStr)]
         public string lpProfilePath;
         [MarshalAs(UnmanagedType.LPTStr)]
         public string lpDefaultPath;
         [MarshalAs(UnmanagedType.LPTStr)]
         public string lpServerName;
         [MarshalAs(UnmanagedType.LPTStr)]
         public string lpPolicyPath;
         public IntPtr hProfile;
     }

     [DllImport("advapi32.dll", SetLastError = true)]
     [return: MarshalAs(UnmanagedType.Bool)]
     internal static extern bool DuplicateTokenEx(
         IntPtr hExistingToken,
         uint dwDesiredAccess,
         ref SECURITY_ATTRIBUTES lpThreadAttributes,
         Int32 ImpersonationLevel,
         Int32 dwTokenType,
         ref IntPtr phNewToken);

     internal const uint TOKEN_QUERY = 0x0008;
     internal const uint TOKEN_DUPLICATE = 0x0002;
     internal const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
     internal const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;

     internal enum SECURITY_IMPERSONATION_LEVEL
     {
         SecurityAnonymous,
         SecurityIdentification,
         SecurityImpersonation,
         SecurityDelegation
     }

     internal enum TOKEN_TYPE
     {
         TokenPrimary = 1,
         TokenImpersonation
     }

     /// <summary>
     /// 从服务启动外部UI应用进程
     /// </summary>
     /// <remarks>服务是System权限,应用是普通权限</remarks>
     /// <param name="exePath"></param>
     /// <returns></returns>
     /// <exception cref="Exception"></exception>
     public PROCESS_INFORMATION StartUiProcessFromService(string exePath)
     {
         //获取Session ID
         var sId = WTSGetActiveConsoleSessionId();
         H3CLog.Info($"WTSGetActiveConsoleSessionId:{sId}");
         if (sId == 0)
         {
             var errorCode = GetLastError();
             throw new Exception($"CreateProcessAsUser failed, WTSGetActiveConsoleSessionId failed with error code:{errorCode}.");
         }

         var isOk = WTSQueryUserToken(sId, out var hToken);
         H3CLog.Info($"WTSQueryUserToken:{hToken}");
         if (!isOk || hToken ==  IntPtr.Zero)
         {
             var errorCode = GetLastError();
             throw new Exception($"CreateProcessAsUser failed, WTSQueryUserToken failed with error code:{errorCode}.");
         }

         // 复制令牌
         IntPtr primaryToken = IntPtr.Zero;
         var securityAttrs = new SECURITY_ATTRIBUTES();
         securityAttrs.nLength = (uint)Marshal.SizeOf(securityAttrs);
         securityAttrs.bInheritHandle = false;
         securityAttrs.lpSecurityDescriptor = IntPtr.Zero;
         //var lpThreadAttr = new SECURITY_ATTRIBUTES();
         //lpThreadAttr.nLength = (uint)Marshal.SizeOf(lpThreadAttr);
         var lpStratupInfo = new STARTUPINFO();
         lpStratupInfo.cb = (uint)Marshal.SizeOf(lpStratupInfo);
         lpStratupInfo.lpDesktop = @"winsta0\default";

         bool retdup = DuplicateTokenEx(hToken, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY, ref securityAttrs,
             (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref primaryToken);
         if (!retdup)
         {
             var errorCode = GetLastError();
             H3CLog.Error($"DuplicateTokenEx failed with error code: {errorCode}");
             throw new Exception($"DuplicateTokenEx failed with error code: {errorCode}");
         }

         try
         {
             var isEnvironmentOk = CreateEnvironmentBlock(out var hEnv, primaryToken, false);
             if (!isEnvironmentOk)
             {
                 var errorCode = GetLastError();
                 H3CLog.Error($"CreateEnvironmentBlock failed with error code: {errorCode}");
                 throw new Exception($"CreateEnvironmentBlock failed with error code: {errorCode}");
             }
             try
             {
                 PROCESS_INFORMATION lpProcessInfo;
                 string arguments = "1 LinseerCopilotService";
                 isOk = CreateProcessAsUser(primaryToken, exePath, arguments, ref securityAttrs, ref securityAttrs, false, CREATE_UNICODE_ENVIRONMENT, hEnv, null, ref lpStratupInfo, out lpProcessInfo);
                 //isOk = CreateProcessAsUser(hToken, exePath, arguments, ref lpProcessAttr, ref lpThreadAttr, false, 0, 0, null, ref lpStratupInfo, out lpProcessInfo);
                 if (!isOk)
                 {
                     var errorCode = GetLastError();
                     throw new Exception($"CreateProcessAsUser failed with error code: {errorCode}");
                 }
                 return lpProcessInfo;
             }
             finally
             {
                 DestroyEnvironmentBlock(hEnv);
             }
         }
         finally
         {
             CloseHandle(primaryToken);
         }
     }
 }

再次补充:
传递工作空间,否者将继承调用的工作空间,在删除调用的工作空间(目录)时,如何被调用的应用在运行。将导致文件目录被占用,从而无法删除该目录。
针对上面的问题如何解决呢,只需要设置被调用的程序的工作空间即可

   string workDir = Path.GetDirectoryName(exePath) ?? string.Empty;

result = CreateProcessAsUser(primaryToken, exePath, args, ref securityAttrs, ref securityAttrs, false, CREATE_UNICODE_ENVIRONMENT, hEnv, workDir, ref startupInfo, out var lpProcessInfo);

posted on 2025-08-22 11:09  TanZhiWei  阅读(11)  评论(0)    收藏  举报