9

使用Print Screen+Alt键组合手动捕获窗口时,我得到以下信息:

在此处输入图像描述

但如果我尝试使用Windows API以编程方式执行此操作,我会得到以下信息:

在此处输入图像描述

为什么会出现差异?如何以编程方式获得第一个?

这是我的代码:

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);

    public Bitmap PrintWindow()
    {
        Bitmap bmp = new Bitmap(windowRect.Width, windowRect.Height, PixelFormat.Format32bppArgb);
        Graphics gfxBmp = Graphics.FromImage(bmp);
        IntPtr hdcBitmap = gfxBmp.GetHdc();

        bool success = PrintWindow(windowHandle, hdcBitmap, 0);
        gfxBmp.ReleaseHdc(hdcBitmap);

        if (!success)
        {
            Console.WriteLine("Error copying image");
            Console.WriteLine(getLastError());
        }

        gfxBmp.Dispose();

        return bmp;
    }

更新: 用 BitBlt 做同样的事情。

下面是来自 CodeProject 的代码,它仍然返回一个黑色蒙版图像:

public Image CaptureWindow(IntPtr handle)
{
    // get te hDC of the target window
    IntPtr hdcSrc = User32.GetWindowDC(handle);
    // get the size
    User32.RECT windowRect = new User32.RECT();
    User32.GetWindowRect(handle,ref windowRect);
    int width = windowRect.right - windowRect.left;
    int height = windowRect.bottom - windowRect.top;
    // create a device context we can copy to
    IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
    // create a bitmap we can copy it to,
    // using GetDeviceCaps to get the width/height
    IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,width,height);
    // select the bitmap object
    IntPtr hOld = GDI32.SelectObject(hdcDest,hBitmap);
    // bitblt over
    GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
    // restore selection
    GDI32.SelectObject(hdcDest,hOld);
    // clean up
    GDI32.DeleteDC(hdcDest);
    User32.ReleaseDC(handle,hdcSrc);
    // get a .NET image object for it
    Image img = Image.FromHbitmap(hBitmap);
    // free up the Bitmap object
    GDI32.DeleteObject(hBitmap);

    img.Save("SampleImage.png");
    return img;
}

我尝试了许多CopyPixelOperation, 的组合(大约 131,000 个中的 15,000 个),但它仍然不起作用。

使用 Windows 8、AMD Radeon HD 6870。


更新 2

看起来窗户是透明的,让窗户的蓝色渗出。当我将窗口颜色更改为黑色(使用 Windows 个性化对话框)时,我得到的内容与第二个窗口大致相似。但边界仍然缺失。

我还没有找到解决方案,但它是对问题的洞察力。

4

4 回答 4

10

PrintWindow 不起作用的原因是它取决于应用程序对WM_PRINT消息的正确处理。许多应用程序在使用 WM_PRINT 时都很不稳定,并且没有正确实现它或从未对其进行测试。因此,除非您仅在已知和经过测试的应用程序上使用它,否则依赖它是一个坏主意。

如果您想抓取屏幕上显示的窗口,只需从桌面窗口句柄 ( GetDesktopWindow() ) 中删除它,并且只删除包含该窗口的矩形。

透明度是窗口抓取的一个问题。捕捉现代 Windows 操作系统的窗口是不可能的,因为没有图像文件类型支持像模糊底层图像这样的幻想。但是,可以将简单的透明度捕获到 PNG 文件中。

小心假设窗口在屏幕上看起来最好。根据下面的内容,这可能不是真的。复制下面的任何内容也可能是个坏主意,因为它可能不被批准出现在图像中,例如在 Excel 屏幕截图下的业务演示文稿中出现的明确背景图像:)。

如果从桌面 blt,则复制您在屏幕上看到的内容,包括任何覆盖窗口和底层窗口(透明的)。与 PrintWindow 一样,仅抓取窗口通常被视为“更干净”,但您可能希望将其合成在您选择的背景上,如白色或蓝色。如果你想从屏幕上 blt,有一些方法可以暂时隐藏覆盖你的目标的窗口,但它是 EnumWindows 等的一堆工作。

在 Windows 中正确抓取窗口并不是一件容易的事,并且有许多屏幕抓取应用程序相互竞争,因为它们处理这个问题的能力。此外,在 Windows 中,有几种方法可以使窗口区域透明,它们也可以一起使用。

此外,在 Vista+ 中,还有DWM 缩略图 API,它允许您获取在窗口上绘制的应用程序窗口的副本。有关演示和源代码,请参阅ShareX(以前称为 zScreen)。

最后,您可以查看Open Broadcaster Software的源代码,它使用 Direct3D 进行屏幕抓取。

于 2013-05-12T00:44:15.637 回答
5

图像看起来不同的原因Alt+PrntScrn是因为它实际上并没有拍摄所选窗口的快照 - 它正在拍摄桌面窗口之类的快照,但剪掉了适当的部分。

为了证明这一点,请使用任务管理器之类的始终位于顶部的窗口并将其定位,使其覆盖您正在创建快照的窗口。您会看到它实际上包括快照中的两个窗口。而调用PrintWindow只会单独返回指定的窗口。

所以,如果你想Alt+PrntScrn准确地模拟,你需要从桌面上 BitBlt。像这样的东西:

IntPtr hDesktop = User32.GetDesktopWindow();
IntPtr hdcSrc = User32.GetWindowDC(hDesktop);
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, windowRect.left, windowRect.top, CopyPixelOperation.SourceCopy);

根据您的示例代码,我有点猜测这里的语法。原始 Windows API 调用如下所示:

HWND hDesktop = GetDesktopWindow();
HDC hdcSrc = GetDC(hDesktop);
BitBlt(hdcDest, 0, 0, width, height, hdcSrc, windowRect.left, windowRect.top, SRCCOPY);

至少这对我有用。如果您在从这里弄清楚其余部分有任何问题,请在评论中告诉我。

于 2013-05-11T22:02:03.417 回答
0

“为什么会出现差异?我如何以编程方式获得第一个?”

BitBlt 与 PrintWindow

于 2013-05-05T01:07:41.480 回答
0

不确定是不是这样,但BitBlt接受一个rop (光栅操作代码)参数,该参数指示数据如何组合到目标

这默认为BLACKNESS,在复制屏幕截图数据之前用全黑像素预填充目标内存

您可能想要将此参数设置为CAPTUREBLT,这似乎包括屏幕上的所有内容,而不仅仅是您要捕获的特定窗口

https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt

ofcBitBlt在桌面上运行的窗口句柄

于 2021-02-23T00:20:43.027 回答