windows 在 XP 中从隐藏或剪切的窗口复制内容?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/242570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Copying content from a hidden or clipped window in XP?
提问by
I need to copy the content of a window (BitBlt) which is hidden, to another window. The problem is that once I hide the source window, the device context I got isn't painted anymore.
我需要将隐藏的窗口 (BitBlt) 的内容复制到另一个窗口。问题是一旦我隐藏了源窗口,我得到的设备上下文就不再被绘制了。
回答by elifiner
What you need is the PrintWindowfunction that's available in Win32 API since Windows XP. If you need it to work with older versions of Windows, you can try WM_PRINT, although I've never been able to make it work.
您需要的是从 Windows XP 开始在 Win32 API 中可用的PrintWindow函数。如果你需要它与旧版本的 Windows 一起工作,你可以尝试WM_PRINT,尽管我从来没有能够让它工作。
There's a nice article herethat shows how to use PrintWindow, and here's the relevant code snippet from that article:
有一个很好的文章在这里显示了如何在使用PrintWindow,这里的那篇文章相关的代码片段:
// 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);
}
I should have some Python code that uses wxPython to achieve the same thing. Drop me a note if you want it.
我应该有一些使用 wxPython 来实现相同功能的 Python 代码。如果你想要的话,给我一张便条。
回答by slashmais
Copy the source bitmap to a memory bitmap before closing/hiding the window.
在关闭/隐藏窗口之前将源位图复制到内存位图。
回答by Anthony Williams
You could try sending a WM_PRINT
message to the window. For many windows (including all standard windows and common controls) this will cause it to paint into the supplied DC.
您可以尝试向WM_PRINT
窗口发送消息。对于许多窗口(包括所有标准窗口和通用控件),这将导致它绘制到提供的 DC 中。
Also, if you pass an HDC as the wparam of a WM_PAINT message, many windows (such as the common controls) will paint into that DC rather than onto the screen.
此外,如果您将 HDC 作为 WM_PAINT 消息的 wparam 传递,许多窗口(例如公共控件)将绘制到该 DC 而不是屏幕上。
回答by DavidK
Unfortunately, I think you're going to have real problems getting this to work reliably. You don't say exactly what you're doing, but I assume that, given the window handle, you're grabbing the device context associated with the window by calling GetWindowDC(), and then using the resulting device context.
不幸的是,我认为要让它可靠地工作,你会遇到真正的问题。您没有准确说明您在做什么,但我假设,给定窗口句柄,您通过调用 GetWindowDC() 获取与窗口关联的设备上下文,然后使用生成的设备上下文。
This will work okay on XP when the window is visible. However, on Vista, if desktop compositing is enabled, it won't even work properly then: you'll get 0 back from GetWindowDC(). Fundamentally, grabbing window device contexts isn't going to work reliably.
当窗口可见时,这将在 XP 上正常工作。但是,在 Vista 上,如果启用了桌面合成,那么它甚至无法正常工作:您将从 GetWindowDC() 返回 0。从根本上说,获取窗口设备上下文不会可靠地工作。
If the window you're trying to copy from is part of your own application, I'd suggest modifying your code to support the WM___PRINT message: this acts like WM_PAINT, but allows you to supply a device context to draw into.
如果您尝试从中复制的窗口是您自己的应用程序的一部分,我建议修改您的代码以支持 WM___PRINT 消息:这与 WM_PAINT 类似,但允许您提供要绘制的设备上下文。
If the window isn't from your application, you're basically out of luck: if the window is hidden, the image of what it would show if it were visible doesn't exist anywhere.
如果窗口不是来自您的应用程序,那么您基本上就不走运了:如果窗口是隐藏的,那么它在可见时将显示的图像不存在任何地方。
回答by HS.
Maybe you can trigger a redraw operation on the window with InvalidateRect?
也许您可以使用 InvalidateRect 在窗口上触发重绘操作?
回答by HS.
The PrintWindow function doesn't seem to work on a hidden window, only on visible ones.
PrintWindow 函数似乎不适用于隐藏窗口,只能用于可见窗口。
回答by Jon Bright
Approaching things from a different perspective, are you sure that's really what you want to be doing? You don't, for example, want to be using CreateCompatibleDC and CreateCompatibleBitmap to create your invisible drawing surface, drawing on that and then using BitBlt?
从不同的角度看待事物,你确定这真的是你想要做的吗?例如,您不想使用 CreateCompatibleDC 和 CreateCompatibleBitmap 来创建不可见的绘图表面,在其上绘图然后使用 BitBlt?
Some more information about the background to what you're up to might enable someone to come up with either a solution or some lateral thinking...
更多关于你的工作背景的信息可能会让某人想出一个解决方案或一些横向思维......
回答by rogerdpack
http://msdn.microsoft.com/en-us/library/dd144909.aspx(getPixel) might help...
http://msdn.microsoft.com/en-us/library/dd144909.aspx(getPixel) 可能会有所帮助...
回答by Orwellophile
I just tested this in Windows 7, should work fine from XP up.
我刚刚在 Windows 7 中测试了这个,从 XP 开始应该可以正常工作。
It brings the window to the foreground without giving it focus, before capturing it. It's not perfection, but it's the best you're going to do if you can't get PrintWindow() to work.
在捕获窗口之前,它将窗口带到前景而不给它焦点。这不是完美的,但如果您不能让 PrintWindow() 工作,这是您要做的最好的事情。
It's a static method, so you just can simply call it like so:
这是一个静态方法,所以你可以简单地像这样调用它:
Orwellophile.TakeScreenShotOfWindow("window.jpg", Form.Handle);
No mess, no fuss. It's from a larger class, so hopefully nothing is missing. The originals are:
没有混乱,没有大惊小怪。它来自一个更大的班级,所以希望没有遗漏什么。原文是:
http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/VHD%20Director/UnhandledExceptionManager.csand http://sfinktah.trac.cvsdude.com/vhddirector/browser/main/CSharp.cc/Win32Messaging.csalthough they're nowhere as neat as the example I've pasted below.
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;
}
Note that you can implement your own light-weight RECT class/struct, but this is the one I use. I've attached it separately due to it's size
请注意,您可以实现自己的轻量级 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();
}
}
回答by ckg
For a window that is hidden behind another window you can set it to be transparent (with a high alpha so that it doesn't look transparent). It should then be possible to capture the whole window with BitBlt.
对于隐藏在另一个窗口后面的窗口,您可以将其设置为透明(使用高 alpha 使其看起来不透明)。然后应该可以使用 BitBlt 捕获整个窗口。