17

Java アプリケーションで、ユーザーが定義したパスを持つ 1 ~ 3 個の外部プログラムを開始する必要があります。私にはいくつかの要件があります:

  1. すでに実行中のプログラムを実行したくない

  2. Java アプリケーションからどのプログラムにもフォーカスを奪われたくない

  3. それらのいずれかが起動に失敗したかどうかは気にしません。彼らはただ静かに失敗する必要があります。

これが私がこれまでに思いついたものです:

ProcessBuilder pb = new ProcessBuilder(userDefinedPath1);
try {
    pb.start();
}
catch (Exception e) {
    // Something went wrong, just ignore
}

そして、他の 2 つのパスでそれをさらに 3 回繰り返します。これは期待どおりに始まり、3 番目の要件を問題なく満たしていますが、最初の 2 つで失敗します。

これを行う最善の方法は何ですか?

編集:

  1. これらの他のアプリを制御することはできません。彼らはサードパーティです。また、いつでもユーザーが手動で開始または停止できた可能性があります。

  2. 実行可能ファイルの正確な名前 (「blah.exe」など) はわかっており、それらは常に同じですが、実行可能ファイルへのパスは必ずしも同じではありません。

  3. ここでは、バッチ ファイル ラッパーは実行できません。

  4. 他のアプリはJava アプリではなく、単なる古い Windows 実行可能ファイルです。

4

9 に答える 9

6

他の 2 つのアプリを制御できないと思います...もし制御できたとしても、それほど悪くはありません。ソケットをリッスンさせて、あなたが来たときにソケットが使用可能かどうかを確認するだけで済みます。上。

次の解決策は、実際には言語に依存しない可能性があります。バッチ ファイル ラッパーを使用してシステム全体を管理できます。起動時にファイルを作成し、停止時にファイルを削除するバッチファイルを作成します。Unix システムでは、この手法が頻繁に使用されます。つまり、このファイルをロック ファイルと呼ぶことがよくあります。

あなたのアプリだけがこれらの他のアプリを開始する場合、それを開始したかどうかを単純に追跡することができます。ユーザーは、他のメカニズムを介してこれらのプログラムを起動した可能性があります。

他のアプリの起動を制御できず、それらを起動するためのバッチ ファイルを作成することさえできない場合は、やりたいことを実行できません (アプリは常にバッチ ファイルを使用する必要があることに注意してください)。 、ユーザーが手動で開始した場合でも)。

プロセスのステータスを取得して解析するのは最後の努力かもしれませんが、PS で他のアプリが何と呼ばれていたかを正確に知る必要があります。これは簡単なことではありません。また、すべての Java アプリは、ほとんどのプロセス ステータスの出力でまったく同じ署名を持つ傾向があるため、これが役に立たなくなる可能性があります。

問題は、これらのプログラムの 1 つがアプリの外部で開始された場合、それが正確なプロセス ステータス シグネチャであることがたまたまわかっていない限り、その事実を特定する方法がほとんどないことです。

于 2009-03-18T22:16:09.837 に答える
6

Linux用の2つの回答を提供します。

プログラムが既に実行されている場合は実行しないでください。これを Main.java というファイルに入れます。

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

class JustOneLock {
  FileLock lock;
  FileChannel channel;

  public boolean isAppActive() throws Exception{
    File file = new File(System.getProperty("user.home"),
            "FireZeMissiles1111" + ".tmp");
    channel = new RandomAccessFile(file, "rw").getChannel();

    lock = channel.tryLock();
    if (lock == null) {
      return true;
    }
    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          lock.release();
          channel.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
    return false;
  }
}

public class Main {
  public static void main(String[] args)throws Exception {
    JustOneLock u = new JustOneLock();

    if (u.isAppActive()) {
      System.out.println("Already active, stop!");
      System.exit(1);
    }
    else {
      System.out.println("NOT active... Do hard work for 5 seconds.");
      try{Thread.sleep(5000);}catch(Exception e){}
    }
  }
}

コンパイルして実行します。次に、新しいターミナルを開き、他のターミナルが実行されている間にもう一度実行しようとしますが、実行されません。

Windows の別の答え

このプログラムは、現在のシステムで既に実行されている場合、実行できません。これは、Windows のみのシステム用です。

import java.io.*;
import java.util.prefs.Preferences;

public class JavaApplication3 {

