Как я могу запустить программу EXE из службы Windows, используя C #? - PullRequest
40 голосов
/ 15 марта 2011

Как запустить программу EXE из службы Windows с помощью C #?

Это мой код:

System.Diagnostics.Process.Start(@"E:\PROJECT XL\INI SQLLOADER\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");

Когда я запускаю этот сервис, приложение не запускается.
Что не так с моим кодом?

Ответы [ 8 ]

63 голосов
/ 15 марта 2011

Это никогда не будет работать , по крайней мере, в Windows Vista или более поздней версии.Основная проблема в том, что вы пытаетесь выполнить это из службы Windows, а не из стандартного приложения Windows.Показанный вами код будет отлично работать в приложениях Windows Forms, WPF или Console, но он совсем не будет работать в службе Windows.

Службы Windows не могут запускать дополнительные приложения, поскольку они не работаютв контексте любого конкретного пользователя.В отличие от обычных приложений Windows, службы теперь работают в изолированном сеансе и им запрещено взаимодействовать с пользователем или рабочим столом.Это не оставляет места для запуска приложения.

Дополнительная информация доступна в ответах на следующие вопросы:

Лучшее решение вашей проблемы, как вы, наверное, уже поняли, это создание стандартного приложения Windows вместо службы.Они предназначены для запуска конкретным пользователем и связаны с рабочим столом этого пользователя.Таким образом, вы можете запускать дополнительные приложения в любое время, используя код, который вы уже показали.

Другое возможное решение, предполагающее, что вашему консольному приложению не требуется интерфейс или какой-либо вывод, -поручить процессу не создавать окно.Это не позволит Windows заблокировать создание вашего процесса, поскольку он больше не будет запрашивать создание окна консоли.Вы можете найти соответствующий код в этом ответе на связанный вопрос.

9 голосов
/ 28 ноября 2016

Я пробовал эту статью Код проекта , у меня все работает нормально. Я тоже использовал код. Статья отлично объясняет скриншот.

Я добавляю необходимые объяснения к этому сценарию

Вы только что загрузили свой компьютер и собираетесь войти в систему. Когда вы входите в систему, система присваивает вам уникальный идентификатор сеанса. В Windows Vista, первому пользователю, который войдет в систему, ОС назначит идентификатор сеанса 1. Следующему пользователю, который войдет в систему, будет присвоен идентификатор сеанса 2. И так далее, и так далее. Вы можете просмотреть идентификатор сеанса, назначенный каждому вошедшему в систему пользователю, на вкладке «Пользователи» в диспетчере задач. enter image description here

Но для вашей службы Windows установлен идентификатор сеанса 0. Этот сеанс изолирован от других сеансов. Это в конечном итоге не позволяет службе Windows вызывать приложение, работающее в сеансе пользователя, например 1 или 2.

Чтобы вызвать приложение из службы Windows, вам необходимо скопировать элемент управления из winlogon.exe, который действует как зарегистрированный пользователь, как показано на скриншоте ниже. enter image description here

Важные коды

// obtain the process id of the winlogon process that 
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
    if ((uint)p.SessionId == dwSessionId)
    {
        winlogonPid = (uint)p.Id;
    }
}

// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
    CloseHandle(hProcess);
    return false;
}

// 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);

// copy the access token of the winlogon process; 
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
    (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
      CloseHandle(hProcess);
      CloseHandle(hPToken);
      return false;
    }

 STARTUPINFO si = new STARTUPINFO();
 si.cb = (int)Marshal.SizeOf(si);

// interactive window station parameter; basically this indicates 
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";

// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

// create a new process in the current User's logon session
 bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token
                            null,             // file to execute
                            applicationName,  // 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 si,           // pointer to STARTUPINFO structure
                            out procInfo      // receives information about new process
                            );
3 голосов
/ 03 июля 2018

Вы можете использовать из планировщика задач Windows для этой цели, есть много библиотек, таких как TaskScheduler , которые могут вам помочь.

Например, рассмотрим, что мы хотим запланировать задачу, которая будет выполняться раз в пятьсекунд спустя:

using (var ts = new TaskService())
        {

            var t = ts.Execute("notepad.exe")
                .Once()
                .Starting(DateTime.Now.AddSeconds(5))
                .AsTask("myTask");

        }

notepad.exe будет выполнено через пять секунд.

для получения подробной информации и дополнительной информации, пожалуйста, перейдите на wiki

, есливы знаете, какой класс и метод в этой сборке вам нужны, вы можете сами вызывать их так:

        Assembly assembly = Assembly.LoadFrom("yourApp.exe");
        Type[] types = assembly.GetTypes();
        foreach (Type t in types)
        {
            if (t.Name == "YourClass")
            {
                MethodInfo method = t.GetMethod("YourMethod",
                BindingFlags.Public | BindingFlags.Instance);
                if (method != null)
                {
                    ParameterInfo[] parameters = method.GetParameters();
                    object classInstance = Activator.CreateInstance(t, null);

                    var result = method.Invoke(classInstance, parameters.Length == 0 ? null : parameters);

                    break;
                }
            }

        }
2 голосов
/ 24 сентября 2015

Во-первых, мы собираемся создать службу Windows, которая работает под системной учетной записью.Эта служба будет нести ответственность за создание интерактивного процесса в рамках текущего активного сеанса пользователя.Этот недавно созданный процесс будет отображать пользовательский интерфейс и работать с полными правами администратора.Когда первый пользователь входит в систему на компьютере, эта служба будет запущена и будет запущена в сеансе 0;однако процесс, который порождает эта служба, будет запущен на рабочем столе текущего пользователя.Мы будем называть эту службу LoaderService.

