2010-12-13 19 views
7

¿Alguien sabe cómo capturar una captura de pantalla en Java (no es pantalla propia, pero cualquier otra ventana en el escritorio y que hacer no necesariamente tiene que ser el activa ventana) en Windows ? Aquí hay varios temas sobre este tema similar, pero aún no he encontrado una respuesta.Java - ventana de la imagen

He intentado usar JNA, pero se atascó después de algunos intentos. Por ejemplo ...

public class Main {

public static void main(String[] args) { 
    Main m = new Main(); 

    List<WindowInfo> 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<WindowInfo> getWindows() { 

    final List<WindowInfo> list = new ArrayList<WindowInfo>(); 


    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<WindowInfo>() { 
     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; 
} 

También he probado el equivalente de "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 

O tengo que usar JNI, o cualquier otra herramienta?

Gracias de antemano ...

Respuesta

10

Aquí hay un ejemplo de trabajo.

La aplicación que se está capturando no se puede minimizar, pero no necesita tener el foco ni estar arriba (es decir, visible).

El código proporcionado en el hilo C# relacionado, el artículo MSDN Capturing an Image y jmemoryeditorw proporcionó las piezas necesarias.

El código usa GetDC y GetClientRect para capturar el área del cliente de la ventana. Se pueden reemplazar por GetWindowDC y GetWindowRect si desea capturar toda la ventana, incluidas las decoraciones de las ventanas.

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



} 

tuviera que definir algunas funciones adicionales que no estaban incluidos en platform.jar (que se puede encontrar en el sitio web 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); 

} 
+0

muy agradable ... gran respuesta! ¡Gracias! – rog8gm

+0

¿crees que puedes explicar cómo funciona la clase HDC? Intenté hacer un programa smilar antes de poder contener un montón de instancias de HDC y luego convertirlas a una imagen de buffer, lo que me pregunta es dónde se almacena la información de HDC – user4090

+1

Esto lo hace un poco más rápido: 'BufferedImage image = new BufferedImage (ancho, alto, BufferedImage.TYPE_INT_RGB); MemoryImageSource source = new MemoryImageSource (ancho, alto, buffer.getIntArray (0, ancho * alto), 0, ancho); image.getGraphics(). DrawImage (Toolkit.getDefaultToolkit(). CreateImage (origen), 0, 0, nulo); ' – onkelv

5

Use java.awt.Robot.createScreenCapture().

He aquí un ejemplo: robo

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

Código originalmente de here.

+0

Esto no funcionaría para las ventanas en segundo plano, mientras que la solución JNA sí lo haría. – acheron55

+0

Esta metodología no funciona demasiado bien cuando hay una ventana de notificación que aparece sobre el rectángulo de captura. –

3

Para su pregunta original, aquí va.

La captura de una ventana inactiva en Windows es bastante sencillo, utilizando la clase de robot, sólo y únicamente si la ventana es visible en el momento de la captura. Si desea evitar ese requisito, TIENE que usar la API de DWM.

Uso de la API de Windows normal (pre Vista), puede utilizar GetWindowRect (mango, RECT), donde mango es un manejador de la ventana que desea capturar. Esto le dará un objeto RECT (supongo que está utilizando JNA), aquí está la secuencia de código que debe escribir:

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 

Sin embargo !! Esto funcionará como un encanto SÓLO si su ventana está actualmente visible. Si se reduce al mínimo, obtendrá una excepción en java (porque tiene x e y negativos). Y si está parcialmente oculto, también capturará una captura de pantalla de las otras ventanas que están encima de él.

No puede resolver su problema en cajas que no tienen dwm (Administrador de Windows de escritorio) ya que tiene una API que permite que diferentes ventanas escriban en un búfer temporal antes de que realmente se pinten en la pantalla.

En XP y no - ejecución de máquinas DWM, sin embargo, que hay que contentarse con el código que te di.

Además, puede echar un vistazo a la siguiente pregunta: link text

Editar:

Aquí es una interesante guía (en C#, sin embargo, pero se puede utilizar la aplicación de JNA + Java del mismos principios) que le darán una mejor comprensión del DWM y cómo usarlo para hacer EXACTAMENTE lo que quiere.

link text

Desambiguacióneditar Acabo de ver que tiene un enlace a la misma guía en C# que te di. ¿Cuál parece ser el problema solo al reescribir el código para Java/JNA?

EditEditEdit Para responder a su pregunta adicional (cómo convertir su Bitbit a un BufferedImage), aquí es un tipo que lo hizo en su proyecto Open Source. Es una bonita pieza de trabajo y le dan una cierta apreciación:

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

Usted puede notar que si se ejecuta el programa, se le dará todos los procesos y también ... sus iconos. Si profundiza en el código, verá cómo se convierten de BitBit a BufferedImages.

Saludos y tengo que decir, una muy buena pregunta.

+0

muchas gracias baba !! este enlace ([http://code.google.com/p/jmemoryeditorw/][1]) parece realmente prometedor. Te dejaré saber cómo voy en unos días. ¡Gracias de nuevo! [1]: http://code.google.com/p/jmemoryeditorw/ – rog8gm