2013-12-21 103 views
0

這似乎應該很容易解決,但我不熟悉使用批處理文件來解決它自己。我有一個Java方法,它創建一個流程構建器並在流程中運行一個批處理文件。批處理文件使用xcopy命令將一個目錄複製到另一個目錄。當批處理文件在後臺運行時,包含JTextArea的Java窗口將顯示進程的輸出(正在複製的目錄)。該窗口也有一個停止按鈕,它調用下面的代碼:批處理文件進程在Java中死亡:xcopy不關閉

stopped = true; 
backgroundTask.cancel(true); 
backgroundTask.done(); 

的實現方法是這樣的:現在

protected void done() { 
    statusLabel.setText((this.getState()).toString() + " " + status); 
    stopButton.setEnabled(false); 
    bar.setIndeterminate(false); 
    if(stopped == false){ 
     JOptionPane.showMessageDialog(null, "Backup Complete."); 
     closeWindow(); 
    } 
    else if (stopped == true){ 
     JOptionPane.showMessageDialog(null, "Backup Cancelled."); 
     closeWindow(); 
    } 
} 

,爲了在後臺運行的批處理文件,我用下面的代碼(最初由trashgod向我建議):

protected Integer doInBackground() throws IOException { 
    try { 
     ProcessBuilder pb = new ProcessBuilder(commands); 
     pb.redirectErrorStream(true); 
     Process p = pb.start(); 
     String s; 
     BufferedReader stdout = new BufferedReader(
     new InputStreamReader(p.getInputStream())); 
     while ((s = stdout.readLine()) != null && !isCancelled()) { 
      publish(s); 
     } 
     if (!isCancelled()) { 
      status = p.waitFor(); 
     } 
     p.getInputStream().close(); 
     p.getOutputStream().close(); 
     p.getErrorStream().close(); 
     p.destroy(); 
     closeWindow(); 
    } catch (IOException | InterruptedException ex) { 
     ex.printStackTrace(System.err); 
    }    
    return status; 
} 

我遇到的問題是:當我運行程序,將文件複製就好了,除非我按在窗口,我停止按鈕前景。當我這樣做,它告訴我備份取消(因爲它應該),但它留下的運行三個額外的流程,其在任務管理器中可見: enter image description here

enter image description here

enter image description here

我的猜測是,第一個 - 「擴展複製工具」是罪魁禍首。由於它沒有關閉,所以其他兩個cmd進程仍在運行。但是,這是一個相當沒有教養的猜測。

當我運行該程序,然後停止它,Windows資源管理器變得非常不穩定,有時凍結,有時甚至崩潰。通過文件夾導航 - 特別是複製到的目錄 - 非常慢,並且即使在處理(假定)停止之後,目錄仍然被複制。我相信這是因爲這些線路從來沒有達到:

p.getInputStream().close(); 
p.getOutputStream().close(); 
p.getErrorStream().close(); 
p.destroy(); 

所以這個過程永遠不會被殺死。當按下停止按鈕時,我仍然在努力完成殺死進程的任何方式,但如果有人有想法,我很樂意聽到他們的聲音!

編輯

我選擇發佈整個類,因爲只給某些方法可能不提供足夠的信息。這裏是整個班級:

package diana; 

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.Toolkit; 
import java.awt.event.*; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.List; 

import javax.swing.*; 

