org.sikuli.natives.WinUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sikulixapi Show documentation
Show all versions of sikulixapi Show documentation
... for visual testing and automation
/*
* Copyright (c) 2010-2019, sikuli.org, sikulix.com - MIT license
*/
package org.sikuli.natives;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Psapi;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.ptr.IntByReference;
import org.apache.commons.io.FilenameUtils;
import org.sikuli.script.App;
import org.sikuli.script.Region;
import org.sikuli.script.support.RunTime;
import org.sikuli.script.runners.ProcessRunner;
import java.awt.Rectangle;
import java.awt.Window;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import com.sun.jna.platform.win32.Kernel32;
public class WinUtil implements OSUtil {
static final int BUFFERSIZE = 32 * 1024 - 1;
static final Kernel32 kernel32 = Kernel32.INSTANCE;
static final SXUser32 sxuser32 = SXUser32.INSTANCE;
static final User32 user32 = User32.INSTANCE;
static final Psapi psapi = Psapi.INSTANCE;
public static final class ProcessInfo {
private int pid;
private String imageName;
public ProcessInfo(
final int pid,
final String imageName) {
this.pid = pid;
this.imageName = imageName;
}
public int getPid() {
return pid;
}
public String getImageName() {
return imageName;
}
}
public static final class WindowInfo {
public HWND hwnd;
public int pid;
public String title;
public WindowInfo(HWND hwnd, int pid, String title) {
super();
this.hwnd = hwnd;
this.pid = pid;
this.title = title;
}
public HWND getHwnd() {
return hwnd;
}
public int getPid() {
return pid;
}
public String getTitle() {
return title;
}
}
public static List allWindows() {
/* Initialize the empty window list. */
final List windows = new ArrayList<>();
/* Enumerate all of the windows and add all of the one for the
* given process id to our list. */
boolean result = user32.EnumWindows(
new WinUser.WNDENUMPROC() {
public boolean callback(
final HWND hwnd, final Pointer data) {
if (user32.IsWindowVisible(hwnd)) {
IntByReference windowPid = new IntByReference();
user32.GetWindowThreadProcessId(hwnd, windowPid);
String windowTitle = getWindowTitle(hwnd);
windows.add(new WindowInfo(hwnd, windowPid.getValue(), windowTitle));
}
return true;
}
},
null);
/* Handle errors. */
if (!result && Kernel32.INSTANCE.GetLastError() != 0) {
throw new RuntimeException("Couldn't enumerate windows.");
}
/* Return the window list. */
return windows;
}
public static String getWindowTitle(HWND hWnd) {
char[] text = new char[1024];
int length = user32.GetWindowText(hWnd, text, 1024);
return length > 0 ? new String(text, 0, length) : null;
}
public static String getTopWindowTitle(int pid) {
List windows = getWindowsForPid(pid);
if (!windows.isEmpty()) {
return getWindowsForPid(pid).get(0).getTitle();
}
return null;
}
public static List allProcesses() {
List processList = new ArrayList();
HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(
Tlhelp32.TH32CS_SNAPPROCESS, new DWORD(0));
try {
Tlhelp32.PROCESSENTRY32.ByReference pe
= new Tlhelp32.PROCESSENTRY32.ByReference();
for (boolean more = Kernel32.INSTANCE.Process32First(snapshot, pe);
more;
more = Kernel32.INSTANCE.Process32Next(snapshot, pe)) {
int pid = pe.th32ProcessID.intValue();
String name = getProcessImageName(pe.th32ProcessID.intValue());
if (null == name) {
continue;
}
processList.add(new ProcessInfo(pid, name));
}
return processList;
} finally {
Kernel32.INSTANCE.CloseHandle(snapshot);
}
}
private static String getProcessImageName(int pid) {
HANDLE hProcess = Kernel32.INSTANCE.OpenProcess(
0x1000,
false,
pid);
if (hProcess != null) {
try {
char[] imageNameChars = new char[1024];
IntByReference imageNameLen
= new IntByReference(imageNameChars.length);
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, 0, imageNameChars, imageNameLen)) {
String name = FilenameUtils.getName(new String(imageNameChars, 0, imageNameLen.getValue()));
return name;
}
return null;
} finally {
Kernel32.INSTANCE.CloseHandle(hProcess);
}
}
return null;
}
private static List getWindowsForPid(int pid) {
return allWindows().stream().filter((w) -> w.getPid() == pid).collect(Collectors.toList());
}
private static List getWindowsForName(String name) {
return allWindows().stream().filter((w) -> {
String imageName = getProcessImageName(w.getPid());
if (imageName != null && imageName.equals(name + ".exe")) {
return true;
}
String windowTitle = w.getTitle();
if (windowTitle != null && windowTitle.contains(name)) {
return true;
}
return false;
}).collect(Collectors.toList());
}
public static String getEnv(String envKey) {
char[] retChar = new char[BUFFERSIZE];
String envVal = null;
int retInt = kernel32.GetEnvironmentVariable(envKey, retChar, BUFFERSIZE);
if (retInt > 0) {
envVal = new String(Arrays.copyOfRange(retChar, 0, retInt));
}
return envVal;
}
public static String setEnv(String envKey, String envVal) {
boolean retOK = kernel32.SetEnvironmentVariable(envKey, envVal);
if (retOK) {
return getEnv(envKey);
}
return null;
}
/*
https://msdn.microsoft.com/pt-br/library/windows/desktop/dd375731
VK_NUM_LOCK 0x90
VK_SCROLL 0x91
VK_CAPITAL 0x14
*/
private static int WinNumLock = 0x90;
private static int WinScrollLock = 0x91;
private static int WinCapsLock = 0x14;
public static int isNumLockOn() {
int state = sxuser32.GetKeyState(WinNumLock);
return state;
}
public static int isScrollLockOn() {
int state = sxuser32.GetKeyState(WinScrollLock);
return state;
}
public static int isCapsLockOn() {
int state = sxuser32.GetKeyState(WinCapsLock);
return state;
}
@Override
public void checkFeatureAvailability() {
RunTime.loadLibrary("WinUtil");
}
@Override
public App get(App app) {
if (app.getPID() > 0) {
app = getTaskByPID(app);
} else {
app = getTaskByName(app);
}
return app;
}
private static App getTaskByName(App app) {
String appName = app.getToken().isEmpty() ? app.getName() + ".exe" : app.getToken();
List processes = allProcesses();
for (ProcessInfo p : processes) {
if (p.getImageName() != null && p.getImageName().equals(appName)) {
app.setPID(p.getPid());
app.setWindow(getTopWindowTitle(p.getPid()));
return app;
}
}
return getTaskByWindow(app);
}
private static App getTaskByPID(App app) {
if (!app.isValid()) {
return app;
}
List processes = allProcesses();
for (ProcessInfo p : processes) {
if (p.getPid() == app.getPID()) {
app.setWindow(getTopWindowTitle(p.getPid()));
return app;
}
}
app.reset();
return app;
}
private static App getTaskByWindow(App app) {
String title = app.getName();
List windows = allWindows();
for (WindowInfo window : windows) {
String windowTitle = window.getTitle();
if (windowTitle != null && windowTitle.contains(title)) {
app.setPID(window.getPid());
app.setWindow(windowTitle);
return app;
}
}
return app;
}
@Override
public List getApps(String name) {
List apps = new ArrayList<>();
List processes = allProcesses();
for (ProcessInfo p : processes) {
if (p.getImageName().contains(name)) {
String winTitle = getTopWindowTitle(p.getPid());
if (winTitle == null) continue;
App theApp = new App();
theApp.setName(p.getImageName());
theApp.setWindow(winTitle);
theApp.setPID(p.getPid());
apps.add(theApp);
}
}
return apps;
}
@Override
public boolean open(App app) {
if (app.isValid()) {
return 0 == switchApp(app.getPID(), 0);
} else {
String cmd = app.getExec();
String workDir = app.getWorkDir();
if (!app.getOptions().isEmpty()) {
start(cmd, workDir, app.getOptions());
} else {
start(cmd, workDir);
}
}
return true;
}
private int start(String... cmd) {
return ProcessRunner.startApp(cmd);
}
@Override
public boolean switchto(App app) {
if (!app.isValid()) {
return false;
}
int loopCount = 0;
while (loopCount < 100) {
int pid = switchApp(app.getPID(), 0);
if (pid > 0) {
if (pid == app.getPID()) {
app.setFocused(true);
getTaskByPID(app);
return true;
}
} else {
break;
}
loopCount++;
}
return false;
}
@Override
public App switchto(String title, int index) {
App app = new App();
int pid = switchApp(title, index);
if (pid > 0) {
app.setPID(pid);
return getTaskByPID(app);
}
return app;
}
@Override
public boolean close(App app) {
if (closeApp(app.getPID()) == 0) {
app.reset();
return true;
}
return false;
}
public native int closeApp(int pid);
@Override
public Rectangle getWindow(App app) {
return getWindow(app, 0);
}
@Override
public Rectangle getWindow(App app, int winNum) {
get(app);
if (!app.isValid()) {
return null;
}
return getWindow(app.getPID(), winNum);
}
@Override
public Rectangle getWindow(String title) {
return getWindow(title, 0);
}
private Rectangle getWindow(String title, int winNum) {
HWND hwnd = getHwnd(title, winNum);
return hwnd != null ? _getWindow(hwnd, winNum) : null;
}
private Rectangle getWindow(int pid, int winNum) {
HWND hwnd = getHwnd(pid, winNum);
return hwnd != null ? _getWindow(hwnd, winNum) : null;
}
private Rectangle _getWindow(HWND hwnd, int winNum) {
Rectangle rect = getRegion(hwnd, winNum);
return rect;
}
@Override
public Rectangle getFocusedWindow() {
return getFocusedRegion();
}
@Override
public List getWindows(App app) {
app = get(app);
List windows = getWindowsForPid(app.getPID());
List regions = new ArrayList<>();
for (WindowInfo w : windows) {
regions.add(Region.create(_getWindow(w.getHwnd(), 0)));
}
return regions;
}
private int switchAppWindow(WindowInfo window) {
HWND hwnd = window.getHwnd();
WinUser.WINDOWPLACEMENT lpwndpl = new WinUser.WINDOWPLACEMENT();
user32.GetWindowPlacement(hwnd, lpwndpl);
if (lpwndpl.showCmd == WinUser.SW_SHOWMINIMIZED || lpwndpl.showCmd == WinUser.SW_MINIMIZE) {
user32.ShowWindow(hwnd, WinUser.SW_RESTORE);
}
boolean success = user32.SetForegroundWindow(hwnd);
if (success) {
user32.SetFocus(hwnd);
IntByReference windowPid = new IntByReference();
user32.GetWindowThreadProcessId(hwnd, windowPid);
return windowPid.getValue();
} else {
return 0;
}
}
public int switchApp(String appName, int num) {
List windows = getWindowsForName(appName);
if (windows.size() > num) {
return switchAppWindow(windows.get(num));
}
return 0;
}
public int switchApp(int pid, int num) {
List windows = getWindowsForPid(pid);
if (windows.size() > num) {
return switchAppWindow(windows.get(num));
}
return 0;
}
private static HWND getHwnd(String appName, int winNum) {
List windows = getWindowsForName(appName);
if (windows.size() > winNum) {
return windows.get(winNum).getHwnd();
}
return null;
}
private static HWND getHwnd(int pid, int winNum) {
List windows = getWindowsForPid(pid);
if (windows.size() > winNum) {
return windows.get(winNum).getHwnd();
}
return null;
}
private static Rectangle getRegion(HWND hwnd, int winNum) {
RECT rect = new User32.RECT();
boolean success = user32.GetWindowRect(hwnd, rect);
return success ? rect.toRectangle() : null;
}
private static Rectangle getFocusedRegion() {
HWND hwnd = user32.GetForegroundWindow();
RECT rect = new User32.RECT();
boolean success = user32.GetWindowRect(hwnd, rect);
return success ? rect.toRectangle() : null;
}
}