执行显示控制台窗口的命令,并获取进程的句柄

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了执行显示控制台窗口的命令,并获取进程的句柄相关的知识,希望对你有一定的参考价值。

我正在尝试从Java运行一个命令,它将启动一个运行几分钟的进程。我需要触发命令并获取进程句柄并继续循环中的其他操作。我会定期监控该过程是否仍处于活动状态。

我还需要显示控制台窗口以显示用户的进程输出。

目前,我已尝试运行Runtime和ProcessBuilder类的方法来运行我的命令,但它们都没有帮助我实现我的目标。

示例代码:

//Changing the directory and running Maven exec: java command on the POM file in that directory.

String cmd = "cd C:/Test & mvn exec:java"; 
String finalCmd = "cmd /c ""+ cmd +""";

Process process = Runtime.getRuntime().exec(finalCmd);
Thread.sleep(10);
boolean alive = process.isAlive();

变量alive的值为True,但我没有看到该过程已开始。程序执行完成后,只有这个过程开始,我不确定为什么会这样。

另外为了显示控制台窗口,我从谷歌发现我需要使用以下命令:

String finalCmd = "cmd /c start cmd.exe /c "" + cmd + """;

但是,有了这个,进程立即开始,但我没有得到进程句柄,因为我发现alive变量显示为false。

有人知道如何实现这一目标吗?我很好,如果不可能同时做两个,但至少我需要启动进程执行并获取句柄以便稍后在我的代码中监视进程状态。

答案

这是一个使用WMIC的解决方案。

public static void main( String[] args ) throws Exception {

    // Vars
    Process process;
    String output;

    // Execution
    process = Runtime.getRuntime().exec("cmd /c wmic process call create calc.exe | findstr ProcessId");
    output = readTrimmedOutput(process.getInputStream());
    System.out.println("Output from command: " + output);

    // Basic string manipulation to get process id
    String str_proc_id = output.split(" = ")[1].replace(";","");
    System.out.println("ProcessId is: " + str_proc_id);

    // Some thread delay that you can comment/uncomment for testing if running or not
    Thread.sleep(5000);

    // Finding if process is still running
    process = Runtime.getRuntime().exec("cmd /c wmic process get processid | findstr " + str_proc_id);
    output = readTrimmedOutput(process.getInputStream());

    boolean isRunning = output.contains(str_proc_id);
    System.out.println("Is process still running? " + isRunning);

}

private static String readTrimmedOutput(InputStream is) throws Exception {
    BufferedReader breader = new BufferedReader(new InputStreamReader(is));
    String line = breader.readLine();
    return line != null ? line.trim() : "";
}

样本输出

Output from command: ProcessId = 6480;
ProcessId is: 6480
Is process still running? true

要显示/显示cmd控制台,请将某些行更改为:

// Execution
String your_command = "cmd.exe /c "dir"";
process = Runtime.getRuntime().exec("cmd /c wmic process call create "" + your_command + "" | findstr ProcessId");

参考文献:

https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx

https://www.computerhope.com/wmic.htm

另一答案

这里发生的一些事情不正确:

  • 我们需要将我们的命令作为字符串标记传递给exec()命令
  • 我们需要等待进程退出process.waitFor()而不是sleep,这将阻止当前线程,所以如果你不希望你需要在另一个线程中执行它或使用ExecutorService。
  • 建议检查waitFor()的输出值以查看我们的命令是否正确执行(值为0)(任何其他值,如果执行不成功,通常为正1)
  • 可选(要查看输出)我们需要将标准OUT和ERR重定向到某个地方,比如将它打印到console(),尽管你可以将它放到文件某个GUI窗口等。

因此,以下代码至少应该起作用:

