/// <summary> /// This event fires when the document is completely rendered. It then takes a picture, formats it /// and stops the thread thereby returning control to the caller. /// </summary> /// <param name="sender">The sender of this event.</param> /// <param name="e">The parameters sent along with this event.</param> private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) { WebBrowser webBrowser = (WebBrowser)sender; int tempHeight = this.Height; int tempWidth = this.Width; int tempWebShotHeight = this.webShotHeight; int tempWebShotWidth = this.webShotWidth; try { if (this.autoSize && webBrowser.Document != null && webBrowser.Document.Body != null) { tempHeight = webBrowser.Document.Body.ScrollRectangle.Height; tempWidth = webBrowser.Document.Body.ScrollRectangle.Width; } webBrowser.ClientSize = new Size(tempWidth, tempHeight); webBrowser.ScrollBarsEnabled = false; this.image = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height); webBrowser.BringToFront(); webBrowser.DrawToBitmap(this.image, webBrowser.Bounds); if (this.autoSizeWebShot && webBrowser.Document != null && webBrowser.Document.Body != null) { tempWebShotHeight = webBrowser.Document.Body.ScrollRectangle.Height; tempWebShotWidth = webBrowser.Document.Body.ScrollRectangle.Width; } if (this.keepWebShotProportional && !this.autoSizeWebShot) { double proportionalHeight = (Convert.ToDouble(this.webShotWidth) / Convert.ToDouble(tempWidth)) * Convert.ToDouble(tempHeight); tempWebShotHeight = Convert.ToInt32(Math.Round(proportionalHeight, 0)); } Image thumbnailImage = this.image.GetThumbnailImage(tempWebShotWidth, tempWebShotHeight, null, IntPtr.Zero); this.image = (Bitmap)thumbnailImage; } catch (Exception) { // Because of the threading model, we can't accurately capture errors. They happen all the time, so I assumed failure // from the beginning and only set success flags if everything is ok at the end. } finally { if (this.manualResetEvent != null) { this.manualResetEvent.Set(); } this.webShotResult = this.image != null ? WebShotStatus.Captured : WebShotStatus.Failed; webBrowser.Dispose(); } }
/// <summary> /// Save a WebShot image generated by the current instance. /// </summary> /// <param name="fileName">The full path and file name to save to. If the file exists, it will be overwritten.</param> /// <returns>Returns one of the values from WebShotStatus.</returns> public WebShotStatus SaveWebShot(string fileName) { Bitmap bitmap = this.GetWebShotImage(); if (bitmap != null && this.webShotResult == WebShotStatus.Captured) { try { ImageCodecInfo encoder = ImageCodecInfo.GetImageEncoders()[this.webShotImageFormat.GetHashCode()]; EncoderParameters parms = new EncoderParameters(1); parms.Param[0] = new EncoderParameter(Encoder.Quality, this.webShotImageQuality); bitmap.Save(fileName, encoder, parms); } catch (Exception) { this.webShotResult = WebShotStatus.Failed; throw; } } return this.webShotResult; }
/// <summary> /// This method is used inside a thread. It starts a hidden browser window, navigates to the provided URL, and /// waits for the browser DocumentCompleted event to fire before ending the thread. This method is responsible for /// timing out if the timeout is reached. /// </summary> private void RenderWebShotImage() { try { WebBrowser webBrowser = new WebBrowser(); webBrowser.ScrollBarsEnabled = false; webBrowser.Visible = true; if (this.autoSize) { webBrowser.Size = new Size(0, 0); } else { webBrowser.Size = new Size(this.Width, this.Height); } webBrowser.ScriptErrorsSuppressed = true; DateTime time = DateTime.Now; webBrowser.DocumentCompleted += this.WebBrowser_DocumentCompleted; webBrowser.NewWindow += this.WebBrowser_NewWindow; webBrowser.Navigate(this.Url); while (true) { Thread.Sleep(0); TimeSpan elapsedTime = DateTime.Now - time; if (elapsedTime.TotalSeconds >= this.timeout) { this.webShotResult = WebShotStatus.Timeout; this.manualResetEvent.Set(); } Application.DoEvents(); } } catch (Exception) { // Because of the threading model, we can't accurately capture errors. They happen all the time, so I assumed failure // from the beginning and only set success flags if everything is ok at the end. } }
/// <summary> /// Captures a WebShot image. /// </summary> /// <returns>Returns one of the values from WebShotStatus.</returns> public WebShotStatus CaptureWebShotImage() { this.webShotResult = WebShotStatus.Failed; Thread t = new Thread(this.RenderWebShotImage); t.SetApartmentState(ApartmentState.STA); t.Start(); this.manualResetEvent.WaitOne(); t.Abort(); return this.webShotResult; }