WebBrowser 疯狂

3

编辑:原问题过长,充满了猜测。我已将其缩减为剩余的谜团。

这个问题起源于一个名为Screenshot method generates black images的帖子。原作者想要每隔n秒连续截取程序的屏幕截图,其中包括一个WebBrowser,即使用户已注销也能进行截图。

当用户注销时,他没有屏幕了。因此,任何尝试读取屏幕的操作都会失败。如果使用窗口句柄,结果就是一个黑盒子;如果使用CopyFromScreen,则会出现GDI错误异常。

但程序窗口仍然存在,即使用户已注销,使用DrawToBitmap也能正常工作。

以下是条件和剩余问题:

  • 用户不能以任何方式触摸/点击WebBrowser。如果用户通过滚动、点击或导航,后续的DrawToBitmap调用会导致空白框。

  • 只要WebBrowser保持不变,下一次DrawToBitmap调用之前刷新即可。

  • 在触摸后,需要通过执行webBrowser1.Url = new Uri(URLpath);重新加载URL。

  • 当导航到新的URL时,必须将其存储。我在Navigated事件中这样做。

  • 无论如何,如果网页包含一个<input type="text" ..> fieldDrawToBitmap都会失败(显示空白框)。

  • 通过在DocumentText中使用Replace("<input", "<in_put");来修复此问题,但不使用其他技巧的话,这将失去CSS样式表。

为了测试,请将两个按钮、一个标签、一个计时器、一个组合框和一个WebBrowser放在一个窗体上,并复制代码;将文件路径更改为适合您设置的文件夹,并观察..:

public Form1()
{
    InitializeComponent();
    this.button1.Click += new System.EventHandler(this.button1_Click);
    this.button2.Click += new System.EventHandler(this.button2_Click);
    this.button1.Text = "Start";
    this.button2.Text = "Stop";
    this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
    this.comboBox1.Items.AddRange(new object[] {
        "https://stackoverflow.com/questions",
        "http://webcam.zirndorf.de/marktplatz/gross.jpg"});
    scapeRect = this.ClientRectangle;
    webBrowser1.Url = new Uri("https://stackoverflow.com/questions");
    this.comboBox1.SelectedIndexChanged += 
                   new System.EventHandler(this.comboBox1_SelectedIndexChanged);

}

Rectangle scapeRect = Rectangle.Empty;
int imgIndex = 0;
int urlIndex = 0;

private void button1_Click(object sender, EventArgs e)
{
    timer1.Interval = 10 * 1000;  // every 10 seconds
    timer1.Start();
}

private void button2_Click(object sender, EventArgs e)
{
    timer1.Stop();
}


private void timer1_Tick(object sender, EventArgs e)
{
    imgIndex ++;
    label1.Text = imgIndex .ToString();
    webBrowser1.Url = new Uri(comboBox1.Text); // this works almost always
    //webBrowser1.Refresh();                   // this works only if the WB is 'untouched'   
    string filename = "d:\\scrape\\AB_sos_Screen" + imgIndex .ToString("000") + ".png";
    Bitmap bmp = new Bitmap(scapeRect.Width, scapeRect.Height);
    this.DrawToBitmap(bmp, scapeRect);
    bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
    bmp.Dispose();
}

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    if (comboBox1.Text != "") webBrowser1.Url = new Uri(comboBox1.Text);
}




private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    if (!comboBox1.Items.Contains(e.Url.ToString()))
        urlIndex = comboBox1.Items.Add(e.Url.ToString());
    else
        urlIndex = comboBox1.Items.IndexOf(e.Url.ToString());
    if (urlIndex >= 0) comboBox1.SelectedIndex = urlIndex;
    button1.Focus();
}

我现在可以几乎自由地导航,屏幕抓取功能也一直在工作 - 除了像“用户”或“标签”页面这样有文本输入字段的页面。
我想知道是否有人能够重现这个问题?
还是能够解释一下?
或者说我只是在做无用功,因为这个东西根本就不可靠?
最终编辑:
虽然得到一个解释会很好,但获得一个有效的解决方案可能已经足够了。OP找到了一段使用user32.dll中的PrintWindow调用并解决所有问题的代码。它可以在注销后工作,在单击WebBrowser并抓取包括那些具有文本输入字段的页面之后刷新。以下是我的版本:
using System.Runtime.InteropServices;
//...
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);

public Bitmap CaptureControl(Control ctl)
{
    //Bitmap bmp = new Bitmap(ctl.Width, ctl.Height);  // includes borders
    Bitmap bmp = new Bitmap(ctl.ClientRectangle.Width, ctl.ClientRectangle.Height);  // content only
    using (Graphics graphics = Graphics.FromImage(bmp))
    {
        IntPtr hDC = graphics.GetHdc();
        try      { PrintWindow(ctl.Handle, hDC, (uint)0);   }
        finally  { graphics.ReleaseHdc(hDC);                }
    }
    return bmp;
}

这可以捕获带或不带边框的表单或控件。

你可能想尝试以下方法,我认为它适用于你的情况:https://dev59.com/nHzaa4cB1Zd3GeqPO1q7#21828265 - noseratio - open to work
@Noseratio:谢谢,有趣的帖子。不过手头的问题已经通过使用PrintWindow解决了,参见编辑! - TaW
TaW,我在发布评论后注意到了你的编辑。在我的解决方案中,我使用OleDraw来实现这一点,显然它更好地遵守了一些CSS规则。 - noseratio - open to work
可能是重复的问题:屏幕截图方法生成黑色图像 - TaW
1个回答

0

只是想分享一下我的经验,经过长时间的努力解决这个问题。

上述基于PrintWindow的方法只是在大部分WebBrowser控件上绘制了一个黑色矩形,尽管奇怪的是,最后几行显示的文本似乎是正常的。所以即使黑色矩形也是不一致的!但我能够让DrawToBitmap()工作。

然而,有各种隐藏的要求。

  • 首先,您的表单中只能有一个WebBrowser控件。当我尝试添加第二个时,它会正常显示,但在绘制到位图时会变成空白。
  • 其次,WebBrowser必须是表单中最上层的控件,并且不能应用任何顶部/底部边距。违反这一点往往会导致我的HTML底部被切断,而足够大的顶部/底部边距往往会导致页面内容在绘制到位图时垂直拉伸。
  • 第三,为了防止WebBrowser被触摸,创建一个禁用的Control来包装它,并将WebBrowser放在该控件内(使用Dock属性设置为Fill)。您将不得不处理整个HTML文档的内容显示,其中大部分内容都在此处(即在DocumentCompleted事件处理程序中将您的Web浏览器和包含控件的大小设置为Web浏览器的Document.Body.ScrollRectangle)。

但到目前为止,这种方法对我来说一直很稳定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接