Далее процесс winlogon.exe отвечает за управление процедурами входа и выхода пользователя из системы.Мы знаем, что у каждого пользователя, который входит в систему на компьютере, будет уникальный идентификатор сеанса и соответствующий процесс winlogon.exe, связанный с его сеансом.Теперь, как мы упоминали выше, LoaderService работает под системной учетной записью.Мы также подтвердили, что каждый процесс winlogon.exe на компьютере выполняется под системной учетной записью.Поскольку системная учетная запись является владельцем процессов LoaderService и winlogon.exe, наша LoaderService может скопировать токен доступа (и идентификатор сеанса) процесса winlogon.exe и затем вызвать функцию Win32 API CreateProcessAsUser, чтобы запустить процесс вв данный момент активная сессия вошедшего в систему пользователя.Поскольку идентификатор сеанса, расположенный в токене доступа скопированного процесса winlogon.exe, больше 0, мы можем запустить интерактивный процесс с использованием этого токена.

Попробуйте этот. Подрыв Vista UAC в 32- и 64-разрядных архитектурах

2 голосов
/ 09 января 2012

Вы можете очень хорошо выполнить .exe из службы Windows в Windows XP.Я делал это сам в прошлом.

Необходимо убедиться, что вы установили флажок «Разрешить взаимодействие с рабочим столом» в свойствах службы Windows.Если это не будет сделано, оно не будет выполнено.

Мне нужно проверить в Windows 7 или Vista, так как для этих версий требуются дополнительные привилегии безопасности, что может привести к ошибке, но я вполне уверен, что это может быть достигнуто либопрямо или косвенно.Для XP я уверен, что сделал это сам.

0 голосов
/ 11 апреля 2019

Лучший ответ с большинством голосов не неправильный, но все равно противоположный тому, что я бы опубликовал. Я говорю , это будет полностью работать , чтобы запустить исполняемый файл, и вы можете сделать это в контексте любого пользователя . По логике вещей, вы просто не можете иметь пользовательский интерфейс или запрашивать ввод пользователя ...

Вот мой совет:

  1. Создайте простое консольное приложение, которое будет выполнять то, что должен делать ваш сервис, без вмешательства пользователя. Я действительно рекомендую не использовать тип проекта Windows Service, особенно потому, что вы (в настоящее время) не можете использовать .NET Core.
  2. Добавьте код для запуска вашего exe-файла, который вы хотите вызвать из сервиса

Пример для запуска, например. plink.exe. Вы даже можете прослушать вывод:

var psi = new ProcessStartInfo()
{
    FileName = "./Client/plink.exe", //path to your *.exe
    Arguments = "-telnet -P 23 127.0.0.1 -l myUsername -raw", //arguments
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    UseShellExecute = false,
    CreateNoWindow = true //no window, you can't show it anyway
};

var p = Process.Start(psi);
  1. Используйте NSSM (диспетчер служб не для сосания), чтобы зарегистрировать это консольное приложение в качестве службы. NSSM может управляться через командную строку и может отображать пользовательский интерфейс для настройки службы или вы настраиваете его через командную строку. Вы можете запустить службу в контексте любого пользователя, если вам известны данные для входа в систему этого пользователя.

Я взял учетную запись LocalSystem, которая по умолчанию и больше, чем Local Service. Это работало нормально без необходимости вводить данные для входа в систему конкретного пользователя. Я даже не поставил галочку «Разрешить сервису взаимодействовать с рабочим столом», что вы могли бы сделать, если вам нужны более высокие разрешения.

Option to allow service to interact with desktop

Наконец, я просто хочу сказать, как забавно, что главный ответ говорит о полной противоположности моему ответу, и все же мы оба правы, это просто, как вы интерпретируете вопрос :-D. Если вы сейчас говорите, но не можете с типом проекта службы Windows - вы МОЖЕТЕ, но у меня было это раньше, и установка была неаккуратной, и это было своего рода непреднамеренный взлом, пока я не нашел NSSM.

0 голосов
/ 09 января 2012

System.Diagnostics.Process.Start ("Exe Name");

0 голосов
/ 15 марта 2011

Я думаю, что вы копируете .exe в другое место.Это может быть проблема, я думаю.Когда вы копируете исполняемый файл, вы не копируете его зависимости.

Итак, вы можете поместить все зависимые библиотеки в GAC, чтобы любой .net exe мог получить к нему доступ

Иначе,не копируйте exe-файл в новое место.Просто создайте переменную окружения и вызовите exe в вашем c #.Поскольку путь определен в переменных среды, exe is может быть доступен вашей программе на c #.

Обновление:

Ранее у меня была какая-то такая же проблема в моем проекте c # .net 3.5 вкоторый я пытался запустить файл .exe из кода c # .net, и этот exe был не чем иным, как другим проектом exe (где я добавил несколько поддерживающих dll для моей функциональности) и теми методами dll, которые я использовал в своем приложении exe.Наконец я решил эту проблему, создав это приложение как отдельный проект для того же решения, и добавил выходные данные этого проекта в свой проект развертывания.В соответствии с этим сценарием я ответил: «Если это не то, что он хочет, то мне очень жаль».

...