Most of the applications I develop are web based and therefore, when deployed on Windows, are running as a service. This is great most of the time but when I want to programmatically control the application it can present some problems because starting and stopping services requires elevated permissions. This article discusses a solution that I’ve come up with that I’m happy to deploy.
This problem has to be tackled from several angles in order to get a solution. Firstly, Java can’t directly interact with the system in order to start and stop services so it will have to make use of one of two command line utilities provided by Windows: net.exe or sc.exe. Both utilities are capable of starting and stopping services but the second sc (Service Control) is much more capable and can provide more information about a service.
In order to run an external application you need to invoke a runtime exec from your Java application which can be as simple as:
Runtime.getRuntime().exec( "some_command" );
In our case it’s a little more complex as not only to we need to execute an external command we also need to do it with elevated permissions. To elevate a command we need to use a little utility executable called elevate.exe (there are several available out there). This executable is marked as requiring elevated permissions and therefore will cause the UAC prompt to appear when it is called. All it does is then execute whatever command it has been given as an argument. Since permissions flow from parent to child the command in the argument also gets elevated.
A code snippet showing basic usage is shown below.
String[] stop = {"elevate_x64.exe", "cmd.exe", "/c", "sc", "stop", "ServiceA"}; Process p = Runtime.getRuntime().exec(stop); p.waitFor(); BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); }
While good this solution isn’t perfect. For a start there are some limitations to what can be passed to elevate.exe because of the way arguments are handled in Windows but for simple tasks like this it works perfectly. The other problem I have with this solution is that there is no way that I can find to simply say “this command will always run as an administrator”. Marking the executable as “Run as Administrator” thinking it will prevent you from being pestered with a UAC box won’t work all that happens if the Java process then can’t run the elevate.exe command. This second issue probably prevents this technique from being used in a web application as there would be no one available to click the UAC yes button but for a desktop application it’s a workable solution.
References
- http://stackoverflow.com/questions/4662574/how-do-i-elevate-my-uac-permissions-from-java
- http://stackoverflow.com/questions/1076794/uac-and-java
- http://stackoverflow.com/questions/1385866/java-run-as-administrator
- http://stackoverflow.com/questions/9075098/start-windows-service-from-java
- http://jpassing.com/2007/12/08/launch-elevated-processes-from-the-command-line/