2009-05-21 26 views

Trả lời

32

Dưới đây là một số mã để giúp bạn bắt đầu:

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

Nó hoạt động, nhưng cần cải thiện:

  • Bạn có thể muốn sử dụng một cơ chế khác nhau để có được quá trình xử lý (hoặc ít nhất là làm một số mã hóa phòng thủ)
  • Nếu cửa sổ mục tiêu của bạn không ở nền trước, bạn sẽ kết thúc với một ảnh chụp màn hình đúng kích cỡ/vị trí, nhưng sẽ được lấp đầy với bất kỳ thứ gì ở tiền cảnh (bạn có thể muốn kéo cửa sổ đã cho vào thứ e foreground đầu tiên)
  • Bạn có thể muốn làm điều gì đó khác hơn là chỉ lưu bmp vào một thư mục tạm thời
+0

này không mang lại cửa sổ cho quá trình chọn vào foreground trong win7, vì vậy bạn sẽ có được một ảnh chụp màn hình với các hoạt động cửa sổ – greenfeet

+0

@alconja tôi sử dụng mã của bạn để có một bản chụp của notepad. nhưng nó có một bản chụp của studio trực quan đó là cửa sổ hoạt động. chúng ta có thể sử dụng nó để chụp ảnh không có cửa sổ đang hoạt động nào không? –

+0

@FastSnail - Tôi sẽ đề nghị bạn thử câu trả lời khác, nhưng tôi thấy từ ý kiến ​​của bạn rằng điều đó không có tác dụng ... Một tùy chọn khác có thể là tìm phương pháp pinvoke để kéo ứng dụng/cửa sổ mục tiêu vào mặt trước đầu tiên. Ví dụ, [SwitchToThisWindow] (http://www.pinvoke.net/default.aspx/user32.switchtothiswindow), có thể ... – Alconja

1

Bạn có thể xem xét P/Invoking cách win32 để thực hiện việc này, an article to this effect ... sắp xếp.

Về cơ bản, trải qua sự cố khi thiết lập DC thành bitmap và gửi WM_PRINT đến cửa sổ ứng dụng được đề cập. Nó khá khó chịu, tất cả đã nói, nhưng có thể làm việc cho bạn.

Các chức năng bạn có thể cần: SendMessage, GetDC, CreateCompatibleBitmpSelectObject.

Tôi không thể nói tôi đã từng làm điều này trước đây, nhưng đây là cách tôi sẽ tấn công vấn đề. (Chà, tôi có thể làm điều đó trong C tinh khiết nhưng vẫn còn, gần như cách tôi tấn công nó).

8

Dựa trên câu trả lời Alconja, tôi thực hiện một vài cải tiến:

[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

sau khi lướt qua điều này tôi nhận thấy rằng, nếu quá trình đóng trong 'Thread.Sleep (1000); 'bạn sẽ có một vòng lặp vô hạn. –

+0

@NicolasTyler đúng. Để làm rõ, vấn đề là gọi 'GetWindowRect' với HWND không còn hợp lệ sẽ luôn trả về 0, nghĩa là vòng lặp' while' trong câu trả lời này sẽ không bao giờ thoát ra và chỉ ghi CPU mãi mãi, một lỗi khá nghiêm trọng. Nhưng nếu không tôi nghĩ câu trả lời này là một giải pháp thanh lịch. Có thể chỉ giới hạn số lần thử lại cố định và có thể ngủ một chút ở giữa. Hoặc không thử lại trong phương pháp này. –

+0

Câu trả lời này cũng không loại bỏ đối tượng 'Graphics'. –

91

Các win32 PrintWindow api sẽ chụp ảnh bitmap cửa sổ ngay cả khi cửa sổ bị che phủ bởi các cửa sổ khác hoặc nếu màn hình tắt màn hình:

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

Tham chiếu đến RECT ở trên có thể được đặt lại lved với lớp sau:

[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

Giải pháp tuyệt vời. Tôi chỉ muốn chỉ ra rằng đôi khi PixelFormat.Format32bppArgb cung cấp các tạo tác màu trắng. Trong trường hợp đó, chỉ cần cố gắng sử dụng một số định dạng khác thay vào đó, chẳng hạn như PixelFormat.Format24bppRgb – Dave

+0

nếu không được giảm thiểu, nó cũng làm cho cửa sổ bạn nhắm mục tiêu nhấp nháy khi bạn PrintWindow() –

+2

làm cách nào để sử dụng phương pháp này? PrintWindow ('những gì để vượt qua đây') –

Các vấn đề liên quan