java - para - manual de programacion android pdf
Elevar la aplicaciĆ³n Java mientras se ejecuta (3)
Un problema desagradable surgió con mi software. Estoy creando un programa que interactúa con otro software existente (un juego). El usuario ha informado que ejecuta el juego con privilegios de administrador y en esas circunstancias, mi programa deja de funcionar para él.
Una breve investigación reveló que algunas personas realmente necesitan ejecutar el juego con una cuenta de administrador y otras no. Sería genial si mi programa pudiera detectar esto y advertir al usuario si el juego se está ejecutando con una cuenta de administrador:
Si el usuario hace clic en "Elevar", me gustaría pedirle a windows que java.exe
el archivo java.exe
ejecuta mi archivo jar
e invoque el diálogo típico de UAC.
Obviamente, esta vez la pregunta no sería sobre el actualizador de Java sino JRE
Mi pregunta es: ¿Es esto posible? ¿Windows puede elevar el privilegio de mi instancia java.exe
? ¿Tiene Java una forma de hacerlo? ¿O puedo usar el comando de la línea de comandos?
Quiero evitar reiniciar el programa (aunque probablemente no sea tan importante).
Editar: Si observa los comentarios, verá que no se puede evitar el reinicio de una aplicación: el proceso solo puede comenzar de forma elevada, no de forma elevada . Esto un poco cambia la pregunta, por desgracia. Básicamente, ahora suena más como: " ¿Cómo reiniciar mi aplicación con derechos de administrador? ". A menos que, por supuesto, haya un truco como dos java.exe
compartiendo un tarro ...
Como se ha señalado en los comentarios, lamentablemente el Java (o cualquier otro proceso) no puede elevarse mientras se ejecuta. Mientras que en el caso de JWM, podría ser teóricamente posible mover todo el contexto del programa del usuario normal java.exe a uno elevado, no creo que sea posible. Espero que algún día alguien venga y me diga que estoy equivocado.
Sorprendentemente, incluso con el reinicio en su lugar, esta fue una tarea difícil que me tomó un tiempo para averiguar.
La parte no java
Primero, ¿cómo ejecutamos exactamente un programa elevado desde la línea de comandos? Hay una respuesta y puedes ver que no es simple. Pero podemos dividirlo en este script VBS:
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "program name", "command line parameters", "working directory", "runas", 1
Pronto, también resultará que no tendremos ningún éxito ejecutando java.exe desde el script VBS . Al final, decidí ejecutar un archivo por lotes de ayuda. Finalmente, here (respuesta a la pregunta en el último enlace) tenemos un conjunto completo de dos scripts que realmente ejecutan el archivo .jar
dado. Aquí hay una versión mejorada que permite pruebas rápidas al arrastrar y soltar el archivo Jar:
'' Require first command line parameter
if WScript.Arguments.Count = 0 then
MsgBox("Jar file name required.")
WScript.Quit 1
end if
'' Get the script location, the directorry where it''s running
Set objShell = CreateObject("Wscript.Shell")
strPath = Wscript.ScriptFullName
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strPath)
strFolder = objFSO.GetParentFolderName(objFile)
''MsgBox(strFolder)
'' Create the object that serves as runnable something
Set UAC = CreateObject("Shell.Application")
'' Args:
'' path to executable to run
'' command line parameters - first parameter of this file, which is the jar file name
'' working directory (this doesn''t work but I use it nevertheless)
'' runas command which invokes elevation
'' 0 means do not show the window. Normally, you show the window, but not this console window
'' which just blinks and disappears anyway
UAC.ShellExecute "run-normally.bat", WScript.Arguments(0), strFolder, "runas", 0
WScript.Quit 0
La parte de Java
La parte de Java es más sencilla. Lo que debemos hacer es abrir un nuevo proceso y ejecutar los scripts preparados en él.
/**
* Start this very jar file elevated on Windows. It is strongly recommended to close any existing IO
* before calling this method and avoid writing anything more to files. The new instance of this same
* program will be started and simultaneous write/write or read/write would cause errors.
* @throws FileNotFoundException if the helper vbs script was not found
* @throws IOException if there was another failure inboking VBS script
*/
public void StartWithAdminRights() throws FileNotFoundException, IOException {
//The path to the helper script. This scripts takes 1 argument which is a Jar file full path
File runAsAdmin = new File("run-as-admin.vbs");;
//Our
String jarPath;
//System.out.println("Current relative path is: " + s);
try {
jarPath = "/""+new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath()+"/"";
} catch (URISyntaxException ex) {
throw new FileNotFoundException("Could not fetch the path to the current jar file. Got this URISyntax exception:"+ex);
}
//If the jar path was created but doesn''t contain .jar, we''re (most likely) not running from jar
//typically this happens when running the program from IDE
//These 4 lines just serve as a fallback in testing, should be deleted in production
//code and replaced with another FileNotFoundException
if(!jarPath.contains(".jar")) {
Path currentRelativePath = Paths.get("");
jarPath = "/""+currentRelativePath.toAbsolutePath().toString()+"//AutoClient.jar/"";
}
//Now we check if the path to vbs script exists, if it does we execute it
if(runAsAdmin.exists()) {
String command = "cscript /""+runAsAdmin.getAbsolutePath()+"/" "+jarPath;
System.out.println("Executing ''"+command+"''");
//Note that .exec is asynchronous
//After it starts, you must terminate your program ASAP, or you''ll have 2 instances running
Runtime.getRuntime().exec(command);
}
else
throw new FileNotFoundException("The VBSScript used for elevation not found at "+runAsAdmin.getAbsolutePath());
}
Esta es mi versión. Crea un script VBScript, luego lo ejecuta. Esto solo funciona si el programa que se está ejecutando está en un archivo jar, por lo que tendrá que ejecutar su IDE como administrador para probar su programa.
public static void relaunchAsAdmin() throws IOException {
relaunchAsAdmin(ThisClass.class); //Change ThisClass to the class that this method is in
}
public static void relaunchAsAdmin(Class<?> clazz) throws IOException {
if(isCurrentProcessElevated()) {
return;
}
final String dir = System.getProperty("java.io.tmpdir");
final File script = new File(dir, "relaunchAsAdmin" + System.nanoTime() +
".vbs");
try {
script.createNewFile();
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(script));
osw.append("Set s=CreateObject(/"Shell.Application/")" + ln + "s.ShellExecute /"" +
System.getProperty("java.home") + "//bin//java.exe" + "/",/"-jar /"/"" +
new File(clazz.getProtectionDomain().getCodeSource(
).getLocation().toURI()).getAbsolutePath() + "/"/"/",,/"runas/",0" +
ln + "x=createObject(/"scripting.fileSystemObject/").deleteFile(" +
"WScript.scriptfullname)");
osw.close();
if(System.getenv("processor_architecture").equals("x86")) {
Runtime.getRuntime().exec("C://Windows//System32//wscript.exe /"" +
script.getAbsolutePath() + "/"");
} else {
Runtime.getRuntime().exec("C://Windows//SysWoW64//wscript.exe /"" +
script.getAbsolutePath() + "/"");
}
} catch(URISyntaxException e) {
e.printStackTrace();
}
Runtime.getRuntime().exit(0);
}
Tenga en cuenta que es un poco desordenado. He estado usando este método anteriormente, por lo que se ha ajustado a 100 caracteres (excepto el comentario que escribí para esta respuesta). los
isCurrentProcessElevated()
El método tendrá que ser implementado de una manera u otra. Podría intentar usar JNI, o podría usar un método Java puro, como escribir en el directorio de Archivos de programa o System32 y ver si falla.
Obviamente, esta solución solo funcionará en Windows. Nunca tuve que elevarme a los sistemas Linux o Mac (principalmente porque no tengo ningún sistema Mac y no uso Linux, solo juego con él).
Si todavía te interesa: en Windows 7 funciona mi JavaElevator. Eleva un proceso Java en ejecución cuando se usa en el método principal de la aplicación Java. Simplemente agregue -elevate
como último parámetro del programa y use el elevador en el método principal.
La clase de ascensor:
package test;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.ShellAPI;
import com.sun.jna.platform.win32.WinDef;
/**
* Elevates a Java process to administrator rights if requested.
*/
public class JavaElevator {
/** The program argument indicating the need of being elevated */
private static final String ELEVATE_ARG = "-elevate";
/**
* If requested, elevates the Java process started with the given arguments to administrator level.
*
* @param args The Java program arguments
* @return The cleaned program arguments
*/
public static String[] elevate(String[] args) {
String[] result = args;
// Check for elevation marker.
boolean elevate = false;
if (args.length > 0) {
elevate = args[args.length - 1].equals(ELEVATE_ARG);
}
if (elevate) {
// Get the command and remove the elevation marker.
String command = System.getProperty("sun.java.command");
command = command.replace(ELEVATE_ARG, "");
// Get class path and default java home.
String classPath = System.getProperty("java.class.path");
String javaHome = System.getProperty("java.home");
String vm = javaHome + "//bin//java.exe";
// Check for alternate VM for elevation. Full path to the VM may be passed with: -Delevation.vm=...
if (System.getProperties().contains("elevation.vm")) {
vm = System.getProperty("elevation.vm");
}
String parameters = "-cp " + classPath;
parameters += " " + command;
Shell32.INSTANCE.ShellExecute(null, "runas", vm, parameters, null, 0);
int lastError = Kernel32.INSTANCE.GetLastError();
if (lastError != 0) {
String errorMessage = Kernel32Util.formatMessageFromLastErrorCode(lastError);
errorMessage += "/n vm: " + vm;
errorMessage += "/n parameters: " + parameters;
throw new IllegalStateException("Error performing elevation: " + lastError + ": " + errorMessage);
}
System.exit(0);
}
return result;
}
}
Uso en el método principal de la aplicación Java:
public static void main(String[] args) {
String[] args1 = JavaElevator.elevate(args);
if (args1.length > 0) {
// Continue as intended.
...
Sé que esta es una implementación muy básica, suficiente para uno de mis problemas diarios: iniciar un proceso elevado desde Eclipse. Pero tal vez apunte a alguien en alguna dirección ...