2012-04-12 20 views
9

Tôi đang sử dụng lệnh Runtime.getRuntime(). Exec() trong Java để bắt đầu một tệp lô, từ đó bắt đầu quá trình khác cho nền tảng cửa sổ.Làm cách nào để chấm dứt một cây xử lý khỏi Java?

javaw.exe(Process1) 
|___xyz.bat(Process2) 
     |___javaw.exe(Process3) 

Runtime.getRuntime(). Exec() trả về một đối tượng Process trong đó có một phương pháp phá hủy, nhưng khi tôi sử dụng tiêu diệt(), nó giết chết chỉ xyz.bat và lá phụ quá trình lơ lửng của file .BAT .

Có cách nào sạch trong Java để phá hủy cây quá trình bắt đầu bằng quy trình xử lý hàng loạt dưới dạng root không?

* Tôi không thể sử dụng bất kỳ thư viện tùy chỉnh \ Để loại bỏ các tập tin batch để từng vượt qua vấn đề

+0

Tôi có thể hỏi tại sao không có yêu cầu thư viện tùy chỉnh nào không? Theo kinh nghiệm của tôi, các yêu cầu như vậy thường có lý do rất nghèo nàn và có thể thương lượng với lý do cần thư viện (trong trường hợp này, một tính năng bắt buộc bị thiếu trong nền tảng Java, cụ thể là cách liệt kê các tiến trình con của một tiến trình cha mẹ). – Jules

Trả lời

10

này là không thể sử dụng Java API tiêu chuẩn (xem chỉnh sửa ở cuối bài này để cập nhật thay đổi này). Bạn sẽ cần một số mã nguồn gốc của một số loại. Sử dụng JNA, tôi đã sử dụng mã mà trông như thế này:

public class Win32Process 
{ 
    WinNT.HANDLE handle; 
    int pid; 

    Win32Process (int pid) throws IOException 
    { 
     handle = Kernel32.INSTANCE.OpenProcess ( 
       0x0400| /* PROCESS_QUERY_INFORMATION */ 
       0x0800| /* PROCESS_SUSPEND_RESUME */ 
       0x0001| /* PROCESS_TERMINATE */ 
       0x00100000 /* SYNCHRONIZE */, 
       false, 
       pid); 
     if (handle == null) 
      throw new IOException ("OpenProcess failed: " + 
        Kernel32Util.formatMessageFromLastErrorCode (Kernel32.INSTANCE.GetLastError())); 
     this.pid = pid; 
    } 

    @Override 
    protected void finalize() throws Throwable 
    { 
     Kernel32.INSTANCE.CloseHandle (handle); 
    } 

    public void terminate() 
    { 
     Kernel32.INSTANCE.TerminateProcess (handle, 0); 
    } 

    public List<Win32Process> getChildren() throws IOException 
    { 
     ArrayList<Win32Process> result = new ArrayList<Win32Process>(); 
     WinNT.HANDLE hSnap = KernelExtra.INSTANCE.CreateToolhelp32Snapshot (KernelExtra.TH32CS_SNAPPROCESS, new DWORD(0)); 
     KernelExtra.PROCESSENTRY32.ByReference ent = new KernelExtra.PROCESSENTRY32.ByReference(); 
     if (!KernelExtra.INSTANCE.Process32First (hSnap, ent)) return result; 
     do { 
      if (ent.th32ParentProcessID.intValue() == pid) result.add (new Win32Process (ent.th32ProcessID.intValue())); 
     } while (KernelExtra.INSTANCE.Process32Next (hSnap, ent)); 
     Kernel32.INSTANCE.CloseHandle (hSnap); 
     return result; 
    } 

}

Mã này sử dụng tờ khai JNA sau đó không có trong thư viện JNA tiêu chuẩn:

public interface KernelExtra extends StdCallLibrary { 

