Запуск приложения из C# сервиса. Windows 7
От: licedey  
Дата: 26.02.10 09:57
Оценка:
Здравствуйте,

Собственно тривиальная задача. Создаю C# сервис в студии 2008. В обработчике OnStart
делаю Process.Start("c:\windows\notepad.exe"). Процесс собственно не запускается.
Остальные задачи, вроде записи в файл или реестр выполняются на ура. Ясно, что проблема
в правах доступа.
Есть простое .net решение?
Для плюсов делал CreateProcessAsUser.
Спасибо.
Re: Запуск приложения из C# сервиса. Windows 7
От: AK107  
Дата: 26.02.10 10:01
Оценка:
Здравствуйте, licedey, Вы писали:

L>Есть простое .net решение?

L>Для плюсов делал CreateProcessAsUser.

такую перегрузку Process.Start пробовали?
Re: Запуск приложения из C# сервиса. Windows 7
От: Nonmanual Worker  
Дата: 26.02.10 10:51
Оценка:
Здравствуйте, licedey, Вы писали:

L>делаю Process.Start("c:\windows\notepad.exe"). Процесс собственно не запускается.

Какое исключение возникает?
В MSDN все требования расписаны подробно.
Определите исключение и смотрите MSDN.
Re[2]: Запуск приложения из C# сервиса. Windows 7
От: licedey  
Дата: 26.02.10 11:01
Оценка:
Здравствуйте, Nonmanual Worker, Вы писали:

NW>Здравствуйте, licedey, Вы писали:


L>>делаю Process.Start("c:\windows\notepad.exe"). Процесс собственно не запускается.

NW>Какое исключение возникает?
NW>В MSDN все требования расписаны подробно.
NW>Определите исключение и смотрите MSDN.

Win32Exception. Пишет Access Denied. После установки флажка "Allow interact with desktop" в настройках
сервиса, можно посмотреть запущенный блокнот на другом декстопе. Т.е. текущий пропадает, а в непонятном месте запускается.
Re: Запуск приложения из C# сервиса. Windows 7
От: Mishka Норвегия  
Дата: 26.02.10 11:01
Оценка:
Здравствуйте, licedey, Вы писали:

L>Здравствуйте,


L>Собственно тривиальная задача. Создаю C# сервис в студии 2008. В обработчике OnStart

L>делаю Process.Start("c:\windows\notepad.exe"). Процесс собственно не запускается.
L>Остальные задачи, вроде записи в файл или реестр выполняются на ура. Ясно, что проблема
L>в правах доступа.
L>Есть простое .net решение?
L>Для плюсов делал CreateProcessAsUser.
L>Спасибо.

Иногда случается так, хоть и очень редко, но всё же стоит проверить, что файл c:\windows\notepad.exe не существует
Re: Запуск приложения из C# сервиса. Windows 7
От: licedey  
Дата: 26.02.10 11:05
Оценка:
1. Перерыл MSDN. Пока удалось найти только что начиная с Висты взаимодействие с десктопом по умолчанию запрещено.
Возможно нужно сначала установить параметры дектопа для процесса?
2. Пробовал вызывать Process.Start с юзером SYSTEM, LocalSystem, LocalService, Administrator, User. Пишет Access Denied.
Re[2]: Запуск приложения из C# сервиса. Windows 7
От: Nonmanual Worker  
Дата: 26.02.10 11:59
Оценка:
Здравствуйте, licedey, Вы писали:

L>1. Перерыл MSDN. Пока удалось найти только что начиная с Висты взаимодействие с десктопом по умолчанию запрещено.

Интерактивные сервисы — нехорошее дело.
Возможно неверно выбранная архитектура привела вас к такой задаче.
Делегируйте запуск блокнота например out-of-process COM серверу.
Re: Запуск приложения из C# сервиса. Windows 7
От: Forcas Украина  
Дата: 26.02.10 12:13
Оценка: 28 (5)
Здравствуйте, licedey, Вы писали:

L>Здравствуйте,


L>Собственно тривиальная задача. Создаю C# сервис в студии 2008. В обработчике OnStart

L>делаю Process.Start("c:\windows\notepad.exe"). Процесс собственно не запускается.
L>Остальные задачи, вроде записи в файл или реестр выполняются на ура. Ясно, что проблема
L>в правах доступа.
L>Есть простое .net решение?
L>Для плюсов делал CreateProcessAsUser.
L>Спасибо.

Привет!

