Java управление процессами windows

Создание Windows службы на Java

February 22, 2019 Jazz Team Технические статьи

Введение

В рамках одного из проектов требовалось разработать Windows службу, которая могла бы выполнять ряд действий с помощью Windows API, Websocket и стандартных средств Java. Далее в статье будут описаны шаги, которые были сделаны для создания такой службы.
Потребность в Windows службе возникла из-за необходимости иметь программу со следующими возможностями:

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

Создание минимизированной версии JRE

Так как GraalVM всё ещё не поддерживает создание исполняемых файлов под Windows, было решено воспользоваться другими возможностями, которые предоставляет экосистема Java, а именно создание минимизированной версии JRE.

Для того, чтобы создать минимизированную версию JRE, для начала необходимо узнать зависимости на определенные пакеты, которые будут включены в JRE.

В первую очередь необходимо собрать jar-файл “fat jar” со всеми зависимостями.

Затем выполнить команду jdeps -s , чтобы получить список всех зависимостей. Например:

Далее создаём нашу версию JRE с данными зависимостями:

jlink –module-path –add-modules

java.base,java.datatransfer,java.desktop,java.logging,java.net.http,java.sql,java.xml,jdk.unsupported –strip-debug –compress 2 –no-header-files –no-man-pages –output

Обратите внимание, что перечисление пакетов для опции –add-modules необходимо разделять запятой и не ставить между ними пробелов. Остальные опции отвечают за сжатие и убирание файлов и другой информации, которая не пригодится для выполнения программы.

После выполнения этих действий JRE будет занимать порядка 30 mb, вместо сотен.

Создание Windows службы из любого приложения

Java не имеет стандартных средств по созданию служб, поэтому были изучены сторонние инструменты и был выбран WinSW в силу его бесплатности и простоты использования.

WinSW

WinSW – это утилита, которая позволяет запустить и обернуть любой процесс как Windows службу. Для того, чтобы начать с ней работать, необходимо скачать исполняемый и конфигурационный файлы по этой ссылке https://github.com/kohsuke/winsw/releases.

Необходимо поместить эти два файла в директорию. Переименовать исполняемый файл на своё усмотрение и дать такое же название файлу конфигурации, затем поместить в эту директорию jar-файл приложения и созданную JRE.

В конфигурационном файле необходимо прописать минимальную конфигурацию:

jre\bin\java.exe – относительный путь внутри нашей папки к исполняемому файлу нашей JRE.

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

Список команд можно посмотреть здесь .

Взаимодействие Java и Windows API

Для использования функций Windows (таких как создание нового процесса или добавление ключей реестра) в нашем приложении был использован JNA.
JNA (Java Native Access) предоставляет Java-программам легкий доступ к библиотекам, написанным на другом языке, без написания чего-либо, кроме кода Java. JNA позволяет напрямую вызывать нативные функции, используя обычный вызов метода Java. Большинство методов не требуют специальной обработки или конфигурации; не требуется шаблон или сгенерированный код.
Подключить и работать с JNA очень просто, для этого необходимо скачать jar-файл или подключить зависимость в сборщик проекта – в нашем случает Maven:

В нашем проекте мы использовали JNA для достижения следующих целей: заблокировать и сделать вновь доступным диспетчер задач 1) по комбинации Ctrl+Shift+Esc и 2) в меню, доступном по комбинации Ctrl+Alt+Del.

Читайте также:  Как посмотреть gui linux

Для достижения этого были использованы класс Advapi32Util (удобная обёртка над библиотекой advapi32.dll) и интерфейс WinReg с полезными константами , которые предоставляют функциональность для внесения изменений в реестр Windows (Рисунок 1. Класс TaskManager с методами enable() и disable() для изменения ключей реестра диспетчера задач).

Рисунок 1. Класс TaskManager с методами enable() и disable() для изменения ключей реестра диспетчера задач.

  • Создать новый процесс от имени определённого пользователя Windows. Для этого мы использовали метод CreateProcessAsUser()интерфейса Advapi32. В метод необходимо передать следующие параметры:
    • hToken – дескриптор токена пользователя, для которого мы запускаем процесс.
    • lpApplicationName – имя модуля, который должен быть выполнен.
    • lpCommandLine – командная строка для выполнения.
    • lpProcessAttributes – указатель на структуру SECURITY_ATTRIBUTES, которая определяет дескриптор безопасности для нового объекта процесса и определяет, могут ли дочерние процессы наследовать возвращенный дескриптор процесса.
    • lpThreadAttributes – указатель на структуру SECURITY_ATTRIBUTES, который определяет дескриптор безопасности для нового объекта потока и определяет, могут ли дочерние процессы наследовать возвращенный дескриптор потока.Создать новый процесс от имени определённого пользователя Windows. Для этого мы использовали метод CreateProcessAsUser() интерфейса Advapi32. В метод необходимо передать следующие параметры:
    • bInheritHandles – если этот параметр TRUE, каждый наследуемый дескриптор вызывающего процесса наследуется новым процессом. Если параметр FALSE, процессы не наследуются.
    • dwCreationFlags – флаги, которые контролируют класс приоритета и создают процесс.
    • lpEnvironment – указатель на блок среды для нового процесса. Если этот параметр равен NULL, новый процесс использует среду вызывающего процесса. Блок среды состоит из блока с нулевым завершением строк с нулевым завершением. Каждая строка имеет следующий вид: name = value \ 0.
    • lpCurrentDirectory – полный путь к текущему каталогу для процесса. Строка также может указывать путь UNC (universal naming convention).
    • lpStartupInfo – указатель на структуру STARTUPINFO или STARTUPINFOEX.lpProcessInformation – указатель на структуру PROCESS_INFORMATION, которая получает идентификационную информацию о новом процессе.

