WebBrowser Madness

旧城冷巷雨未停 提交于 2019-12-29 09:08:10

问题


Edit: The original question was a long one with many wild guesses. I have cut it back to the remaining mysteries..

I've been struggling and puzzling all day now and guess I ought to present my problem to the community.

It originated from the post called Screenshot method generates black images. The original poster wants to continuously take screenshots of his program, which includes a WebBrowser, every n seconds even when the user is logged off.

When the user is logged out he has no screen anymore. Therefore any attempt to read the screen will fail. If using a window handle the result is a black box, when using CopyFromScreen a GDI-error exception.

But the program window is still there and using DrawToBitmap works fine even when the user is logged out.

Here are the conditions and the remaining issues:

  • The user must not touch/click the WebBrowser in any way. If he does by, say, Scrolling, Clicking, Navigating the subsqeuent DrawToBitmap calls result in an empty box.

  • While the WebBrowser remains untouched, it is enough to do a Refresh before the next DrawToBitmap call.

  • After touching it it is neccessary to load the URL again by doing a webBrowser1.Url = new Uri(URLpath);

  • When navigating the new URL must be stored to do this. I do that in the Navigated event.

  • No matter what, DrawToBitmap fails (with the empty box) if the webpage contains a <input type="text" ..> field.

  • By vandalising the DocumentText with a Replace("<input", "<in_put"); this could be healed, but without further tricks this will lose the CSS sheets..

To test it throw two Buttons, a Label, a Timer, a Combobox and a WebBrowser on a Form and copy the code; change the filepath to a folder that fits your setup and watch..:

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();
}

I can now navigate almost freely and the screen scraping keeps working - except for pages with textual input fields like e.g. the Users or the Tags pages.

I wonder if anybdoy can reproduce ..?

Or explain??

Or have I been just 'ghost hunting' after all and the thing is simply unreliable???

Final Edit:

While it would have been nice to get an explanation, getting a working solution probably has to be good enough.. The OP has found code that uses a PrintWindow call to user32.dll and solves all problems. It works while being logged off, works with Refreshing even after clicking in the WebBrowser and scrapes all pages, including those with textual input fields. Here is my version of it:

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;
}

This can capture a Form or a Control with or without Borders.


回答1:


Just wanted to add my experience, after a long day of beating my head against this problem.

The above PrintWindow-based method just drew a black rectangle over most of the WebBrowser control, though oddly, the last few lines of displayed text seemed to come through. So even the black rectangle is inconsistent! But I was able to get DrawToBitmap() to work.

However, there are all sorts of hidden requirements.

  • First, you can only have one WebBrowser control in your form — when I tried to add a second one, it would display fine, but it would come out blank when drawn to a bitmap.
  • Second, the WebBrowser has to be the topmost control in your form, and it can't have any top/bottom margins applied to it. Violating this tended to lead to the bottom of my displayed HTML getting cut off, and large-enough top/bottom margins tended to lead to the page's content getting vertically stretched, when drawn to bitmap.
  • Third, to keep the WebBrowser from getting touched, create a disabled Control to wrap it, and put the WebBrowser inside of that control (with a Dock of Fill). You'll have to deal with displaying the contents of the entire HTML document, most of which is covered here (i.e. set your web-browser, and containing control's size to the web-browser's Document.Body.ScrollRectangle in a DocumentCompleted event-handler).

But so far, this method is working consistently for me.



来源:https://stackoverflow.com/questions/24343393/webbrowser-madness

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!