Process process = Runtime.getRuntime().exec(new String[] {"cmd", "/c", "cd", "C:\dev", "&&", "dir"});
int outputVal = process.waitFor();
boolean alive = process.isAlive();
System.out.format("alive %s, outputVal: %d
",alive, outputVal);

进一步建议:

  • 使用ProcessBuilder而不是runTime.exec(),它允许更多的控制,是自JDK 1.5以来的推荐方式
  • 读取inputStream

所以代码看起来像这样:

    List<String> cmdList = Arrays.asList("cmd", "/c", "cd", "C:\dev", "&&", "dir");
    ProcessBuilder pb = new ProcessBuilder(cmdList);
    pb.redirectErrorStream(true); //redirect STD ERR to STD OUT
    Process process = pb.start();
    try (final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        String line = null;
        while ((line = br.readLine()) != null) {
              System.out.println("std-out-line: " + line);
        }
    }
    int outputVal = process.waitFor();
    System.out.format("outputVal: %d
", outputVal);

由于waitFor()是一个阻塞调用,因此您可以在单独的线程中或使用executorService执行此操作。示例代码:

    final StringBuffer outputSb = new StringBuffer();
    ExecutorService executorService = null;
    try {
        executorService = Executors.newSingleThreadExecutor();
        final Future<Integer> future = executorService.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                try (final BufferedReader br = new BufferedReader(
                        new InputStreamReader(process.getInputStream()))) {
                    String line = null;
                    while ((line = br.readLine()) != null) {
                        outputSb.append("std-out-line: ");
                        outputSb.append(line);
                        outputSb.append('
');
                    }
                }
                int exitValue = process.waitFor();
                System.out.format("exitValue: %d
", exitValue);

                return exitValue;
            }
        });

        while (!future.isDone()) {
            System.out.println("Waiting for command to finish doing something else..");
            Thread.sleep(1 * 1000);
        }

        int exitValue = future.get();
        System.out.println("Output: " + outputSb);

    } finally {
        executorService.shutdown();
    }
另一答案

因为我不太了解你真正需要什么,所以我带来了一个全面的例子,从java类(例如类A)打开cmd并启动另一个java类(类B)的进程并从B类执行一些操作B级告知A类是否正在处理。因此,整个过程就是从A类开始的命令提示中排除B类,并将信息从B类发送到A以通知它它仍在运行。

在我的例子中,我把Main class作为A类,myProcess class作为B类。你可以在下面的代码中看到Main class是Opening cmd并且正在执行myProcess class然后myProcess class通过在Main class中创建的套接字发送有关进程的信息

//imports
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
//class
public class Main 
{
  //fields

  //methods


    public static void main(String[] args) throws Exception
    {

        Runtime run = Runtime.getRuntime();
        String new_dir = "C:\Users\Parsa\Desktop\New folder (2)";//imagine the directory of myProcess.class is in this folder
        startServer();
        run.exec("cmd.exe /c cd ""+new_dir+"" & start cmd.exe /k "java myProcess"");

    }


    public static void startServer() 
    {
        Thread myThread = new Thread() 
        {
            @Override
            public void run() 
            {
                ServerSocket ss;// creating an open port for receiving data from network
                try {
                    ss = new ServerSocket(60010);//open port number 60010--> it can really be anything that is empty

                    Socket s = ss.accept();//Listens for a connection to be made to this socket and accepts it

                    BufferedReader in = new BufferedReader(
                            new InputStreamReader(s.getInputStream()));//get the inputstream and change it to a buffered reader in order to get a string from remote network
                    String line = null;
                    while ((line = in.readLine()) != null) //read the input
                    {
                        System.out.println(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };

        myThread.start();


    }

}

myProcess类:

顺便提一下,您需要通过命令提示符手动编译Process类,并从Main类执行Process.class文件

而myProcess类是

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;

public class myProcess extends Thread
{
  //field

  //methods
    public static void main(String[] args) throws Exception
    {
        System.out.println("myProcess has started");
        startSender();

    }


    public static void startSender() 
    {
        Thread myThread = new Thread() 
        {
            @Override
            public void run() 
            {
                try 
                {
                    Socket s = new Socket("localhost", 60010);
                    BufferedWriter out = new BufferedWriter(
                            new Out

以上是关于执行显示控制台窗口的命令,并获取进程的句柄的主要内容,如果未能解决你的问题,请参考以下文章

windows 下在进程中使用createprocess创建一个窗口进程,并获取这个窗口HWND句柄

C#获取当前窗体句柄及该窗体里的信息

VB 运行一个程序,并获取句柄

.NET (C#):只有进程句柄或 PID 时获取子窗口?

一个进程有很多窗口,怎么取这个进程的下所有窗口句柄

C# 根据进程ID获取进程主窗口句柄