    /** 
    * Includes all heaps of the process specified in th32ProcessID in the snapshot. To enumerate the heaps, see 
    * Heap32ListFirst. 
    */ 
    WinDef.DWORD TH32CS_SNAPHEAPLIST = new WinDef.DWORD(0x00000001); 

    /** 
    * Includes all processes in the system in the snapshot. To enumerate the processes, see Process32First. 
    */ 
    WinDef.DWORD TH32CS_SNAPPROCESS = new WinDef.DWORD(0x00000002); 

    /** 
    * Includes all threads in the system in the snapshot. To enumerate the threads, see Thread32First. 
    */ 
    WinDef.DWORD TH32CS_SNAPTHREAD = new WinDef.DWORD(0x00000004); 

    /** 
    * Includes all modules of the process specified in th32ProcessID in the snapshot. To enumerate the modules, see 
    * Module32First. If the function fails with ERROR_BAD_LENGTH, retry the function until it succeeds. 
    */ 
    WinDef.DWORD TH32CS_SNAPMODULE = new WinDef.DWORD(0x00000008); 

    /** 
    * Includes all 32-bit modules of the process specified in th32ProcessID in the snapshot when called from a 64-bit 
    * process. This flag can be combined with TH32CS_SNAPMODULE or TH32CS_SNAPALL. If the function fails with 
    * ERROR_BAD_LENGTH, retry the function until it succeeds. 
    */ 
    WinDef.DWORD TH32CS_SNAPMODULE32 = new WinDef.DWORD(0x00000010); 

    /** 
    * Includes all processes and threads in the system, plus the heaps and modules of the process specified in th32ProcessID. 
    */ 
    WinDef.DWORD TH32CS_SNAPALL  = new WinDef.DWORD((TH32CS_SNAPHEAPLIST.intValue() | 
      TH32CS_SNAPPROCESS.intValue() | TH32CS_SNAPTHREAD.intValue() | TH32CS_SNAPMODULE.intValue())); 

    /** 
    * Indicates that the snapshot handle is to be inheritable. 
    */ 
    WinDef.DWORD TH32CS_INHERIT  = new WinDef.DWORD(0x80000000); 

    /** 
    * Describes an entry from a list of the processes residing in the system address space when a snapshot was taken. 
    */ 
    public static class PROCESSENTRY32 extends Structure { 

     public static class ByReference extends PROCESSENTRY32 implements Structure.ByReference { 
      public ByReference() { 
      } 

      public ByReference(Pointer memory) { 
       super(memory); 
      } 
     } 

     public PROCESSENTRY32() { 
      dwSize = new WinDef.DWORD(size()); 
     } 

     public PROCESSENTRY32(Pointer memory) { 
      useMemory(memory); 
      read(); 
     } 

     /** 
     * The size of the structure, in bytes. Before calling the Process32First function, set this member to 
     * sizeof(PROCESSENTRY32). If you do not initialize dwSize, Process32First fails. 
     */ 
     public WinDef.DWORD dwSize; 

     /** 
     * This member is no longer used and is always set to zero. 
     */ 
     public WinDef.DWORD cntUsage; 

     /** 
     * The process identifier. 
     */ 
     public WinDef.DWORD th32ProcessID; 

     /** 
     * This member is no longer used and is always set to zero. 
     */ 
     public BaseTSD.ULONG_PTR th32DefaultHeapID; 

     /** 
     * This member is no longer used and is always set to zero. 
     */ 
     public WinDef.DWORD th32ModuleID; 

     /** 
     * The number of execution threads started by the process. 
     */ 
     public WinDef.DWORD cntThreads; 

     /** 
     * The identifier of the process that created this process (its parent process). 
     */ 
     public WinDef.DWORD th32ParentProcessID; 

     /** 
     * The base priority of any threads created by this process. 
     */ 
     public WinDef.LONG pcPriClassBase; 

     /** 
     * This member is no longer used, and is always set to zero. 
     */ 
     public WinDef.DWORD dwFlags; 

