运行时提升Java应用程序

我的软件出现了一个令人讨厌的问题。 我正在制作与其他现有软件(游戏)交互的程序。 用户报告说他以pipe理员权限运行游戏,在这种情况下,我的程序停止工作。

简短的调查显示,有些人确实需要在pipe理员帐户下运行游戏,有些则不需要。 如果我的程序能够检测到这一点,并且如果游戏以pipe理员帐户运行,则警告用户:

图片描述

如果用户点击“Elevate”,我想问Windows提升运行我的jar文件的java.exe并调用典型的UAC对话框。

图片描述
很明显,这次的问题不是关于Java更新,而是JRE

我的问题是:这可能吗? Windows可以提升我的java.exe实例的权限? java有办法做到这一点? 或者我可以使用命令行命令?

我想避免重新启动程序(尽pipe这可能不是那么重要)。

编辑:如果你看看评论,你会看到没有避免重新启动一个应用程序 – 进程只能开始提升,而不是boost 。 这不幸的是,这个问题转移了问题。 基本上,现在听起来更像是:“ 如何用pipe理员权限重新启动我的应用程序? ”。 当然,除非有两个java.exe共享一个jar的窍门…

正如已经指出的那样,遗憾的是Java(或其他进程)在运行时不能被提升。 而在JWM的情况下,理论上可以将整个程序上下文从普通用户java.exe升级到高级,我不认为这是可能的。 我希望有一天有人会来告诉我我错了。

令人惊讶的是,即使重新启动 ,这也是一个棘手的任务,花了我一段时间才弄清楚。

非java部分

首先,我们如何精确地运行从命令行提升的程序? 有一个答案 ,你可以看到这并不简单。 但是我们可以把它分解成这个VBS脚本:

 Set UAC = CreateObject("Shell.Application") UAC.ShellExecute "program name", "command line parameters", "working directory", "runas", 1 

不久,我们就不会有任何成功从VBS脚本运行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部分

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 asynchronousous //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()); } 

如果仍然感兴趣:在Windows 7中我的JavaElevator的作品。 当在Java应用程序的主要方法中使用时,它将提升正在运行的Java进程。 简单地添加-elevate作为最后一个程序参数,并在主要方法中使用电梯。

电梯级别:

 package test; import com.sun.jna.Native; import com.sun.jna.platform.win32.coreel32; import com.sun.jna.platform.win32.coreel32Util; 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 = coreel32.INSTANCE.GetLastError(); if (lastError != 0) { String errorMessage = coreel32Util.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开始提升进程。 但也许它指向某个人在一个dicrection …

这是我的版本。 它创建一个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 – 我只是玩它)。