web-dev-qa-db-ja.com

実行中にJavaアプリケーションを昇格する

私のソフトウェアで厄介な問題が発生しました。別の既存のソフトウェア(ゲーム)と相互作用するプログラムを作成しています。ユーザーから、管理者権限でゲームを実行しているとの報告がありました。その場合、私のプログラムは動作しなくなります。

簡単な調査により、管理者アカウントでゲームを実行する必要がある人もいれば、そうでない人もいることが明らかになりました。私のプログラムがこれを検出し、ゲームが管理者アカウントで実行されている場合にユーザーに警告できると便利です。

image description

ユーザーが[昇格]をクリックした場合、ウィンドウにjarファイルを実行しているJava.exeを昇格させ、通常のUACダイアログを呼び出すように依頼したいと思います。

image description
明らかに、今回の質問はJavaアップデーターではなく、JRE

私の質問は:これは可能ですか? Windowsは私のJava.exeインスタンスの特権を上げることができますか? Javaそれを行う方法はありますか?またはコマンドラインコマンドを使用できますか?

プログラムの再起動は避けたいです(おそらくそれほど大したことではないでしょうが)。

編集:コメントを見ると、アプリケーションの再起動を回避することはできないことがわかります-プロセスはstartしかできません-)昇格ではなく昇格になります。残念ながら、これは質問を少し変えます。基本的に、「管理者権限でアプリケーションを再起動する方法は?」のように聞こえます。もちろん、2つのJava.exe1つのjarファイルを共有するようなトリックがない限り...

コメントで指摘されているように、悲しいことに、実行中にJava(または他のプロセス)を昇格させることはできません。JWMの場合、理論的にはプログラムコンテキスト全体を通常から移動することが可能です。ユーザーJava.exeを昇格したものに変更することは不可能だと思います。いつか誰かが来て、私が間違っていると言ってくれることを願っています。

驚いたことに、restartが設定されていても、これは難しい作業であり、理解するのに時間がかかりました。

非Java部分

まず、コマンドラインから昇格したプログラムを正確に実行するにはどうすればよいですか? 答えがあります そしてあなたはそれが単純ではないことがわかります。しかし、私たちはそれをこのVBSスクリプトに分解することができます:

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

すぐに、私たち VBSスクリプトからJava.exeを実行しても成功しません であることがわかります。結局、ヘルパーバッチファイルを実行することにしました。最後に、 ここ (最後のリンクの質問への回答)指定された.jarファイルを実際に昇格して実行する2つのスクリプトの完全なセットがあります。 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());
   }

それでも関心がある場合: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アプリケーションのmainメソッドでの使用法:

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

これは非常に基本的な実装であり、日常の問題の1つであるEclipseからの昇格されたプロセスの開始には十分です。しかし多分それは誰かをある方向に向けています...

6
KaBe

これは私のバージョンです。 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を使​​用してみるか、ProgramFilesまたはSystem32ディレクトリに書き込んで失敗するかどうかを確認するなどの純粋なJavaメソッドを使用することができます。

明らかに、このソリューションはWindowsでのみ機能します。 LinuxまたはMacシステムで昇格する必要はありませんでした(主に、Macシステムがなく、Linuxを使用していないためです。Linuxで遊んでいます)。

0
TheRandomLabs