Копирование содержимого из скрытого или вырезанного окна в XP? - PullRequest
10 голосов
/ 28 октября 2008

Мне нужно скопировать содержимое окна (BitBlt), которое скрыто, в другое окно. Проблема в том, что, как только я прячу окно источника, контекст устройства, который я получил, больше не окрашивается.

Ответы [ 10 ]

10 голосов
/ 28 октября 2008

Вам нужна функция PrintWindow , которая доступна в Win32 API начиная с Windows XP. Если вам это нужно для работы с более старыми версиями Windows, вы можете попробовать WM_PRINT , хотя я так и не смог заставить его работать.

Здесь есть хорошая статья , которая показывает, как использовать PrintWindow, и вот соответствующий фрагмент кода из этой статьи:

// Takes a snapshot of the window hwnd, stored in the memory device context hdcMem
HDC hdc = GetWindowDC(hwnd);
if (hdc)
{
    HDC hdcMem = CreateCompatibleDC(hdc);
    if (hdcMem)
    {
        RECT rc;
        GetWindowRect(hwnd, &rc);

        HBITMAP hbitmap = CreateCompatibleBitmap(hdc, RECTWIDTH(rc), RECTHEIGHT(rc));
        if (hbitmap)
        {
            SelectObject(hdcMem, hbitmap);

            PrintWindow(hwnd, hdcMem, 0);

            DeleteObject(hbitmap);
        }
        DeleteObject(hdcMem);
    }
    ReleaseDC(hwnd, hdc);
}

У меня должен быть некоторый код Python, который использует wxPython для достижения того же. Напишите мне, если хотите.

1 голос
/ 28 октября 2008

Вы можете попробовать отправить сообщение WM_PRINT в окно. Для многих окон (включая все стандартные окна и общие элементы управления) это приведет к его окраске в прилагаемый DC.

Кроме того, если вы передадите HDC в качестве wparam сообщения WM_PAINT, многие окна (такие как общие элементы управления) будут рисовать в этом DC, а не на экране.

1 голос
/ 28 октября 2008

Скопируйте исходное растровое изображение в растровое изображение памяти перед закрытием / скрытием окна.

0 голосов
/ 18 апреля 2012

Для окна, которое скрыто за другим окном, вы можете установить его прозрачным (с высокой альфой, чтобы оно не выглядело прозрачным). После этого должно быть возможно захватить все окно с помощью BitBlt.

0 голосов
/ 04 января 2012

Я только что проверил это в Windows 7, должно работать нормально с XP.

Оно выводит окно на передний план, не фокусируя его, перед тем, как захватить его. Это не совершенство, но лучше всего, если вы не сможете заставить PrintWindow () работать.

Это статический метод, поэтому вы можете просто вызвать его так:

Orwellophile.TakeScreenShotOfWindow("window.jpg", Form.Handle);

Нет беспорядка, нет суеты. Это из более крупного класса, так что, надеюсь, ничего не пропало. Оригиналы:

http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/VHD%20Director/UnhandledExceptionManager.cs и http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/CSharp.cc/Win32Messaging.cs хотя они нигде не так хороши, как пример, который я вставил ниже.

