控制台关闭时调用方法(Java)

控制台关闭时调用方法(Java),java,console,exit,Java,Console,Exit,我有一个在命令窗口中运行的应用程序。我有一个扫描仪,它检测用户何时键入/quit,并运行相应的quit()方法 这非常有效,但是有没有办法确定用户是否在不输入/quit的情况下关闭控制台,并且仍然运行quit()?使用关机挂钩。仍然不能100%保证它会被调用,但这是您可以尝试捕捉关机的最佳方式。一般来说,您应该在关机挂钩中尽可能做到绝对最小。关闭资源,删除一些日志记录语句或printstatements,但不要做任何需要很长时间的事情 我复制并稍微修改了Robert在这里找到的答案,以匹配您的代

我有一个在命令窗口中运行的应用程序。我有一个
扫描仪
,它检测用户何时键入
/quit
,并运行相应的
quit()
方法


这非常有效,但是有没有办法确定用户是否在不输入
/quit
的情况下关闭控制台,并且仍然运行
quit()

使用关机挂钩。仍然不能100%保证它会被调用,但这是您可以尝试捕捉关机的最佳方式。一般来说,您应该在关机挂钩中尽可能做到绝对最小。关闭资源,删除一些日志记录语句或printstatements,但不要做任何需要很长时间的事情

我复制并稍微修改了Robert在这里找到的答案,以匹配您的代码。您应该将此代码放在您有权调用quit()的地方。您应该只调用一次,但是您应该在整个过程的早期调用它

Runtime.getRuntime().addShutdownHook(new Thread() {

    @Override
    public void run() {
        quit();
    }

});
请参阅此问题以了解有关关机挂钩正在执行的操作的更多详细信息:

有关何时调用和何时不调用关闭挂钩的更多详细信息,请参阅此问题的公认答案


有关使用quit()TL需要多少时间的信息,请参见此问题;DR-视情况而定

这是一种风险更大但更有效的方法,可以阻止程序退出。有几种方法可以故意破坏此方法,并且操作系统也可能会在某些地方意外破坏此方法。这就是为什么我说“风险更大”

您创建了一个类,Bodyguard,它计算出自己的进程id,然后以该进程id作为输入运行RealProgram。然后,保镖等待RealProgram关闭,然后终止。同时,RealProgram监视并检查保镖的进程是否仍在运行。如果它看到保镖已经死亡,那么它将退出主循环并在终止之前调用quit()

我已经在Windows10上测试了这一点,运行Java8,并通过eclipse调试器杀死了保镖。当保镖被杀时,RealProgram在调用quit()后关闭。当RealProgram被杀死时,保镖也会终止

因为我想你会想玩转代码,所以我在这里留下了一些调试元素,比如writer

我认为:

  • 使用用于获取进程id的代码
  • 使用有关如何启动第二个JVM的代码
  • 了解如何从Windows和Linux获取正在运行的进程
保镖级别:

package so_38154756;

import java.lang.reflect.InvocationTargetException;

public class Bodyguard {

    public static void main(String[] args){
        Bodyguard lamb = new Bodyguard();
        lamb.initiateSacrifice();
    }

    private void initiateSacrifice() {
        try{
            int ownPid = getPID();
            runRealProgram(ownPid);
        } catch(Exception e){
            System.out.println("Sorry, this isn't working.");
        }
    }

    private int getPID() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        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);
        int pid = (Integer) pid_method.invoke(mgmt);
        return pid;
    }

    private void runRealProgram(int ownPid) throws Exception {
        String separator = System.getProperty("file.separator");
        String classpath = System.getProperty("java.class.path");
        String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
        ProcessBuilder processBuilder = new ProcessBuilder(path, "-cp", classpath, RealProgram.class.getName(), Integer.valueOf(ownPid).toString());
        Process process = processBuilder.start();
        process.waitFor();//will wait forever, but will close when the RealProgram closes
    }

}
以及RealProgram类:

package so_38154756;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Pattern;

public class RealProgram {

    private Integer bodyguardPID;
    private PrintWriter writer;

    public RealProgram(Integer bodyguardPID, PrintWriter writer) {
        this.bodyguardPID = bodyguardPID;
        this.writer = writer;
    }

    public static void main(String[] args){
        Integer sacrificialPID = Integer.parseInt(args[0]);
        try {
            PrintWriter writer = new PrintWriter(new File("output.txt"));
            RealProgram rp = new RealProgram(sacrificialPID, writer);
            writer.println("Past Constructor");
            rp.checkToSeeIfBodyguardLives();
            writer.println("Bodyguard had died");
            writer.close();//yes, yes, this could be slightly prettier with Java 8's try-with-resources
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void checkToSeeIfBodyguardLives() {
        while(true){
            try { Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//I wait because I was debugging - you may find it wise to not wait
            //do whatever it is you're doing
            if(!isBodyguardAlive()){
                quit();
                break;
            }
        }
    }

    private boolean isBodyguardAlive() {
        try {
            String process;
            Pattern pattern = Pattern.compile(".*\\\""+bodyguardPID+"\\\".*");//Windows - have tested
            //you'll have to figure out your own different pattern for linux - possible source for editing
            writer.println("Searching for PID of "+bodyguardPID);
            //Process p = Runtime.getRuntime().exec("ps -few"); - Linux - haven't tested - again, possible source for editing
            Process p = Runtime.getRuntime().exec(System.getenv("windir") +"\\system32\\"+"tasklist.exe /fo csv /nh");//Windows - have tested
            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while ((process = input.readLine()) != null) {
                if(pattern.matcher(process).matches()){ 
                    return true;
                }
            }
            input.close();
        } catch (Exception err) {
            err.printStackTrace();
        }
        return false;
    }

    private void quit(){
        writer.println("Detected Bodyguard has died");
    }
}

添加一个?嗯,当您关闭windows命令提示符时,它似乎会在执行任何操作之前关闭。。。我在里面放了一个
线程.sleep()
,它仍然立即关闭。在做任何事情之前,我会看看是否还有其他答案。谢谢你=D@JeutnargDepending操作系统是如何杀死进程的,它可能有机会也可能没有机会做任何事情。这就是关机挂钩不可靠的原因。@criticaldiamonds您的礼貌激励了我-请参阅我的其他答案