我需要将隐藏的窗口(BitBlt)的内容复制到另一个窗口。问题是,一旦我隐藏了源窗口,我得到的设备上下文就不再被绘制了。
答案 0 :(得分:10)
您需要的是自Windows XP以来Win32 API中可用的PrintWindow功能。如果你需要它来使用旧版本的Windows,你可以试试WM_PRINT,虽然我从来没有能够使它工作。
有一篇很好的文章here展示了如何使用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);
}
我应该有一些使用wxPython实现相同功能的Python代码。如果你需要,请给我留言。
答案 1 :(得分:1)
在关闭/隐藏窗口之前,将源位图复制到内存位图。
答案 2 :(得分:1)
您可以尝试向窗口发送WM_PRINT
消息。对于许多窗口(包括所有标准窗口和常用控件),这将导致它绘制到提供的DC中。
此外,如果您将HDC作为WM_PAINT消息的wparam传递,许多窗口(例如常用控件)将绘制到该DC而不是屏幕上。
答案 3 :(得分:0)
也许您可以使用InvalidateRect在窗口上触发重绘操作?
答案 4 :(得分:0)
不幸的是,我认为你会遇到真正的问题让它可靠地运作。你没有确切地说你正在做什么,但我认为,给定窗口句柄,你通过调用GetWindowDC(),然后使用生成的设备上下文抓取与窗口关联的设备上下文。
当窗口可见时,这将在XP上正常工作。但是,在Vista上,如果启用了桌面合成,它甚至无法正常工作:你将从GetWindowDC()获得0。从根本上说,抓取窗口设备上下文不会可靠地工作。
如果您尝试复制的窗口是您自己的应用程序的一部分,我建议修改您的代码以支持WM___PRINT消息:这类似于WM_PAINT,但允许您提供要绘制的设备上下文。
如果窗口不是来自您的应用程序,那么您基本上是运气不好:如果窗口被隐藏,那么在任何地方都不会显示可见的图像。
答案 5 :(得分:0)
PrintWindow功能似乎不适用于隐藏窗口,仅适用于可见窗口。
答案 6 :(得分:0)
从不同的角度来看待事情,你确定这真的是你想要做的吗?例如,您不希望使用CreateCompatibleDC和CreateCompatibleBitmap来创建不可见的绘图表面,绘制它然后使用BitBlt?
有关您所处理的背景的更多信息可能会使某人想出一个解决方案或一些横向思维......
答案 7 :(得分:0)
http://msdn.microsoft.com/en-us/library/dd144909.aspx(getPixel)可能有帮助......
答案 8 :(得分:0)
我刚刚在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();
}
}
答案 9 :(得分:0)
对于隐藏在另一个窗口后面的窗口,您可以将其设置为透明(具有高alpha值,使其看起来不透明)。然后应该可以用BitBlt捕获整个窗口。