Externe Anwendung beenden

OSchriever

Bekanntes Mitglied
Hi,
ich brauche eine Lösung um mit Java eine externe Anwendung zu schließen. D.h. ich möchte von meinem Programm aus ein anderes Programm beenden, als wenn man auf das Schließen-Kreuz klicken würde. Wichtig: Das Programm darf nicht abgebrochen werden(über Taskmanager geschlossen werden).
Kann mir da jemand weiterhelfen?

Zur Info: Ich muss auf die Datenbank eines Programms zugreifen, das funktioniert nur wenn das Programm aus ist und das möchte ich gerne automatisieren, da die Kunden ständig das Programm geöffnet lassen und dann die Kopplung nicht funktioniert...

Vielen Dank
 
Das Windows "taskkill" Programm über Runtime.getRuntime().exec ausführen.

EDIT: Mit "über Kreuz beenden" meinst du, dass beim regulären Beenden des Programmes noch was passiert, welches beim harten Abbruch nicht passiert?
 
Hi,
beim regulären beenden wird wahrscheinlich gespeichert(denke ich). Beim normalen beenden gibt es keine Probleme mit der Kopplung. Ich hatte schon ein Programm laufen, welches im Hintergrund läuft und das Programm zeitgesteuert "killt", das führt zu Problemen in der Datenbank, die ich jedes mal wieder reparieren muss. 🙁
Mit Taskkill wird das Programm doch auch hart abgebrochen?
LG
 
Probiers mal mit JNA. Folgendes Beispiel sendet an ein Fenster mit dem Titel "Dokument - WordPad" ein WM_CLOSE.

Java:
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

public class Main {
    public static void main(String[] args) {
        String windowTitle = "Dokument - WordPad";
        WinDef.HWND hwnd = User32.INSTANCE.FindWindow(null, windowTitle);
        if (hwnd != null) {
            User32.INSTANCE.PostMessage(hwnd, WinUser.WM_CLOSE, new WinDef.WPARAM(), new WinDef.LPARAM());
        }
    }
}
 
Nicht das X, aber ein anderer Weg (für andere Aufgaben), wenn man von Java aus etwas töten möchte:

Wenn man ein externes Programm mit dem ProcessBuilder startet, dann kann man es von Java auch wieder beenden. ProcesssBuilder start() liefert Process, das hat eine destroy() Methode.

Eine andere Lösung wäre die PID und ProcessHandle seit Java 9, hier hat man ebenfalls ein destroy()/destroyForcibly().
 
Wäre es vllt auch möglich
Probiers mal mit JNA. Folgendes Beispiel sendet an ein Fenster mit dem Titel "Dokument - WordPad" ein WM_CLOSE.

Java:
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

public class Main {
    public static void main(String[] args) {
        String windowTitle = "Dokument - WordPad";
        WinDef.HWND hwnd = User32.INSTANCE.FindWindow(null, windowTitle);
        if (hwnd != null) {
            User32.INSTANCE.PostMessage(hwnd, WinUser.WM_CLOSE, new WinDef.WPARAM(), new WinDef.LPARAM());
        }
    }
}

Danke, probiere das gleich mal aus... 🙂
 
Nicht das X, aber ein anderer Weg (für andere Aufgaben), wenn man von Java aus etwas töten möchte:

Wenn man ein externes Programm mit dem ProcessBuilder startet, dann kann man es von Java auch wieder beenden. ProcesssBuilder start() liefert Process, das hat eine destroy() Methode.

Eine andere Lösung wäre die PID und ProcessHandle seit Java 9, hier hat man ebenfalls ein destroy()/destroyForcibly().

Ist der weg mit der PID und ProcessHandle auch in der Insel(Edition 11) nachzulesen? Da muss ich das Externe Programm aber auch selbst gestartet haben oder?
 
Wenn man in Java die PID hat, kann man alles töten, probier's aus 🙂

ProcessHandle.allProcesses().forEach( ProcessHandle::destroy );

Das ist Teil der 2. Insel, das Update dauert aber noch.
 
Probiers mal mit JNA. Folgendes Beispiel sendet an ein Fenster mit dem Titel "Dokument - WordPad" ein WM_CLOSE.

Java:
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

public class Main {
    public static void main(String[] args) {
        String windowTitle = "Dokument - WordPad";
        WinDef.HWND hwnd = User32.INSTANCE.FindWindow(null, windowTitle);
        if (hwnd != null) {
            User32.INSTANCE.PostMessage(hwnd, WinUser.WM_CLOSE, new WinDef.WPARAM(), new WinDef.LPARAM());
        }
    }
}
Das funktioniert bei mir nicht, hast du das getestet?
LG
 
