Java – 窗口图像

有没有人知道如何捕获Java中的屏幕截图(不是它自己的屏幕,而是桌面上的任何其他窗口,它们不一定必须是活动窗口)? 这个类似的主题有很多线索,但我还没有找到答案。

我尝试过使用JNA,但经过几次尝试后卡住了。 例如…

public class Main { public static void main(String[] args) { Main m = new Main(); List list = m.getWindows(); for (int i=0;i<list.size();i++) { WindowInfo info = list.get(i); System.out.println(info.getTitle()); } WindowInfo wi = list.get(0); W32API.HDC hdcSrc = User32.instance.GetWindowDC(wi.getHwnd()); W32API.HDC hdcMemory = Gdi32.instance.CreateCompatibleDC(hdcSrc); //W32API.HBITMAP hBitmapMemory = Gdi32.instance.CreateCompatibleBitmap(hdcSrc, int width, int height); int width = wi.getRect().right - wi.getRect().left; int height = wi.getRect().bottom - wi.getRect().top; W32API.HBITMAP hBitmapMemory = Gdi32.instance.CreateCompatibleBitmap(hdcSrc, width, height); W32API.HANDLE hOld = Gdi32.instance.SelectObject(hdcMemory, hBitmapMemory); Gdi32.instance.BitBlt(hdcMemory, 0, 0, width, height, hdcSrc, width+2, height+2, 0x00CC0020); /* # now how do we convert to a BufferedImage??? */ // clean up Gdi32.instance.SelectObject(hdcMemory, hOld); Gdi32.instance.DeleteDC(hdcMemory); Gdi32.instance.DeleteObject(hBitmapMemory); User32.instance.ReleaseDC(wi.getHwnd(), hdcSrc); } /** * * @return */ private List getWindows() { final List list = new ArrayList(); User32.instance.EnumWindows(new WndEnumProc() { public boolean callback(int hWnd, int lParam) { if (User32.instance.IsWindowVisible(hWnd)) { RECT r = new RECT(); User32.instance.GetWindowRect(hWnd, r); byte[] buffer = new byte[1024]; User32.instance.GetWindowTextA(hWnd, buffer, buffer.length); String title = Native.toString(buffer); if (title!=null&&title.length()>0) { list.add(new WindowInfo(hWnd, r, title)); } } return true; } }, 0); Collections.sort(list, new Comparator() { public int compare(WindowInfo o1, WindowInfo o2) { int i1 = (o1.getTitle()!=null&&o1.getTitle().length()>0?o1.getTitle():" ").charAt(0); int i2 = (o2.getTitle()!=null&&o2.getTitle().length()>0?o2.getTitle():" ").charAt(0); return i1 - i2; } }); return list; } } 

我也试过相当于“PrintWindow()”的API ……

 Graphics g = form.CreateGraphics(); Bitmap bmp = new Bitmap(form.Size.Width, form.Size.Height, g); Graphics memoryGraphics = Graphics.FromImage(bmp); IntPtr dc = memoryGraphics.GetHdc(); bool success = PrintWindow(form.Handle, dc, 0); memoryGraphics.ReleaseHdc(dc); // bmp now contains the screenshot 

或者我必须使用JNI或任何其他工具?

这是一个有效的例子。

被捕获的应用程序无法最小化,但它不需要具有焦点或位于顶部(即可见)。

相关C#线程中提供的代码,MSDN文章捕获图像和jmemoryeditorw提供了必要的部分。

该代码使用GetDC和GetClientRect来捕获窗口的客户区域。 如果要捕获包括窗口装饰在内的整个窗口,可以用GetWindowDC和GetWindowRect替换它们。

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

我必须定义一些未包含在platform.jar中的额外函数(可以在JNA网站上找到)。

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

使用java.awt.Robot.createScreenCapture()

这是一个例子:

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

代码最初是从这里偷来的 。

对于你原来的问题,这里就是这样。

在Windows中捕获非活动窗口非常简单,仅使用机器人类,只有在捕获时窗口可见时才可以使用。 如果要避免该要求,则必须使用DWM API。

使用普通的Windows API(Vista之前),您可以使用GetWindowRect(handle,RECT),其中handle是您要捕获的窗口的处理程序。 这将获得一个RECT对象(我假设您正在使用JNA),这是您应该编写的代码序列:

 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 

然而!! 如果您的窗口当前可见,这将仅作为魅力。 如果它被最小化,你将在java中得到一些exception(因为它有负x和y)。 如果它被部分隐藏,您还将截取其上的其他窗口。

您无法在没有dwm(桌面Windows管理器)的盒子上解决您的问题,因为它有一个API,允许不同的窗口在实际绘制到屏幕之前写入临时缓冲区。

但是,在XP和非运行的DWM机器上,你会遇到我给你的代码。

此外,您可以查看以下问题: 链接文本

编辑:

这是一个有趣的指南(在C#中,但您可以使用JNA + Java应用相同的原理),这将使您更好地理解DWM以及如何使用它来完全按照您的需要进行操作。

链接文字

EditEdit刚刚看到你有一个链接到我给你的C#中的同一个指南。 只是重写Java / JNA的代码似乎有什么问题?

EditEditEdit要回答您的其他问题(如何将BitBit转换为BufferedImage),这里有一个人在他的开源项目中做过这个。 这是一件很好的工作,并给予他一些赞赏:

http://code.google.com/p/jmemoryeditorw/

您可能会注意到,如果您运行该程序,它将为您提供所有进程以及…他们的图标。 如果您深入研究代码,您将看到它们如何从BitBit转换为BufferedImages。

干杯和我不得不说,这是一个非常好的问题。