Does anyone know how to capture a screen shot in Java (not it\'s own screen, but any other window on the desktop and they don\'t necessarily have to be the
Here's a working example.
The application being captured can't be minimized but it doesn't need to have focus or be on top (i.e. visible).
The code provided in the related C# thread, the MSDN article Capturing an Image and jmemoryeditorw provided the necessary pieces.
The code uses GetDC and GetClientRect to capture the client area of the window. They can be replaced by GetWindowDC and GetWindowRect if you want to capture the whole window including window decorations.
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import jna.extra.GDI32Extra;
import jna.extra.User32Extra;
import jna.extra.WinGDIExtra;
import com.sun.jna.Memory;
import com.sun.jna.platform.win32.GDI32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HBITMAP;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinGDI;
import com.sun.jna.platform.win32.WinGDI.BITMAPINFO;
import com.sun.jna.platform.win32.WinNT.HANDLE;
public class Paint extends JFrame {
public BufferedImage capture(HWND hWnd) {
HDC hdcWindow = User32.INSTANCE.GetDC(hWnd);
HDC hdcMemDC = GDI32.INSTANCE.CreateCompatibleDC(hdcWindow);
RECT bounds = new RECT();
User32Extra.INSTANCE.GetClientRect(hWnd, bounds);
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
HBITMAP hBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(hdcWindow, width, height);
HANDLE hOld = GDI32.INSTANCE.SelectObject(hdcMemDC, hBitmap);
GDI32Extra.INSTANCE.BitBlt(hdcMemDC, 0, 0, width, height, hdcWindow, 0, 0, WinGDIExtra.SRCCOPY);
GDI32.INSTANCE.SelectObject(hdcMemDC, hOld);
GDI32.INSTANCE.DeleteDC(hdcMemDC);
BITMAPINFO bmi = new BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
Memory buffer = new Memory(width * height * 4);
GDI32.INSTANCE.GetDIBits(hdcWindow, hBitmap, 0, height, buffer, bmi, WinGDI.DIB_RGB_COLORS);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.setRGB(0, 0, width, height, buffer.getIntArray(0, width * height), 0, width);
GDI32.INSTANCE.DeleteObject(hBitmap);
User32.INSTANCE.ReleaseDC(hWnd, hdcWindow);
return image;
}
public static void main(String[] args) {
new Paint();
}
BufferedImage image;
public Paint() {
HWND hWnd = User32.INSTANCE.FindWindow(null, "Untitled - Notepad");
this.image = capture(hWnd);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image, 20, 40, null);
}
}
I had to define some extra functions that weren't included in platform.jar (which can be found on the JNA website).
package jna.extra;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.GDI32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.win32.W32APIOptions;
public interface GDI32Extra extends GDI32 {
GDI32Extra INSTANCE = (GDI32Extra) Native.loadLibrary("gdi32", GDI32Extra.class, W32APIOptions.DEFAULT_OPTIONS);
public boolean BitBlt(HDC hObject, int nXDest, int nYDest, int nWidth, int nHeight, HDC hObjectSource, int nXSrc, int nYSrc, DWORD dwRop);
}
package jna.extra;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.win32.W32APIOptions;
public interface User32Extra extends User32 {
User32Extra INSTANCE = (User32Extra) Native.loadLibrary("user32", User32Extra.class, W32APIOptions.DEFAULT_OPTIONS);
public HDC GetWindowDC(HWND hWnd);
public boolean GetClientRect(HWND hWnd, RECT rect);
}
package jna.extra;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinGDI;
public interface WinGDIExtra extends WinGDI {
public DWORD SRCCOPY = new DWORD(0x00CC0020);
}
Use java.awt.Robot.createScreenCapture()
.
Here's an example:
try {
Robot robot = new Robot();
Rectangle size = new Rectangle(Toolkit.getDefaultToolkit()
.getScreenSize());
BufferedImage buf = robot.createScreenCapture(size);
ImageIO.write(buf, "png", new File("d:/test.png"));
} catch (AWTException ae) {
throw new RuntimeException("something went wrong");
}
Code originally stolen from here.
For your original question, here it goes.
Capturing an inactive window in Windows is pretty straightforward, using the robot class, ONLY and ONLY if the window is visible at the moment of capturing. If you want to avoid that requirement, you HAVE to use the DWM API.
Using the normal Windows API (pre Vista), you can use GetWindowRect(handle,RECT) where handle is a handler to the window you want to capture. This will get you a RECT object (I assume you are using JNA), here is the sequence of code you should write:
RECT dimensionsOfWindow = new RECT();
GetWindowRect( handlerToWindow, dimensionsOfWindow );//now in the dimensionsOfWindow you have the dimensions
Robot robot = new Robot();
BufferedImage img = robot.createScreenCapture( dimensionsOfWindow.toRectangle() );//now in the img object you have only the image of your desired window
However!! This will work as a charm ONLY if your window is currently visible. If it is minimized, you will get some exception in java (because it has negative x and y ). And if it is partially hidden, you will also screenshot the other windows that are on top of it.
You can't solve your problem on boxes that don't have dwm (Desktop Windows Manager) as it has an API that allows different windows to write to a temp buffer before they actually are painted to the screen.
On XP and non - running DWM machines, however, you are stuck with the code I gave you.
Additionally , you can take a look at the following question: link text
Edit:
Here is an interesting guide (in C#, though, but you can use JNA+Java applying the same principles) that will give you a better understanding of the DWM and how to use it to do EXACTLY what you want.
link text
EditEdit Just saw you have a link to the same guide in C# that I gave you. What seems to be the problem in just rewriting the code for Java/JNA?
EditEditEdit To answer your additional question (how to convert your BitBit to a BufferedImage ), here is a guy who did it in his Open Source project. It is a nice piece of work and give him some appreciation:
http://code.google.com/p/jmemoryeditorw/
You might notice that if you run the program, it will give you all the processes and also...their Icons. If you dig in the code, you will see how they are converted from BitBit to BufferedImages.
Cheers and I have to say, a very nice question.