2010-12-13 16 views
7

Czy ktoś wie, jak robić zrzut ekranu w Java (nie swój własny ekran, ale każde inne okno na pulpicie i oni nie koniecznie musi być aktywny okno ) w systemie Windows ? W tym podobnym temacie jest wiele wątków, ale jeszcze nie znalazłem odpowiedzi.Java - Window Obraz

Próbowałem używać JNA, ale utknąłem po kilku próbach. Na przykład ...

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

Próbowałem również równowartość "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 

Czy muszę używać JNI lub jakiegokolwiek innego narzędzia?

góry dzięki ...

Odpowiedz

10

Oto działający przykład.

Nie można zminimalizować przechwytywanej aplikacji, ale nie musi ona mieć fokusa lub być na wierzchu (tzn. Widoczna).

Kod podany w powiązanym wątku C#, artykuł MSDN Capturing an Image i jmemoryeditorw dostarczyły potrzebne elementy.

Kod wykorzystuje GetDC i GetClientRect do przechwytywania obszaru roboczego okna. Można je zastąpić GetWindowDC i GetWindowRect, jeśli chcesz uchwycić całe okno, w tym dekoracje okna.

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



} 

Musiałem zdefiniować kilka dodatkowych funkcji, które nie zostały zawarte w platform.jar (które można znaleźć na stronie internetowej 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

bardzo ładny ... świetna odpowiedź! dzięki! – rog8gm

+0

hej, czy myślisz, że możesz wyjaśnić, jak działa klasa HDC? Spróbowałem zrobić smily program zanim mogłem pomieścić kilka przypadków HDC, a następnie przekonwertować je na obraz buforowy, zastanawiam się, gdzie przechowywane są informacje HDC. – user4090

+1

To sprawia, że ​​jest trochę szybciej: 'BufferedImage image = new BufferedImage (szerokość, wysokość, BufferedImage.TYPE_INT_RGB); MemoryImageSource source = new MemoryImageSource (szerokość, wysokość, buffer.getIntArray (0, szerokość * wysokość), 0, szerokość); image.getGraphics(). DrawImage (Toolkit.getDefaultToolkit(). CreateImage (źródło), 0, 0, null); ' – onkelv

5

Stosować java.awt.Robot.createScreenCapture().

Oto przykład:

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

Kod pierwotnie skradzione here.

+0

Nie działa to w przypadku okien w tle, a rozwiązanie JNA. – acheron55

+0

Ta metodologia nie działa zbyt dobrze, gdy pojawi się okno powiadomień nad prostokątem przechwytywania. –

3

Na twoje oryginalne pytanie, oto idzie.

Przechwytywanie nieaktywnego okna w systemie Windows jest całkiem proste, używając klasy robot, TYLKO i TYLKO, jeśli okno jest widoczne w momencie przechwytywania. Jeśli chcesz uniknąć tego wymogu, MUSISZ użyć interfejsu API DWM.

Korzystając ze zwykłego API Windows (pre Vista), można użyć GetWindowRect (uchwyt, RECT), w którym uchwyt jest uchwytem do okna, które chcesz przechwycić. To będzie Ci obiekt RECT (zakładam używasz JNA), tutaj jest sekwencja kodu należy napisać:

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 

Jednak !! Będzie to działać TYLKO jako urok, jeśli twoje okno jest aktualnie widoczne. Jeśli jest zminimalizowany, otrzymasz wyjątek w java (ponieważ ma ujemny x i y). A jeśli jest częściowo ukryty, zrzutu ekranu również inne okna, które są na nim.

Nie możesz rozwiązać problemu w skrzynkach, które nie mają DWM (Desktop Windows Manager), ponieważ ma API, który pozwala innym oknom na zapisywanie w buforze tymczasowym zanim zostaną one faktycznie zamalowane na ekranie.

Na maszynach XP i nieobsługiwanych DWM utknąłeś jednak pod kodem, który ci podałem.

Dodatkowo, można spojrzeć na następujące pytanie: link text

Edit:

Oto ciekawy przewodnik (w języku C#, chociaż, ale można użyć stosując JNA + Java te same zasady), które pozwolą ci lepiej zrozumieć DWM i jak z niego korzystać, aby DOKŁADNIE to, czego chcesz.

link text

EditEdit prostu zobaczyłem masz link do tego samego przewodnika w języku C#, który ci dałem. Co wydaje się być problemem przy przepisywaniu kodu dla Javy/JNA?

EditEditEdit Aby odpowiedzieć na dodatkowe pytanie (jak do konwertowania BitBit do BufferedImage), tu jest facet, który zrobił to w jego projekt Open Source. Jest to ładny kawałek pracy i dać mu uznanie:

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

Można zauważyć, że po uruchomieniu programu, to daje wszystkie procesy, a także ... ich ikony. Po skopiowaniu kodu zobaczysz, jak są konwertowane z BitBit na BufferedImages.

Pozdrowienia i muszę powiedzieć, bardzo miłe pytanie.

+0

bardzo dziękuję baba !! ten link ([http://code.google.com/p/jmemoryeditorw/][1]) wygląda naprawdę obiecująco! Powiem ci, jak za kilka dni pójdę. Dzięki jeszcze raz! [1]: http://code.google.com/p/jmemoryeditorw/ – rog8gm