Das hat gepasst. Habe den Code so übernommen und WordPad geöffnet.
Hier die Exception:

Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jna/platform/win32/User32
at de.schrieverkommt.Main.main(Main.java:11)
Caused by: java.lang.ClassNotFoundException: com.sun.jna.platform.win32.User32
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
... 1 more

Ich habe jna 5.5.0
 
Ja, ich habe zwei Abhängigkeiten:
Code:
net.java.dev.jna:jna:5.5.0
net.java.dev.jna:jna-platform:5.5.0
Bin aber mit Java 1.8 unterwegs.
 
Ok, es klappt beim WordPad und z.B. bei Word auch, aber wenn ich jetzt den TeamViewer schließen möchte, dann klappt es nicht. Warum? 🙁
 
Nee hab es auch mit diversen anderen Programmen versucht, das klappt nicht.
Mit Enum habe ich auch schon probiert, damit bekomme ich auch nix geschlossen...
Das kann ja irgendwo nicht sein 🙂 Probier mal:
Java:
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.ptr.IntByReference;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

public class Main {
    static class TopLevelWindow {
        public WinDef.HWND hwnd;
        public int pid;
        public String title;
        public String process;

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 23 * hash + this.pid;
            hash = 23 * hash + Objects.hashCode(this.title);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null || obj == this || getClass() != obj.getClass()) {
                return obj == this;
            }
            final TopLevelWindow other = (TopLevelWindow) obj;
            return this.pid == other.pid &&
                    Objects.equals(this.title, other.title);
        }
    }
    
    static class WindowModel extends AbstractTableModel {
        private List<TopLevelWindow> data;
        static String[] COLS = {"PID", "HWND", "Command", "Title"};
        
        public WindowModel(Collection<TopLevelWindow> data) {
            setData(data);
        }
        
        public void close(int row) {
            if (row < 0 || row >= data.size()) { return; }
            User32.INSTANCE.PostMessage(data.get(row).hwnd, WinUser.WM_CLOSE, new WinDef.WPARAM(), new WinDef.LPARAM());
        }
        
        public final void setData(Collection<TopLevelWindow> data) {
            this.data = data.stream()
                    .sorted((w1,w2) -> w1.pid - w2.pid)
                    .collect(Collectors.toList());
            fireTableDataChanged();
        }
        
        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public int getColumnCount() {
            return COLS.length;
        }

        @Override
        public String getColumnName(int column) {
            return COLS[column];
        }
        
        

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            TopLevelWindow window = data.get(rowIndex);
            switch(columnIndex) {
                case 0: return window.pid;
                case 1: return window.hwnd.toString().substring(7);
                case 2: return window.process;
                case 3: return window.title;
            }
            return "";
        }
        
    }
    
    static Set<TopLevelWindow> getWindows() {
        final Set<TopLevelWindow> windows = new HashSet<>();
        WinUser.WNDENUMPROC proc = new WinUser.WNDENUMPROC() {
            @Override
            public boolean callback(WinDef.HWND hwnd, Pointer pntr) {
                int len = User32.INSTANCE.GetWindowTextLength(hwnd);
                if (len >= 0) {
                    char[] title = new char[len+1];
                    int titleLen = User32.INSTANCE.GetWindowText(hwnd, title, len+1);

                    IntByReference pid = new IntByReference();
                    User32.INSTANCE.GetWindowThreadProcessId(hwnd, pid);
                    
                    HANDLE handle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, pid.getValue());
                    char[] path = new char[1024];
                    IntByReference pathLen = new IntByReference(path.length);
                    if (!Kernel32.INSTANCE.QueryFullProcessImageName(handle, 0, path, pathLen)) {
                        pathLen.setValue(0);
                    }
                    Kernel32.INSTANCE.CloseHandle(handle);
                    
                    TopLevelWindow win = new TopLevelWindow();
                    win.hwnd = hwnd;
                    win.pid = pid.getValue();
                    win.process = new String(path, 0, pathLen.getValue());
                    win.title = new String(title, 0, titleLen);
                    if (!win.process.isEmpty()) {
                        windows.add(win);
                    }
                }
                return true;
            }
        };
        
        User32.INSTANCE.EnumWindows(proc, null);
        return windows;
    }
    
    
    public static void main(String[] args) {
        WindowModel model = new WindowModel(getWindows());
        JTable table = new JTable(model);
        JButton refresh = new JButton("Refresh");
        refresh.addActionListener(e -> model.setData(getWindows()));
        JButton close = new JButton("Send WM_CLOSE");
        close.addActionListener(e -> model.close(table.getSelectedRow()));
        JPanel buttons = new JPanel(new GridLayout(1, 2));
        buttons.add(refresh);
        buttons.add(close);
        
        JFrame frame = new JFrame("Closer");
        frame.add(new JScrollPane(table));
        frame.add(buttons, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}
 
Hi,
vielen Dank schonmal. Dadurch habe ich viel besseren Überblick als mit meiner Version. Das Problem ist bloß weiterhin, dass sich das Programm nicht schließen lässt, weil es hier nicht mal aufgeführt wird. Ich habe ein paar Screenshots angefügt. Wie kann es sein, dass das erkannt wird?
Das zu schließende Programm heißt Milk Manager....
MilkManager.PNGprg1.PNGprg2.PNGprg3.PNGprg4.PNG
 
Dann schau dir doch erst einmal die Details an. Wie startest Du die Applikation? Worauf verweist z.B. der Shortcut. Oder wo wurde es wie hin installiert?
Geh in den TaskManager unter Details. Findest Du den Prozess vom MilkManager dort?
 
Die Applikation wird ganz normal aus dem Startmenü gestartet. Die PID ist 16332, das Programm läuft unter dem Namen manager.exe, aber kann ich das dann auch schließen wenn ich nur die PID weiß? Ich habe mit jna noch nicht gearbeitet, tut mir leid wenn ich so blöd fragen muss.
Aber vielen Dank, dass sich hier so gekümmert wird. Das ist sehr beeindruckend...
LG
 
Was mich wundert: wenn manager.exe im Taskmanager auftaucht und Du auch ein Fenster hast, dann sollte es in der Liste auf auftauchen.

Ansonsten halt aus #8 über den Prozess gehen und nicht über das Fenster. Aber das ist ein hartes killen während dem Fenster nur gesagt worden wäre: Schließe Dich. (Also die Anwendung kann noch reagieren - so wie bei einem Klick auf das x).
 
Aber vielen Dank, dass sich hier so gekümmert wird. Das ist sehr beeindruckend...
Die Kuh muss gemolken werden 😛

Das beste ist ja MooMonitor+ - Brunfterkennung -> ich kann nicht mehr.

Ich habe mit jna noch nicht gearbeitet,
JNA ist im Prinzip ein Java-Wrapper für die Windows API.

Du kannst noch zwei Dinge ausprobiren:
1. spaßeshalber die Prüfung if (!win.process.isEmpty()) { rausnehmen, ob dann die PID auftaucht.
2. falls nicht, starte die Anwendung mal mit Adminrechten, nicht, dass sich da noch irgend so eine Schweinerei (wir wollen ja beim Thema bleiben) versteckt.
 
Die Kuh muss gemolken werden 😛

Das beste ist ja MooMonitor+ - Brunfterkennung -> ich kann nicht mehr.


JNA ist im Prinzip ein Java-Wrapper für die Windows API.

Du kannst noch zwei Dinge ausprobiren:
1. spaßeshalber die Prüfung if (!win.process.isEmpty()) { rausnehmen, ob dann die PID auftaucht.
2. falls nicht, starte die Anwendung mal mit Adminrechten, nicht, dass sich da noch irgend so eine Schweinerei (wir wollen ja beim Thema bleiben) versteckt.
Ja, das mit dem MooMonitor klingt lustig, ist aber fast bei jedem unserer Kunden zu finden und spart Unmengen an Antibiotika ein, da viele Krankheiten erkannt werden, bevor sie "hart behandelt" werden müssen. Und Antibiotika wollen wir ja so wenig wie möglich. 😀

Zum Thema: Ich werde mich morgen mal ran hängen und weiter probieren. Wenn alles sofort klappen würde, dann hätten wir ja alle keinen Spaß dran.
Gruß und nochmals danke
 
Soo ich habe die Abfrage auskommentiert und mir werden die Milkmanager-Prozesse mit der PID 2624 angezeigt. Leider ist das Beenden so immer noch nicht möglich, da mir der Milkmanager als mehrere Prozesse angezeigt wird.
Kann man das auch so umprogrammieren, dass alles mit der PID 2624 beendet wird?
Was mich dann aber wieder wundert: In dem Dairymaster-Installationspaket sind noch mehrere Programme vorhanden, wenn ich das Programm "Database Utility" (kleines Programm zum Wiederherstellen von Datenbanken) öffne und dann schließen will, passiert auch nix.
LG
 

Anhänge

  • MM.PNG
    MM.PNG
    32,5 KB · Aufrufe: 15
Das ist mir ehrlich gesagt ein Rätsel.

1. Du könntest statt WM_CLOSE auch WM_QUIT probieren.
2. Du probieren, die PID zu verwenden und ProcessHandle.of(pid).ifPresent(p -> p.destroy()); aufzurufen. Davor kannst Du noch testen, ob ProcessHandle.of(pid).map(p -> p.supportsNormalTermination()).orElse(false) true liefert. Falls ja, könntest Du Glück haben, dass Du mit destroy() das gewünschte Ergebnis erzielst.
 
Hi,

1. Du könntest statt WM_CLOSE auch WM_QUIT probieren.
Funktioniert nicht.

2. Du probieren, die PID zu verwenden und ProcessHandle.of(pid).ifPresent(p -> p.destroy()); aufzurufen. Davor kannst Du noch testen, ob ProcessHandle.of(pid).map(p -> p.supportsNormalTermination()).orElse(false) true liefert. Falls ja, könntest Du Glück haben, dass Du mit destroy() das gewünschte Ergebnis erzielst.

Funktioniert auch nicht, weil false...

Und jetzt?
Die Kuh will wohl nicht gemolken werden
LG
 
Hm... jetzt würde ich sagen, wendest Du Dich an den Softwarehersteller und fragst, was das für ein Müll ist 🙂 Ich weiß nicht mehr weiter 🙁 Wenn Du das destroy aufrufst, dürfte das nun einem Prozess beenden via Task Manager entsprechen.

Vielleicht hat @JustNobody noch eine Idee, der hatte, glaub ich, früher mal intensiver mit Microsoft zu tun.
 
Ach menno - ich wollte Dich das machen lassen 🙂

Ich würde es erst einmal ohne Java analysieren. Da könnte man z.B. mit der Sysinternals Suite sich alles anzeigen lassen. Die ersten Schritte gehen aber auch mit dem Taskmanager.

Unter welchem User laufen die Prozesse? Kannst Du die Prozesse sauber identifizieren? Wenn ja: Über welches Merkmal?

Bei der Sysinternals Suite wäre interessant: pslist zur Anzeige von Prozessen (ohne User) und auch pskill um ein Prozess zu killen.

Interessant könnte die Ausgabe von psinfo sein.

Um Details eines Prozesses anzusehen: procmon.

Genereller Hinweis: Die 64 Bit Variante hat immer noch ein 64 angehängt....

Wenn man auf der Ebene des Betriebssystems weiss, was zu tun ist und wie es genau geht, dann kann man das auch in Java umsetzen. Aber der erste Schritt wäre für mich die genaue Analyse der Problems / Anforderung.
 
Unter welchem User laufen die Prozesse?
Die laufen unter "Technik", das ist der Benutzer dieses Notebooks.
pslist zur Anzeige von Prozessen (ohne User) und auch pskill um ein Prozess zu killen.

Interessant könnte die Ausgabe von psinfo sein.
Die funktionieren bei mir alle nicht, da wird das jeweilige Programm immer gleich wieder abgebrochen.

Ich habe ein Bild der Procmon-Ausgabe angefügt, hoffe das hilft schon etwas weiter?
Da habe ich über die PID des MilkManagers gefiltert.
 

Anhänge

  • Process Monitor.PNG
    Process Monitor.PNG
    13,5 KB · Aufrufe: 9
Das sind Kommandozeilen-Tools. Also in einer Eingabeaufforderung sind die Programme zu starten. Und dann halt mal schauen, ob die Applikationen mittels pskill geschlossen werden können ...
 
Ja, das wäre natürlich auch noch eine Möglichkeit: externes Tool zum Beenden der Anwendung aufrufen. An die PID kommen wir ja ran...
 
Wobei da ja irgend etwas schief gelaufen ist. Die Tools machen ja nichts magisches. Es gibt also keinen Grund, wieso ein Windows Tool einen Prozess beenden kann aber das unter Java nicht funktionieren sollte. Ich selbst habe etwas gegen dieses aufrufen von externen Programmen, denn da gibt man zu viel Kontrolle ab...

Daher sollte man mit Windows Bordmitteln (bzw. inoffiziellen Standard-Tools wie Sysinternals Suite - gehört auch zu Microsoft!) da erst einmal ran gehen. Wenn wir damit
a) einen Überblick bekommen haben, was wir wollen.
b) einen ersten Lösungsweg haben
dann kann man sich über die Implementation Gedanken machen.

Generell sind hier valide Möglichkeiten aufgezeigt worden und die sollten aus meiner Sicht auch problemlos funktionieren.
 
Also mit pskill kann ich über die ID die Anwendung schließen... 🙂
Aber kill klingt auch schon wieder bisschen nach hartem Abbruch oder?
 

Neue Themen


Zurück
Oben