Web浏览器疯狂

编辑:最初的问题很长,有许多猜测。 我已经把它切回到剩下的奥秘……

我现在一整天都在苦苦挣扎和猜测,我猜我应该向社区提出我的问题。

它起源于名为Screenshot方法的post生成黑色图像 。 即使用户注销 ,原始海报也希望每隔n秒连续拍摄一次包含WebBrowser的程序截图。

当用户退出时,他不再有屏幕了。 因此,任何读取屏幕的尝试都将失败。 如果使用窗口句柄,则结果为黑框,当使用CopyFromScreen时出现GDI错误exception。

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

以下是条件和剩余问题:

  • 用户不得以任何方式触摸/单击WebBrowser 。 如果他这样做,比如滚动,点击,导航子队列的DrawToBitmap调用会产生一个空框。

  • 虽然WebBrowser保持不变,但在下一个DrawToBitmap调用之前进行Refresh就足够了。

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

  • 必须存储导航新URL才能执行此操作。 我在Navigated事件中这样做。

  • 无论如何,如果网页包含 field ,则DrawToBitmap失败(使用空框)。

  • 通过使用Replace("<input", "<in_put");破坏DocumentText Replace("<input", "<in_put"); 这可以治愈,但没有进一步的技巧,这将失去CSS表..

为了测试它Buttons, a Label, a Timer, a Combobox and a WebBrowser on a Form抛出两个Buttons, a Label, a Timer, a Combobox and a WebBrowser on a Form并复制代码; 将文件路径更改为适合您的设置并观看的文件夹..:

 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://sofzh.miximages.com/c%23/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(); } 

我现在可以几乎自由地导航并且屏幕抓取工作仍然有效 – 除了具有文本输入字段的页面,例如用户标签页面。

我想知道是否可以重现..?

或解释??

或者说我毕竟只是’鬼狩猎’而且事情根本不可靠?

最终编辑:

虽然获得解释会很好,但获得一个可行的解决方案可能已经足够好了PrintWindow找到了使用PrintWindow调用user32.dll并解决所有问题的代码。 它在注销时起作用,即使在单击WebBrowser并擦除所有页面(包括具有文本输入字段的页面)之后也可以使用Refreshing 。 这是我的版本:

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

这可以使用或不使用边框捕获表单或控件。

在经过漫长的一天打击我的脑袋来解决这个问题后,我只想补充一点经验。

上面的基于PrintWindow的方法只是在大多数WebBrowser控件上绘制了一个黑色矩形,但奇怪的是,显示文本的最后几行似乎已经通过了。 所以即使黑色矩形也不一致! 但我能够让DrawToBitmap()工作。

但是,有各种各样的隐藏要求。

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

但到目前为止,这种方法对我来说始终如一。