10

WindowsでJavaでスクリーンショットをキャプチャする方法を知っている人はいますか(それ自体の画面ではなく、デスクトップ上の他のウィンドウであり、必ずしもアクティブなウィンドウである必要はありません)? この同様のテーマに関するスレッドが多数ありますが、まだ答えが見つかりません。

JNA を使用してみましたが、数回試行した後に動かなくなりました。例えば...

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

「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 やその他のツールを使用する必要がありますか?

4

3 に答える 3

16

これが実際の例です。

キャプチャされるアプリケーションを最小化することはできませんが、フォーカスを設定したり、最前面に表示する (つまり、表示する) 必要はありません。

関連する C# スレッドで提供されているコード、MSDN の記事Capturing an Imageおよび 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 Web サイトで見つけることができます) に含まれていないいくつかの追加関数を定義する必要がありました。

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

}
于 2011-01-13T16:21:25.450 に答える
4

を使用し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");
    }

コードはもともとhereから盗まれました。

于 2010-12-14T04:55:53.193 に答える
4

元の質問については、ここにあります。

Windows で非アクティブなウィンドウをキャプチャするのは非常に簡単です。キャプチャの瞬間にウィンドウが表示されている場合にのみ、ロボット クラスを使用します。その要件を回避したい場合は、DWM API を使用する必要があります。

通常の Windows API (Vista より前) を使用すると、 GetWindowRect(handle,RECT) を使用できます。ハンドルは、キャプチャするウィンドウへのハンドラーです。これにより、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 で例外が発生します (負の 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 にどのように変換されるかがわかります。

乾杯、私は言わなければならない、非常に良い質問.

于 2010-12-15T18:22:53.260 に答える