    public static void main(String[] args){
        if(isRunning()){
            System.out.println("Two instances of this program cannot " +
                    "be running at the same time.  Exiting now");
        }
        else{
            onStart();
            epicHeavyWorkGoesHere();
            onFinish();
        }
    }
    public static void epicHeavyWorkGoesHere(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {}
    }
    public static void onStart(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");
        prefs.put("RUNNINGPID", getCurrentPID());
    }
    public static void onFinish(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");
        prefs.put("RUNNINGPID", "");
    }
    public static boolean isRunning(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");

        if (prefs.get("RUNNINGPID", null) == null || prefs.get("RUNNINGPID", null).equals(""))
            return false;

        if (isProcessIdRunningOnWindows(Integer.parseInt(prefs.get("RUNNINGPID", null))))
            return true;
        return false;
    }
    public static String getCurrentPID(){
        //This function is designed to get the PID from the windows system, it may
        //not work for Linux or Mac.  You'll have to acquire a suitable getCurrentPID function
        try{
            java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
            java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
            jvm.setAccessible(true);
            sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
            java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
            pid_method.setAccessible(true);
            return pid_method.invoke(mgmt) + "";
        }
        catch(Exception e){
            throw new RuntimeException("Cannot get the current PID");
        }
    }
    public static boolean isProcessIdRunningOnWindows(int pid){
        //This Function only works for windows, if you want it to work on linux
        //you will have to go find a replacement method that takes the processID
        //as a parameter and spits out a true/false if it is running on the system.
        try {
            Runtime runtime = Runtime.getRuntime();
            String cmds[] = {"cmd", "/c", "tasklist /FI \"PID eq " + pid + "\""};
            Process proc = runtime.exec(cmds);

            InputStream inputstream = proc.getInputStream();
            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
            String line;
            while ((line = bufferedreader.readLine()) != null) {
                if (line.contains(" " + pid + " ")){
                    return true;
                }
            }
            return false;
        }
        catch (Exception ex) {
            throw new RuntimeException("Cannot run the tasklist command to query if a pid is running or not");
        }
    }
}

上記のコードの戦略は、最後の実行から PID を維持することです。その PID がシステムで実行されていることが判明した場合は、開始しないでください。終わったらリセット。

設定は Windows レジストリに保存されます。HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs

ファイル ロックを使用して Java アプリケーションが同時に 2 回実行されないようにすることをお勧めします。プログラムがクラッシュまたは永久にハングして強制終了された場合、ロックは一貫性のない状態のままになり、再起動しても存続する可能性があるためです。プログラムは、まだ実行中のプログラムと、クラッシュしてロックされたファイルをロックしたままにしているプログラムとの違いをどのように認識するのでしょうか?

于 2013-08-02T04:31:11.350 に答える
3

これは私が問題を解決するためにしたことです、それは純粋なJavaを使用するための簡単な解決策です:

次のコードが一度クラッシュすると、一貫性のない状態のままになり、ハングしたプログラムやクラッシュしたプログラムを処理する方法がないことに注意してください。

public void main(String[] args){
    if(isRunning()){
        JOptionPane.showMessageDialog(this, "2 instances of this program cannot be running at the same time. \n Exiting now");
        System.exit(0);
    }else{
        onStart();
    }
}    

public final void onStart(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    prefs.put("RUNNING", "true");
}

public final void onFinish(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    prefs.put("RUNNING", "false");
}

public boolean isRunning(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    return prefs.get("RUNNING", null) != null ? Boolean.valueOf(prefs.get("RUNNING", null)) : false;
}

private void formWindowClosing(java.awt.event.WindowEvent evt) {
    onFinish();
}
于 2012-02-01T15:24:34.120 に答える
1

UNIX ベースのシステムでこの問題に対処しているとしたら、ネイティブ関数を記述してプロセス情報を収集し、外部アプリケーションの起動を試みたいと思うでしょう。これは Java の精神に反しますが、収集しようとしている情報の種類とアプリケーションの起動の制御は JVM の外部にあります。

私は Windows プログラミングについて十分な知識を持っているわけではありませんが、.NET 言語または単純な C++ でアクセスできる Windows API 呼び出しが 2 番目の基準に役立つ可能性があると思います。おそらく、質問を変更して、助けてくれる Java 以外の開発者を引き付けることができます。


編集:

Windows APIのShell Execute Functionを確認してください。SW_SHOWNOACTIVATE オプションを使用すると、現在のウィンドウをアクティブのままにしておくことができます。

于 2009-03-18T23:31:48.253 に答える
1

プログラムまたはプロセスが実行されているかどうかを確認する (Windows)を参照してください。

このヒントは、特定のプログラムが実行されているかどうかを検出します。vbscript のせいで見栄えはよくありませんが、実装は簡単です。

于 2009-03-18T23:35:09.413 に答える
1

すべてのシステムに対してできる小さなことが 1 つあります。これには、ファイル ロック機能が含まれます。

プログラムの実行時 まず、ファイルを作成してロックします。lockfile のような名前を付けます。タイムスタンプを生成して暗号化します。これに書き込みます。

同じプログラムの実行時 まず、ロック ファイルが存在するかどうかを確認します。ロックしてみてください。このファイルがロックされている可能性は無視してください。タイムスタンプを読み取って確認します。タイムスタンプが非常に小さい場合 (1 時間前など)、プログラムを実行してみてください。ロックされているため、タイムスタンプを書き戻すとエラーが発生します。この場合、すでに開いているプログラムがあるとします。

プログラムの終了時に、このロック ファイルを削除します。

于 2016-05-04T22:55:28.890 に答える
0

jps を使用して、実行中の Java プログラムのステータスを知ることができます。jps は、javac、java などの Java ツールの 1 つです。

于 2009-07-14T08:29:18.347 に答える
0

Windows XP (pro?) では、コマンド「tasklist」を起動し、出力を解析して、プロセスが実行されているかどうかを判断できます。フォーカスの問題を回避するためにスレッドを使用できます(私は思います)

于 2009-03-23T07:43:21.103 に答える