@SuppressWarnings("serial") 
public class Progress extends JFrame { 
    public String[] commands; 
    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER); 
    private final JTextArea textArea = new JTextArea(20, 20); 
    private JButton stopButton = new JButton("Stop"); 
    private JProgressBar bar = new JProgressBar(); 
    private BackgroundTask backgroundTask; 
    private ProcessBuilder pb; 
    private Process p; 
    public boolean stopped = false; 

    public void setCommands(String[] cmds) { 
     commands = cmds; 
    } 
    private final ActionListener buttonActions = new ActionListener() { 
     @Override 
     public void actionPerformed(ActionEvent ae) { 
      JButton source = (JButton) ae.getSource(); 
      if (source == stopButton) { 
       stopped = true; 
       backgroundTask.cancel(true); 
       backgroundTask.done(); 
      } else { 
       backgroundTask = new BackgroundTask(commands); 
      } 
     } 
    }; 

    private void displayGUI(String[] cmds) { 
     commands = cmds; 
     JFrame frame = new JFrame("Backup Progress"); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     JPanel panel = new JPanel(); 
     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
     panel.setLayout(new BorderLayout(5, 5)); 
     JScrollPane sp = new JScrollPane(); 
     sp.setBorder(BorderFactory.createTitledBorder("Output: ")); 
     sp.setViewportView(textArea); 
     textArea.setText(null); 
     stopButton.setEnabled(true); 
     backgroundTask = new BackgroundTask(commands); 
     backgroundTask.execute(); 
     bar.setIndeterminate(true); 
     stopButton.addActionListener(buttonActions); 
     JPanel buttonPanel = new JPanel(); 
     buttonPanel.add(stopButton); 
     buttonPanel.add(bar); 
     panel.add(statusLabel, BorderLayout.PAGE_START); 
     panel.add(sp, BorderLayout.CENTER); 
     panel.add(buttonPanel, BorderLayout.PAGE_END); 
     frame.setContentPane(panel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

    /* Close current window */ 
    public void closeWindow() throws IOException { 
     p.getInputStream().close(); 
     p.getOutputStream().close(); 
     p.getErrorStream().close(); 
     p.destroy(); 
     WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING); 
     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close); 
     System.exit(0); 
    } 

    private class BackgroundTask extends SwingWorker<Integer, String> { 
     private int status; 
     public String[] commands; 
     public BackgroundTask(String[] cmds) { 
      commands = cmds; 
      statusLabel.setText((this.getState()).toString()); 
     } 

     @Override 
     protected Integer doInBackground() throws IOException { 
      try { 
       pb = new ProcessBuilder(commands); 
       pb.redirectErrorStream(true); 
       p = pb.start(); 
       String s; 
       BufferedReader stdout = new BufferedReader(
        new InputStreamReader(p.getInputStream())); 
       while ((s = stdout.readLine()) != null && !isCancelled()) { 
        publish(s); 
       } 
       if (!isCancelled()) { 
        status = p.waitFor(); 
       } 
       closeWindow(); 
      } catch (IOException | InterruptedException ex) { 
       ex.printStackTrace(System.err); 
      } 
      return status; 
     } 

     @Override 
     protected void process(List<String> messages) { 
      statusLabel.setText((this.getState()).toString()); 
      for (String message : messages) { 
       textArea.append(message + "\n"); 
      } 
     } 

     @Override 
     protected void done() { 
      statusLabel.setText((this.getState()).toString() + " " + status); 
      stopButton.setEnabled(false); 
      bar.setIndeterminate(false); 
      if (stopped == false) { 
       JOptionPane.showMessageDialog(null, "Backup Complete."); 
       try { 
        closeWindow(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } else if (stopped == true) { 
       JOptionPane.showMessageDialog(null, "Backup Cancelled."); 
       try { 
        closeWindow(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    public void run(String[] cmds) { 
     commands = cmds; 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new Progress().displayGUI(commands); 
      } 
     }); 
    } 
} 

再次,我不能讚揚這個代碼,因爲它主要是由SO成員垃圾回收提供。此外,請原諒那裏留下的調試,我可能忘記刪除。

回答

1

我的一個想法是期望能夠阻止這個整體過程是不合理的。你正在啓動一個shell並向它傳遞一個命令,然後停止原來的程序 - 它應該做些什麼來停止複製?如果你是一個使用命令shell的用戶,你可以輸入control-C,但我不知道是否有可用於Java的編程等價物,它可以做同樣的事情。

+0

這是一個很好的觀點。我希望有一種方法可以從Java程序化地殺死整個進程。我想我們會看到。 – DerStrom8

1

有幾件事情中脫穎而出

BufferedReader#readLine是一個阻塞方法和

你在一個環繞整個邏輯可能不是當前線程(和開啓)的中斷響應標誌try-catch阻止,這意味着如果InterruptedException被引發,則會跳過您嘗試用於處理該流程的代碼的整個部分。

像這樣的東西可能是一個更好的方法。 InputStream#read依然是你的死穴,而是因爲我現在檢查isCancelled前想讀的東西,這是不太可能造成問題的一個很大的

InputStream is = null; 
Process p = null; 
try { 
    ProcessBuilder pb = new ProcessBuilder(commands); 
    pb.redirectErrorStream(true); 
    p = pb.start(); 

    StringBuilder sb = new StringBuilder(128); 
    is = p.getInputStream(); 
    int in = -1; 
    while (!isCancelled() && (in = is.read()) != -1) { 
     sb.append((char)in)); 
     if (((char)in) == '\n') { 
      publish(sb.toString()); 
      sb.delete(0, sb.length()); 
     } 
    } 
    if (!isCancelled()) { 
     status = p.waitFor(); 
    } else { 
     p.destroy(); 
    } 
} catch (IOException ex) { 
    ex.printStackTrace(System.err); 
} catch (InterruptedException ex) { 
    ex.printStackTrace(System.err); 
    try { 
     p.destroy(); 
    } catch (Exception exp) { 
    } 
} finally { 
    try { 
     is.close(); 
    } catch (Exception exp) { 
    } 
    // Make sure you are re-syncing this to the EDT first... 
    closeWindow(); 
} 

(NB直接輸入,所以我沒有測試它)

+0

嗯,我唯一的問題是它部分取代了其他幾個方法所需的其他方法。也許我必須發佈整個班級,以便找到一種方法使其工作,而不會徹底改變整個事物的結構。我會繼續關注這個例子,看看我能否爲我的目的而工作。多謝。 – DerStrom8

+0

因此,最重要的變化是通過char讀取輸出字符,而不是使用BufferedReader ...我過去遇到過這個問題,所以請避免這樣做。其餘的只是更多的控制異常處理;) – MadProgrammer

+0

好吧,我想我現在明白了。但是我有一個新問題:按字符閱讀輸出字符是否需要更長的時間?這個程序運行速度非常緩慢,我寧願它也不會慢一些;) – DerStrom8