Рисунок 2. Метод для создания нового процесса для определённого пользователя Windows.

  • Получить токен активного пользователя, т.к. он необходим для создания процесса от определённого пользователя.

Работа с процессами

Для работы и слежения за процессами в Windows был использован, добавленный в Java 9, класс ProcessHandle. ProcessHandle позволяет получать и производить различные манипуляции с процессами. В частности, при решении задачи, требовалось собирать PID процессов, фильтровать процессы на основе имени и принудительно завершать необходимые процессы.

Рисунок 3. Класс ProcessHandler с методами takeSnapshot() для создания снимка текущих процессов и closeNewProcesses() для завершения процессов, отличных от снимка.

Взаимодействие с другими компонентами системы

WebSocket

Для Java существует стандартизированный API для работы с WebSocket.

Но одного API недостаточно, поэтому для запуска кода была выбрана одна из его реализаций – Tyrus.

Далее можно создать минималистичный сервер и указать обработчики (EndPoints).

Заготовка обработчика выглядит следующим образом:

HTTP-клиент

С выпуском 11-ой версии Java в ней появился удобный HTTP-клиент, поэтому потребность в сторонних клиентах исчезла.

Для создания экземпляра клиента необходимо воспользоваться билдером. В простейшем случае:

Далее необходимо создать запрос(request), например:

Затем этот запрос можно использовать для отправки на сервер:

Заключение

Благодаря модульной организации версий Java 9 и выше, утилите WinSW, обновлённому Process API для взаимодействия с процессами операционной системы и библиотеки JNA (Java Native Access), которая предоставляет программам Java простой доступ к нативным библиотекам, мы смогли создать Windows службу с использованием языка Java, на котором была реализована и серверная часть. Что в итоге позволило не вводить в процесс разработки новый язык.

Читайте также:  Как переименовать локального пользователя windows 10

Как с помощью Java выполнить Shell-команду

Обзор

В этом руководстве мы рассмотрим два способа выполнения shell -команд из программы на Java . Первый способ – использовать класс Runtime и вызвать его метод exec . Второй (более гибкий способ) – создать экземпляр класса ProcessBuilder .

Зависимость операционной системы

Сначала нужно определить операционную систему, на которой работает наша JVM . В Windows необходимо запустить команду в качестве аргумента оболочки cmd.exe, а в остальных ОС мы будем использовать стандартную оболочку sh:

Ввод и вывод

Также нужно подключиться к входным и выходным потокам нашего процесса. По крайней мере, нужно получить выходные данные, иначе процесс зависнет.

Реализуем класс StreamGobbler, который использует InputStream :

Примечание. Этот класс реализует интерфейс Runnable , а это означает, что он может быть выполнен любым исполнителем.

Runtime.exec()

Метод Runtime.exec() — это простой, но недостаточно гибкий способ создания нового подпроцесса .

В следующем примере мы запросим список пользователей из локальной директории и выведем его в консоли:

ProcessBuilder

Класс ProcessBuilder является более гибким в использовании, чем Runtime. Он позволяет настраивать целый ряд параметров.

  • изменить рабочий каталог, в котором работает shell-команда,
  • перенаправить потоки ввода и вывода;
  • наследовать их в потоках текущего процесса JVM, используя builder.inheritIO().

Заключение

Shell команды в Java можно выполнять двумя различными способами. Но если нужно настроить выполнение созданного процесса, то используйте класс ProcessBuilder .

Программные исходники примеров, приведенных в этой статье, доступны на GitHub .

Javist

Свежие записи

От runtime.exec к ProcessBuilder

Перед JDK 5.0, единственным способом Ответвляемся процесс и запустить его на местном User Runtime заключалась в использовании Exec () Метод java.lang.Runtime класс. JDK 5.0 добавляет новый способ выполнения команды в отдельном процессе, через класс под названием ProcessBuilder. Вы можете найти ProcessBuilder в java.lang пакетом (например, среды выполнения и процесс). Этот совет обсуждает и сравнивает обоих подходов.

Если вы знакомы со средой класс, вы знаете, что оно также позволяет обнаружить использование памяти и добавить выключение крючок. Но, пожалуй, самым популярным использование классов до 5,0 было выполнить команду в отдельном процессе. Это было сделано с помощью одного из шести версий Exec () метод просмотра:

