2009-05-21 13 views

risposta

32

Ecco po 'di codice per iniziare:

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

Funziona, ma si dovrebbe migliorare:

  • Si consiglia di utilizzare un meccanismo diverso per ottenere l'handle di processo (o almeno fare qualche codifica difensiva)
  • Se la finestra di destinazione non è in primo piano, finirai con uno screenshot della giusta dimensione/posizione, ma sarà riempito con qualsiasi cosa sia in primo piano (probabilmente vuoi tirare la finestra data in th e in primo piano primo)
  • Probabilmente si desidera fare qualcosa di diverso da solo salvare il BMP in una directory temporanea
+0

questo non portare la finestra per il processo selezionato in primo piano in win7, in modo da otterrete una schermata con la finestra attiva – greenfeet

+0

@alconja ho usato il codice per scattare un'istantanea del blocco note. ma ha scattato un'istantanea dello studio visivo che era la finestra attiva. Possiamo usarlo per scattare una foto di nessuna finestra attiva? –

+0

@FastSnail - Stavo per suggerire di provare l'altra risposta, ma vedo dai tuoi commenti che non funziona neanche ... Un'altra opzione potrebbe essere quella di provare a trovare un metodo pinvoke che estrae l'app/finestra di destinazione il primo piano in primo luogo. Ad esempio, [SwitchToThisWindow] (http://www.pinvoke.net/default.aspx/user32.switchtothiswindow), forse ... – Alconja

1

Si potrebbe guardare in P/Invoking il modo win32 di fare questo, an article to this effect ... sorta di.

Fondamentalmente, passare attraverso il problema di impostare un DC in un bitmap e inviare WM_PRINT alla finestra dell'applicazione in questione. È piuttosto cattivo, tutto sommato, ma potrebbe funzionare per te.

Funzioni che potrebbero essere necessarie: SendMessage, GetDC, CreateCompatibleBitmp e SelectObject.

Non posso dire di averlo mai fatto prima, ma è così che attaccherei il problema. (Beh, probabilmente lo farei in puro C ma ancora, all'incirca nel modo in cui lo attaccherei).

8

in base alla risposta del Alconja, ho fatto alcuni miglioramenti:

[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

dopo averlo sfogliato ho notato che, se il processo si chiude durante 'Thread.Sleep (1000); 'avrai un ciclo infinito. –

+0

@NicolasTyler ha ragione. Per chiarire, il problema è che chiamare 'GetWindowRect' con un HWND che non è più valido restituirà sempre zero, il che significa che il ciclo' while' in questa risposta non uscirà mai e masterizzerà la CPU per sempre, che è un bug piuttosto serio. Ma per il resto penso che questa risposta sia una soluzione elegante. Forse limitati a un numero fisso di tentativi, e forse dormi un po 'in mezzo. O non riprovare in questo metodo. –

+0

Anche questa risposta non dispone dell'oggetto 'Graphics'. –

91

Win32 PrintWindow api catturerà una bitmap finestra anche se la finestra è coperto da altre finestre o se non é fe:

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

il riferimento alla rect sopra può essere reso lved con la seguente classe:

[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

Ottima soluzione. Voglio solo far notare che a volte PixelFormat.Format32bppArgb dà artefatti bianchi. In tal caso, prova a utilizzare un altro formato, ad esempio PixelFormat.Format24bppRgb – Dave

+0

, non se è ridotto a icona, inoltre rende la finestra di puntamento del flicker quando PrintWindow() –

+2

come faccio a utilizzare questo metodo? PrintWindow ('cosa passare qui ') –

Problemi correlati