Проблема не совсем тривиальна из-за изменения в системе безопасности Vista + Win7.
Пришлось самому столкнуться с такой проблемой.

Немного теории здесь: здесь

Решение заключается в следующем:

1) определяем, кто залогинен и имеет активный десктоп
2) получаем хэндл любого процесса, запущенного от имени этого юзера
3) "крадём" креденшили у этого процесса
4) запускаем то, что надо с креденшилами юзера на его активном десктопе


А вот рабочий код для запуска чего надо под кем попало:

using System;
using System.Text;
using System.Security;
using System.Management;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Win32
{
    public class Win32API
    {
        #region WMI Constants

        private const String cstrScope = "root\\CIMV2";
        private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";

        #endregion

        #region Win32 API routines

        [StructLayout(LayoutKind.Sequential)]
        struct SECURITY_ATTRIBUTES
        {
            public Int32 Length;
            public IntPtr lpSecurityDescriptor;
            public Boolean bInheritHandle;
        }

        enum TOKEN_TYPE
        {
            TokenPrimary = 1,
            TokenImpersonation = 2
        }

        enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId,
            TokenGroupsAndPrivileges,
            TokenSessionReference,
            TokenSandBoxInert,
            TokenAuditPolicy,
            TokenOrigin,
            MaxTokenInfoClass  // MaxTokenInfoClass should always be the last enum
        }

        [StructLayout(LayoutKind.Sequential)]
        struct STARTUPINFO
        {
            public Int32 cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public UInt32 dwX;
            public UInt32 dwY;
            public UInt32 dwXSize;
            public UInt32 dwYSize;
            public UInt32 dwXCountChars;
            public UInt32 dwYCountChars;
            public UInt32 dwFillAttribute;
            public UInt32 dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public UInt32 dwProcessId;
            public UInt32 dwThreadId;
        }

        enum SECURITY_IMPERSONATION_LEVEL
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3,
        }

        [StructLayout(LayoutKind.Sequential)]
        struct LUID
        {
            public Int32 LowPart;
            public Int32 HighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct LUID_AND_ATRIBUTES
        {
            LUID Luid;
            Int32 Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct TOKEN_PRIVILEGES
        {
            public Int32 PrivilegeCount;
            //LUID_AND_ATRIBUTES
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public Int32[] Privileges;
        }

        const Int32 READ_CONTROL = 0x00020000;

        const Int32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;

        const Int32 STANDARD_RIGHTS_READ = READ_CONTROL;
        const Int32 STANDARD_RIGHTS_WRITE = READ_CONTROL;
        const Int32 STANDARD_RIGHTS_EXECUTE = READ_CONTROL;

        const Int32 STANDARD_RIGHTS_ALL = 0x001F0000;

        const Int32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;

        const Int32 TOKEN_ASSIGN_PRIMARY = 0x0001;
        const Int32 TOKEN_DUPLICATE = 0x0002;
        const Int32 TOKEN_IMPERSONATE = 0x0004;
        const Int32 TOKEN_QUERY = 0x0008;
        const Int32 TOKEN_QUERY_SOURCE = 0x0010;
        const Int32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
        const Int32 TOKEN_ADJUST_GROUPS = 0x0040;
        const Int32 TOKEN_ADJUST_DEFAULT = 0x0080;
        const Int32 TOKEN_ADJUST_SESSIONID = 0x0100;

        const Int32 TOKEN_ALL_ACCESS_P = (
            STANDARD_RIGHTS_REQUIRED |
            TOKEN_ASSIGN_PRIMARY |
            TOKEN_DUPLICATE |
            TOKEN_IMPERSONATE |
            TOKEN_QUERY |
            TOKEN_QUERY_SOURCE |
            TOKEN_ADJUST_PRIVILEGES |
            TOKEN_ADJUST_GROUPS |
            TOKEN_ADJUST_DEFAULT);

        const Int32 TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;

        const Int32 TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;


        const Int32 TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
                                      TOKEN_ADJUST_PRIVILEGES |
                                      TOKEN_ADJUST_GROUPS |
                                      TOKEN_ADJUST_DEFAULT;

        const Int32 TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;

        const UInt32 MAXIMUM_ALLOWED = 0x2000000;

        const Int32 CREATE_NEW_PROCESS_GROUP = 0x00000200;
        const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;

        const Int32 IDLE_PRIORITY_CLASS = 0x40;
        const Int32 NORMAL_PRIORITY_CLASS = 0x20;
        const Int32 HIGH_PRIORITY_CLASS = 0x80;
        const Int32 REALTIME_PRIORITY_CLASS = 0x100;

        const Int32 CREATE_NEW_CONSOLE = 0x00000010;

        const string SE_DEBUG_NAME = "SeDebugPrivilege";
        const string SE_RESTORE_NAME = "SeRestorePrivilege";
        const string SE_BACKUP_NAME = "SeBackupPrivilege";

        const Int32 SE_PRIVILEGE_ENABLED = 0x0002;

        const Int32 ERROR_NOT_ALL_ASSIGNED = 1300;

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESSENTRY32
        {
            UInt32 dwSize;
            UInt32 cntUsage;
            UInt32 th32ProcessID;
            IntPtr th32DefaultHeapID;
            UInt32 th32ModuleID;
            UInt32 cntThreads;
            UInt32 th32ParentProcessID;
            Int32 pcPriClassBase;
            UInt32 dwFlags;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            string szExeFile;
        }

        const UInt32 TH32CS_SNAPPROCESS = 0x00000002;

        const Int32 INVALID_HANDLE_VALUE = -1;

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern Boolean CloseHandle(IntPtr hSnapshot);

        [DllImport("kernel32.dll")]
        public static extern UInt32 WTSGetActiveConsoleSessionId();

        [DllImport("Wtsapi32.dll")]
        static extern UInt32 WTSQueryUserToken(UInt32 SessionId, ref IntPtr phToken);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Boolean LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);

        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        extern static Boolean CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, Boolean bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvironment,
            String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
        extern static Boolean DuplicateTokenEx(IntPtr ExistingTokenHandle, UInt32 dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpThreadAttributes, Int32 TokenType,
            Int32 ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

        [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);

        [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
        static extern Boolean OpenProcessToken(IntPtr ProcessHandle, // handle to process
                                            Int32 DesiredAccess, // desired access to process
                                            ref IntPtr TokenHandle); // handle to open access token

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Boolean AdjustTokenPrivileges(IntPtr TokenHandle, Boolean DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref UInt32 TokenInformation, UInt32 TokenInformationLength);

        [DllImport("userenv.dll", SetLastError = true)]
        static extern Boolean CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, Boolean bInherit);

        #endregion

        #region Methods

        /// <summary>
        /// Method returns name of the user that logged in on workstation
        /// </summary>
        public static String GetLoggedInUserName()
        {
            String userName = String.Empty;
            
            try
            {
                ManagementObjectSearcher searcher =
                    new ManagementObjectSearcher(cstrScope, cstrLoggenInUser);

                foreach (ManagementObject queryObj in searcher.Get())
                {
                    userName = queryObj["UserName"].ToString();
                    break;
                }
            }
            catch
            {
                userName = String.Empty;
            }
            
            return userName;
        }

        /// <summary>
        /// Creates the process in the interactive desktop with credentials of the logged in user.
        /// </summary>
        public static Boolean CreateProcessAsUser(String commandLine, 
            String workingDirectory,
            String userAppName, 
            out StringBuilder output)
        {
            Boolean processStarted = false;
            output = new StringBuilder();

            try
            {
                UInt32 dwSessionId = WTSGetActiveConsoleSessionId();
                output.AppendLine(String.Format("Active console session Id: {0}", dwSessionId));

                IntPtr hUserToken = IntPtr.Zero;
                WTSQueryUserToken(dwSessionId, ref hUserToken);

                if (hUserToken != IntPtr.Zero)
                {
                    output.AppendLine(String.Format("WTSQueryUserToken() OK (hUserToken:{0})", hUserToken));

                    Process[] processes = Process.GetProcessesByName(userAppName);

                    if (processes.Length == 0)
                    {
                        output.AppendLine(String.Format("Application '{0}' can not be found in the running processes", userAppName));
                        return false;
                    }

                    Int32 userAppProcessId = -1;

                    for (Int32 k = 0; k < processes.Length; k++)
                    {
                        output.AppendLine(String.Format("Process: '{0}', PID: {1}, Handle: {2}, Session: {3}",
                            processes[k].ProcessName, processes[k].Id, processes[k].Handle, processes[k].SessionId));

                        if ((UInt32)processes[k].SessionId == dwSessionId)
                        {
                            userAppProcessId = processes[k].Id;
                        }
                    }

                    if (userAppProcessId == -1)
                    {
                        output.AppendLine(String.Format("Application '{0}' is not found in the processes of the current session", userAppName));
                        return false;
                    }

                    IntPtr hProcess = OpenProcess((Int32)MAXIMUM_ALLOWED, false, (UInt32)userAppProcessId);

                    IntPtr hPToken = IntPtr.Zero;

                    OpenProcessToken(hProcess,
                        TOKEN_ADJUST_PRIVILEGES
                        | TOKEN_QUERY
                        | TOKEN_DUPLICATE
                        | TOKEN_ASSIGN_PRIMARY
                        | TOKEN_ADJUST_SESSIONID
                        | TOKEN_READ
                        | TOKEN_WRITE,
                        ref hPToken);

                    if (hPToken != IntPtr.Zero)
                    {
                        output.AppendLine(String.Format("OpenProcessToken() OK (Token: {0})", hPToken));

                        LUID luid = new LUID();

                        if (LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
                        {
                            output.AppendLine(String.Format("LookupPrivilegeValue() OK (High: {0}, Low: {1})", luid.HighPart, luid.LowPart));

                            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
                            sa.Length = Marshal.SizeOf(sa);

                            IntPtr hUserTokenDup = IntPtr.Zero;
                            DuplicateTokenEx(hPToken,
                                (Int32)MAXIMUM_ALLOWED,
                                ref sa,
                                (Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                                (Int32)TOKEN_TYPE.TokenPrimary,
                                ref hUserTokenDup);

                            if (hUserTokenDup != IntPtr.Zero)
                            {
                                output.AppendLine(String.Format("DuplicateTokenEx() OK (hToken: {0})", hUserTokenDup));

                                TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
                                      {
                                          PrivilegeCount = 1, 
                                        Privileges = new Int32[3]
                                      };

                                tp.Privileges[1] = luid.HighPart;
                                tp.Privileges[0] = luid.LowPart;
                                tp.Privileges[2] = SE_PRIVILEGE_ENABLED;

                                //Adjust Token privilege
                                if (SetTokenInformation(hUserTokenDup,
                                    TOKEN_INFORMATION_CLASS.TokenSessionId,
                                    ref dwSessionId,
                                    (UInt32)IntPtr.Size))
                                {
                                    output.AppendLine(String.Format("SetTokenInformation() OK"));

                                    if (AdjustTokenPrivileges(hUserTokenDup,
                                        false, ref tp, Marshal.SizeOf(tp),
                                        IntPtr.Zero, IntPtr.Zero))
                                    {
                                        output.AppendLine("AdjustTokenPrivileges() OK");

                                        Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

                                        IntPtr pEnv = IntPtr.Zero;
                                        if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
                                        {
                                            dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
                                        }
                                        else
                                        {
                                            output.AppendLine(String.Format("CreateEnvironmentBlock() FAILED (Last Error: {0})", Marshal.GetLastWin32Error()));
                                            pEnv = IntPtr.Zero;
                                        }

                                        // Launch the process in the client's logon session.
                                        PROCESS_INFORMATION pi;

                                        STARTUPINFO si = new STARTUPINFO();
                                        si.cb = Marshal.SizeOf(si);
                                        si.lpDesktop = "winsta0\\default";

                                        output.AppendLine(String.Format("CreateProcess (Path:{0}, CurrDir:{1})", commandLine, workingDirectory));
                                        
                                        if (CreateProcessAsUser(hUserTokenDup,    // client's access token
                                                null,                // file to execute
                                                commandLine,        // command line
                                                ref sa,                // pointer to process SECURITY_ATTRIBUTES
                                                ref sa,                // pointer to thread SECURITY_ATTRIBUTES
                                                false,                // handles are not inheritable
                                                dwCreationFlags,    // creation flags
                                                pEnv,                // pointer to new environment block 
                                                workingDirectory,    // name of current directory 
                                                ref si,                // pointer to STARTUPINFO structure
                                                out pi                // receives information about new process
                                            ))
                                        {
                                            processStarted = true;
                                            output.AppendLine(String.Format("CreateProcessAsUser() OK (PID: {0})", pi.dwProcessId));
                                        }
                                        else
                                        {
                                            output.AppendLine(String.Format("CreateProcessAsUser() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                        }
                                    }
                                    else
                                    {
                                        output.AppendLine(String.Format("AdjustTokenPrivileges() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                    }
                                }
                                else
                                {
                                    output.AppendLine(String.Format("SetTokenInformation() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                                }

                                CloseHandle(hUserTokenDup);
                            }
                            else
                            {
                                output.AppendLine(String.Format("DuplicateTokenEx() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                            }
                        }
                        else
                        {
                            output.AppendLine(String.Format("LookupPrivilegeValue() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                        }

                        CloseHandle(hPToken);
                    }
                    else
                    {
                        output.AppendLine(String.Format("OpenProcessToken() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
                    }

                    CloseHandle(hUserToken);
                }
                else
                {
                    output.AppendLine(String.Format("WTSQueryUserToken failed: {0}", Marshal.GetLastWin32Error()));
                }

            }
            catch (Exception ex)
            {
                output.AppendLine("Exception occurred: " + ex.Message);
            }

            return processStarted;
        }

        #endregion
    }
}
Re[2]: Запуск приложения из C# сервиса. Windows 7
От: Forcas Украина  
Дата: 26.02.10 12:18
Оценка:
F> А вот рабочий код для запуска чего надо под кем попало:

И ещё немного — код не совсем мой, был взят отсюда:

http://www.codeproject.com/KB/vista-security/VistaSessionsC_.aspx?fid=417720&amp;df=90&amp;mpp=25&amp;noise=3&amp;sort=Position&amp;view=Quick&amp;select=2065057#xx2065057xx

и немного доработан.
Re[2]: Запуск приложения из C# сервиса. Windows 7
От: licedey  
Дата: 26.02.10 13:47
Оценка:
Здравствуйте, Forcas, Вы писали:

F>Здравствуйте, licedey, Вы писали:


L>>Здравствуйте,


L>>Собственно тривиальная задача. Создаю C# сервис в студии 2008. В обработчике OnStart

L>>делаю Process.Start("c:\windows\notepad.exe"). Процесс собственно не запускается.
L>>Остальные задачи, вроде записи в файл или реестр выполняются на ура. Ясно, что проблема
L>>в правах доступа.
L>>Есть простое .net решение?
L>>Для плюсов делал CreateProcessAsUser.
L>>Спасибо.

F>Привет!


F>Проблема не совсем тривиальна из-за изменения в системе безопасности Vista + Win7.

F>Пришлось самому столкнуться с такой проблемой.

F>Немного теории здесь: здесь


F>Решение заключается в следующем:


F>1) определяем, кто залогинен и имеет активный десктоп

F>2) получаем хэндл любого процесса, запущенного от имени этого юзера
F>3) "крадём" креденшили у этого процесса
F>4) запускаем то, что надо с креденшилами юзера на его активном десктопе


F>А вот рабочий код для запуска чего надо под кем попало:

.......

Спасибо большое! 2 дня не отрываясь решение искал
Пример использования для совсем ленивых:

try
{            
    StringBuilder output = new StringBuilder();
    if (!Win32API.CreateProcessAsUser("C:\\WINDOWS\\notepad.exe", "C:\\WINDOWS\\", "winlogon", out output))
         throw new Win32Exception(output.ToString());
    else
         throw new Win32Exception("Process RUN!!!");
}
catch (Win32Exception ex)
{
    File.WriteAllText("c:\\hello!.txt", ex.Message + " " + ex.ErrorCode.ToString());
}
Re[2]: Запуск приложения из C# сервиса. Windows 7
От: HowardLovekraft  
Дата: 26.02.10 19:06
Оценка: +4
Здравствуйте, Forcas, Вы писали:

F>1) определяем, кто залогинен и имеет активный десктоп

F>
F>        /// <summary>
F>        /// Method returns name of the user that logged in on workstation
F>        /// </summary>
F>        public static String GetLoggedInUserName()
F>        {
F>            String userName = String.Empty;
            
F>            try
F>            {
F>                ManagementObjectSearcher searcher =
F>                    new ManagementObjectSearcher(cstrScope, cstrLoggenInUser);

F>                foreach (ManagementObject queryObj in searcher.Get())
F>                {
F>                    userName = queryObj["UserName"].ToString();
F>                    break;
F>                }
F>            }
F>            catch
F>            {
F>                userName = String.Empty;
F>            }
            
F>            return userName;
F>        }
F>

Почему ситуация, когда залогинено больше одного пользователя, вами не рассматривается?
А вообще, идея запускать приложения из сервиса довольно анальна, равно как и catch без параметров.
Re[2]: Запуск приложения из C# сервиса. Windows 7
От: Jolly Roger  
Дата: 27.02.10 03:46
Оценка:
Здравствуйте, Forcas, Вы писали:

К сожалению, данную реализацию "CreateProcessAsUser(String commandLine ..." нельзя признать удовлетворительной. Из всего, что находится между вызовами WTSQueryUserToken и "if (CreateProcessAsUser(..." реальный смысл имеет только создание блока переменных окружения, всё остальное — бесполезный нагрев процессора WTSQueryUserToken уже возвращает primary token с нужным sessionId, вполне пригодный для передачи в CreateProcessAsUser, никаких дополнительных телодвижений не требуется.
"Нормальные герои всегда идут в обход!"
Re[3]: Запуск приложения из C# сервиса. Windows 7
От: Jolly Roger  
Дата: 27.02.10 03:46
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Почему ситуация, когда залогинено больше одного пользователя, вами не рассматривается?


С разруливанием этого справляется WTSGetActiveConsoleSessionId. А данную функцию скорее всего предполагалось использовать для проверки наличия хотя-бы одного залогиненного пользователя
"Нормальные герои всегда идут в обход!"
Re[3]: Запуск приложения из C# сервиса. Windows 7
От: Forcas Украина  
Дата: 02.03.10 10:25
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, Forcas, Вы писали:


JR>К сожалению, данную реализацию "CreateProcessAsUser(String commandLine ..." нельзя признать удовлетворительной. Из всего, что находится между вызовами WTSQueryUserToken и "if (CreateProcessAsUser(..." реальный смысл имеет только создание блока переменных окружения, всё остальное — бесполезный нагрев процессора WTSQueryUserToken уже возвращает primary token с нужным sessionId, вполне пригодный для передачи в CreateProcessAsUser, никаких дополнительных телодвижений не требуется.


Вы, видимо, непревзойдённый практик — рекомендую взять и проверить Ваш путь.
Поверьте, я — не любитель усложнять себе жизнь. Проще — не получилось.
Re[3]: Запуск приложения из C# сервиса. Windows 7
От: Forcas Украина  
Дата: 02.03.10 10:33
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, Forcas, Вы писали:


F>>1) определяем, кто залогинен и имеет активный десктоп

F>>
F>>        /// <summary>
F>>        /// Method returns name of the user that logged in on workstation
F>>        /// </summary>
F>>        public static String GetLoggedInUserName()
F>>        {
F>>            String userName = String.Empty;
            
F>>            try
F>>            {
F>>                ManagementObjectSearcher searcher =
F>>                    new ManagementObjectSearcher(cstrScope, cstrLoggenInUser);

F>>                foreach (ManagementObject queryObj in searcher.Get())
F>>                {
F>>                    userName = queryObj["UserName"].ToString();
F>>                    break;
F>>                }
F>>            }
F>>            catch
F>>            {
F>>                userName = String.Empty;
F>>            }
            
F>>            return userName;
F>>        }
F>>

HL>Почему ситуация, когда залогинено больше одного пользователя, вами не рассматривается?
HL>А вообще, идея запускать приложения из сервиса довольно анальна, равно как и catch без параметров.

Смысл существования вышеописанного кода в следующем — запуск приложение при логине юзера, с одним _НО_

Для ключа HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon значение в Shell = "" (пустая срока, т.е. после логина видим пустой десктоп).

В этом случае некому обработать 2 возможных способа запуска процесса:
1) из HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
2) из HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon путь в значении "Userinit"

2-й путь работает только до Win XP но — начиная с висты из соображений безопасности убрали возможность запускать процессы таким способом (некоторые вирусы такое любят)

Вот и пришлось писать то, что выше.

PS: прошу прощения за пустой catch
Re[4]: Запуск приложения из C# сервиса. Windows 7
От: Forcas Украина  
Дата: 02.03.10 10:38
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, HowardLovekraft, Вы писали:


HL>>Почему ситуация, когда залогинено больше одного пользователя, вами не рассматривается?


JR>С разруливанием этого справляется WTSGetActiveConsoleSessionId. А данную функцию скорее всего предполагалось использовать для проверки наличия хотя-бы одного залогиненного пользователя


Истинно так
Re[4]: Запуск приложения из C# сервиса. Windows 7
От: Jolly Roger  
Дата: 02.03.10 11:35
Оценка:
Здравствуйте, Forcas, Вы писали:

F>Вы, видимо, непревзойдённый практик — рекомендую взять и проверить Ваш путь.

F>Поверьте, я — не любитель усложнять себе жизнь. Проще — не получилось.

Свой путь я впервые "проверил" лет 10 назад, когда ещё WTS API и в помине не было А впрочем, как Вам угодно
"Нормальные герои всегда идут в обход!"
Re[5]: Запуск приложения из C# сервиса. Windows 7
От: Forcas Украина  
Дата: 02.03.10 15:18
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, Forcas, Вы писали:


F>>Вы, видимо, непревзойдённый практик — рекомендую взять и проверить Ваш путь.

F>>Поверьте, я — не любитель усложнять себе жизнь. Проще — не получилось.

JR>Свой путь я впервые "проверил" лет 10 назад, когда ещё WTS API и в помине не было А впрочем, как Вам угодно


Извините, если резко высказался. Проще — ну никак не получалось.
Без цепочки DuplicateTokenEx -> SetTokenInformation -> AdjustTokenPrivileges — никак не получалось заставить это работать.
В данном случае именно WTS было спасением
Re[6]: Запуск приложения из C# сервиса. Windows 7
От: Jolly Roger  
Дата: 03.03.10 07:42
Оценка: 3 (1)
Здравствуйте, Forcas, Вы писали:

F>Без цепочки DuplicateTokenEx -> SetTokenInformation -> AdjustTokenPrivileges — никак не получалось заставить это работать.


Ну давайте попробуем ещё раз, как-нибудь так

        [StructLayout(LayoutKind.Sequential)]
        struct STARTUPINFO
        {
            public Int32 cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public UInt32 dwX;
            public UInt32 dwY;
            public UInt32 dwXSize;
            public UInt32 dwYSize;
            public UInt32 dwXCountChars;
            public UInt32 dwYCountChars;
            public UInt32 dwFillAttribute;
            public UInt32 dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public UInt32 dwProcessId;
            public UInt32 dwThreadId;
        }

        const UInt32 WTSConnectState = 8;

        [Flags]
        enum WTS_CONNECTSTATE_CLASS
        {
            WTSActive,
            WTSConnected,
            WTSConnectQuery,
            WTSShadow,
            WTSDisconnected,
            WTSIdle,
            WTSListen,
            WTSReset,
            WTSDown,
            WTSInit
        }

        [DllImport("kernel32.dll")]
        public static extern int FormatMessage(
            int Flags, IntPtr Source, int MessageID, int LanguageID,
            StringBuilder Buffer, int Size, IntPtr Args);

        public static string GetErrorMessage(int ErrorCode)
        {
            var buf = new StringBuilder(256);
            int len = FormatMessage(0x1200, IntPtr.Zero,
                ErrorCode, 0, buf, buf.Capacity, IntPtr.Zero);
            if (len <= 0) return "";
            int k = buf.Length - 1;
            for (; k > 0; k--)
            {
                char u = buf[k];
                if (u > ' ' && u != '.') break;
            }
            buf.Length = k + 1;
            buf.Append('.');
            return buf.ToString();
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll")]
        public static extern Int32 WTSGetActiveConsoleSessionId();

        [DllImport("Wtsapi32.dll", SetLastError = true)]
        static extern bool WTSQueryUserToken(Int32 SessionId, ref IntPtr hToken);

        [DllImport("Wtsapi32.dll", SetLastError = true)]
        static extern bool WTSQuerySessionInformation(
            IntPtr hServer, Int32 SessionId, UInt32 WTSInfoClass,
            out IntPtr ppBuffer, out Int32 BytesReturned);

        [DllImport("Wtsapi32.dll")]
        static extern void WTSFreeMemory(IntPtr pBuf);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool CreateProcessAsUser(
            IntPtr hToken, String lpApplicationName, String lpCommandLine, 
            IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, 
            bool bInheritHandle, UInt32 dwCreationFlags, IntPtr lpEnvironment,
            String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, 
            out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();

        [DllImport("advapi32", SetLastError = true)]
        static extern bool OpenProcessToken(
            IntPtr ProcessHandle, Int32 DesiredAccess, ref IntPtr TokenHandle);

        [DllImport("userenv.dll", SetLastError = true)]
        static extern bool CreateEnvironmentBlock(
            ref IntPtr lpEnvironment, IntPtr hToken, Boolean bInherit);

        [DllImport("userenv.dll", SetLastError = true)]
        static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool SetTokenInformation(
            IntPtr TokenHandle, UInt32 TokenInformationClass, 
            ref Int32 TokenInformation, Int32 TokenInformationLength);

        private int RunInteractiveProcess(Int32 SessId, string FileName, string Args, bool AsSystem)
        {
#if LOG_ON
            EventLog.WriteEntry("Enter");
#endif
            var userToken = IntPtr.Zero; ;
            var Environment = IntPtr.Zero;
            try
            {
                int result;
                if (AsSystem)
                {
                    if (!OpenProcessToken(GetCurrentProcess(), 0x2000000, ref userToken))
                        return Marshal.GetLastWin32Error();
                    if (!SetTokenInformation(userToken, 12, ref SessId, Marshal.SizeOf(SessId)))
                        return Marshal.GetLastWin32Error();
                }
                else
                {
                    if (!WTSQueryUserToken(SessId, ref userToken))
                    {
                        result = Marshal.GetLastWin32Error();
#if LOG_ON
                    EventLog.WriteEntry(string.Format("QueryToken = {0}", result));
#endif
                        return result;
                    }
                    if (!CreateEnvironmentBlock(ref Environment, userToken, false))
                        return Marshal.GetLastWin32Error();
#if LOG_ON
                    EventLog.WriteEntry("Environment created");
#endif
                }

                var SI = new STARTUPINFO();
                SI.cb = Marshal.SizeOf(SI);
                if (AsSystem) SI.lpDesktop = @"WinSta0\Default";
                UInt32 ctorFlags = 0;
                if (Environment != IntPtr.Zero) ctorFlags |= 0x400;
                var PI = new PROCESS_INFORMATION();
#if LOG_ON
                EventLog.WriteEntry("Before create process");
#endif
                if (CreateProcessAsUser(userToken,
                    FileName, Args, IntPtr.Zero, IntPtr.Zero, false,
                    ctorFlags, Environment, null, ref SI, out PI))
                {
                    CloseHandle(PI.hProcess);
                    CloseHandle(PI.hThread);
                }
                else
                    return Marshal.GetLastWin32Error();
            }
            finally
            {
#if LOG_ON
                EventLog.WriteEntry("Finally");
#endif
                if (userToken != IntPtr.Zero) CloseHandle(userToken);
                if (Environment != IntPtr.Zero) DestroyEnvironmentBlock(Environment);
            }
#if LOG_ON
            EventLog.WriteEntry("Leave - Success");
#endif
            return 0;
        }


Использовать примерно так:

        private void Execute()
        {
            var SessId = WTSGetActiveConsoleSessionId();
#if LOG_ON
            EventLog.WriteEntry(string.Format("SessId = {0}", SessId));
#endif
            int R;
            IntPtr pBuf = new IntPtr();
            Int32 bufLen;
            if (!WTSQuerySessionInformation(
                IntPtr.Zero, SessId, WTSConnectState, out pBuf, out bufLen))
            {
                R = Marshal.GetLastWin32Error();
                string s = string.Format(
                    "Query session information failed({0}: {1}",
                    R, GetErrorMessage(R));
                EventLog.WriteEntry(s);
                return;
            }

            WTS_CONNECTSTATE_CLASS state;
            unsafe
            {
                state = *(WTS_CONNECTSTATE_CLASS*)(pBuf);
            }
            WTSFreeMemory(pBuf);

            if (state != WTS_CONNECTSTATE_CLASS.WTSActive)
            {
                string s =
                    string.Format("Console session is inactive. Current state = {0}", state);
                EventLog.WriteEntry(s);
                return;
            }

            R = RunInteractiveProcess(
                SessId, @"D:\Windows\System32\Notepad.exe", null, true);
            if (R != 0)
            {
                string s = string.Format(
                    "RunInteractiveProcess failed({0}): {1}", R, GetErrorMessage(R));
                EventLog.WriteEntry(s);
            }
        }


Последний параметр в RunInteractiveProcess определяет, от чьего имени запускать. Если он false, то запуск производится от имени пользователя сессии. Если true — от имени учётки самого сервиса, при этом предполагается, что сервис работает под LocalSystem, при других учётках возни гораздо больше.
"Нормальные герои всегда идут в обход!"
Re[7]: Запуск приложения из C# сервиса. Windows 7
От: Jolly Roger  
Дата: 03.03.10 08:05
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

F>> [Flags]

F>> enum WTS_CONNECTSTATE_CLASS

[Flags] здесь не нужен, это перечисление — напутал
"Нормальные герои всегда идут в обход!"
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.