经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
在System身份运行的.NET程序中以指定的用户身份启动可交互式进程
来源:cnblogs  作者:VAllen  时间:2024/6/25 8:55:38  对本文有异议

今天在技术群里,石头哥向大家提了个问题:"如何在一个以System身份运行的.NET程序(Windows Services)中,以其它活动的用户身份启动可交互式进程(桌面应用程序、控制台程序、等带有UI和交互式体验的程序)"?

我以前有过类似的需求,是在GitLab流水线中运行带有UI的自动化测试程序

其中流水线是GitLab Runner执行的,而GitLab Runner则被注册为Windows服务,以System身份启动的。

然后我在流水线里,巴拉巴拉写了一大串PowerShell脚本代码,通过调用任务计划程序实现了这个需求

但我没试过在C#里实现这个功能。

对此,我很感兴趣,于是着手研究,最终捣鼓出来了。

二话不多说,上代码:

  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using System.Runtime.Versioning;
  7. using System.Text;
  8. using Microsoft.Win32.SafeHandles;
  9. namespace AllenCai.Windows
  10. {
  11. /// <summary>
  12. /// 进程工具类
  13. /// </summary>
  14. #if NET5_0_OR_GREATER
  15. [SupportedOSPlatform("windows")]
  16. #endif
  17. public static class ProcessUtils
  18. {
  19. /// <summary>
  20. /// 在当前活动的用户会话中启动进程
  21. /// </summary>
  22. /// <param name="fileName">程序名称或程序路径</param>
  23. /// <param name="commandLine">命令行参数</param>
  24. /// <param name="workDir">工作目录</param>
  25. /// <param name="noWindow">是否无窗口</param>
  26. /// <param name="minimize">是否最小化</param>
  27. /// <returns></returns>
  28. /// <exception cref="ArgumentNullException"></exception>
  29. /// <exception cref="ApplicationException"></exception>
  30. /// <exception cref="Win32Exception"></exception>
  31. public static int StartProcessAsActiveUser(string fileName, string commandLine = null, string workDir = null, bool noWindow = false, bool minimize = false)
  32. {
  33. if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName));
  34. // 获取当前活动的控制台会话ID和安全的用户访问令牌
  35. IntPtr userToken = GetSessionUserToken();
  36. if (userToken == IntPtr.Zero)
  37. throw new ApplicationException("Failed to get user token for the active session.");
  38. IntPtr duplicateToken = IntPtr.Zero;
  39. IntPtr environmentBlock = IntPtr.Zero;
  40. try
  41. {
  42. // 复制令牌
  43. SecurityAttributes sa = new SecurityAttributes();
  44. sa.Length = Marshal.SizeOf(sa);
  45. if (!DuplicateTokenEx(userToken, MAXIMUM_ALLOWED, ref sa, SecurityImpersonationLevel.SecurityIdentification, TokenType.TokenPrimary, out duplicateToken))
  46. throw new ApplicationException("Could not duplicate token.");
  47. // 创建环境块(检索该用户的环境变量)
  48. if (!CreateEnvironmentBlock(out environmentBlock, duplicateToken, false))
  49. throw new ApplicationException("Could not create environment block.");
  50. bool theCommandIsInPath;
  51. // 如果文件名不包含路径分隔符,则尝试先在workDir参数中查找。如果找不到,再在指定用户会话的PATH环境变量中查找。如果还是找不到,则抛出异常
  52. if ((!fileName.Contains('/') && !fileName.Contains('\\')))
  53. {
  54. if (!string.IsNullOrEmpty(workDir))
  55. {
  56. if (File.Exists(Path.Combine(workDir, fileName)))
  57. {
  58. // 在指定的工作目录中找到可执行命令文件
  59. theCommandIsInPath = false;
  60. }
  61. else
  62. {
  63. // 在指定的工作目录(workDir)中找不到可执行命令文件,再在指定用户会话的PATH环境变量中查找。如果还是找不到,则抛出异常
  64. if (!InPathOfSpecificUserEnvironment(in duplicateToken, in environmentBlock, fileName))
  65. {
  66. throw new ApplicationException($"The file '{fileName}' was not found in the specified directory '{workDir}' or in the PATH environment variable.");
  67. }
  68. else
  69. {
  70. // 在指定用户会话的PATH环境变量中找到可执行命令文件
  71. theCommandIsInPath = true;
  72. }
  73. }
  74. }
  75. else
  76. {
  77. // 在指定用户会话的PATH环境变量中查找,如果找不到,则抛出异常
  78. if (!InPathOfSpecificUserEnvironment(in duplicateToken, in environmentBlock, fileName))
  79. {
  80. throw new ApplicationException($"The file '{fileName}' was not found in the PATH environment variable.");
  81. }
  82. // 在指定用户会话的PATH环境变量中找到可执行命令文件
  83. theCommandIsInPath = true;
  84. }
  85. }
  86. else
  87. {
  88. theCommandIsInPath = false;
  89. }
  90. string file;
  91. if (!theCommandIsInPath && !Path.IsPathRooted(fileName))
  92. {
  93. file = !string.IsNullOrEmpty(workDir) ? Path.GetFullPath(Path.Combine(workDir, fileName)) : Path.GetFullPath(fileName);
  94. }
  95. else
  96. {
  97. file = fileName;
  98. }
  99. if (string.IsNullOrWhiteSpace(workDir)) workDir = theCommandIsInPath ? Environment.CurrentDirectory : Path.GetDirectoryName(file);
  100. if (string.IsNullOrWhiteSpace(commandLine)) commandLine = "";
  101. // 启动信息
  102. ProcessStartInfo psi = new ProcessStartInfo
  103. {
  104. UseShellExecute = true,
  105. FileName = $"{file} {commandLine}", //解决带参数的进程起不来或者起来的进程没有参数的问题
  106. Arguments = commandLine,
  107. WorkingDirectory = workDir,
  108. RedirectStandardError = false,
  109. RedirectStandardOutput = false,
  110. RedirectStandardInput = false,
  111. CreateNoWindow = noWindow,
  112. WindowStyle = minimize ? ProcessWindowStyle.Minimized : ProcessWindowStyle.Normal
  113. };
  114. // 在指定的用户会话中创建进程
  115. SecurityAttributes saProcessAttributes = new SecurityAttributes();
  116. SecurityAttributes saThreadAttributes = new SecurityAttributes();
  117. CreateProcessFlags createProcessFlags = (noWindow ? CreateProcessFlags.CREATE_NO_WINDOW : CreateProcessFlags.CREATE_NEW_CONSOLE) | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT;
  118. bool success = CreateProcessAsUser(duplicateToken, null, $"{file} {commandLine}", ref saProcessAttributes, ref saThreadAttributes, false, createProcessFlags, environmentBlock, null, ref psi, out ProcessInformation pi);
  119. if (!success)
  120. {
  121. throw new Win32Exception(Marshal.GetLastWin32Error());
  122. //throw new ApplicationException("Could not create process as user.");
  123. }
  124. return pi.dwProcessId;
  125. }
  126. finally
  127. {
  128. // 清理资源
  129. if (userToken != IntPtr.Zero) CloseHandle(userToken);
  130. if (duplicateToken != IntPtr.Zero) CloseHandle(duplicateToken);
  131. if (environmentBlock != IntPtr.Zero) DestroyEnvironmentBlock(environmentBlock);
  132. }
  133. }
  134. /// <summary>
  135. /// 使用win32api实现在指定用户身份的环境变量中查找命令(command参数)是否存在。
  136. /// </summary>
  137. private static bool InPathOfSpecificUserEnvironment(in IntPtr userToken, in IntPtr environmentBlock, in string command)
  138. {
  139. // 在指定用户会话环境中执行命令,并且获得控制台标准输出内容
  140. string commandLine = $"cmd.exe /c chcp 65001 && where {command}";
  141. string output = ExecuteCommandAsUserAndReturnStdOutput(userToken, environmentBlock, commandLine, Encoding.UTF8);
  142. // OperatingSystem.IsOSPlatform("WINDOWS") 该方法仅在 .NET Core及以上版本可用,在 .NET Standard 和 .NET Framework 中不可用。
  143. // 现有操作系统中,Windows 操作系统的目录分隔符为 '\',而 Unix 操作系统的目录分隔符为 '/',因此可以用它来判断和区分操作系统。
  144. // 如果是Windows操作系统,则不区分大小写
  145. var comparison = Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
  146. return output.IndexOf(command, comparison) >= 0;
  147. }
  148. /// <summary>
  149. /// 在指定用户会话环境中执行命令,并且返回控制台标准输出内容
  150. /// </summary>
  151. private static string ExecuteCommandAsUserAndReturnStdOutput(in IntPtr userToken, in IntPtr environmentBlock, string commandLine, Encoding encoding)
  152. {
  153. // 创建匿名管道
  154. var saPipeAttributes = new SecurityAttributes();
  155. saPipeAttributes.Length = Marshal.SizeOf(saPipeAttributes);
  156. saPipeAttributes.InheritHandle = true; // 允许句柄被继承
  157. //saPipeAttributes.SecurityDescriptor = IntPtr.Zero;
  158. if (!CreatePipe(out IntPtr readPipe, out IntPtr writePipe, ref saPipeAttributes, 0))
  159. {
  160. throw new Win32Exception(Marshal.GetLastWin32Error());
  161. }
  162. // 确保管道句柄有效
  163. if (readPipe == IntPtr.Zero)
  164. {
  165. throw new InvalidOperationException("Failed to create read pipe.");
  166. }
  167. if (writePipe == IntPtr.Zero)
  168. {
  169. throw new InvalidOperationException("Failed to create write pipe.");
  170. }
  171. try
  172. {
  173. // 确保读取句柄不被子进程继承
  174. SetHandleInformation(readPipe, 0x00000001/*HANDLE_FLAG_INHERIT*/, 0);
  175. var startInfo = new StartupInfo();
  176. startInfo.cb = Marshal.SizeOf(startInfo);
  177. // 设置子进程的标准输出为管道的写入端
  178. startInfo.hStdError = writePipe;
  179. startInfo.hStdOutput = writePipe;
  180. startInfo.dwFlags = StartupInfoFlags.STARTF_USESTDHANDLES;
  181. // 在用户会话中创建进程
  182. const CreateProcessFlags createProcessFlags = CreateProcessFlags.CREATE_NEW_CONSOLE | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT;
  183. var success = CreateProcessAsUser(
  184. userToken,
  185. null,
  186. commandLine,
  187. ref saPipeAttributes,
  188. ref saPipeAttributes,
  189. true,
  190. createProcessFlags,
  191. environmentBlock,
  192. null,
  193. ref startInfo,
  194. out ProcessInformation pi);
  195. if (!success)
  196. {
  197. throw new Win32Exception(Marshal.GetLastWin32Error());
  198. }
  199. // 关闭管道的写入端句柄,因为它已经被子进程继承
  200. CloseHandle(writePipe);
  201. writePipe = IntPtr.Zero;
  202. // 从管道的读取端读取数据
  203. string output;
  204. using (var streamReader = new StreamReader(new FileStream(new SafeFileHandle(readPipe, true), FileAccess.Read, 4096, false), encoding))
  205. {
  206. // 读取控制台标准输出内容
  207. output = streamReader.ReadToEnd();
  208. Trace.WriteLine($"The commandLine [{commandLine}] std output -> {output}");
  209. }
  210. // 关闭进程和线程句柄
  211. CloseHandle(pi.hProcess);
  212. CloseHandle(pi.hThread);
  213. // 返回控制台标准输出内容
  214. return output;
  215. }
  216. finally
  217. {
  218. if (readPipe != IntPtr.Zero) CloseHandle(readPipe);
  219. if (writePipe != IntPtr.Zero) CloseHandle(writePipe);
  220. }
  221. }
  222. /// <summary>
  223. /// 获取活动会话的用户访问令牌
  224. /// </summary>
  225. /// <exception cref="Win32Exception"></exception>
  226. private static IntPtr GetSessionUserToken()
  227. {
  228. // 获取当前活动的控制台会话ID
  229. uint sessionId = WTSGetActiveConsoleSessionId();
  230. // 获取活动会话的用户访问令牌
  231. bool success = WTSQueryUserToken(sessionId, out IntPtr hToken);
  232. // 如果失败,则从会话列表中获取第一个活动的会话ID,并再次尝试获取用户访问令牌
  233. if (!success)
  234. {
  235. sessionId = GetFirstActiveSessionOfEnumerateSessions();
  236. success = WTSQueryUserToken(sessionId, out hToken);
  237. if (!success)
  238. throw new Win32Exception(Marshal.GetLastWin32Error());
  239. }
  240. return hToken;
  241. }
  242. /// <summary>
  243. /// 枚举所有用户会话,获取第一个活动的会话ID
  244. /// </summary>
  245. private static uint GetFirstActiveSessionOfEnumerateSessions()
  246. {
  247. IntPtr pSessionInfo = IntPtr.Zero;
  248. try
  249. {
  250. int sessionCount = 0;
  251. // 枚举所有用户会话
  252. if (WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
  253. {
  254. int arrayElementSize = Marshal.SizeOf(typeof(WtsSessionInfo));
  255. IntPtr current = pSessionInfo;
  256. for (int i = 0; i < sessionCount; i++)
  257. {
  258. WtsSessionInfo si = (WtsSessionInfo)Marshal.PtrToStructure(current, typeof(WtsSessionInfo));
  259. current += arrayElementSize;
  260. if (si.State == WtsConnectStateClass.WTSActive)
  261. {
  262. return si.SessionID;
  263. }
  264. }
  265. }
  266. return uint.MaxValue;
  267. }
  268. finally
  269. {
  270. WTSFreeMemory(pSessionInfo);
  271. CloseHandle(pSessionInfo);
  272. }
  273. }
  274. [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  275. 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);
  276. /// <summary>
  277. /// 以指定用户的身份启动进程
  278. /// </summary>
  279. [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  280. private static extern bool CreateProcessAsUser(
  281. IntPtr hToken,
  282. string lpApplicationName,
  283. string lpCommandLine,
  284. ref SecurityAttributes lpProcessAttributes,
  285. ref SecurityAttributes lpThreadAttributes,
  286. bool bInheritHandles,
  287. CreateProcessFlags dwCreationFlags,
  288. IntPtr lpEnvironment,
  289. string lpCurrentDirectory,
  290. ref ProcessStartInfo lpStartupInfo,
  291. out ProcessInformation lpProcessInformation);
  292. /// <summary>
  293. /// 获取当前活动的控制台会话ID
  294. /// </summary>
  295. [DllImport("kernel32.dll", SetLastError = true)]
  296. private static extern uint WTSGetActiveConsoleSessionId();
  297. /// <summary>
  298. /// 枚举所有用户会话
  299. /// </summary>
  300. [DllImport("wtsapi32.dll", SetLastError = true)]
  301. private static extern int WTSEnumerateSessions(IntPtr hServer, int reserved, int version, ref IntPtr ppSessionInfo, ref int pCount);
  302. /// <summary>
  303. /// 获取活动会话的用户访问令牌
  304. /// </summary>
  305. [DllImport("wtsapi32.dll", SetLastError = true)]
  306. private static extern bool WTSQueryUserToken(uint sessionId, out IntPtr phToken);
  307. /// <summary>
  308. /// 复制访问令牌
  309. /// </summary>
  310. [DllImport("advapi32.dll", SetLastError = true)]
  311. private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, ref SecurityAttributes lpTokenAttributes, SecurityImpersonationLevel impersonationLevel, TokenType tokenType, out IntPtr phNewToken);
  312. /// <summary>
  313. /// 创建环境块(检索指定用户的环境)
  314. /// </summary>
  315. [DllImport("userenv.dll", SetLastError = true)]
  316. private static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
  317. /// <summary>
  318. /// 释放环境块
  319. /// </summary>
  320. [DllImport("userenv.dll", SetLastError = true)]
  321. private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
  322. [DllImport("kernel32.dll", SetLastError = true)]
  323. private static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SecurityAttributes lpPipeAttributes, uint nSize);
  324. [DllImport("kernel32.dll", SetLastError = true)]
  325. public static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);
  326. [DllImport("wtsapi32.dll", SetLastError = false)]
  327. private static extern void WTSFreeMemory(IntPtr memory);
  328. [DllImport("kernel32.dll", SetLastError = true)]
  329. private static extern bool CloseHandle(IntPtr hObject);
  330. [StructLayout(LayoutKind.Sequential)]
  331. private struct WtsSessionInfo
  332. {
  333. public readonly uint SessionID;
  334. [MarshalAs(UnmanagedType.LPStr)]
  335. public readonly string pWinStationName;
  336. public readonly WtsConnectStateClass State;
  337. }
  338. [StructLayout(LayoutKind.Sequential)]
  339. private struct SecurityAttributes
  340. {
  341. public int Length;
  342. public IntPtr SecurityDescriptor;
  343. public bool InheritHandle;
  344. }
  345. [StructLayout(LayoutKind.Sequential)]
  346. private struct StartupInfo
  347. {
  348. public int cb;
  349. public string lpReserved;
  350. public string lpDesktop;
  351. public string lpTitle;
  352. public uint dwX;
  353. public uint dwY;
  354. public uint dwXSize;
  355. public uint dwYSize;
  356. public uint dwXCountChars;
  357. public uint dwYCountChars;
  358. public uint dwFillAttribute;
  359. public StartupInfoFlags dwFlags;
  360. public UInt16 wShowWindow;
  361. public UInt16 cbReserved2;
  362. public unsafe byte* lpReserved2;
  363. public IntPtr hStdInput;
  364. public IntPtr hStdOutput;
  365. public IntPtr hStdError;
  366. }
  367. [StructLayout(LayoutKind.Sequential)]
  368. private struct ProcessInformation
  369. {
  370. public IntPtr hProcess;
  371. public IntPtr hThread;
  372. public int dwProcessId;
  373. public int dwThreadId;
  374. }
  375. private const uint TOKEN_DUPLICATE = 0x0002;
  376. private const uint MAXIMUM_ALLOWED = 0x2000000;
  377. /// <summary>
  378. /// Process Creation Flags。<br/>
  379. /// More:https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
  380. /// </summary>
  381. [Flags]
  382. private enum CreateProcessFlags : uint
  383. {
  384. DEBUG_PROCESS = 0x00000001,
  385. DEBUG_ONLY_THIS_PROCESS = 0x00000002,
  386. CREATE_SUSPENDED = 0x00000004,
  387. DETACHED_PROCESS = 0x00000008,
  388. /// <summary>
  389. /// 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 />
  390. /// This flag cannot be used with <see cref="DETACHED_PROCESS"/>.
  391. /// </summary>
  392. CREATE_NEW_CONSOLE = 0x00000010,
  393. NORMAL_PRIORITY_CLASS = 0x00000020,
  394. IDLE_PRIORITY_CLASS = 0x00000040,
  395. HIGH_PRIORITY_CLASS = 0x00000080,
  396. REALTIME_PRIORITY_CLASS = 0x00000100,
  397. CREATE_NEW_PROCESS_GROUP = 0x00000200,
  398. /// <summary>
  399. /// If this flag is set, the environment block pointed to by lpEnvironment uses Unicode characters. Otherwise, the environment block uses ANSI characters.
  400. /// </summary>
  401. CREATE_UNICODE_ENVIRONMENT = 0x00000400,
  402. CREATE_SEPARATE_WOW_VDM = 0x00000800,
  403. CREATE_SHARED_WOW_VDM = 0x00001000,
  404. CREATE_FORCEDOS = 0x00002000,
  405. BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
  406. ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
  407. INHERIT_PARENT_AFFINITY = 0x00010000,
  408. INHERIT_CALLER_PRIORITY = 0x00020000,
  409. CREATE_PROTECTED_PROCESS = 0x00040000,
  410. EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
  411. PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000,
  412. PROCESS_MODE_BACKGROUND_END = 0x00200000,
  413. CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
  414. CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
  415. CREATE_DEFAULT_ERROR_MODE = 0x04000000,
  416. /// <summary>
  417. /// 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 />
  418. /// 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"/>.
  419. /// </summary>
  420. CREATE_NO_WINDOW = 0x08000000,
  421. PROFILE_USER = 0x10000000,
  422. PROFILE_KERNEL = 0x20000000,
  423. PROFILE_SERVER = 0x40000000,
  424. CREATE_IGNORE_SYSTEM_DEFAULT = 0x80000000,
  425. }
  426. /// <summary>
  427. /// 指定创建进程时的窗口工作站、桌面、标准句柄和main窗口的外观。<br/>
  428. /// More:https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
  429. /// </summary>
  430. [Flags]
  431. private enum StartupInfoFlags : uint
  432. {
  433. /// <summary>
  434. /// 强制反馈光标显示,即使用户没有启用。
  435. /// </summary>
  436. STARTF_FORCEONFEEDBACK = 0x00000040,
  437. /// <summary>
  438. /// 强制反馈光标不显示,即使用户启用了它。
  439. /// </summary>
  440. STARTF_FORCEOFFFEEDBACK = 0x00000080,
  441. /// <summary>
  442. /// 防止应用程序被固定在任务栏或开始菜单。
  443. /// </summary>
  444. STARTF_PREVENTPINNING = 0x00002000,
  445. /// <summary>
  446. /// 不再支持,原用于强制控制台应用程序全屏运行。
  447. /// </summary>
  448. STARTF_RUNFULLSCREEN = 0x00000020,
  449. /// <summary>
  450. /// lpTitle成员是一个AppUserModelID。
  451. /// </summary>
  452. STARTF_TITLEISAPPID = 0x00001000,
  453. /// <summary>
  454. /// lpTitle成员是一个链接名。
  455. /// </summary>
  456. STARTF_TITLEISLINKNAME = 0x00000800,
  457. /// <summary>
  458. /// 启动程序来自不受信任的源,可能会显示警告。
  459. /// </summary>
  460. STARTF_UNTRUSTEDSOURCE = 0x00008000,
  461. /// <summary>
  462. /// 使用dwXCountChars和dwYCountChars成员。
  463. /// </summary>
  464. STARTF_USECOUNTCHARS = 0x00000008,
  465. /// <summary>
  466. /// 使用dwFillAttribute成员。
  467. /// </summary>
  468. STARTF_USEFILLATTRIBUTE = 0x00000010,
  469. /// <summary>
  470. /// 使用hStdInput成员指定热键。
  471. /// </summary>
  472. STARTF_USEHOTKEY = 0x00000200,
  473. /// <summary>
  474. /// 使用dwX和dwY成员。
  475. /// </summary>
  476. STARTF_USEPOSITION = 0x00000004,
  477. /// <summary>
  478. /// 使用wShowWindow成员。
  479. /// </summary>
  480. STARTF_USESHOWWINDOW = 0x00000001,
  481. /// <summary>
  482. /// 使用dwXSize和dwYSize成员。
  483. /// </summary>
  484. STARTF_USESIZE = 0x00000002,
  485. /// <summary>
  486. /// 使用hStdInput、hStdOutput和hStdError成员。
  487. /// </summary>
  488. STARTF_USESTDHANDLES = 0x00000100
  489. }
  490. private enum WtsConnectStateClass
  491. {
  492. WTSActive,
  493. WTSConnected,
  494. WTSConnectQuery,
  495. WTSShadow,
  496. WTSDisconnected,
  497. WTSIdle,
  498. WTSListen,
  499. WTSReset,
  500. WTSDown,
  501. WTSInit
  502. }
  503. private enum SecurityImpersonationLevel
  504. {
  505. SecurityAnonymous,
  506. SecurityIdentification,
  507. SecurityImpersonation,
  508. SecurityDelegation
  509. }
  510. private enum TokenType
  511. {
  512. TokenPrimary = 1,
  513. TokenImpersonation
  514. }
  515. }
  516. }

用法:

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

Windows 7~11Windows Server 2016~2022 操作系统,测试通过。

PS: 2024-06-23 修改

  1. 之前的代码还有些问题,如果传入的是一个没有路径的命令,且workDir参数不为空时,则会被拼接,导致启动时找不到这个命令。
  2. 增加检查传入的命令在指定的用户会话环境中是否存在,如果不存在,则抛出异常。

原文链接:https://www.cnblogs.com/VAllen/p/18257879/in-dotnet-program-run-as-system-to-start-an-interactive-process-as-the-specified-user

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号