截图方法生成黑色图像

时间:2014-06-20 15:41:39

标签: c# windows-7 bitmap browser screenshot

在c#中未能使用control.drawtobitmap之后,我的第二个选择是获取桌面的屏幕截图并裁剪出所需的部分。 一旦我切换用户帐户,我的打嗝就出现了,虽然程序没有崩溃,一旦用户切换,程序只生成纯黑色图像。

我使用此代码作为参考: WebBrowser.DrawToBitmap() or other methods?

我认为这在逻辑上是有道理的,因为这可以帮助Windows节省资源。

在我的情况下,我有哪些选择/解决方案?

修改1 对测试代码进行了修改:

        int c = 0;
        while (true)
        {
            try
            {
                c++;
                Rectangle formBounds = this.Bounds;
                Bitmap bmp = new Bitmap(formBounds.Width, formBounds.Height);
                using (Graphics g = Graphics.FromImage(bmp))
                    g.CopyFromScreen(formBounds.Location, Point.Empty, formBounds.Size);
                bmp.Save("picture" + c.ToString() + ".jpg");
                Thread.Sleep(5000);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

这在用户帐户上完美运行,但是一旦切换用户,它就会返回异常:句柄无效。

有什么想法吗?

编辑2:

DrawToBitmap中的错误不是完全随机的...... 如果我使用你提供的代码:

Bitmap bmp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
this.DrawToBitmap(bmp, this.ClientRectangle);
bmp.Save(".\\picture.jpg");

效果很好,例如:http://oi61.tinypic.com/1z23ynp.jpg

但是,当我右键单击Web浏览器控件时,DrawToBitmap将返回一个空白图像。

示例:http://oi60.tinypic.com/9ay0yc.jpg

所以我可以通过添加

轻松克服这个错误
((Control)webbrowser1).Enabled = false;

这使得在网络浏览器上无法点击任何内容,但不幸的是,停用它会使我的项目无用,因为它的主要功能是模仿网页浏览器控件上的鼠标点击。 虽然如果隐藏窗口,这也可能是个问题。

目前正在查看这篇文章,其中提供了代码以提供窗口句柄。

Simulate click into a hidden window 看来它可能有一些价值......看看吧。

4 个答案:

答案 0 :(得分:4)

您使用DrawToBitmap遇到了什么问题? 它在这里工作正常,(W8.1,VS2013)即使使用Webcontrol也可以在切换用户之后。 (但最后请看条件的编辑!)

Bitmap bmp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
this.DrawToBitmap(bmp, this.ClientRectangle);
// Clipboard.SetImage(bmp); for testing only
bmp.Dispose();

以下是获取窗口截图的代码:

Rectangle formBounds = this.Bounds;
Bitmap bmp = new Bitmap(formBounds.Width, formBounds.Height );
using (Graphics g = Graphics.FromImage(bmp))
       g.CopyFromScreen(formBounds.Location, Point.Empty, formBounds.Size);
//Clipboard.SetImage(bmp); for testing only
bmp.Dispose();

我可以像我想的那样切换用户,程序继续工作。

顺便说一下,你发布的链接真的很旧,很多东西可能都有所改进。

修改

随着更新的问题,事情会更加清晰。

因此,即使用户已更改,您也希望不断获取程序的屏幕截图,对吧? 并且你想显示一个WebControl,对吧?

用户可以有三种类型的桌面:登录/注销屏幕,屏幕保护程序屏幕和一个或多个普通桌面屏幕。但是当用户注销时,他根本没有桌面屏幕。

因此,如果用户没有活动桌面,则屏幕截图方法将不起作用,g.CopyFromScreen也不会导致GDI错误,也不会像在网络上的各种解决方案中那样使用窗口句柄,包括你的链接导致。所有这些最多只会显示一个空白或黑屏。

所以DrawToBitmap方法是唯一有效的方法。

您写道它有随机错误。那不是我所看到的。

当用户任意方式与WebBrowser进行交互时,问题会出现可预测。这包括滚动或点击带或不带导航。在这些互动之后,WebBrowser会将自己绘制为一个空框,直到重新加载其网址 - 不仅刷新 - 而且真正由webBrowser1.Uri = new Uri(uriPath)重新加载。可以这样做,请参阅我的other post

执行WebBrowserDrawToBitmap还有另一个问题:对于包含<input type="text"元素的任何网页,它都会失败(使用所述空框)。我不确定解决此问题的最佳方法是什么,更不用说为什么它首先发生了..截图方法没有特定的问题。

编辑2:

OP挖出代码的代码,通过调用PrintWindow,似乎解决了我们遇到的所有问题:它在注销时有效,即使点击{{1}后也可以使用Refeshing并擦除所有页面,包括那些带有文本输入字段的页面。 Hoorah!

在减少松弛之后,这个版本可以创建WebBrowser或仅Form(或任何其他WebBroser)的副本,包含或不包含边框:

Control

答案 1 :(得分:2)

最后,即使我切换了用户,这段代码似乎也能正常工作。

获取任何未保存的记事本进程的屏幕截图的代码(“无标题 - 记事本”)

    private void Form1_Load(object sender, EventArgs e)
    {
        //loop for debugging
        int c = 0;
        while(true)
        {
            c++;
            System.Drawing.Bitmap image = CaptureWindow(FindWindow(null, "Untitled - Notepad"));
            image.Save(".\\picture"+c.ToString()+".jpg");
            Thread.Sleep(5000);
        }

    }

    [DllImport("user32.dll")]
    public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr hWnd);
    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    public System.Drawing.Bitmap CaptureWindow(IntPtr hWnd)
    {
        System.Drawing.Rectangle rctForm = System.Drawing.Rectangle.Empty;
        using (System.Drawing.Graphics grfx = System.Drawing.Graphics.FromHdc(GetWindowDC(hWnd)))
        {
            rctForm = System.Drawing.Rectangle.Round(grfx.VisibleClipBounds);
        }
        System.Drawing.Bitmap pImage = new System.Drawing.Bitmap(rctForm.Width, rctForm.Height);
        System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(pImage);
        IntPtr hDC = graphics.GetHdc();

        try
        {
            PrintWindow(hWnd, hDC, (uint)0);
        }
        finally
        {
            graphics.ReleaseHdc(hDC);
        }
        return pImage;
    }

请注意,该窗口可能已隐藏,但仍必须在用户帐户中最大化才能获得完整的屏幕截图。

答案 2 :(得分:1)

数字版权管理可能会阻止这种情况,因为Windows会增加对数字媒体的保护。

例如,如果您尝试使用Microsoft的图形渲染在Media Player或Media Center中创建某些内容的屏幕截图 - 是的,Microsoft将“保护您免受任何潜在的诉讼。”

试一试:点击键盘上的“Print Screen”,然后进入Microsoft Paint并尝试将屏幕截图粘贴到其中。有什么吗?

答案 3 :(得分:0)

我遇到了同样的问题,我无法使用CopyFromScreen方法,因为带有WebBrowser的表单可能会被隐藏。 PrintWindow方法也无法很好地生成带有黑色区域的图像,尤其是当我的MDI表单被MDI父表单部分覆盖时。

最后,我在SO上的this topic中使用了OleDraw方法,但将其集成到了从WebBrowser派生的类中。基本上,它只是覆盖对WM_PRINT消息的标准控制响应。这样,不仅可以对WebBrowser进行正常的Control.DrawToBitmap映射,还可以对其中包含WebBrowser的表单进行正常的Control.DrawToBitmap映射。如果该表单是隐藏的(也被其他表单(包括MDI父表单)覆盖),它也可以使用,并且在用户锁定与Win + L的会话(我尚未对其进行测试)时也可以使用。

public class WebBrowserEx : WebBrowser
{
    private const uint DVASPECT_CONTENT = 1;

    [DllImport("ole32.dll", PreserveSig = false)]
    private static extern void OleDraw([MarshalAs(UnmanagedType.IUnknown)] object pUnk,
        uint dwAspect,
        IntPtr hdcDraw,
        [In] ref System.Drawing.Rectangle lprcBounds
    );

    protected override void WndProc(ref Message m)
    {
        const int WM_PRINT = 0x0317;

        switch (m.Msg)
        {
            case WM_PRINT:
                Rectangle browserRect = new Rectangle(0, 0, this.Width, this.Height);

                // Don't know why, but drawing with OleDraw directly on HDC from m.WParam.
                // results in badly scaled (stretched) image of the browser.
                // So, drawing to an intermediate bitmap first.
                using (Bitmap browserBitmap = new Bitmap(browserRect.Width, browserRect.Height))
                {
                    using (var graphics = Graphics.FromImage(browserBitmap))
                    {
                        var hdc = graphics.GetHdc();
                        OleDraw(this.ActiveXInstance, DVASPECT_CONTENT, hdc, ref browserRect);
                        graphics.ReleaseHdc(hdc);
                    }

                    using (var graphics = Graphics.FromHdc(m.WParam))
                    {
                        graphics.DrawImage(browserBitmap, Point.Empty);
                    }
                }
                // ignore default WndProc
                return;

        }

        base.WndProc(ref m);
    }
}