运行时提升Java应用程序

Tom*_*ica 23 java windows admin

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

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

图片描述

如果用户单击"Elevate",我想要求windows提升java.exe运行我的jar文件并调用典型的UAC对话框.

图片描述
显然,这次问题不是关于java更新程序而是JRE

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

我想避免重新启动程序(尽管它可能不是那么重要).

编辑: 如果您查看注释,您将看到无法避免重新启动应用程序 - 进程只能启动升级而不会升高.不幸的是,这有点改变了这个问题.基本上,现在听起来更像是:" 如何使用管理员权限重新启动我的应用程序? ".当然,除非有两个java.exe共享一个罐子的技巧......

小智 8

如果仍然感兴趣:在 Windows 7 中,我的 JavaElevator 可以工作。当在 Java 应用程序的 main 方法中使用时,它会提升正在运行的 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在 Java 应用程序的 main 方法中的用法:

public static void main(String[] args) {
    String[] args1 = JavaElevator.elevate(args);
    if (args1.length > 0) {
       // Continue as intended.
       ... 
Run Code Online (Sandbox Code Playgroud)

我知道,这是一个非常基本的实现 - 足以解决我的日常问题之一:从 Eclipse 启动一个提升的进程。但也许它指向某个方向的人......


Tom*_*ica 3

正如评论中所指出的,遗憾的是 Java(或任何其他进程)在运行时无法提升。虽然在 JWM 的情况下,理论上可以将整个程序上下文从普通用户 java.exe 移动到提升的用户,但我认为这是不可能的。我希望有一天有人会来告诉我我错了。

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

非java部分

首先,我们如何准确地运行从命令行提升的程序?答案是有的,你会发现它并不简单。但我们可以将其分解为以下 VBS 脚本:

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

很快,事实证明我们无法从 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
Run Code Online (Sandbox Code Playgroud)

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());
   }
Run Code Online (Sandbox Code Playgroud)