【问题标题】:Process.Start with different credentials with UAC onProcess.Start 使用 UAC 开启不同的凭据
【发布时间】:2023-04-10 11:20:01
【问题描述】:

我正在尝试使用 Process.Start 启动另一个进程,并在 UAC 开启的情况下以不同的凭据运行。我收到以下错误:

System.ComponentModel.Win32Exception: 登录失败:用户帐户 限制。可能的原因是 不允许使用空白密码,请登录 小时限制或政策 限制已在 System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo 开始信息)在 System.Diagnostics.Process.Start()
在 System.Diagnostics.Process.Start(ProcessStartInfo 启动信息)

如果我关闭 UAC,它可以正常工作。如果我在不指定用户名和密码的情况下启动该过程,它可以正常工作。以下是代码的副本:

            ProcessStartInfo processInfo = new ProcessStartInfo();
            processInfo.Verb = "runas";
            processInfo.FileName = command;
            processInfo.Arguments = parameters;
            processInfo.UserName = txtUserName.Text;
            processInfo.Password = password;
            processInfo.Domain = "myDomain";
            processInfo.UseShellExecute = false;
            processInfo.LoadUserProfile = true;

            Process.Start(processInfo);

我相信这与桌面会话有关,但不确定为什么在 UAC 关闭时它会起作用。

任何关于如何在不关闭 UAC、修改注册表或更改本地/组策略的情况下使其工作的想法将不胜感激。

谢谢

编辑

上面代码的进程是以管理员身份启动的:

      static void Main(string[] args)
      {
         //make sure we are running with admin privileges
         if (VerifyIsAdmin())
         {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyApp(args));
         }
      }

      private static bool VerifyIsAdmin()
      {
         WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
         bool isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
         if (!isAdmin)
         {
            ProcessStartInfo processInfo = new ProcessStartInfo();
            processInfo.Verb = "runas";
            processInfo.FileName = Application.ExecutablePath;
            Process.Start(processInfo);
         }
         return isAdmin;
      }

编辑

试过CreateProcessWithLogonW ...有同样的问题。

经过进一步搜索,似乎是需要智能卡登录(scforceoption)的组策略有问题。话虽如此,我仍然不明白为什么当 UAC 关闭时它会起作用。它仍然需要在登录时使用智能卡。

看起来我无法解决这个问题。任何见解/建议将不胜感激。