1

你沒有說你的批處理文件是否執行除了調用XCOPY任何行動。否則,您可能需要考慮使用Java來執行文件副本,而不是運行外部進程。中斷自己的代碼要比停止外部過程容易得多:

static void copyTree(final Path source, final Path destination) 
throws IOException { 
    if (Files.isDirectory(source)) { 
     Files.walkFileTree(source, new SimpleFileVisitor<Path>() 
     { 
      @Override 
      public FileVisitResult preVisitDirectory(Path dir, 
             BasicFileAttributes attributes) 
      throws IOException { 
       if (Thread.interrupted()) { 
        throw new InterruptedIOException(); 
       } 

       Path destinationDir = 
        destination.resolve(source.relativize(dir)); 
       Files.createDirectories(destinationDir); 

       BasicFileAttributeView view = 
        Files.getFileAttributeView(destinationDir, 
         BasicFileAttributeView.class); 
       view.setTimes(
        attributes.lastModifiedTime(), 
        attributes.lastAccessTime(), 
        attributes.creationTime()); 

       return FileVisitResult.CONTINUE; 
      } 

      @Override 
      public FileVisitResult visitFile(Path file, 
             BasicFileAttributes attributes) 
      throws IOException { 
       if (Thread.interrupted()) { 
        throw new InterruptedIOException(); 
       } 

       Files.copy(file, 
        destination.resolve(source.relativize(file)), 
        StandardCopyOption.COPY_ATTRIBUTES, 
        LinkOption.NOFOLLOW_LINKS); 

       return FileVisitResult.CONTINUE; 
      } 
     }); 
    } else { 
     Files.copy(source, destination, 
      StandardCopyOption.COPY_ATTRIBUTES, 
      LinkOption.NOFOLLOW_LINKS); 
    } 
} 
+0

這很有趣,你應該提一下,因爲這是我一直在考慮的事情。然而,我已將大量工作放入批處理文件中,並且它們用於創建目錄以及將文件複製到它們,所以我認爲對於該版本的程序,我想現在堅持使用它們。我已經考慮過一個新版本,它在不久的將來不會依賴批處理文件(只要我得到這個版本的工作)。我的第一個版本只使用批處理文件,而這實際上是版本2。 – DerStrom8