jjzjj

c# - 对于某些版本的 Windows,Windows 服务无法在用户使用 WTSQueryUserToken 登录时启动交互过程

coder 2024-06-11 原文

关注此link ,我在我的 C# 解决方案中实现了 WTSQueryUserToken 并从我的 Windows 服务的 OnStart 调用了 CreateProcessAsUserWrapper.LaunchChildProcess("app_path") 方法以 "LocalSystem" 运行。它能够启动可与桌面交互的进程,但适用于 Windows Professional 而不是 Windows Ultimate。我在 Windows Professional 64 位上尝试过它,它能够在用户以交互方式登录时成功启动进程,但在 Windows Ultimate 64 位版本上,CreateProcessAsUser 方法返回 false并且可以看到该服务在 SCM 上运行,而该进程不是。

我的进程是一个 win 表单应用程序,需要使用当前登录的用户帐户运行以进行桌面交互并使用 InteropServices。会不会是框架问题?如何解决问题?请帮忙。

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

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

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

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

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

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 之前,该服务能够运行该应用程序,但无法在 System32 文件夹下登录。为实现这一点,我更新了上面的代码,现在 SetTokenInformation() 方法显示权限错误。

编辑: 最后,该服务可以在登录用户帐户上启动我的进程,并将我的所有日​​志文件写入正确的用户应用程序数据路径。我已经删除了 SetTokenInformation。使用 CreateEnvironmentBlock() 方法跟踪代码帮助进程获得单独的用户环境并检索用户应用程序数据路径。

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 提升,但必须在用户的上下文中运行,即使用户不是管理员。这与它是从服务启动的事实没有直接关系,但这是一个复杂的因素,因为它意味着启动应用程序的尝试失败,而不是导致提升对话框。解决方案是将应用程序 list 中的 requestedExecutionLevelrequireAdministrator 更改为默认值 asInvoker

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

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

关于c# - 对于某些版本的 Windows,Windows 服务无法在用户使用 WTSQueryUserToken 登录时启动交互过程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28674344/

有关c# - 对于某些版本的 Windows,Windows 服务无法在用户使用 WTSQueryUserToken 登录时启动交互过程的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

随机推荐