2009-05-21 15 views

Respuesta

32

Aquí hay algo de código para empezar:

public void CaptureApplication(string procName) 
{ 
    var proc = Process.GetProcessesByName(procName)[0]; 
    var rect = new User32.Rect(); 
    User32.GetWindowRect(proc.MainWindowHandle, ref rect); 

    int width = rect.right - rect.left; 
    int height = rect.bottom - rect.top; 

    var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); 
    Graphics graphics = Graphics.FromImage(bmp); 
    graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy); 

    bmp.Save("c:\\tmp\\test.png", ImageFormat.Png); 
} 

private class User32 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    public struct Rect 
    { 
     public int left; 
     public int top; 
     public int right; 
     public int bottom; 
    } 

    [DllImport("user32.dll")] 
    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect); 
} 

Funciona, pero necesita mejoras:

  • Es posible que desee utilizar un mecanismo diferente para obtener el identificador de proceso (o al menos hacer alguna codificación defensiva)
  • Si su ventana de destino no está en primer plano, terminará con una captura de pantalla con el tamaño/posición correctos, pero solo se rellenará con lo que esté en primer plano (es probable que desee extraer la ventana dada en th primer correo primer plano)
  • Es posible que desee hacer algo más que simplemente guardar el bmp a un directorio temporal
+0

esto no trae la ventana para el proceso seleccionado al primer plano en win7, por lo que obtendrá una captura de pantalla con la ventana activa – greenfeet

+0

@alconja i utiliza el código para tomar una instantánea del bloc de notas. Pero tomó una instantánea de Visual Studio que era la ventana activa. ¿Podemos usar esto para tomar una foto de una ventana no activa? –

+0

@ FastSnail - Le sugerí que pruebe la otra respuesta, pero veo por sus comentarios que eso tampoco funciona ... Otra opción podría ser intentar encontrar un método de pinvoke que arrastre la aplicación/ventana de destino a el primer plano primero. Por ejemplo, [SwitchToThisWindow] (http://www.pinvoke.net/default.aspx/user32.switchtothiswindow), tal vez ... – Alconja

1

Podrías mirar en P/Invoking la manera win32 de hacer esto, an article to this effect ... más o menos.

Básicamente, tome la molestia de configurar una CC en un mapa de bits y envíe WM_PRINT a la ventana de la aplicación en cuestión. Es bastante desagradable, todo dicho, pero puede funcionar para usted.

Funciones que puede necesitar: SendMessage, GetDC, CreateCompatibleBitmp y SelectObject.

No puedo decir que haya hecho esto antes, pero así es como atacaría el problema. (Bueno, probablemente lo haría en C pura, pero aún así, más o menos como lo atacaría).

8

Sobre la base de la respuesta de Alconja, he hecho algunas mejoras:

[StructLayout(LayoutKind.Sequential)] 
public struct Rect 
{ 
    public int left; 
    public int top; 
    public int right; 
    public int bottom; 
} 

[DllImport("user32.dll")] 
private static extern int SetForegroundWindow(IntPtr hWnd); 

private const int SW_RESTORE = 9; 

[DllImport("user32.dll")] 
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow); 

[DllImport("user32.dll")] 
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect); 

public Bitmap CaptureApplication(string procName) 
{ 
    Process proc; 

    // Cater for cases when the process can't be located. 
    try 
    { 
     proc = Process.GetProcessesByName(procName)[0]; 
    } 
    catch (IndexOutOfRangeException e) 
    { 
     return null; 
    } 

    // You need to focus on the application 
    SetForegroundWindow(proc.MainWindowHandle); 
    ShowWindow(proc.MainWindowHandle, SW_RESTORE); 

    // You need some amount of delay, but 1 second may be overkill 
    Thread.Sleep(1000); 

    Rect rect = new Rect(); 
    IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect); 

    // sometimes it gives error. 
    while (error == (IntPtr)0) 
    { 
     error = GetWindowRect(proc.MainWindowHandle, ref rect); 
    } 

    int width = rect.right - rect.left; 
    int height = rect.bottom - rect.top; 

    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); 
    Graphics.FromImage(bmp).CopyFromScreen(rect.left, 
              rect.top, 
              0, 
              0, 
              new Size(width, height), 
              CopyPixelOperation.SourceCopy); 

    return bmp; 
} 
+2

después de hojear esto noté que, si el proceso se cierra durante el 'Thread.Sleep (1000); 'tendrás un ciclo infinito. –

+0

@NicolasTyler tiene razón. Para aclarar, el problema es que llamar 'GetWindowRect' con un HWND que ya no es válido siempre devolverá cero, lo que significa que el ciclo' while' en esta respuesta nunca saldrá y solo quemará la CPU para siempre, lo cual es un error bastante serio. Pero de lo contrario, creo que esta respuesta es una solución elegante. Tal vez limitar a un número fijo de reintentos, y tal vez dormir un poco en el medio. O no intente de nuevo en este método. –

+0

Esta respuesta tampoco descarta el objeto 'Gráficos'. –

91

El Win32 PrintWindow api capturará un mapa de bits ventana incluso si la ventana está cubierta por otras ventanas o si es fuera de la pantalla:

[DllImport("user32.dll")] 
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); 
[DllImport("user32.dll")] 
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags); 

public static Bitmap PrintWindow(IntPtr hwnd)  
{  
    RECT rc;   
    GetWindowRect(hwnd, out rc); 

    Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);   
    Graphics gfxBmp = Graphics.FromImage(bmp);   
    IntPtr hdcBitmap = gfxBmp.GetHdc();   

    PrintWindow(hwnd, hdcBitmap, 0); 

    gfxBmp.ReleaseHdc(hdcBitmap);    
    gfxBmp.Dispose(); 

    return bmp; 
} 

la referencia a rect anterior se puede reso lved con la clase siguiente:

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

    public RECT(RECT 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.Width + _Left; 
      _Bottom = value.Height + _Top; 
     } 
    } 

    public static implicit operator Rectangle(RECT Rectangle) 
    { 
     return new Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height); 
    } 
    public static implicit operator RECT(Rectangle Rectangle) 
    { 
     return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom); 
    } 
    public static bool operator ==(RECT Rectangle1, RECT Rectangle2) 
    { 
     return Rectangle1.Equals(Rectangle2); 
    } 
    public static bool operator !=(RECT Rectangle1, RECT Rectangle2) 
    { 
     return !Rectangle1.Equals(Rectangle2); 
    } 

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

    public override int GetHashCode() 
    { 
     return ToString().GetHashCode(); 
    } 

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

Gran solución. Solo quiero señalar que a veces PixelFormat.Format32bppArgb proporciona artefactos blancos. En ese caso, simplemente intente usar algún otro formato, como PixelFormat.Format24bppRgb – Dave

+0

, aunque se minimice, también hace que la ventana a la que se dirige parpadee cuando PrintWindow() –

+2

¿cómo uso este método? PrintWindow ('qué pasar aquí') –

Cuestiones relacionadas