Службе Windows не удается запустить интерактивный процесс при входе пользователя в систему с помощью WTSQueryUserToken для некоторых версий Windows

Перейдя по этой ссылке, я реализовал WTSQueryUserToken в своем решении C# и назвал CreateProcessAsUserWrapper.LaunchChildProcess("app_path") метод из OnStart моей службы Windows, которая работает как "LocalSystem". Он может запустить процесс, который может взаимодействовать с рабочим столом, но для Windows Professional, а не для Windows Ultimate. Я попробовал это на 64-битной Windows Professional, и он может успешно запустить процесс при входе пользователя в систему в интерактивном режиме, но на 64-битной версии Windows Ultimate 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 form (process). Я удалил оба файла, построил msi с WiX и проверил снова. Теперь он успешно работает на каждой машине, процесс запускается с каждой зарегистрированной учетной записью пользователя. Но одна проблема возникает сейчас. На самом деле мне нужно написать какой-нибудь журнал в папке windows / system, чего я сейчас не могу сделать. Я получаю " UnauthorizedAccessException "выброшено из процесса. Более того, ни одному пользователю, кроме администратора, не разрешено останавливать или удалять процесс, но в этом случае пользователь может запустить диспетчер задач и завершить процесс оттуда, поскольку он работает с соответствующей учетной записью пользователя. Можно ли запустить процесс с учетной записью пользователя, но с повышенными привилегиями, чтобы я мог записать свой журнал в папку windows / system и не дать пользователю остановить процесс без разрешения администратора? По крайней мере, решить первую проблему, т.е. записать файлы журнала под систему папка очень важна для меня.

Изменить: после добавления SetTokenInformation вместе с DuplicateTokenEx мой сервис не запускает приложение с журналом SetTokenInformation() возвращает ЛОЖЬ. ОШИБКА: требуемая привилегия не удерживается клиентом ". Я предоставляю полный код, пожалуйста, проверьте, правильно ли я обновил его на всех этапах.

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;
        }
    }
}

Как я уже упоминал в моем предыдущем редактировании, ранее перед добавлением DuplicateTokenEx & SetTokenInformation Служба смогла запустить приложение, но не смогла войти в папку 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
    );

1 ответ

Решение

Подвести итоги:

  • Первоначальная проблема заключалась в том, что целевой исполняемый файл был настроен так, чтобы требовать повышения прав UAC, но его необходимо запускать в контексте пользователя, даже если пользователь не является администратором. Это не имеет прямого отношения к тому факту, что оно запускалось из службы, но это было усложняющим фактором, поскольку означало, что попытка запустить приложение не удалась, а не вызвала диалог повышения прав. Решение было изменить requestedExecutionLevel в манифесте приложения от requireAdministrator в asInvoker который является значением по умолчанию.

  • Когда приложение было переконфигурировано, чтобы не требовать повышения уровня контроля учетных записей, оно не работало должным образом, поскольку оно пыталось записать файлы журнала в глобальную папку. Было принято решение записать файлы журнала в подходящее место для каждого пользователя.

  • Последняя проблема заключалась в том, что приложение не правильно находило местоположение для каждого пользователя при запуске из службы. Это произошло потому, что ему была предоставлена ​​копия блока среды службы, а не подходящий для пользователя. Было решено использовать CreateEnvironmentBlock() для создания подходящего блока среды и передать его как CreateProcessAsUser's lpEnvironment аргумент.

Другие вопросы по тегам