【问题讨论】:

    标签: c# .net windows-vista uac


    【解决方案1】:

    你必须做以下事情: 1. 获取谁已经登录并且这个用户有一个活跃的桌面 2. 获取该用户启动的进程句柄 3. 从这个过程中“窃取”凭证 4. 使用这些凭据启动进程

    以下代码应该可以解决问题:

    using System;
    using System.Text;
    using System.Security;
    using System.Management;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    
    namespace Win32
    {
        public class Win32API
        {
            #region WMI Constants
    
            private const String cstrScope = "root\\CIMV2";
            private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";
    
            #endregion
    
            #region Win32 API routines
    
            [StructLayout(LayoutKind.Sequential)]
            struct SECURITY_ATTRIBUTES
            {
                public Int32 Length;
                public IntPtr lpSecurityDescriptor;
                public Boolean bInheritHandle;
            }
    
            enum TOKEN_TYPE
            {
                TokenPrimary = 1,
                TokenImpersonation = 2
            }
    
            enum TOKEN_INFORMATION_CLASS
            {
                TokenUser = 1,
                TokenGroups,
                TokenPrivileges,
                TokenOwner,
                TokenPrimaryGroup,
                TokenDefaultDacl,
                TokenSource,
                TokenType,
                TokenImpersonationLevel,
                TokenStatistics,
                TokenRestrictedSids,
                TokenSessionId,
                TokenGroupsAndPrivileges,
                TokenSessionReference,
                TokenSandBoxInert,
                TokenAuditPolicy,
                TokenOrigin,
                MaxTokenInfoClass  // MaxTokenInfoClass should always be the last enum
            }
    
            [StructLayout(LayoutKind.Sequential)]
            struct STARTUPINFO
            {
                public Int32 cb;
                public String lpReserved;
                public String lpDesktop;
                public String lpTitle;
                public UInt32 dwX;
                public UInt32 dwY;
                public UInt32 dwXSize;
                public UInt32 dwYSize;
                public UInt32 dwXCountChars;
                public UInt32 dwYCountChars;
                public UInt32 dwFillAttribute;
                public UInt32 dwFlags;
                public short wShowWindow;
                public short cbReserved2;
                public IntPtr lpReserved2;
                public IntPtr hStdInput;
                public IntPtr hStdOutput;
                public IntPtr hStdError;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            struct PROCESS_INFORMATION
            {
                public IntPtr hProcess;
                public IntPtr hThread;
                public UInt32 dwProcessId;
                public UInt32 dwThreadId;
            }
    
            enum SECURITY_IMPERSONATION_LEVEL
            {
                SecurityAnonymous = 0,
                SecurityIdentification = 1,
                SecurityImpersonation = 2,
                SecurityDelegation = 3,
            }
    
            [StructLayout(LayoutKind.Sequential)]
            struct LUID
            {
                public Int32 LowPart;
                public Int32 HighPart;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            struct LUID_AND_ATRIBUTES
            {
                LUID Luid;
                Int32 Attributes;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            struct TOKEN_PRIVILEGES
            {
                public Int32 PrivilegeCount;
                //LUID_AND_ATRIBUTES
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
                public Int32[] Privileges;
            }
    
            const Int32 READ_CONTROL = 0x00020000;
    
            const Int32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
    
            const Int32 STANDARD_RIGHTS_READ = READ_CONTROL;
            const Int32 STANDARD_RIGHTS_WRITE = READ_CONTROL;
            const Int32 STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
    
            const Int32 STANDARD_RIGHTS_ALL = 0x001F0000;
    
            const Int32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
    
            const Int32 TOKEN_ASSIGN_PRIMARY = 0x0001;
            const Int32 TOKEN_DUPLICATE = 0x0002;
            const Int32 TOKEN_IMPERSONATE = 0x0004;
            const Int32 TOKEN_QUERY = 0x0008;
            const Int32 TOKEN_QUERY_SOURCE = 0x0010;
            const Int32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
            const Int32 TOKEN_ADJUST_GROUPS = 0x0040;
            const Int32 TOKEN_ADJUST_DEFAULT = 0x0080;
            const Int32 TOKEN_ADJUST_SESSIONID = 0x0100;
    
            const Int32 TOKEN_ALL_ACCESS_P = (
                STANDARD_RIGHTS_REQUIRED |
                TOKEN_ASSIGN_PRIMARY |
                TOKEN_DUPLICATE |
                TOKEN_IMPERSONATE |
                TOKEN_QUERY |
                TOKEN_QUERY_SOURCE |
                TOKEN_ADJUST_PRIVILEGES |
                TOKEN_ADJUST_GROUPS |
                TOKEN_ADJUST_DEFAULT);
    
            const Int32 TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
    
            const Int32 TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
    
    
            const Int32 TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
                                          TOKEN_ADJUST_PRIVILEGES |
                                          TOKEN_ADJUST_GROUPS |
                                          TOKEN_ADJUST_DEFAULT;
    
            const Int32 TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
    
            const UInt32 MAXIMUM_ALLOWED = 0x2000000;
    
            const Int32 CREATE_NEW_PROCESS_GROUP = 0x00000200;
            const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;
    
            const Int32 IDLE_PRIORITY_CLASS = 0x40;
            const Int32 NORMAL_PRIORITY_CLASS = 0x20;
            const Int32 HIGH_PRIORITY_CLASS = 0x80;
            const Int32 REALTIME_PRIORITY_CLASS = 0x100;
    
            const Int32 CREATE_NEW_CONSOLE = 0x00000010;
    
            const string SE_DEBUG_NAME = "SeDebugPrivilege";
            const string SE_RESTORE_NAME = "SeRestorePrivilege";
            const string SE_BACKUP_NAME = "SeBackupPrivilege";
    
            const Int32 SE_PRIVILEGE_ENABLED = 0x0002;
    
            const Int32 ERROR_NOT_ALL_ASSIGNED = 1300;
    
            [StructLayout(LayoutKind.Sequential)]
            struct PROCESSENTRY32
            {
                UInt32 dwSize;
                UInt32 cntUsage;
                UInt32 th32ProcessID;
                IntPtr th32DefaultHeapID;
                UInt32 th32ModuleID;
                UInt32 cntThreads;
                UInt32 th32ParentProcessID;
                Int32 pcPriClassBase;
                UInt32 dwFlags;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
                string szExeFile;
            }
    
            const UInt32 TH32CS_SNAPPROCESS = 0x00000002;
    
            const Int32 INVALID_HANDLE_VALUE = -1;
    
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern Boolean CloseHandle(IntPtr hSnapshot);
    
            [DllImport("kernel32.dll")]
            public static extern UInt32 WTSGetActiveConsoleSessionId();
    
            [DllImport("Wtsapi32.dll")]
            static extern UInt32 WTSQueryUserToken(UInt32 SessionId, ref IntPtr phToken);
    
            [DllImport("advapi32.dll", SetLastError = true)]
            static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
    
            [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            extern static Boolean CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
                ref SECURITY_ATTRIBUTES lpThreadAttributes, Boolean bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvironment,
                String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
    
            [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
            extern static Boolean DuplicateTokenEx(IntPtr ExistingTokenHandle, UInt32 dwDesiredAccess,
                ref SECURITY_ATTRIBUTES lpThreadAttributes, Int32 TokenType,
                Int32 ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
    
            [DllImport("kernel32.dll")]
            static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);
    
            [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
            static extern Boolean OpenProcessToken(IntPtr ProcessHandle, // handle to process
                                                Int32 DesiredAccess, // desired access to process
                                                ref IntPtr TokenHandle); // handle to open access token
    
            [DllImport("advapi32.dll", SetLastError = true)]
            static extern Boolean AdjustTokenPrivileges(IntPtr TokenHandle, Boolean DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
    
            [DllImport("advapi32.dll", SetLastError = true)]
            static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref UInt32 TokenInformation, UInt32 TokenInformationLength);
    
            [DllImport("userenv.dll", SetLastError = true)]
            static extern Boolean CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, Boolean bInherit);
    
            #endregion
    
            #region Methods
    
            /// <summary>
            /// Method returns name of the user that logged in on workstation
            /// </summary>
            public static String GetLoggedInUserName()
            {
                String userName = String.Empty;
    
                try
                {
                    ManagementObjectSearcher searcher =
                        new ManagementObjectSearcher(cstrScope, cstrLoggenInUser);
    
                    foreach (ManagementObject queryObj in searcher.Get())
                    {
                        userName = queryObj["UserName"].ToString();
                        break;
                    }
                }
                catch
                {
                    userName = String.Empty;
                }
    
                return userName;
            }
    
            /// <summary>
            /// Creates the process in the interactive desktop with credentials of the logged in user.
            /// </summary>
            public static Boolean CreateProcessAsUser(String commandLine, 
                String workingDirectory,
                String userAppName, 
                out StringBuilder output)
            {
                Boolean processStarted = false;
                output = new StringBuilder();
    
                try
                {
                    UInt32 dwSessionId = WTSGetActiveConsoleSessionId();
                    output.AppendLine(String.Format("Active console session Id: {0}", dwSessionId));
    
                    IntPtr hUserToken = IntPtr.Zero;
                    WTSQueryUserToken(dwSessionId, ref hUserToken);
    
                    if (hUserToken != IntPtr.Zero)
                    {
                        output.AppendLine(String.Format("WTSQueryUserToken() OK (hUserToken:{0})", hUserToken));
    
                        Process[] processes = Process.GetProcessesByName(userAppName);
    
                        if (processes.Length == 0)
                        {
                            output.AppendLine(String.Format("Application '{0}' can not be found in the running processes", userAppName));
                            return false;
                        }
    
                        Int32 userAppProcessId = -1;
    
                        for (Int32 k = 0; k < processes.Length; k++)
                        {
                            output.AppendLine(String.Format("Process: '{0}', PID: {1}, Handle: {2}, Session: {3}",
                                processes[k].ProcessName, processes[k].Id, processes[k].Handle, processes[k].SessionId));
    
                            if ((UInt32)processes[k].SessionId == dwSessionId)
                            {
                                userAppProcessId = processes[k].Id;
                            }
                        }
    
                        if (userAppProcessId == -1)
                        {
                            output.AppendLine(String.Format("Application '{0}' is not found in the processes of the current session", userAppName));
                            return false;
                        }
    
                        IntPtr hProcess = OpenProcess((Int32)MAXIMUM_ALLOWED, false, (UInt32)userAppProcessId);
    
                        IntPtr hPToken = IntPtr.Zero;
    
                        OpenProcessToken(hProcess,
                            TOKEN_ADJUST_PRIVILEGES
                            | TOKEN_QUERY
                            | TOKEN_DUPLICATE
                            | TOKEN_ASSIGN_PRIMARY
                            | TOKEN_ADJUST_SESSIONID
                            | TOKEN_READ
                            | TOKEN_WRITE,
                            ref hPToken);
    
                        if (hPToken != IntPtr.Zero)
                        {
                            output.AppendLine(String.Format("OpenProcessToken() OK (Token: {0})", hPToken));
    
                            LUID luid = new LUID();
    
                            if (LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
                            {
                                output.AppendLine(String.Format("LookupPrivilegeValue() OK (High: {0}, Low: {1})", luid.HighPart, luid.LowPart));
    
                                SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
                                sa.Length = Marshal.SizeOf(sa);
    
                                IntPtr hUserTokenDup = IntPtr.Zero;
                                DuplicateTokenEx(hPToken,
                                    (Int32)MAXIMUM_ALLOWED,
                                    ref sa,
                                    (Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                                    (Int32)TOKEN_TYPE.TokenPrimary,
                                    ref hUserTokenDup);
    
                                if (hUserTokenDup != IntPtr.Zero)
                                {
                                    output.AppendLine(String.Format("DuplicateTokenEx() OK (hToken: {0})", hUserTokenDup));
    
                                    TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
                                          {
                                              PrivilegeCount = 1, 
                                            Privileges = new Int32[3]
                                          };
    
                                    tp.Privileges[1] = luid.HighPart;
                                    tp.Privileges[0] = luid.LowPart;
                                    tp.Privileges[2] = SE_PRIVILEGE_ENABLED;
    
                                    //Adjust Token privilege
                                    if (SetTokenInformation(hUserTokenDup,
                                        TOKEN_INFORMATION_CLASS.TokenSessionId,
                                        ref dwSessionId,
                                        (UInt32)IntPtr.Size))
                                    {
                                        output.AppendLine(String.Format("SetTokenInformation() OK"));
    
                                        if (AdjustTokenPrivileges(hUserTokenDup,
                                            false, ref tp, Marshal.SizeOf(tp),
                                            IntPtr.Zero, IntPtr.Zero))
                                        {
                                            output.AppendLine("AdjustTokenPrivileges() OK");
    
                                            Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
    
                                            IntPtr pEnv = IntPtr.Zero;
                                            if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
                                            {
                                                dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
                                            }
                                            else
                                            {
                                                output.AppendLine(String.Format("CreateEnvironmentBlock() FAILED (Last Error: {0})", Marshal.GetLastWin32Error()));
                                                pEnv = IntPtr.Zero;
                                            }
    
                                            // Launch the process in the client's logon session.
                                            PROCESS_INFORMATION pi;
    
                                            STARTUPINFO si = new STARTUPINFO();
                                            si.cb = Marshal.SizeOf(si);
                                            si.lpDesktop = "winsta0\\default";
    
                                            output.AppendLine(String.Format("CreateProcess (Path:{0}, CurrDir:{1})", commandLine, workingDirectory));
    
                                            if (CreateProcessAsUser(hUserTokenDup,    // client's access token
                                                    null,                // file to execute
                                                    commandLine,        // command line
                                                    ref sa,                // pointer to process SECURITY_ATTRIBUTES
                                                    ref sa,                // pointer to thread SECURITY_ATTRIBUTES
                                                    false,                // handles are not inheritable
                                                    dwCreationFlags,    // creation flags
                                                    pEnv,                // pointer to new environment block 
                                                    workingDirectory,    // name of current directory 
                                                    ref si,                // pointer to STARTUPINFO structure
                                                    out pi                // receives information about new process
                                                ))
                                            {
                                                processStarted = true;
                                                output.AppendLine(String.Format("CreateProcessAsUser() OK (PID: {0})", pi.dwProcessId));
                                            }
                                            else
                                            {
                                                output.AppendLine(String.Format("CreateProcessAsUser() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                            }
                                        }
                                        else
                                        {
                                            output.AppendLine(String.Format("AdjustTokenPrivileges() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                        }
                                    }
                                    else
                                    {
                                        output.AppendLine(String.Format("SetTokenInformation() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                    }
    
                                    CloseHandle(hUserTokenDup);
                                }
                                else
                                {
                                    output.AppendLine(String.Format("DuplicateTokenEx() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                }
                            }
                            else
                            {
                                output.AppendLine(String.Format("LookupPrivilegeValue() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                            }
    
                            CloseHandle(hPToken);
                        }
                        else
                        {
                            output.AppendLine(String.Format("OpenProcessToken() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                        }
    
                        CloseHandle(hUserToken);
                    }
                    else
                    {
                        output.AppendLine(String.Format("WTSQueryUserToken failed: {0}", Marshal.GetLastWin32Error()));
                    }
    
                }
                catch (Exception ex)
                {
                    output.AppendLine("Exception occurred: " + ex.Message);
                }
    
                return processStarted;
            }
    
            #endregion
        }
    }
    

    用法:

    try
    {            
        StringBuilder output = new StringBuilder();
        if (!Win32API.CreateProcessAsUser("C:\\WINDOWS\\notepad.exe", "C:\\WINDOWS\\", "winlogon", out output))
             throw new Win32Exception(output.ToString());
        else
             throw new Win32Exception("Process RUN!!!");
    }
    catch (Win32Exception ex)
    {
        File.WriteAllText("c:\\hello!.txt", ex.Message + " " + ex.ErrorCode.ToString());
    }
    

    代码的功劳来自 rsdn.ru 的 Forcas

    【讨论】:

    • 谢谢尤金,但重点是我想在另一组凭据下启动该过程,而不是登录用户的凭据。类似于 xp 中的 runas 命令,不涉及 uac。
    • 这在 Windows 7 上不起作用,可能是因为那里没有 Winlogon.exe。另外,依赖文件或进程名称是一个非常讨厌的习惯。
    • 哦,拜托。这是一个示例,每个 Windows 安装都有记事本
    • 那个代码很奇怪。它首先使用 WTSQueryUserToken 获取令牌,但从不使用它。嗯?它会在目标会话中寻找 winlogon.exe,这是一种不好的做法,因为该进程可能在未来版本的 Windows 中不存在,并从中获取令牌 - 然后 然后 它会去设置会话目标会话的令牌的 ID。你好?令牌来自来自目标会话。它已经具有正确的会话 ID。
    • 如果您已经作为本地系统运行(如果不是,此代码将无法工作,因为它调用 WTSQueryUserToken),那么您只需在目标会话是复制您的 自己的 令牌并将会话 ID 设置为目标会话的 ID。如果您想以登录用户的身份在目标会话中运行代码,您可以只使用来自 WTSQueryUserToken 的令牌。无论哪种方式,都没有必要从另一个进程中窃取令牌。
    【解决方案2】:

    我发现了一些旧项目,我在下面使用了代码,并且当时可以正常工作(看起来与您的相似),也许它无济于事,但仍然值得一试:

    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.UseShellExecute = true;
    startInfo.WorkingDirectory = Environment.CurrentDirectory;
    startInfo.FileName = Application.ExecutablePath;
    startInfo.Verb = "runas";
    try
    {
        Process p = Process.Start(startInfo);
    }
    catch(System.ComponentModel.Win32Exception ex)
    {
        return;
    }
    

    【讨论】:

      【解决方案3】:

      看起来您并不孤单,而且您可能无法尝试这样做:

      http://www.icodefactory.com/lab/post/UAC-Revealed-7e-elevation-of-rights-from-NET-as-commonly-misunderstood.aspx

      这只是一个猜测,但也许您可以请求管理员权限,然后impersonate the user,然后最后在模拟的同时启动进程?

      编辑:您还可以尝试基于此页面上的评论的 PInvoke 用于在不同用户下启动进程: http://blogs.msdn.com/thottams/archive/2006/08/11/696013.aspx

      【讨论】:

      • 谢谢紫。看来我可能不得不放弃了。
      【解决方案4】:

      尝试为您要启动的其他进程添加清单文件。 Windows 将分析清单文件并在进程开始之前向您显示 UAC 对话框。

      这里是如何嵌入清单文件link1link2的信息

      HTH

      【讨论】:

      • 我有一个清单文件,我得到了 UAC 对话框,但它仍然可以让我在当前用户的凭据下运行。不过感谢您的回复。
      • 好吧,您可以在创建新进程之前进行模拟。此链接 (codeproject.com/KB/cs/cpimpersonation1.aspx) 有更多见解。
      • 我会在星期一试一试(在那之前无法进入系统)...我会让你知道它有效。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-24
      • 2011-02-18
      • 2022-12-06
      • 2015-06-08
      • 1970-01-01
      • 2014-06-14
      • 1970-01-01
      相关资源
      最近更新 更多