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);
}
}