在运行时提升Java应用程序
我的软件出现了一个严重的问题。我正在制作一个与另一个现有软件(游戏)交互的程序。用户报告说他以管理员权限运行游戏,在这种情况下,我的程序将停止为他工作 短期调查显示,有些人确实需要在管理员帐户下运行游戏,而有些人则不需要。如果我的程序能够检测到这一点并在游戏以管理员帐户运行时向用户发出警告,那就太好了: 如果用户单击“提升”,我想让windows提升运行我的在运行时提升Java应用程序,java,windows,admin,Java,Windows,Admin,我的软件出现了一个严重的问题。我正在制作一个与另一个现有软件(游戏)交互的程序。用户报告说他以管理员权限运行游戏,在这种情况下,我的程序将停止为他工作 短期调查显示,有些人确实需要在管理员帐户下运行游戏,而有些人则不需要。如果我的程序能够检测到这一点并在游戏以管理员帐户运行时向用户发出警告,那就太好了: 如果用户单击“提升”,我想让windows提升运行我的jar文件的java.exe,并调用典型的UAC对话框 显然,这次的问题不是关于java更新程序,而是关于JRE 我的问题是:这可能吗?
jar
文件的java.exe
,并调用典型的UAC对话框
显然,这次的问题不是关于java更新程序,而是关于JRE 我的问题是:这可能吗?windows能否提升我的
java.exe
实例的权限?java有办法做到这一点吗?或者我可以使用命令行命令吗
我希望避免重新启动程序(尽管这可能不是什么大问题)
编辑:
如果您查看这些注释,您会发现无法避免重新启动应用程序-流程只能从提升状态开始,而不能从提升状态开始。不幸的是,这有点转移了问题。基本上,它现在听起来更像是:“如何使用管理员权限重新启动我的应用程序?”。当然,除非有一个技巧像两个
java.exe
共享一个jar…注释中指出的那样,否则遗憾的是java(或任何其他进程)在运行时无法提升。在JWM的例子中,理论上可以将整个程序上下文从普通用户java.exe移动到提升的java.exe,但我认为这是不可能的。我希望有一天有人会来告诉我我错了
令人惊讶的是,即使重启到位,这也是一项棘手的任务,我花了一段时间才弄明白
非java部分
首先,我们如何准确地运行从命令行提升的程序?你可以看到这并不简单。但我们可以将其分解为以下VBS脚本:
Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "program name", "command line parameters", "working directory", "runas", 1
很快,我们也发现了。最后,我决定运行一个helper批处理文件。最后,(回答最后一个链接中的问题)我们有一整套两个脚本,它们真正运行给定的.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部分
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);
}
方法必须以某种方式实现。您可以尝试使用JNI,也可以使用纯Java方法,例如在Program Files或System32目录中写入,并查看是否失败
显然,此解决方案仅适用于Windows。我从不需要在Linux或Mac系统上升级(主要是因为我没有任何Mac系统,也不使用Linux-我只是在玩它)。如果仍然感兴趣的话:在Windows 7中,我的Java电梯可以工作。当在Java应用程序的主方法中使用时,它提升了正在运行的Java进程。只需添加
-提升
作为最后一个程序参数,并在主方法中使用电梯
电梯等级:
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启动一个提升的进程。但也许它指向了某些人的方向…我不打算把这个问题作为一个重复的问题来结束,因为这仍然是一个很好的问题,而且措辞略有不同,但有一些问题是这样的:,。实际上还有很多其他问题与这个问题有关,我还在读另一个问题。谢谢你再给我两个!:)目前,我至少在重新启动变体中工作,而且它一点也不简单。你能做的最好的事情就是@SergeyTachenov这里的答案暗示了一些我直到现在都不知道的事情——你不能提升运行过程。因此,我认为需要重新启动。在这里之前,我会问这个问题->[],你可以做的是启动你的java应用程序,然后检查它是否有权限,如果没有权限,然后重新启动自己,要求获得正确的权限(但需要重新启动)。你可以制作一个启动器来检查游戏的权限。然后,启动器以相同的权限调用java应用程序,使用ProcessBuilder和适当的标志,这基本上是重新启动java应用程序。在进程运行时提升进程是不可能的。如果要在重新启动后恢复当前状态,可以创建一个临时文件,并检查当前进程是否已提升。
在哪里正常运行。bat
?@CardinalSystem我不知道,我发布此消息已有一段时间了。但是runnormal.bat
只包含了类似java应用程序jar path
或者运行jar文件的任何命令。谢谢,这帮了大忙。别忘了在类路径中加引号,以防它包含空格。。。
public static void main(String[] args) {
String[] args1 = JavaElevator.elevate(args);
if (args1.length > 0) {
// Continue as intended.
...