2008-10-28 23 views

Trả lời

0

Có thể bạn có thể kích hoạt thao tác vẽ lại trên cửa sổ bằng InvalidateRect?

1

Sao chép bitmap nguồn vào bitmap bộ nhớ trước khi đóng/ẩn cửa sổ.

1

Bạn có thể thử gửi thông báo WM_PRINT tới cửa sổ. Đối với nhiều cửa sổ (bao gồm tất cả các cửa sổ tiêu chuẩn và các điều khiển chung), điều này sẽ làm cho nó sơn vào DC được cung cấp.

Ngoài ra, nếu bạn vượt qua HDC làm wparam của một tin nhắn WM_PAINT, nhiều cửa sổ (chẳng hạn như các điều khiển chung) sẽ vẽ vào DC đó chứ không phải trên màn hình.

0

Thật không may, tôi nghĩ rằng bạn sẽ gặp phải các vấn đề thực tế khi việc này hoạt động đáng tin cậy. Bạn không nói chính xác những gì bạn đang làm, nhưng tôi giả định rằng, với cửa sổ xử lý, bạn đang lấy bối cảnh thiết bị liên kết với cửa sổ bằng cách gọi GetWindowDC(), và sau đó sử dụng bối cảnh thiết bị kết quả.

Điều này sẽ hoạt động tốt trên XP khi cửa sổ hiển thị. Tuy nhiên, trên Vista, nếu tính năng tổng hợp máy tính để bàn được bật, thậm chí nó sẽ không hoạt động chính xác: bạn sẽ nhận được 0 từ GetWindowDC(). Về cơ bản, việc lấy bối cảnh thiết bị cửa sổ sẽ không hoạt động đáng tin cậy.

Nếu cửa sổ bạn đang cố gắng sao chép là một phần của ứng dụng của riêng bạn, tôi khuyên bạn nên sửa đổi mã của mình để hỗ trợ tin nhắn WM___PRINT: hoạt động như WM_PAINT, nhưng cho phép bạn cung cấp ngữ cảnh thiết bị .

Nếu cửa sổ không nằm trong ứng dụng của bạn, về cơ bản bạn sẽ không may: nếu cửa sổ bị ẩn, hình ảnh của cửa sổ sẽ hiển thị nếu nó hiển thị không tồn tại ở bất kỳ đâu.

10

Những gì bạn cần là chức năng PrintWindow có sẵn trong Win32 API từ Windows XP. Nếu bạn cần nó để làm việc với các phiên bản cũ của Windows, bạn có thể thử WM_PRINT, mặc dù tôi chưa bao giờ có thể làm cho nó hoạt động.

Có một bài viết tốt đẹp here cho thấy làm thế nào để sử dụng PrintWindow, và đây là đoạn mã có liên quan từ bài viết rằng:

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

tôi nên có một số mã Python sử dụng wxPython để đạt được điều tương tự. Thả cho tôi một lưu ý nếu bạn muốn.

+0

Tôi đã luôn luôn tự hỏi nếu PrintWindow() sử dụng logic riêng của nó, hoặc nếu nó chỉ gửi một WM_PRINT đến cửa sổ nhất định. MSDN có vẻ mơ hồ vào thời điểm đó. Thời gian cho một chương trình thử nghiệm để kiểm tra rằng ... – DavidK

+0

Nó sử dụng logic riêng của nó, không phải WM_PRINT. –

+1

@DavidK: Phiên bản hiện tại của tài liệu rõ ràng nói rằng 'WM_PRINT' hoặc' WM_PRINTCLIENT' được gửi. –

0

Chức năng PrintWindow dường như không hoạt động trên cửa sổ ẩn, chỉ trên các cửa sổ hiển thị.

+0

nhưng nó có thể hoạt động cho những màn hình ngoài ... – rogerdpack

0

Tiếp cận mọi thứ từ một góc nhìn khác, bạn có chắc đó thực sự là điều bạn muốn làm không? Chẳng hạn, bạn không muốn sử dụng CreateCompatibleDC và CreateCompatibleBitmap để tạo bề mặt vẽ vô hình của bạn, vẽ lên đó và sau đó sử dụng BitBlt?

Một số thông tin thêm về nền để những gì bạn đang lên đến có thể cho phép một người nào đó để đến với một trong hai giải pháp hoặc một số suy nghĩ bên ...

0

Tôi chỉ thử nghiệm này trong Windows 7, nên làm việc tốt từ XP lên.

Nó mang cửa sổ về phía trước mà không đưa tiêu điểm vào trước khi chụp. Nó không hoàn hảo, nhưng đó là điều tốt nhất bạn sẽ làm nếu bạn không thể lấy PrintWindow() để làm việc.

Đó là một phương pháp tĩnh, vì vậy bạn chỉ có thể chỉ đơn giản gọi nó là như vậy:

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

Không lộn xộn, không ồn ào. Đó là từ một lớp học lớn hơn, vì vậy hy vọng không có gì là mất tích. Bản gốc là:

http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/VHD%20Director/UnhandledExceptionManager.cshttp://sfinktah.trac.cvsdude.com/vhddirector/browser/main/CSharp.cc/Win32Messaging.cs mặc dù chúng không có gì gọn gàng như ví dụ tôi đã dán bên dưới.

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

Lưu ý rằng bạn có thể triển khai lớp/cấu trúc RECT trọng lượng nhẹ của riêng mình, nhưng đây là loại tôi sử dụng. Tôi đã đính kèm riêng nó do kích thước của nó là

[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

Mã của bạn không trả lời được câu hỏi. Những gì bạn làm là dễ dàng! Nhưng nếu cửa sổ lớn hơn màn hình hoặc cắt bớt mã của bạn sẽ chỉ chụp được một phần! Câu hỏi đặt ra là cách chụp một cửa sổ phía sau cửa sổ khác hoặc bị ẩn hoặc bị cắt bớt. – Elmue

+0

Mã này cực kỳ khó xử. Bạn có thể làm tương tự với 3 dòng mã mà không có bất kỳ DllImport nào bằng cách sử dụng Graphics.CopyFromScreen() – Elmue

0

Đối với cửa sổ ẩn sau một cửa sổ khác, bạn có thể đặt nó trong suốt (với alpha cao để không trong suốt). Sau đó, bạn có thể chụp toàn bộ cửa sổ bằng BitBlt.

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