Перед вызовом Exec () метод, можно указать команду и ее аргументы, настройки переменных окружения, а также рабочего каталога. Все версии метода возвращения java.lang.Process объект для управления созданной процесса. Это позволяет получить входной или выходной поток подпроцесс и статус выхода (помимо имеющейся информации).

Вот, например, DoRuntime, который показывает, как выполнить команду с оригинальным Время просмотра класс. Команда для запуска передается из командной строки.

import java.io.*;

public class DoRuntime <

public static void main ( String args []) throws IOException <

if ( args.length 0 ) <

System.err.println ( «Need command to run» ) ;

Runtime runtime = Runtime.getRuntime () ;

Process process = runtime.exec ( args ) ;

InputStream is = process.getInputStream () ;

InputStreamReader isr = new InputStreamReader ( is ) ;

BufferedReader br = new BufferedReader ( isr ) ;

System.out.printf ( «Output of running %s is:» ,

while (( line = br.readLine ()) != null ) <

Если вы запустите DoRuntime в Solaris так:

Вы получите вывод, который выглядит примерно следующим образом (в зависимости от содержимого каталога):

Linux пользователи также могут перейти в «Ls», как и команда, чтобы получить список каталогов.

На платформе Microsoft Windows, такие команды, как «Dir» являются внутренними для командного процессора, так что одним аргументом командной строки будет кавычки: «CMD / C Dir» (опять же, выход будет зависеть от содержимого каталога) .

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

Если вы хотите запускать команды в другую папку, и вам необходимо добавить больше аргументов Exec () команду, вы изменения:

Второй параметр в вызове EXEC () определяются методом переменных окружения. Поскольку параметр является «нулевой», подпроцесс наследует настройку среды для текущего процесса.

Так что же случилось с таким подходом? Зачем создавать новый подход? Проблема заключается в том, что подход Runtime.exec не всегда позволяют легко настроить и вызывать подпроцессов. Новый класс ProcessBuilder упрощает вещи. С помощью различных методов в классе, вы можете легко изменить переменные среды для процесса, и начало этому процессу.

Вот простое использование ProcessBuilder, который дублирует функции DoRuntime Пример:

import java.io.*;

public class DoProcessBuilder <

public static void main ( String args []) throws IOException <

if ( args.length 0 ) <

System.err.println ( «Need command to run» ) ;

Process process = new ProcessBuilder ( args ) .start () ;

InputStream is = process.getInputStream () ;

InputStreamReader isr = new InputStreamReader ( is ) ;

BufferedReader br = new BufferedReader ( isr ) ;

System.out.printf ( «Output of running %s is:» ,

while (( line = br.readLine ()) != null ) <

Обратите внимание, что следующие две строки в DoRuntime:

были изменены на следующие строки в DoProcessBuilder:

ProcessBuilder класс имеет два конструктора. Один конструктор принимает список для команды и ее аргументы. Другие конструктор принимает переменное количество аргументов строки.

С ProcessBuilder, вы вызываете Start (), чтобы выполнить команду. До вызова Start (), вы можете управлять тем, как этот процесс будет создан. Если вы хотите, чтобы начать процесс в другом каталоге, вы не проходите файла в качестве аргумента командной строки. Вместо этого, вы устанавливаете рабочую директорию процесса строителя путем передачи файлов в каталоге () метод:

Существует не очевидный метод тип сеттер в ProcessBuilder для установки переменных окружения. Вместо этого, вы получите карту с переменным через окружающую среду () метод, то можно манипулировать на карте:

Параметры для манипулирования окружающей среды включает в себя добавление переменных окружения с нанесенным () метод, и удаление их с удалить () метод. Например:

После задаются переменные среды и каталогов, начала звонка ():

Вы также можете Clear () все переменные с окружающей средой и четко установить те, что вы хотите.

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

Вы можете получить исходный набор переменных среды, вызывая GETENV () Метод системы. Поймите, что не все платформы поддерживают изменение переменных окружения. Если вы попытаетесь изменить переменные окружения на платформе, который запрещает это, операция будет бросать или UnsupportedOperationException или IllegalArgumentException. Кроме того, при работе с менеджером безопасности, вам нужно RuntimePermission для «GETENV .*», в противном случае SecurityException будет брошен.

Помнить, что нельзя забывать Start () после вызова Настройка инстанции. И имейте с использованием процесса класс для управления потоками за этот процесс и получить свой статус завершения.

Слова предостережения по поводу примеров на эту тему. Вполне возможно, что примерами в тупике, если подпроцесс генерирует достаточно вывод переполнения системы. Более надежное решение требует осушения STDOUT STDERR процесса и в отдельных потоках.

Для получения дополнительной информации о ProcessBuilder см. в определении класса.

Copyright (C) 2004-2005 Sun Microsystems, Inc
Все права защищены.

Читайте также:  Microsoft arc touch mouse windows 10
Оцените статью