2008-10-28 8 views

Respuesta

0

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

1

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

1

Puede 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) se pintarán en ese DC en lugar de en la pantalla.

0

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á correctamente 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 es 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.

10

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.

Hay un buen artículo here que muestra cómo utilizar 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); 
} 

que debería haber algo de código Python que utiliza wxPython para lograr la misma cosa. Déjame una nota si la quieres.

+0

Siempre me he preguntado si PrintWindow() usa su propia lógica, o si solo publica una WM_PRINT en la ventana dada. MSDN parece ser vago en ese punto. Tiempo para que un programa de prueba compruebe que ... – DavidK

+0

Utiliza su propia lógica, no WM_PRINT. –

+1

@DavidK: la versión actual de la documentación dice claramente que se envía 'WM_PRINT' o' WM_PRINTCLIENT'. –

0

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

+0

pero podría funcionar para fuera de pantalla ... – rogerdpack

0

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

Algunos más información sobre los antecedentes de lo que estás haciendo podría permitir a alguien para llegar a una solución o algún pensamiento lateral ...

0

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

Trae 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 llamar así:

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

Sin suciedad, 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 en ninguna parte tan aseados 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, 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; 
} 

Tenga en cuenta que puede implementar su propia clase/estructura de RECT liviana, pero esta es la que yo uso. Me 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(); 
    } 
} 
+0

Su código no responde la pregunta. ¡Lo que haces es fácil! ¡Pero si la ventana es más grande que el monitor o recortada, su código lo capturará solo parcialmente! La pregunta era cómo capturar una ventana que está detrás de otra ventana u oculta o recortada. – Elmue

+0

Este código es extremadamente incómodo. Puede hacer lo mismo con 3 líneas de código sin ningún DllImport usando Graphics.CopyFromScreen() – Elmue

0

Para una ventana que se esconde detrás de otra ventana se puede establecer que sea transparente (con un alfa de alta de manera que no se ve transparente). Entonces debería ser posible capturar toda la ventana con BitBlt.

Cuestiones relacionadas