Windows服务无法在用户使用WTSQueryUserTokenlogin某些Windows版本时启动交互式进程

在此链接之后 ,我已经在我的C#解决scheme中实现了WTSQueryUserToken ,并从我的Windows服务的OnStart调用了CreateProcessAsUserWrapper.LaunchChildProcess("app_path")方法,该方法使其作为“LocalSystem”运行。 它能够启动可以与桌面进行交互的进程,但对于Windows Professional而不是Windows Ultimate。 我在Windows专业版64位上试过,它能够成功地启动交互式用户login过程,但在Windows Ultimate 64位版本中, CreateProcessAsUser方法返回false ,可以看到服务在SCM运行,而过程不是。

我的过程是一个胜利forms的应用程序,需要运行当前login的用户帐户进行桌面交互,并使用InteropServices 。 它可以是一个框架问题? 如何解决这个问题? 请帮忙。

编辑:这两台机器都运行Windows 7 64位。 我添加了下面的代码来获取CreateProcessAsUser之后的错误代码,并返回“请求的操作需要提升”作为错误。

 string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; 

它仅在Windows 7 Ultimate中发生,而不在Windows 7 Professional中发生。 我需要为每个login用户运行我的进程,这就是为什么我认为我不能使用CreateProcessWithLogonW方法。 我现在如何解决这个提升权限问题?

编辑:有2个app.manifest文件,一个在服务项目里面,另一个在win窗体(stream程)项目里面。 我删除了这两个文件,用WiX构build了msi并再次testing。 现在它已经在每台机器上成功运行了,每一个login的用户账号都会启动这个进程。 但是,现在是一个问题。 其实我需要在windows / system文件夹下写一些日志,我现在不能这样做。 我正在从进程中抛出“ UnauthorizedAccessException ”。 此外,除pipe理员以外,不允许任何用户停止或卸载该进程,但在这种情况下,用户可以启动任务pipe理器,并从那里结束该进程,因为它正在运行相应的用户帐户。 是否有可能使用用户帐户运行进程但具有提升的权限,以便我可以在Windows / System文件夹下写入我的日志,并防止用户在没有pipe理员权限的情况下停止进程? 至less解决第一个问题,即在系统文件夹下写日志文件对我来说非常重要。