     /** 
     * The name of the executable file for the process. To retrieve the full path to the executable file, call the 
     * Module32First function and check the szExePath member of the MODULEENTRY32 structure that is returned. 
     * However, if the calling process is a 32-bit process, you must call the QueryFullProcessImageName function to 
     * retrieve the full path of the executable file for a 64-bit process. 
     */ 
     public char[] szExeFile = new char[WinDef.MAX_PATH]; 
    } 


    // the following methods are in kernel32.dll, but not declared there in the current version of Kernel32: 

    /** 
    * Takes a snapshot of the specified processes, as well as the heaps, modules, and threads used by these processes. 
    * 
    * @param dwFlags 
    * The portions of the system to be included in the snapshot. 
    * 
    * @param th32ProcessID 
    * The process identifier of the process to be included in the snapshot. This parameter can be zero to indicate 
    * the current process. This parameter is used when the TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, 
    * TH32CS_SNAPMODULE32, or TH32CS_SNAPALL value is specified. Otherwise, it is ignored and all processes are 
    * included in the snapshot. 
    * 
    * If the specified process is the Idle process or one of the CSRSS processes, this function fails and the last 
    * error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from opening them. 
    * 
    * If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the 
    * last error code is ERROR_PARTIAL_COPY (299). 
    * 
    * @return 
    * If the function succeeds, it returns an open handle to the specified snapshot. 
    * 
    * If the function fails, it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError. 
    * Possible error codes include ERROR_BAD_LENGTH. 
    */ 
    public WinNT.HANDLE CreateToolhelp32Snapshot(WinDef.DWORD dwFlags, WinDef.DWORD th32ProcessID); 

    /** 
    * Retrieves information about the first process encountered in a system snapshot. 
    * 
    * @param hSnapshot A handle to the snapshot returned from a previous call to the CreateToolhelp32Snapshot function. 
    * @param lppe A pointer to a PROCESSENTRY32 structure. It contains process information such as the name of the 
    * executable file, the process identifier, and the process identifier of the parent process. 
    * @return 
    * Returns TRUE if the first entry of the process list has been copied to the buffer or FALSE otherwise. The 
    * ERROR_NO_MORE_FILES error value is returned by the GetLastError function if no processes exist or the snapshot 
    * does not contain process information. 
    */ 
    public boolean Process32First(WinNT.HANDLE hSnapshot, KernelExtra.PROCESSENTRY32.ByReference lppe); 

    /** 
    * Retrieves information about the next process recorded in a system snapshot. 
    * 
    * @param hSnapshot A handle to the snapshot returned from a previous call to the CreateToolhelp32Snapshot function. 
    * @param lppe A pointer to a PROCESSENTRY32 structure. 
    * @return 
    * Returns TRUE if the next entry of the process list has been copied to the buffer or FALSE otherwise. The 
    * ERROR_NO_MORE_FILES error value is returned by the GetLastError function if no processes exist or the snapshot 
    * does not contain process information. 
    */ 
    public boolean Process32Next(WinNT.HANDLE hSnapshot, KernelExtra.PROCESSENTRY32.ByReference lppe); 


} 

Bạn sau đó có thể sử dụng phương thức 'getChildren()' để nhận danh sách trẻ em, chấm dứt phụ huynh và sau đó đệ quy chấm dứt các trẻ em.

Tôi tin rằng bạn có thể thêm PID của một java.lang.Process bằng cách sử dụng sự phản chiếu (tôi đã không làm điều này, tuy nhiên, tôi chuyển sang tạo các quy trình bản thân mình bằng cách sử dụng API Win32 để tôi có nhiều quyền kiểm soát hơn).

Vì vậy, đặt nó lại với nhau, bạn sẽ cần một cái gì đó như:

int pid = (some code to extract PID from the process you want to kill); 
Win32Process process = new Win32Process(pid); 
kill(process); 

public void kill(Win32Process target) throws IOException 
{ 
    List<Win32Process> children = target.getChildren(); 
    target.terminateProcess(); 
    for (Win32Process child : children) kill(child); 
} 

