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