¿Copiando contenido de una ventana oculta o recortada en XP?

Necesito copiar el contenido de una ventana (BitBlt) que está oculta, a otra ventana. El problema es que una vez que oculto la ventana fuente, el contexto del dispositivo que obtuve ya no está pintado.

Lo que necesita es la función PrintWindow que está disponible en Win32 API desde Windows XP. Si necesita que funcione con versiones anteriores de Windows, puede probar WM_PRINT , aunque nunca he podido hacerlo funcionar.

Aquí hay un buen artículo que muestra cómo usar PrintWindow, y aquí está el fragmento de código relevante de ese artículo:

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

Debería tener algún código de Python que use wxPython para lograr lo mismo. Déjame una nota si la quieres.

Copie el bitmap de origen en un bitmap de memoria antes de cerrar / ocultar la ventana.

Podría intentar enviar un mensaje WM_PRINT a la ventana. Para muchas ventanas (incluidas todas las ventanas estándar y controles comunes) esto hará que se filtre en el DC suministrado.

Además, si pasa un HDC como el wparam de un mensaje WM_PAINT, muchas ventanas (como los controles comunes) aparecerán en ese DC en lugar de en la pantalla.

¿Tal vez puede desencadenar una operación de redibujado en la ventana con InvalidateRect?

Desafortunadamente, creo que vas a tener problemas reales para que esto funcione de manera confiable. No dice exactamente lo que está haciendo, pero supongo que, dado el identificador de ventana, está tomando el contexto del dispositivo asociado a la ventana llamando a GetWindowDC () y luego utilizando el contexto del dispositivo resultante.

Esto funcionará bien en XP cuando la ventana esté visible. Sin embargo, en Vista, si la composición de escritorio está habilitada, ni siquiera funcionará correctamente, entonces: obtendrá 0 de GetWindowDC (). Fundamentalmente, los contextos de los dispositivos de ventana de agarre no van a funcionar de manera confiable.

Si la ventana desde la que intentas copiar forma parte de tu propia aplicación, te sugiero que modifiques tu código para que admita el mensaje WM___PRINT: esto actúa como WM_PAINT, pero te permite proporcionar un contexto de dispositivo para dibujar.

Si la ventana no es de su aplicación, básicamente no tiene suerte: si la ventana está oculta, la imagen de lo que mostraría si fuera visible no existe en ninguna parte.

La función PrintWindow no parece funcionar en una ventana oculta, solo en las visibles.

Al abordar las cosas desde una perspectiva diferente, ¿estás seguro de que eso es realmente lo que quieres hacer? Por ejemplo, ¿no quiere usar CreateCompatibleDC y CreateCompatibleBitmap para crear su superficie de dibujo invisible, dibujando sobre eso y luego usando BitBlt?

Alguna más información sobre los antecedentes de lo que estás haciendo podría permitirle a alguien encontrar una solución o algo de pensamiento lateral …

Acabo de probar esto en Windows 7, debería funcionar bien desde XP.

Lleva la ventana al primer plano sin enfocarla, antes de capturarla. No es la perfección, pero es lo mejor que vas a hacer si no puedes hacer que funcione PrintWindow ().

Es un método estático, por lo que simplemente puede llamarlo así:

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

Sin desorden, sin problemas. Es de una clase más grande, así que con suerte no falta nada. Los originales son:

http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/VHD%20Director/UnhandledExceptionManager.cs y http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/CSharp.cc/Win32Messaging. cs aunque no están nada bien como el ejemplo que he pegado a continuación.

 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, rX, rY, 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; } 

Tenga en cuenta que puede implementar su propia clase / estructura RECT liviana, pero esta es la que yo uso. Lo he adjuntado por separado debido a su tamaño

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

Para una ventana que está oculta detrás de otra ventana, puede configurarla para que sea transparente (con un alfa alto para que no se vea transparente). Entonces debería ser posible capturar toda la ventana con BitBlt.