从 XP 中的隐藏或剪切窗口复制内容?

Posted

技术标签:

【中文标题】从 XP 中的隐藏或剪切窗口复制内容?【英文标题】:Copying content from a hidden or clipped window in XP? 【发布时间】:2010-09-19 13:05:15 【问题描述】:

我需要将隐藏的窗口 (BitBlt) 的内容复制到另一个窗口。问题是,一旦我隐藏了源窗口,我得到的设备上下文就不再绘制了。

【问题讨论】:

【参考方案1】:

您需要的是 PrintWindow 函数,该函数自 Windows XP 起在 Win32 API 中可用。如果您需要它与旧版本的 Windows 一起工作,您可以尝试WM_PRINT,尽管我一直无法使其工作。

有一篇很好的文章 here 展示了如何使用 PrintWindow,下面是那篇文章中的相关代码 sn-p:

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

我应该有一些使用 wxPython 来实现相同目的的 Python 代码。如果需要,请给我留言。

【讨论】:

我一直想知道 PrintWindow() 是否使用自己的逻辑,或者它是否只是将 WM_PRINT 发布到给定窗口。 MSDN 在这一点上似乎含糊不清。是时候让测试程序检查... 它使用自己的逻辑,而不是 WM_PRINT。 @DavidK:当前版本的文档明确表示发送了WM_PRINTWM_PRINTCLIENT PrintWindow 的优点是,如果窗口被另一个窗口覆盖,甚至在监视器之外,它也会打印窗口。但是您付出的代价是许多非 Microsoft 应用程序不处理 WM_PRINTCLIENT 并且您最终得到一个空白位图。剩下的另一个问题是 PrintWindow 仅将部分复制到屏幕上可见的位图。如果您的窗口有滚动条并且只显示部分内容,则此代码无用。 如何获取隐藏在滚动条上的窗口部分?【参考方案2】:

在关闭/隐藏窗口之前将源位图复制到内存位图。

【讨论】:

【参考方案3】:

您可以尝试向窗口发送WM_PRINT 消息。对于许多窗口(包括所有标准窗口和常用控件),这将导致它绘制到提供的 DC 中。

此外,如果您将 HDC 作为 WM_PAINT 消息的 wparam 传递,许多窗口(例如常用控件)将绘制到该 DC 而不是屏幕上。

【讨论】:

【参考方案4】:

也许您可以使用 InvalidateRect 触发窗口上的重绘操作?

【讨论】:

【参考方案5】:

不幸的是,我认为你会遇到真正的问题来让它可靠地工作。您没有确切说明您在做什么,但我假设,给定窗口句柄,您通过调用 GetWindowDC() 获取与窗口关联的设备上下文,然后使用生成的设备上下文。

当窗口可见时,这在 XP 上可以正常工作。但是,在 Vista 上,如果启用了桌面合成,则它甚至无法正常工作:您将从 GetWindowDC() 返回 0。从根本上说,抓取窗口设备上下文不会可靠地工作。

如果您尝试从中复制的窗口是您自己的应用程序的一部分,我建议修改您的代码以支持 WM___PRINT 消息:这类似于 WM_PAINT,但允许您提供设备上下文以进行绘制。

如果窗口不是来自您的应用程序,那么您基本上就不走运了:如果窗口是隐藏的,那么它在可见时所显示的图像在任何地方都不存在。

【讨论】:

【参考方案6】:

PrintWindow 函数似乎不适用于隐藏窗口,仅适用于可见窗口。

【讨论】:

【参考方案7】:

从不同的角度处理事情,你确定这真的是你想做的事吗?例如,您不想使用 CreateCompatibleDC 和 CreateCompatibleBitmap 创建不可见的绘图表面,在其上绘图然后使用 BitBlt?

更多关于你的背景的信息可能会让某人想出一个解决方案或一些横向思考......

【讨论】:

【参考方案8】:

http://msdn.microsoft.com/en-us/library/dd144909.aspx (getPixel) 可能会有所帮助...

【讨论】:

【参考方案9】:

我刚刚在 Windows 7 中测试过,从 XP 开始应该可以正常工作。

在捕获它之前,它会将窗口带到前台而不给它焦点。这不是完美的,但如果你不能让 PrintWindow() 工作,这是你要做的最好的事情。

这是一个静态方法,所以你可以简单地这样调用它:

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

没有混乱,没有大惊小怪。它来自一个更大的班级,所以希望没有遗漏任何东西。原件是:

http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/VHD%20Director/UnhandledExceptionManager.cs 和http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/CSharp.cc/Win32Messaging.cs 虽然它们没有我在下面粘贴的示例那么简洁。

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;

请注意,您可以实现自己的轻量级 RECT 类/结构,但这是我使用的。由于它的大小,我单独附上了它

[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();
    

【讨论】:

您的代码没有回答问题。你做的很简单!但是,如果窗口大于监视器或被剪辑,您的代码将仅捕获部分!问题是如何捕获位于另一个窗口后面或隐藏或剪切的窗口。 这段代码非常尴尬。您可以使用 Graphics.CopyFromScreen() 对没有任何 DllImport 的 3 行代码执行相同操作【参考方案10】:

对于隐藏在另一个窗口后面的窗口,您可以将其设置为透明(使用高 alpha 使其看起来不透明)。然后应该可以使用 BitBlt 捕获整个窗口。

【讨论】:

以上是关于从 XP 中的隐藏或剪切窗口复制内容?的主要内容,如果未能解决你的问题,请参考以下文章

DEV中dx:ASPxPopupControl 控件的使用(在窗口关闭或隐藏时,清楚文本框中的内容)

js实现复制隐藏域的内容 —— clipboard.js

safekey文件夹锁加密后无法解密,怎么办?

“使用Windows XP风格dpi缩放”禁用W7上任务栏的自动隐藏功能。解决方法?

如何使用 CSS 隐藏图像按钮不打印? [复制]

怎样将vim中的内容复制到windows剪切板?