Подтвердить что ты не робот

Поднять приложение Java во время работы

Отвратительная проблема возникла с моим программным обеспечением. Я делаю программу, которая взаимодействует с другим существующим программным обеспечением (игрой). Пользователь сообщил, что он запускает игру с правами администратора, и при таких обстоятельствах моя программа перестает работать для него.

Краткое исследование показало, что некоторым людям действительно нужно запускать игру под учетной записью администратора, а некоторые нет. Было бы здорово, если моя программа сможет обнаружить это и предупредить пользователя, если игра запущена под учетной записью администратора:

image description

Если пользователь нажимает "Поднять", я хотел бы попросить окна поднять java.exe, запустив мой файл jar, и вызвать типичное диалоговое окно UAC.

image description
Очевидно, на этот раз вопрос будет не о java updater, а JRE

Мой вопрос: возможно ли это? Могут ли окна повысить привилегию экземпляра java.exe? Есть ли у java способ сделать это? Или я могу использовать команду командной строки?

Я хочу избежать перезапуска программы (хотя это, вероятно, не было бы такой большой проблемой).

Edit: Если вы посмотрите в комментариях, вы увидите, что не избежать перезапуска приложения - процесс может только подняться, а не подняться. К сожалению, этот вопрос меняет вопрос. В принципе, теперь это звучит скорее: "Как перезапустить приложение с правами администратора?". Если, конечно, нет трюка, подобного двум java.exe, разделяющим одну банку...

4b9b3361

Ответ 1

Как указано в комментариях, к сожалению, Java (или любой другой процесс) не может быть повышен во время работы. Хотя в случае JWM теоретически возможно переместить весь контекст программы с обычного java.exe пользователя на повышенный, я не думаю, что это возможно. Надеюсь, когда-нибудь кто-нибудь придет и скажет мне, что я неправ.

Удивительно, но даже при перезапуске это была сложная задача, которая потребовала времени, чтобы понять.

Неаваальная часть

Во-первых, как мы точно запускаем программу, повышенную из командной строки? Там ответ, и вы можете видеть это не просто. Но мы можем разбить его на этот VBS script:

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

Вскоре выяснится, что не будет успешно работать с java.exe из VBS script. В конце концов, я решил запустить вспомогательный пакетный файл. Наконец, здесь (ответ на вопрос в последней ссылке) у нас есть полный набор из двух сценариев, которые действительно запускают данный файл .jar. Здесь улучшенная версия, которая позволяет быстро тестировать путем drag'n'dropping Jar file на нем:

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

Ответ 2

Если все еще интересно: в Windows 7 работает JavaElevator. Он поднимает запущенный Java-процесс, когда он используется в основном методе приложения Java. Просто добавьте -elevate в качестве последнего параметра программы и используйте лифт в основном методе.

Класс лифта:

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. Но, возможно, это указывает на кого-то в некотором расчете...

Ответ 3

Это моя версия. Он создает VBScript script, а затем выполняет его. Это работает только в том случае, если запущенная программа находится в файле 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 - я просто играю с ним).