2009-05-21 20 views

Odpowiedz

32

Oto niektóre kodu do należy zacząć:

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

To działa, ale wymaga poprawy:

  • Można użyć innego mechanizmu, aby uzyskać dojście procesu (lub przynajmniej zrobić niektóre kodowanie defensywne)
  • Jeśli twoje docelowe okno nie jest na pierwszym planie, otrzymasz zrzut ekranu o odpowiednim rozmiarze/pozycji, ale wypełni go wszystko, co jest na pierwszym planie (prawdopodobnie chcesz ciągnąć podane okno na th e pierwszym planie)
  • Prawdopodobnie chcesz zrobić coś innego niż tylko zapisać BMP do katalogu temp
+0

to nie przyniesie okno dla wybranego procesu na pierwszy plan w win7, więc dostaniesz zrzut ekranu z aktywnym oknie – greenfeet

+0

@alconja użyłem kodu, aby zrobić zdjęcie z notatnika., ale zrobiła migawkę wizualnego studia, które było aktywnym oknem. Czy możemy użyć tego do zrobienia zdjęcia żadnego aktywnego okna? –

+0

@FastSnail - Zamierzałem zasugerować, aby wypróbować inną odpowiedź, ale widzę z komentarzy, że to też nie działa ... Inną opcją może być próba znalezienia metody pinvoke, która ściąga docelową aplikację/okno do pierwszy plan na pierwszym planie. Na przykład [SwitchToThisWindow] (http://www.pinvoke.net/default.aspx/user32.switchtothiswindow), może ... – Alconja

1

Możesz zajrzeć do P/Invoking sposobem na zrobienie tego win32, an article to this effect ... sort.

Zasadniczo, przechodź przez problem z ustawieniem DC na bitmapę i wyślij WM_PRINT do okna aplikacji, o którym mowa. To dość paskudne, wszystko powiedziane, ale może pracować dla ciebie. Dostępne są następujące funkcje: SendMessage, GetDC, CreateCompatibleBitmp i SelectObject.

Nie mogę powiedzieć, że kiedykolwiek to zrobiłem, ale w ten sposób zaatakowałbym problem. (Cóż, prawdopodobnie zrobiłbym to w czystym C, ale wciąż, w przybliżeniu sposób, w jaki go zaatakowałem).

8

podstawie odpowiedzi Alconja jest, zrobiłem kilka ulepszeń:

[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

po przejrzeniu tego zauważyłem, że jeśli proces zakończy się podczas 'Thread.Sleep (1000); 'będziesz miał nieskończoną pętlę. –

+0

@NicolasTyler ma rację. Aby wyjaśnić, problem polega na tym, że wywoływanie 'GetWindowRect' z nieistniejącym już HWND zawsze zwróci zero, co oznacza, że ​​pętla' while' w tej odpowiedzi nigdy nie wyjdzie i po prostu wypali procesor na zawsze, co jest dość poważnym błędem. Ale poza tym myślę, że ta odpowiedź jest eleganckim rozwiązaniem. Może ograniczać się do ustalonej liczby prób, a może trochę spać. Lub nie spróbuj ponownie w tej metodzie. –

+0

Ta odpowiedź nie powoduje również usunięcia obiektu 'Graphics'. –

91

PrintWindow win32 API przechwytywania okien bitmapy nawet jeśli okno jest przykryte przez inne okna lub, jeśli jest wyłączony ekran:

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

odniesienie do rect powyżej mogą być Reso lved z następujących klas:

[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

Świetne rozwiązanie. Chcę tylko zwrócić uwagę, że czasami PixelFormat.Format32bppArgb daje białe artefakty. W takim przypadku po prostu spróbuj użyć innego formatu, takiego jak PixelFormat.Format24bppRgb – Dave

+0

nie, jeśli jest on zminimalizowany, ale powoduje, że okno, na które celujesz, migocze, gdy używasz PrintWindow() –

+2

jak korzystać z tej metody? PrintWindow ("co tu przekazać") –