[英]Elevate Java application while running
我的軟件突然出現一個令人討厭的問題。 我正在制作一個與另一個現有軟件(游戲)交互的程序。 用戶報告說他以管理員權限運行游戲,在這種情況下,我的程序停止為他工作。
簡短的調查顯示,有些人確實需要以管理員帳戶運行游戲,而有些則不需要。 如果我的程序能夠以管理員帳戶運行游戲,並且我的程序能夠檢測到並警告用戶,那就太好了:
如果用戶單擊“提升”,我想讓Windows提升運行我的jar
文件的java.exe
並調用典型的UAC對話框。
顯然,這一次的問題不是Java更新程序,而是JRE
我的問題是:這可能嗎? Windows可以提升我的java.exe
實例的特權嗎? java有辦法嗎? 還是可以使用命令行命令?
我想避免重新啟動程序(盡管這可能沒什么大不了的)。
編輯:如果您在評論中查看,您將看到無法避免重新啟動應用程序-進程只能啟動提升,而不能提升 。 不幸的是,這有點改變了問題。 基本上,現在聽起來更像是:“ 如何以管理員權限重新啟動應用程序? ”。 除非,當然,除非有像兩個java.exe
共享一個jar這樣的技巧...
如果仍然感興趣:在Windows 7中,我的JavaElevator可以工作。 當在Java應用程序的主要方法中使用時,它將提升正在運行的Java進程。 只需添加-elevate
作為最后一個程序參數,然后在main方法中使用電梯。
電梯類:
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;
}
}
Java應用程序主要方法中的用法:
public static void main(String[] args) {
String[] args1 = JavaElevator.elevate(args);
if (args1.length > 0) {
// Continue as intended.
...
我知道,這是一個非常基本的實現-足以滿足我的日常需求:從Eclipse啟動提升的進程。 但這也許表明某人有些偏執...
正如評論中指出的那樣,不幸的是,Java(或任何其他進程)在運行時無法提升。 在JWM的情況下,從理論上將整個程序上下文從普通用戶java.exe移動到提升的上下文是有可能的,但我認為這是不可能的。 我希望有一天有人會來告訴我我錯了。
令人驚訝的是,即使重新啟動 ,這也是一項棘手的任務,我花了一些時間才弄清楚。
首先,我們如何准確運行從命令行提升的程序? 有一個答案 ,您會發現這並不簡單。 但是我們可以將其分解為以下VBS腳本:
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "program name", "command line parameters", "working directory", "runas", 1
很快,事實證明, 從VBS script運行java.exe不會成功 。 最后,我決定運行一個助手批處理文件。 最后, 在這里 (最后一個鏈接中的問題答案),我們有兩個完整的腳本集合,它們確實運行提升的給定.jar
文件。 這是經過改進的版本,可以通過拖放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
Java部分更為簡單。 我們需要做的是打開新流程並在其中執行准備好的腳本。
/**
* 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());
}
這是我的版本。 它創建一個VBScript腳本,然后執行它。 僅當正在運行的程序在jar文件中時,此方法才有效,因此您將必須以管理員身份運行IDE才能實際測試您的程序。
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);
}
請注意,這有點混亂。 我以前一直在使用此方法,因此已將其換行為100個字符(我為此答案寫的注釋除外)。 的
isCurrentProcessElevated()
方法必須以一種或另一種方式實現。 您可以嘗試使用JNI,也可以使用純Java方法,例如在Program Files或System32目錄中編寫並查看它是否失敗。
顯然,此解決方案僅適用於Windows。 我從來不需要在Linux或Mac系統上升級(主要是因為我沒有任何Mac系統,並且我不使用Linux-我只是在玩它)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.