Sửa

Nó chỉ ra rằng thiếu sót đặc biệt này của Java API đã được cố định trong Java 9. Xem preview của tài liệu Java 9 here (nếu trang chính xác không tải, bạn cần xem giao diện java.lang.ProcessHandle). Đối với yêu cầu của câu hỏi trên, mã bây giờ sẽ giống như thế này:

Process child = ...; 
kill (child.toHandle()); 

public void kill (ProcessHandle handle) 
{ 
    handle.descendants().forEach((child) -> kill(child)); 
    handle.destroy(); 
} 

(Lưu ý rằng đây không phải là thử nghiệm - Tôi đã không chuyển sang Java 9, nhưng đang tích cực đọc về nó)

0

Bạn không thể giết một cây quá trình cho các cửa sổ sử dụng JDK. Bạn cần phải dựa vào WinAPI.Bạn sẽ phải sử dụng các lệnh gốc hoặc các thư viện JNI, tất cả đều phụ thuộc vào nền tảng và phức tạp hơn một giải pháp Java thuần túy.

Một liên kết mẫu JNI Example

+0

Thật không may tôi không thể sử dụng bất kỳ thư viện bên ngoài hoặc tùy chỉnh. Tuy nhiên, tôi có thể thay đổi tập tin batch.Is có một cách để bắt một tín hiệu hạn trong lô gửi bởi Process.destroy() từ Java? và sau đó sử dụng nó để giết quá trình phụ? – srami

+0

Bạn có thể quản lý các quy trình bằng cách sử dụng tệp lô. Vui lòng xem qua liên kết để có một số tùy chọn có sẵn để quản lý. http://www.robvanderwoude.com/processes.php – Phani

+0

Tôi không tin rằng có bất kỳ cách nào để tạo một tệp loạt Windows bắt tín hiệu như vậy - Java tự nhiên sử dụng TerminateProcess, quá trình này sẽ giết chết trực tiếp mà không cần gửi tín hiệu cho nó trước. Bên cạnh đó, nếu một tiến trình con hiện đang chạy, tệp batch sẽ phải đợi nó thoát ra trước khi nó có thể làm bất cứ điều gì khác, và không có cách nào để Java chấm dứt tiến trình con. Bạn sẽ phải sử dụng thư viện bên ngoài nếu bạn muốn làm điều này. – Jules

1

Một giải pháp thay thế, nếu bạn kiểm soát tiến trình con cũng như tệp lô, sẽ có quy trình con tạo chuỗi, mở một ServerSocket, nghe kết nối với nó và gọi System.exit() nếu nó nhận được mật khẩu chính xác trên đó.

Có thể có các biến chứng nếu bạn cần nhiều phiên bản đồng thời; tại thời điểm đó bạn sẽ cần một số cách để phân bổ số cổng cho chúng.

0

Đây là một tùy chọn khác. Sử dụng kịch bản PowerShell này để thực thi kịch bản dơi của bạn. Khi bạn muốn giết cây, chấm dứt quá trình của kịch bản PowerShell của bạn và nó sẽ thực thi taskkill trên tiến trình con của nó một cách tự động. Tôi có nó gọi taskkill hai lần bởi vì trong một số trường hợp nó không đi trên thử đầu tiên.

Param(
    [string]$path 
) 

$p = [Diagnostics.Process]::Start("$path").Id 

try { 
    while($true) { 
     sleep 100000 
    } 
} finally { 
    taskkill /pid $p 
    taskkill /pid $p 
} 
0

Với java 9, giết chết quá trình chính sẽ làm chết toàn bộ cây quá trình. Bạn có thể làm một cái gì đó như thế này:

Process ptree = Runtime.getRuntime().exec("cmd.exe","/c","xyz.bat"); 
// wait logic 
ptree.destroy(); 

Hãy xem tại blog này và kiểm tra các Deal với Trees Process ví dụ.

Các vấn đề liên quan