编辑:添加SetTokenInformationDuplicateTokenEx我的服务无法启动应用程序与日志SetTokenInformation()返回FALSE。错误:所需的特权不是由客户端” 。 我正在提供完整的代码,请参阅是否已正确更新所有步骤。

 using System; using System.IO; using System.ComponentModel; using System.Security.Principal; using System.Text; using System.Collections; using System.Web; using System.Net; using System.Diagnostics; using System.Runtime.InteropServices; // DllImport namespace CSCreateProcessAsUserFromService { class CreateProcessAsUserWrapper1 { static String tmp_log = Path.Combine(CreateDirectoryAtSystemFolder(), "Temp-Report-Service.txt"); static string errorMessage; public static void LaunchChildProcess(string ChildProcName) { IntPtr ppSessionInfo = IntPtr.Zero; UInt32 SessionCount = 0; File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess() OK 1" + "\n"); if (WTSEnumerateSessions( (IntPtr)WTS_CURRENT_SERVER_HANDLE, // Current RD Session Host Server handle would be zero. 0, // This reserved parameter must be zero. 1, // The version of the enumeration request must be 1. ref ppSessionInfo, // This would point to an array of session info. ref SessionCount // This would indicate the length of the above array. )) { File.AppendAllText(tmp_log, "\n" + "WTSEnumerateSessions OK 2" + "\n"); for (int nCount = 0; nCount < SessionCount; nCount++) { // Extract each session info and check if it is the // "Active Session" of the current logged-on user. //ppSessionInfo = new IntPtr(ppSessionInfo.ToInt32() + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO))); WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure( ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)), typeof(WTS_SESSION_INFO) ); /*WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure( new IntPtr(ppSessionInfo.ToInt32() + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO))), typeof(WTS_SESSION_INFO) );*/ if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) { WindowsIdentity m_ImpersonatedUser; IntPtr hToken = IntPtr.Zero; IntPtr hTokenDuplicate = IntPtr.Zero; const int SecurityImpersonation = 2; const int TokenType = 1; File.AppendAllText(tmp_log, "\n" + "if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) 3" + "\n"); if (RevertToSelf()) { File.AppendAllText(tmp_log, "\n" + "RevertToSelf() TRUE 4 " + "\n"); if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) { File.AppendAllText(tmp_log, "\n" + "if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) TRUE 5 " + "\n"); LUID luid = new LUID(); // 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); //if (DuplicateToken(hToken, SecurityImpersonation, ref hTokenDuplicate) != 0) if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED/*GENERIC_ACCESS*/, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, out/*ref*/ hTokenDuplicate)) { File.AppendAllText(tmp_log, "\n" + " DuplicateTokenEx() TRUE 6 " + "\n"); WindowsImpersonationContext m_ImpersonationContext; m_ImpersonatedUser = new WindowsIdentity(hTokenDuplicate); using (m_ImpersonationContext = m_ImpersonatedUser.Impersonate()) { if (m_ImpersonationContext != null) { File.AppendAllText(tmp_log, Environment.NewLine + "User Name: " + WindowsIdentity.GetCurrent(TokenAccessLevels.MaximumAllowed).Name + Environment.NewLine + "SID: " + WindowsIdentity.GetCurrent(TokenAccessLevels.MaximumAllowed).User.Value); 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(hTokenDuplicate, TOKEN_INFORMATION_CLASS.TokenSessionId, ref tSessionInfo.SessionID, (UInt32)Marshal.SizeOf(tSessionInfo.SessionID))) { File.AppendAllText(tmp_log, "\n" + " SetTokenInformation() TRUE 7 " + "\n"); if (AdjustTokenPrivileges(hTokenDuplicate, false, ref tp, Marshal.SizeOf(tp), IntPtr.Zero, IntPtr.Zero)) { File.AppendAllText(tmp_log, "\n" + " AdjustTokenPrivileges() TRUE 8 " + "\n"); //ImpersonateLoggedOnUser(hToken); //WindowsIdentity.Impersonate(hToken); //errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; //File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-ImpersonateLoggedOnUser-" + errorMessage + "\n"); // Launch the child process interactively // with the token of the logged-on user. PROCESS_INFORMATION tProcessInfo; STARTUPINFO tStartUpInfo = new STARTUPINFO(); tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO)); tStartUpInfo.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; IntPtr pEnv = IntPtr.Zero; if (CreateEnvironmentBlock(ref pEnv, hTokenDuplicate, true)) { dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; File.AppendAllText(tmp_log, "\n" + " CreateEnvironmentBlock() TRUE 9 " + "\n"); } else { pEnv = IntPtr.Zero; File.AppendAllText(tmp_log, "\n" + " CreateEnvironmentBlock() ELSE 9 " + "\n"); } // create a new process in the current user's logon session bool ChildProcStarted = CreateProcessAsUser( hTokenDuplicate, // client's access token ChildProcName, // file to execute null, // 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 null, // name of current directory ref tStartUpInfo, // pointer to STARTUPINFO structure out tProcessInfo // receives information about new process ); /*bool ChildProcStarted = CreateProcessAsUser( //hToken, // Token of the logged-on user. hTokenDuplicate, // Token of the logged-on user. ChildProcName, // Name of the process to be started. null, // Any command line arguments to be passed. IntPtr.Zero, // Default Process' attributes. IntPtr.Zero, // Default Thread's attributes. false, // Does NOT inherit parent's handles. 0, // No any specific creation flag. null, // Default environment path. null, // Default current directory. ref tStartUpInfo, // Process Startup Info. out tProcessInfo // Process information to be returned. );*/ errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-CreateProcessAsUser-" + errorMessage + "\n"); if (ChildProcStarted) { // The child process creation is successful! // If the child process is created, it can be controlled via the out // param "tProcessInfo". For now, as we don't want to do any thing // with the child process, closing the child process' handles // to prevent the handle leak. CloseHandle(tProcessInfo.hThread); CloseHandle(tProcessInfo.hProcess); File.AppendAllText(tmp_log, "\n" + "ChildProcStarted() 10 true!" + "\n"); } else { // CreateProcessAsUser failed! errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + " ChildProcStarted() 10 failed! ERROR: " + errorMessage + "\n"); } // Whether child process was created or not, close the token handle // and break the loop as processing for current active user has been done. CloseHandle(hToken); CloseHandle(hTokenDuplicate); // Undo impersonation //m_ImpersonationContext.Undo(); break; } else { errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + " AdjustTokenPrivileges() FALSE 8 ERROR: " + errorMessage + "\n"); } } else { errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; //File.AppendAllText(tmp_log, "\n" + "LaunchChildProcess-ImpersonateLoggedOnUser-" + errorMessage + "\n"); File.AppendAllText(tmp_log, "\n" + " SetTokenInformation() FALSE 7 ERROR: " + errorMessage + "\n"); } } } } else { errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + "DuplicateTokenEx() FALSE 6 ERROR: " + errorMessage + "\n"); } } else { // WTSQueryUserToken failed! errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + "if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken)) FALSE 5 ERROR: " + errorMessage + "\n"); } } else { errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + "RevertToSelf() FALSE 4 ERROR: " + errorMessage + "\n"); } } else { // This Session is not active! errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + "if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State) FALSE 3 This Session is not active! ERROR: " + errorMessage + "\n"); } } // Free the memory allocated for the session info array. WTSFreeMemory(ppSessionInfo); } else { // WTSEnumerateSessions failed! errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; File.AppendAllText(tmp_log, "\n" + "WTSEnumerateSessions FAILED 2 ERROR: " + errorMessage + "\n"); } } #region P/Invoke WTS APIs /// <summary> /// Struct, Enum and P/Invoke Declarations of WTS APIs. /// </summary> /// private const int WTS_CURRENT_SERVER_HANDLE = 0; private enum WTS_CONNECTSTATE_CLASS { WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct WTS_SESSION_INFO { public UInt32 SessionID; public string pWinStationName; public WTS_CONNECTSTATE_CLASS State; } [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)] static extern bool WTSEnumerateSessions( IntPtr hServer, [MarshalAs(UnmanagedType.U4)] UInt32 Reserved, [MarshalAs(UnmanagedType.U4)] UInt32 Version, ref IntPtr ppSessionInfo, [MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount ); [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)] static extern void WTSFreeMemory(IntPtr pMemory); [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)] static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token); #endregion #region P/Invoke CreateProcessAsUser /// <summary> /// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser. /// </summary> /// #region WMI Constants private const String cstrScope = "root\\CIMV2"; private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem"; #endregion [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int Length; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } #region Enumerations 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 } enum TOKEN_TYPE : int { TokenPrimary = 1, TokenImpersonation = 2 } enum SECURITY_IMPERSONATION_LEVEL : int { SecurityAnonymous = 0, SecurityIdentification = 1, SecurityImpersonation = 2, SecurityDelegation = 3, } [StructLayout(LayoutKind.Sequential)] public struct LUID { public Int32 LowPart; public Int32 HighPart; } [StructLayout(LayoutKind.Sequential)] public 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; } #endregion #region Constants public const uint GENERIC_ACCESS = 0x1000000; 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; 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; #endregion /* [DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)] static extern bool CreateProcessAsUser( IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation );*/ [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", SetLastError = true, CharSet = CharSet.Auto)] static extern bool CloseHandle(IntPtr hHandle); #endregion [DllImport("kernel32.dll")] public static extern UInt32 WTSGetActiveConsoleSessionId(); [DllImport("advapi32.dll", SetLastError = true)] static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid); [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); [DllImport("advapi32.DLL")] public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //handle to token for logged-on user [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken); [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, int ImpersonationLevel, out/*ref*/ IntPtr DuplicateTokenHandle); /// /// A process should call the RevertToSelf function after finishing /// any impersonation begun by using the DdeImpersonateClient, /// ImpersonateDdeClientWindow, ImpersonateLoggedOnUser, ImpersonateNamedPipeClient, /// ImpersonateSelf, ImpersonateAnonymousToken or SetThreadToken function. /// If RevertToSelf fails, your application continues to run in the context of the client, /// which is not appropriate. You should shut down the process if RevertToSelf fails. /// RevertToSelf Function: http://msdn.microsoft.com/en-us/library/aa379317(VS.85).aspx /// /// A boolean value indicates the function succeeded or not. [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool RevertToSelf(); [DllImport("shell32.dll")] public static extern bool SHGetSpecialFolderPath(IntPtr hwndOwner, [Out]StringBuilder lpszPath, int nFolder, bool fCreate); static string System32_SysWOW64_Folder() { StringBuilder path = new StringBuilder(260); SHGetSpecialFolderPath(IntPtr.Zero, path, 0x0029, false); return path.ToString(); } //=================== Create directory ===================== private static string CreateDirectoryAtSystemFolder() { string tmpDir = null; string sysFolderPath = System32_SysWOW64_Folder(); //string sysFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); //System32_SysWOW64_Folder(); //string sysFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); //ProgramFiles_Folder(); tmpDir = sysFolderPath + "\\logdata"; //ConfigurationSettings.AppSettings["ImageSavedPath"].ToString(); if (!System.IO.Directory.Exists(@tmpDir)) { System.IO.Directory.CreateDirectory(@tmpDir); } return tmpDir; } } } 