using System;
using System.Drawing;
using System.Threading;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class Orwellophile {
    public static void TakeScreenshotOfWindow(String strFilename, IntPtr hTargetWindow)
    {
        Rectangle objRectangle;
        RECT r;
        IntPtr hForegroundWindow = GetForegroundWindow();

        GetWindowRect(hTargetWindow, out r);
        objRectangle = r.ToRectangle();

        if (hTargetWindow != hForegroundWindow)
        {
            ShowWindow(hTargetWindow, SW_SHOWNOACTIVATE);
            SetWindowPos(hTargetWindow.ToInt32(), HWND_TOPMOST, r.X, r.Y, r.Width, r.Height, SWP_NOACTIVATE);
            Thread.Sleep(500);
        }

        TakeScreenshotPrivate(strFilename, objRectangle);
    }

    private static void TakeScreenshotPrivate(string strFilename, Rectangle objRectangle)
    {
        Bitmap objBitmap = new Bitmap(objRectangle.Width, objRectangle.Height);
        Graphics objGraphics = default(Graphics);
        IntPtr hdcDest = default(IntPtr);
        int hdcSrc = 0;

        objGraphics = Graphics.FromImage(objBitmap);


        hdcSrc = GetDC(0);                  // Get a device context to the windows desktop and our destination  bitmaps
        hdcDest = objGraphics.GetHdc();     // Copy what is on the desktop to the bitmap
        BitBlt(hdcDest.ToInt32(), 0, 0, objRectangle.Width, objRectangle.Height, hdcSrc, objRectangle.X, objRectangle.Y, SRCCOPY);
        objGraphics.ReleaseHdc(hdcDest);    // Release DC
        ReleaseDC(0, hdcSrc);

        objBitmap.Save(strFilename);
    }


    [DllImport("gdi32.dll", SetLastError = true)]
    static extern IntPtr CreateCompatibleDC(IntPtr hdc);
    [DllImport("user32.dll")]
    static extern IntPtr GetWindowDC(IntPtr hWnd);
    [DllImport("gdi32.dll")]
    static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
    [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
    static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
    [DllImport("User32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags); // To capture only the client area of window, use PW_CLIENTONLY = 0x1 as nFlags
    [DllImport("gdi32.dll")]
    static extern bool DeleteObject(IntPtr hObject);
    [DllImport("user32.dll")]
    static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    static extern bool SetWindowPos(
         int hWnd,           // window handle
         int hWndInsertAfter,    // placement-order handle
         int X,          // horizontal position
         int Y,          // vertical position
         int cx,         // width
         int cy,         // height
         uint uFlags);       // window positioning flags
    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    static public extern IntPtr GetForegroundWindow();
    private const int SW_SHOWNOACTIVATE = 4;
    private const int HWND_TOPMOST = -1;
    private const uint SWP_NOACTIVATE = 0x0010;
    private const int SRCCOPY = 0xcc0020;
}

Обратите внимание, что вы можете реализовать свой собственный легкий класс / структуру RECT, но я использую это. Я прикрепил его отдельно из-за его размера

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    private int _Left;
    private int _Top;
    private int _Right;
    private int _Bottom;

    public RECT(System.Drawing.Rectangle Rectangle)
        : this(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
    {
    }
    public RECT(int Left, int Top, int Right, int Bottom)
    {
        _Left = Left;
        _Top = Top;
        _Right = Right;
        _Bottom = Bottom;
    }

    public int X
    {
        get { return _Left; }
        set { _Left = value; }
    }
    public int Y
    {
        get { return _Top; }
        set { _Top = value; }
    }
    public int Left
    {
        get { return _Left; }
        set { _Left = value; }
    }
    public int Top
    {
        get { return _Top; }
        set { _Top = value; }
    }
    public int Right
    {
        get { return _Right; }
        set { _Right = value; }
    }
    public int Bottom
    {
        get { return _Bottom; }
        set { _Bottom = value; }
    }
    public int Height
    {
        get { return _Bottom - _Top; }
        set { _Bottom = value - _Top; }
    }
    public int Width
    {
        get { return _Right - _Left; }
        set { _Right = value + _Left; }
    }
    public Point Location
    {
        get { return new Point(Left, Top); }
        set
        {
            _Left = value.X;
            _Top = value.Y;
        }
    }
    public Size Size
    {
        get { return new Size(Width, Height); }
        set
        {
            _Right = value.Height + _Left;
            _Bottom = value.Height + _Top;
        }
    }

    public Rectangle ToRectangle()
    {
        return new Rectangle(this.Left, this.Top, this.Width, this.Height);
    }
    static public Rectangle ToRectangle(RECT Rectangle)
    {
        return Rectangle.ToRectangle();
    }
    static public RECT FromRectangle(Rectangle Rectangle)
    {
        return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom);
    }

    static public implicit operator Rectangle(RECT Rectangle)
    {
        return Rectangle.ToRectangle();
    }
    static public implicit operator RECT(Rectangle Rectangle)
    {
        return new RECT(Rectangle);
    }
    static public bool operator ==(RECT Rectangle1, RECT Rectangle2)
    {
        return Rectangle1.Equals(Rectangle2);
    }
    static public bool operator !=(RECT Rectangle1, RECT Rectangle2)
    {
        return !Rectangle1.Equals(Rectangle2);
    }

    public override string ToString()
    {
        return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}";
    }

    public bool Equals(RECT Rectangle)
    {
        return Rectangle.Left == _Left && Rectangle.Top == _Top && Rectangle.Right == _Right && Rectangle.Bottom == _Bottom;
    }
    public override bool Equals(object Object)
    {
        if (Object is RECT)
        {
            return Equals((RECT)Object);
        }
        else if (Object is Rectangle)
        {
            return Equals(new RECT((Rectangle)Object));
        }

        return false;
    }

    public override int GetHashCode()
    {
        return Left.GetHashCode() ^ Right.GetHashCode() ^ Top.GetHashCode() ^ Bottom.GetHashCode();
    }
}
0 голосов
/ 21 сентября 2011

http://msdn.microsoft.com/en-us/library/dd144909.aspx (getPixel) может помочь ...

0 голосов
/ 29 октября 2008

Подходя к вещам с другой точки зрения, вы уверены, что это действительно то, что вы хотите делать? Например, вы не хотите использовать CreateCompatibleDC и CreateCompatibleBitmap для создания невидимой поверхности для рисования, рисования на ней и затем с использованием BitBlt?

Еще немного информации о том, что вы делаете, может помочь кому-то найти решение или побочное мышление ...

0 голосов
/ 28 октября 2008

Функция PrintWindow не работает в скрытом окне, только в видимых.

0 голосов
/ 28 октября 2008

К сожалению, я думаю, что у вас будут реальные проблемы с надежной работой. Вы не говорите точно, что делаете, но я предполагаю, что, учитывая дескриптор окна, вы захватываете контекст устройства, связанный с окном, вызывая GetWindowDC (), а затем используете полученный контекст устройства.

Это будет нормально работать на XP, когда окно видно. Однако в Vista, если наложение рабочего стола включено, тогда оно даже не будет работать должным образом: вы получите 0 обратно от GetWindowDC (). По сути, захват контекстов оконных устройств не будет работать надежно.

Если окно, из которого вы пытаетесь скопировать, является частью вашего собственного приложения, я бы предложил изменить ваш код для поддержки сообщения WM___PRINT: оно действует как WM_PAINT, но позволяет вам предоставлять контекст устройства для рисования.

Если окно не из вашего приложения, вам в основном не повезло: если окно скрыто, изображения того, что оно показало бы, если бы оно было видимым, нигде не существует.

0 голосов
/ 28 октября 2008

Может быть, вы можете запустить операцию перерисовки в окне с InvalidateRect?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...