I don't usually program specifically for Windows. I run Windows out of necessity, but almost all of my recent programming work is in Python, Julia, or R using WSL-2. Windows makes a reasonable desktop environment Linux development. I would consider ditching Windows entirely, but I have to support our local church and family, all of whom used Windows.
Recently, I was asked to develop a simple app that ran on a dedicated Windows notebook connected to a monitoring device. The task was relatively easy, capture a specified Window and save an image of it to a file. The target window title, the output location, and the image type were to be stored in a parameter file that was to be located in a common directory. This sort of thing would be relatively easy in Python, but I was not allowed to install Python on the notebook. The notebook did have Java installed so that was an option. Using C++ and creating a Windows .exe file was also a reasonable choice.
In my misspent youth, I did a lot of Windows programming, but that was long ago and I have avoided writing anything Windows only since then. I don't remember much about Windows internals, so I set about searching the web to find a way to get started. I quickly found this StackOverflow article which showed how to enumerate all the visible windows on a system running Windows 10. (Where would we be without StackOverflow?) The example was written in Java, so I decided to start from there.
The code below is not exactly the same as the code I write for the notebook. I decided to abandon the parameter file and read the necessary arguments from the command line for this example. The rest of the program is identical. The program enumerates the visible windows until it finds a window title containing an input target string. When it finds the target window, it copies the screen contents bounded by the window rectangle to a buffer and writes it to a file in an image format.
In order to interface Java to the Windows OS, a couple of libraries that are not included in the Java SDK are needed: jna-5.12.1.jar and jna-platform-5.12.1.jar. These are the Java Native Access libraries that can be found on GitHub. These libraries provide a Java interface to native OS libraries.
Finding a Window
public static void main(String[] args) { /* get window title, output path, and image format from command line. */ final String target = args[0]; final String outputPath = args[1]; final String format = args[2]; /* contruct file name */ final String outFileName = getOutputFileName(outputPath, format); // create an empty WindowInfo object to hold target window when we find it. final WindowInfo targetWin = new WindowInfo(); // enumerate windows, stop when we find target User32.instance.EnumWindows(new WndEnumProc() { public boolean callback(int hWnd, int lParam) { if (User32.instance.IsWindowVisible(hWnd)) { final WinDef.RECT r = new WinDef.RECT(); User32.instance.GetWindowRect(hWnd, r); if (r.left > -32000) { // If it's not minimized byte[] buffer = new byte[1024]; User32.instance.GetWindowTextA(hWnd, buffer, buffer.length); String title = Native.toString(buffer);
// If we found it, get the window information if (title.indexOf(target) != -1) { targetWin.hwnd = hWnd; targetWin.rect = r; targetWin.title = title; return false; } } } return true; } }, 0); System.out.println(targetWin); captureWindow(targetWin, format, outFileName); } }
/** * captureWindow * Capture MS Windows window as image and save it to a file. * * @param targetWin - a WindowInfo object reference. Window to capture * @param format - output format, png, jpg, etc. * @param fileName - where output should go */ private static void captureWindow(WindowInfo targetWin, String format, String fileName) { try { Robot robot = new Robot(); Rectangle winRect = new Rectangle(targetWin.rect.left, targetWin.rect.top, Math.abs(targetWin.rect.right - targetWin.rect.left + 1), Math.abs(targetWin.rect.bottom - targetWin.rect.top + 1)); BufferedImage screenImage = robot.createScreenCapture(winRect); ImageIO.write(screenImage, format, new File(fileName)); System.out.println("Screenshot saved!"); } catch (AWTException | IOException ex) { System.err.println(ex); } }
/** * getOutputFileName * Construct an output file name from outPath and current date and time. * * @param outPath - where output should be written * @param format - outfile imgae type * @return full path output file based on data nad time. */ private static String getOutputFileName(String outPath, String format) { outPath = outPath.replace('\\', '/'); if (outPath.charAt(outPath.length() - 1) != '/') outPath += "/"; DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"); LocalDateTime now = LocalDateTime.now(); String nowStr = dtf.format(now); String outFileName = outPath + nowStr + "." + format; return outFileName; }
/* * WindowInfo * A class to store window information. */ private static class WindowInfo { public int hwnd; public WinDef.RECT rect; public String title; /* Constructors */ public WindowInfo() { this.hwnd = 0; this.rect = new WinDef.RECT(); this.title = null; } public WindowInfo(int hwnd, WinDef.RECT rect, String title) { this.hwnd = hwnd; this.rect = rect; this.title = title; } /* for printing */ public String toString() { return String.format("(%d,%d)-(%d,%d) : \"%s\"", rect.left, rect.top, rect.right, rect.bottom, title); } }
javac -cp lib/jna-5.12.1.jar;lib/jna-platform-5.12.1.jar src/CaptureWindow.java java -cp src/;lib/jna-5.12.1.jar;lib/jna-platform-5.12.1.jar; CaptureWindow "Hoags Corners" "D:\Documents\analytic_garden\screen_cap_blog\blog\CaptureWindow" png
No comments:
Post a Comment