正如我在我之前的编辑中所提到的,在添加DuplicateTokenExSetTokenInformation之前,该服务能够运行该应用程序,但无法login到System32文件夹下。 为了实现这一点,我更新到上面的代码,现在SetTokenInformation()方法显示特权错误。

编辑:最后,服务可以开始我的进程login用户帐户,并写入所有我的日志文件在适当的用户应用程序数据path。 我已经删除了SetTokenInformation 。 下面的代码与CreateEnvironmentBlock()方法帮助进程得到一个单独的用户环境,并检索用户的appdatapath。

 int dwCreationFlags = DETACHED_PROCESS; IntPtr pEnv = IntPtr.Zero; if (CreateEnvironmentBlock(ref pEnv, hToken, true)) { dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; } else { pEnv = IntPtr.Zero; } bool ChildProcStarted = CreateProcessAsUser( hToken, // client's access token ChildProcName, // file to execute null, // command line ref saProcess, // pointer to process SECURITY_ATTRIBUTES ref saThread, // pointer to thread SECURITY_ATTRIBUTES false, // handles are not inheritable dwCreationFlags, // creation flags pEnv, // pointer to new environment block null, // name of current directory ref tStartUpInfo, // pointer to STARTUPINFO structure out tProcessInfo // receives information about new process ); 

总结:

  • 原来的问题是,目标可执行文件被配置为需要UAC提升,但是必须在用户的上下文中运行,即使用户不是管理员。 这与它从服务中启动并不直接相关,但这是一个复杂的因素,因为这意味着启动应用程序的尝试失败,而不是导致提升对话。 解决方法是将应用程序清单中的requestedExecutionLevelrequireAdministrator更改asInvoker默认值asInvoker

  • 当应用程序重新配置为不需要UAC提升时,由于它正在尝试将日志文件写入全局文件夹,因此无法正常运行。 解决方案是将日志文件写入适当的每个用户位置。

  • 最后一个问题是应用程序在从服务启动时没有正确地找到每个用户的位置。 这是因为它被给了一个服务的环境块的副本,而不是一个合适的用户。 解决方法是使用CreateEnvironmentBlock()生成一个合适的环境块,并将其作为CreateProcessAsUser的lpEnvironment参数传递。