LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

在System身份運行的.NET程序中以指定的用戶身份啟動可交互式進程

freeflydom
2025年5月17日 10:2 本文熱度 47

今天在技術(shù)群里,石頭哥向大家提了個問題:"如何在一個以System身份運行的.NET程序(Windows Services)中,以其它活動的用戶身份啟動可交互式進程(桌面應(yīng)用程序、控制臺程序、等帶有UI和交互式體驗的程序)"?

我以前有過類似的需求,是在GitLab流水線中運行帶有UI的自動化測試程序

其中流水線是GitLab Runner執(zhí)行的,而GitLab Runner則被注冊為Windows服務(wù),以System身份啟動的。

然后我在流水線里,巴拉巴拉寫了一大串PowerShell腳本代碼,通過調(diào)用任務(wù)計劃程序?qū)崿F(xiàn)了這個需求

但我沒試過在C#里實現(xiàn)這個功能。

對此,我很感興趣,于是著手研究,最終搗鼓出來了。

二話不多說,上代碼:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace AllenCai.Windows
{
    /// <summary>
    /// 進程工具類
    /// </summary>
#if NET5_0_OR_GREATER
    [SupportedOSPlatform("windows")]
#endif
    public static class ProcessUtils
    {
        /// <summary>
        /// 在當(dāng)前活動的用戶會話中啟動進程
        /// </summary>
        /// <param name="fileName">程序名稱或程序路徑</param>
        /// <param name="commandLine">命令行參數(shù)</param>
        /// <param name="workDir">工作目錄</param>
        /// <param name="noWindow">是否無窗口</param>
        /// <param name="minimize">是否最小化</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ApplicationException"></exception>
        /// <exception cref="Win32Exception"></exception>
        public static int StartProcessAsActiveUser(string fileName, string commandLine = null, string workDir = null, bool noWindow = false, bool minimize = false)
        {
            if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName));
            // 獲取當(dāng)前活動的控制臺會話ID和安全的用戶訪問令牌
            IntPtr userToken = GetSessionUserToken();
            if (userToken == IntPtr.Zero)
                throw new ApplicationException("Failed to get user token for the active session.");
            IntPtr duplicateToken = IntPtr.Zero;
            IntPtr environmentBlock = IntPtr.Zero;
            try
            {
                // 復(fù)制令牌
                SecurityAttributes sa = new SecurityAttributes();
                sa.Length = Marshal.SizeOf(sa);
                if (!DuplicateTokenEx(userToken, MAXIMUM_ALLOWED, ref sa, SecurityImpersonationLevel.SecurityIdentification, TokenType.TokenPrimary, out duplicateToken))
                    throw new ApplicationException("Could not duplicate token.");
                // 創(chuàng)建環(huán)境塊(檢索該用戶的環(huán)境變量)
                if (!CreateEnvironmentBlock(out environmentBlock, duplicateToken, false))
                    throw new ApplicationException("Could not create environment block.");
                bool theCommandIsInPath;
                // 如果文件名不包含路徑分隔符,則嘗試先在workDir參數(shù)中查找。如果找不到,再在指定用戶會話的PATH環(huán)境變量中查找。如果還是找不到,則拋出異常
                if ((!fileName.Contains('/') && !fileName.Contains('\\')))
                {
                    if (!string.IsNullOrEmpty(workDir))
                    {
                        if (File.Exists(Path.Combine(workDir, fileName)))
                        {
                            // 在指定的工作目錄中找到可執(zhí)行命令文件
                            theCommandIsInPath = false;
                        }
                        else
                        {
                            // 在指定的工作目錄(workDir)中找不到可執(zhí)行命令文件,再在指定用戶會話的PATH環(huán)境變量中查找。如果還是找不到,則拋出異常
                            if (!InPathOfSpecificUserEnvironment(in duplicateToken, in environmentBlock, fileName))
                            {
                                throw new ApplicationException($"The file '{fileName}' was not found in the specified directory '{workDir}' or in the PATH environment variable.");
                            }
                            else
                            {
                                // 在指定用戶會話的PATH環(huán)境變量中找到可執(zhí)行命令文件
                                theCommandIsInPath = true;
                            }
                        }
                    }
                    else
                    {
                        // 在指定用戶會話的PATH環(huán)境變量中查找,如果找不到,則拋出異常
                        if (!InPathOfSpecificUserEnvironment(in duplicateToken, in environmentBlock, fileName))
                        {
                            throw new ApplicationException($"The file '{fileName}' was not found in the PATH environment variable.");
                        }
                        // 在指定用戶會話的PATH環(huán)境變量中找到可執(zhí)行命令文件
                        theCommandIsInPath = true;
                    }
                }
                else
                {
                    theCommandIsInPath = false;
                }
                string file;
                if (!theCommandIsInPath && !Path.IsPathRooted(fileName))
                {
                    file = !string.IsNullOrEmpty(workDir) ? Path.GetFullPath(Path.Combine(workDir, fileName)) : Path.GetFullPath(fileName);
                }
                else
                {
                    file = fileName;
                }
                if (string.IsNullOrWhiteSpace(workDir)) workDir = theCommandIsInPath ? Environment.CurrentDirectory : Path.GetDirectoryName(file);
                if (string.IsNullOrWhiteSpace(commandLine)) commandLine = "";
                // 啟動信息
                ProcessStartInfo psi = new ProcessStartInfo
                {
                    UseShellExecute = true,
                    FileName = $"{file} {commandLine}", //解決帶參數(shù)的進程起不來或者起來的進程沒有參數(shù)的問題
                    Arguments = commandLine,
                    WorkingDirectory = workDir,
                    RedirectStandardError = false,
                    RedirectStandardOutput = false,
                    RedirectStandardInput = false,
                    CreateNoWindow = noWindow,
                    WindowStyle = minimize ? ProcessWindowStyle.Minimized : ProcessWindowStyle.Normal
                };
                // 在指定的用戶會話中創(chuàng)建進程
                SecurityAttributes saProcessAttributes = new SecurityAttributes();
                SecurityAttributes saThreadAttributes = new SecurityAttributes();
                CreateProcessFlags createProcessFlags = (noWindow ? CreateProcessFlags.CREATE_NO_WINDOW : CreateProcessFlags.CREATE_NEW_CONSOLE) | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT;
                bool success = CreateProcessAsUser(duplicateToken, null, $"{file} {commandLine}", ref saProcessAttributes, ref saThreadAttributes, false, createProcessFlags, environmentBlock, null, ref psi, out ProcessInformation pi);
                if (!success)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                    //throw new ApplicationException("Could not create process as user.");
                }
                return pi.dwProcessId;
            }
            finally
            {
                // 清理資源
                if (userToken != IntPtr.Zero) CloseHandle(userToken);
                if (duplicateToken != IntPtr.Zero) CloseHandle(duplicateToken);
                if (environmentBlock != IntPtr.Zero) DestroyEnvironmentBlock(environmentBlock);
            }
        }
        /// <summary>
        /// 使用win32api實現(xiàn)在指定用戶身份的環(huán)境變量中查找命令(command參數(shù))是否存在。
        /// </summary>
        private static bool InPathOfSpecificUserEnvironment(in IntPtr userToken, in IntPtr environmentBlock, in string command)
        {
            // 在指定用戶會話環(huán)境中執(zhí)行命令,并且獲得控制臺標(biāo)準(zhǔn)輸出內(nèi)容
            string commandLine = $"cmd.exe /c chcp 65001 && where {command}";
            string output = ExecuteCommandAsUserAndReturnStdOutput(userToken, environmentBlock, commandLine, Encoding.UTF8);
            // OperatingSystem.IsOSPlatform("WINDOWS") 該方法僅在 .NET Core及以上版本可用,在 .NET Standard 和 .NET Framework 中不可用。
            // 現(xiàn)有操作系統(tǒng)中,Windows 操作系統(tǒng)的目錄分隔符為 '\',而 Unix 操作系統(tǒng)的目錄分隔符為 '/',因此可以用它來判斷和區(qū)分操作系統(tǒng)。
            // 如果是Windows操作系統(tǒng),則不區(qū)分大小寫
            var comparison = Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
            return output.IndexOf(command, comparison) >= 0;
        }
        /// <summary>
        /// 在指定用戶會話環(huán)境中執(zhí)行命令,并且返回控制臺標(biāo)準(zhǔn)輸出內(nèi)容
        /// </summary>
        private static string ExecuteCommandAsUserAndReturnStdOutput(in IntPtr userToken, in IntPtr environmentBlock, string commandLine, Encoding encoding)
        {
            // 創(chuàng)建匿名管道
            var saPipeAttributes = new SecurityAttributes();
            saPipeAttributes.Length = Marshal.SizeOf(saPipeAttributes);
            saPipeAttributes.InheritHandle = true; // 允許句柄被繼承
                                                   //saPipeAttributes.SecurityDescriptor = IntPtr.Zero;
            if (!CreatePipe(out IntPtr readPipe, out IntPtr writePipe, ref saPipeAttributes, 0))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            // 確保管道句柄有效
            if (readPipe == IntPtr.Zero)
            {
                throw new InvalidOperationException("Failed to create read pipe.");
            }
            if (writePipe == IntPtr.Zero)
            {
                throw new InvalidOperationException("Failed to create write pipe.");
            }
            try
            {
                // 確保讀取句柄不被子進程繼承
                SetHandleInformation(readPipe, 0x00000001/*HANDLE_FLAG_INHERIT*/, 0);
                var startInfo = new StartupInfo();
                startInfo.cb = Marshal.SizeOf(startInfo);
                // 設(shè)置子進程的標(biāo)準(zhǔn)輸出為管道的寫入端
                startInfo.hStdError = writePipe;
                startInfo.hStdOutput = writePipe;
                startInfo.dwFlags = StartupInfoFlags.STARTF_USESTDHANDLES;
                // 在用戶會話中創(chuàng)建進程
                const CreateProcessFlags createProcessFlags = CreateProcessFlags.CREATE_NEW_CONSOLE | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT;
                var success = CreateProcessAsUser(
                    userToken,
                    null,
                    commandLine,
                    ref saPipeAttributes,
                    ref saPipeAttributes,
                    true,
                    createProcessFlags,
                    environmentBlock,
                    null,
                    ref startInfo,
                    out ProcessInformation pi);
                if (!success)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                // 關(guān)閉管道的寫入端句柄,因為它已經(jīng)被子進程繼承
                CloseHandle(writePipe);
                writePipe = IntPtr.Zero;
                // 從管道的讀取端讀取數(shù)據(jù)
                string output;
                using (var streamReader = new StreamReader(new FileStream(new SafeFileHandle(readPipe, true), FileAccess.Read, 4096, false), encoding))
                {
                    // 讀取控制臺標(biāo)準(zhǔn)輸出內(nèi)容
                    output = streamReader.ReadToEnd();
                    Trace.WriteLine($"The commandLine [{commandLine}] std output -> {output}");
                }
                // 關(guān)閉進程和線程句柄
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
                // 返回控制臺標(biāo)準(zhǔn)輸出內(nèi)容
                return output;
            }
            finally
            {
                if (readPipe != IntPtr.Zero) CloseHandle(readPipe);
                if (writePipe != IntPtr.Zero) CloseHandle(writePipe);
            }
        }
        /// <summary>
        /// 獲取活動會話的用戶訪問令牌
        /// </summary>
        /// <exception cref="Win32Exception"></exception>
        private static IntPtr GetSessionUserToken()
        {
            // 獲取當(dāng)前活動的控制臺會話ID
            uint sessionId = WTSGetActiveConsoleSessionId();
            // 獲取活動會話的用戶訪問令牌
            bool success = WTSQueryUserToken(sessionId, out IntPtr hToken);
            // 如果失敗,則從會話列表中獲取第一個活動的會話ID,并再次嘗試獲取用戶訪問令牌
            if (!success)
            {
                sessionId = GetFirstActiveSessionOfEnumerateSessions();
                success = WTSQueryUserToken(sessionId, out hToken);
                if (!success)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            return hToken;
        }
        /// <summary>
        /// 枚舉所有用戶會話,獲取第一個活動的會話ID
        /// </summary>
        private static uint GetFirstActiveSessionOfEnumerateSessions()
        {
            IntPtr pSessionInfo = IntPtr.Zero;
            try
            {
                int sessionCount = 0;
                // 枚舉所有用戶會話
                if (WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
                {
                    int arrayElementSize = Marshal.SizeOf(typeof(WtsSessionInfo));
                    IntPtr current = pSessionInfo;
                    for (int i = 0; i < sessionCount; i++)
                    {
                        WtsSessionInfo si = (WtsSessionInfo)Marshal.PtrToStructure(current, typeof(WtsSessionInfo));
                        current += arrayElementSize;
                        if (si.State == WtsConnectStateClass.WTSActive)
                        {
                            return si.SessionID;
                        }
                    }
                }
                return uint.MaxValue;
            }
            finally
            {
                WTSFreeMemory(pSessionInfo);
                CloseHandle(pSessionInfo);
            }
        }
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SecurityAttributes lpProcessAttributes, ref SecurityAttributes lpThreadAttributes, bool bInheritHandles, CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref StartupInfo lpStartupInfo, out ProcessInformation lpProcessInformation);
        /// <summary>
        /// 以指定用戶的身份啟動進程
        /// </summary>
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CreateProcessAsUser(
            IntPtr hToken,
            string lpApplicationName,
            string lpCommandLine,
            ref SecurityAttributes lpProcessAttributes,
            ref SecurityAttributes lpThreadAttributes,
            bool bInheritHandles,
            CreateProcessFlags dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref ProcessStartInfo lpStartupInfo,
            out ProcessInformation lpProcessInformation);
        /// <summary>
        /// 獲取當(dāng)前活動的控制臺會話ID
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern uint WTSGetActiveConsoleSessionId();
        /// <summary>
        /// 枚舉所有用戶會話
        /// </summary>
        [DllImport("wtsapi32.dll", SetLastError = true)]
        private static extern int WTSEnumerateSessions(IntPtr hServer, int reserved, int version, ref IntPtr ppSessionInfo, ref int pCount);
        /// <summary>
        /// 獲取活動會話的用戶訪問令牌
        /// </summary>
        [DllImport("wtsapi32.dll", SetLastError = true)]
        private static extern bool WTSQueryUserToken(uint sessionId, out IntPtr phToken);
        /// <summary>
        /// 復(fù)制訪問令牌
        /// </summary>
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, ref SecurityAttributes lpTokenAttributes, SecurityImpersonationLevel impersonationLevel, TokenType tokenType, out IntPtr phNewToken);
        /// <summary>
        /// 創(chuàng)建環(huán)境塊(檢索指定用戶的環(huán)境)
        /// </summary>
        [DllImport("userenv.dll", SetLastError = true)]
        private static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
        /// <summary>
        /// 釋放環(huán)境塊
        /// </summary>
        [DllImport("userenv.dll", SetLastError = true)]
        private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SecurityAttributes lpPipeAttributes, uint nSize);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);
        [DllImport("wtsapi32.dll", SetLastError = false)]
        private static extern void WTSFreeMemory(IntPtr memory);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr hObject);
        [StructLayout(LayoutKind.Sequential)]
        private struct WtsSessionInfo
        {
            public readonly uint SessionID;
            [MarshalAs(UnmanagedType.LPStr)]
            public readonly string pWinStationName;
            public readonly WtsConnectStateClass State;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct SecurityAttributes
        {
            public int Length;
            public IntPtr SecurityDescriptor;
            public bool InheritHandle;
        }
        [StructLayout(LayoutKind.Sequential)]
        private 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 StartupInfoFlags dwFlags;
            public UInt16 wShowWindow;
            public UInt16 cbReserved2;
            public unsafe byte* lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct ProcessInformation
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }
        private const uint TOKEN_DUPLICATE = 0x0002;
        private const uint MAXIMUM_ALLOWED = 0x2000000;
        /// <summary>
        /// Process Creation Flags。<br/>
        /// More:https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
        /// </summary>
        [Flags]
        private enum CreateProcessFlags : uint
        {
            DEBUG_PROCESS = 0x00000001,
            DEBUG_ONLY_THIS_PROCESS = 0x00000002,
            CREATE_SUSPENDED = 0x00000004,
            DETACHED_PROCESS = 0x00000008,
            /// <summary>
            /// The new process has a new console, instead of inheriting its parent's console (the default). For more information, see Creation of a Console. <br />
            /// This flag cannot be used with <see cref="DETACHED_PROCESS"/>.
            /// </summary>
            CREATE_NEW_CONSOLE = 0x00000010,
            NORMAL_PRIORITY_CLASS = 0x00000020,
            IDLE_PRIORITY_CLASS = 0x00000040,
            HIGH_PRIORITY_CLASS = 0x00000080,
            REALTIME_PRIORITY_CLASS = 0x00000100,
            CREATE_NEW_PROCESS_GROUP = 0x00000200,
            /// <summary>
            /// If this flag is set, the environment block pointed to by lpEnvironment uses Unicode characters. Otherwise, the environment block uses ANSI characters.
            /// </summary>
            CREATE_UNICODE_ENVIRONMENT = 0x00000400,
            CREATE_SEPARATE_WOW_VDM = 0x00000800,
            CREATE_SHARED_WOW_VDM = 0x00001000,
            CREATE_FORCEDOS = 0x00002000,
            BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
            ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
            INHERIT_PARENT_AFFINITY = 0x00010000,
            INHERIT_CALLER_PRIORITY = 0x00020000,
            CREATE_PROTECTED_PROCESS = 0x00040000,
            EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
            PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000,
            PROCESS_MODE_BACKGROUND_END = 0x00200000,
            CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
            CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
            CREATE_DEFAULT_ERROR_MODE = 0x04000000,
            /// <summary>
            /// The process is a console application that is being run without a console window. Therefore, the console handle for the application is not set. <br />
            /// This flag is ignored if the application is not a console application, or if it is used with either <see cref="CREATE_NEW_CONSOLE"/> or <see cref="DETACHED_PROCESS"/>.
            /// </summary>
            CREATE_NO_WINDOW = 0x08000000,
            PROFILE_USER = 0x10000000,
            PROFILE_KERNEL = 0x20000000,
            PROFILE_SERVER = 0x40000000,
            CREATE_IGNORE_SYSTEM_DEFAULT = 0x80000000,
        }
        /// <summary>
        /// 指定創(chuàng)建進程時的窗口工作站、桌面、標(biāo)準(zhǔn)句柄和main窗口的外觀。<br/>
        /// More:https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
        /// </summary>
        [Flags]
        private enum StartupInfoFlags : uint
        {
            /// <summary>
            /// 強制反饋光標(biāo)顯示,即使用戶沒有啟用。
            /// </summary>
            STARTF_FORCEONFEEDBACK = 0x00000040,
            /// <summary>
            /// 強制反饋光標(biāo)不顯示,即使用戶啟用了它。
            /// </summary>
            STARTF_FORCEOFFFEEDBACK = 0x00000080,
            /// <summary>
            /// 防止應(yīng)用程序被固定在任務(wù)欄或開始菜單。
            /// </summary>
            STARTF_PREVENTPINNING = 0x00002000,
            /// <summary>
            /// 不再支持,原用于強制控制臺應(yīng)用程序全屏運行。
            /// </summary>
            STARTF_RUNFULLSCREEN = 0x00000020,
            /// <summary>
            /// lpTitle成員是一個AppUserModelID。
            /// </summary>
            STARTF_TITLEISAPPID = 0x00001000,
            /// <summary>
            /// lpTitle成員是一個鏈接名。
            /// </summary>
            STARTF_TITLEISLINKNAME = 0x00000800,
            /// <summary>
            /// 啟動程序來自不受信任的源,可能會顯示警告。
            /// </summary>
            STARTF_UNTRUSTEDSOURCE = 0x00008000,
            /// <summary>
            /// 使用dwXCountChars和dwYCountChars成員。
            /// </summary>
            STARTF_USECOUNTCHARS = 0x00000008,
            /// <summary>
            /// 使用dwFillAttribute成員。
            /// </summary>
            STARTF_USEFILLATTRIBUTE = 0x00000010,
            /// <summary>
            /// 使用hStdInput成員指定熱鍵。
            /// </summary>
            STARTF_USEHOTKEY = 0x00000200,
            /// <summary>
            /// 使用dwX和dwY成員。
            /// </summary>
            STARTF_USEPOSITION = 0x00000004,
            /// <summary>
            /// 使用wShowWindow成員。
            /// </summary>
            STARTF_USESHOWWINDOW = 0x00000001,
            /// <summary>
            /// 使用dwXSize和dwYSize成員。
            /// </summary>
            STARTF_USESIZE = 0x00000002,
            /// <summary>
            /// 使用hStdInput、hStdOutput和hStdError成員。
            /// </summary>
            STARTF_USESTDHANDLES = 0x00000100
        }
        private enum WtsConnectStateClass
        {
            WTSActive,
            WTSConnected,
            WTSConnectQuery,
            WTSShadow,
            WTSDisconnected,
            WTSIdle,
            WTSListen,
            WTSReset,
            WTSDown,
            WTSInit
        }
        private enum SecurityImpersonationLevel
        {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }
        private enum TokenType
        {
            TokenPrimary = 1,
            TokenImpersonation
        }
    }
}

用法:

ProcessUtils.StartProcessAsActiveUser("ping.exe", "www.baidu.com -t");
ProcessUtils.StartProcessAsActiveUser("notepad.exe");
ProcessUtils.StartProcessAsActiveUser("C:\\Windows\\System32\\notepad.exe");

在 Windows 7~11Windows Server 2016~2022 操作系統(tǒng),測試通過。

轉(zhuǎn)自https://www.cnblogs.com/VAllen/p/18257879


該文章在 2025/5/17 10:03:00 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務(wù)費用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務(wù)都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
亚洲人成影院在线高清 | 最新国产乱了真实在线观看 | 亚洲福利在线看国产精品 | 午夜福利亚洲精品无遮挡 | 日韩欧美中文字幕一区二区 | 亚洲香蕉久久一区 |