PrintWindow位图与PrintScreen Key位图不同

Jas*_*son 9 windows winapi printscreen

使用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;
    }
Run Code Online (Sandbox Code Playgroud)

更新: 使用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;
}
Run Code Online (Sandbox Code Playgroud)

我尝试了很多组合CopyPixelOperation,(131,000中约有15,000个),但它仍然不起作用.

使用Windows 8,AMD Radeon HD 6870.


更新2

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

我还没有找到解决方案,但它能够深入了解问题.

DDS*_*DDS 10

PrintWindow不起作用的原因是它取决于应用程序正确处理WM_PRINT消息.许多应用程序都没有WM_PRINT,并且没有正确实现或者测试它.因此,依赖它是一个坏主意,除非你只在已知和测试的应用程序上使用它.

如果你想抓住屏幕上显示的窗口,只需从桌面窗口句柄(GetDesktopWindow())中删除它,然后只显示包含窗口的矩形.

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

假设窗口看起来最好,请注意屏幕上显示的内容.根据下面的内容,这可能不是真的.复制下面的内容可能也是一个坏主意,因为它可能不会被制裁出现在图像中,就像在Excel的屏幕截图下出现在业务演示文稿中的显式背景图像一样.

如果您从桌面上移开,则复制您在屏幕上看到的内容,包括任何重叠的窗口和底层窗口(透明的地方).与PrintWindow一样,它通常被认为是"更清洁",但您可能希望在您选择的背景上合成它,如白色或蓝色.如果你想从屏幕blt,有一些方法可以暂时隐藏覆盖你的目标的窗口,但这是与EnumWindows等一系列的工作.

正确地抓取窗口并不是Windows中的一项简单任务,并且有很多屏幕抓取应用程序因为它们如何处理这个问题而相互竞争.此外,在Windows中,有几种方法可以使窗口区域透明,并且它们也可以一起使用.

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

最后,您可以查看Open Broadcaster软件的来源,该软件使用Direct3D进行屏幕抓取.


Jam*_*ess 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);
Run Code Online (Sandbox Code Playgroud)

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

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

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


ker*_*ero 0

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

BitBlt 与 PrintWindow