views:

95

answers:

1

Hey,

I'm using the WebBrowser control in an ASP.NET MVC 2 app (don't judge, I'm doing it in an admin section only to be used by me), here's the code:

public static class Screenshot
    {
        private static string _url;
        private static int _width;
        private static byte[] _bytes;

        public static byte[] Get(string url)
        {
            // This method gets a screenshot of the webpage
            // rendered at its full size (height and width)
            return Get(url, 50);
        }

        public static byte[] Get(string url, int width)
        {
            //set properties.
            _url = url;
            _width = width;
            //start screen scraper.
            var webBrowseThread = new Thread(new ThreadStart(TakeScreenshot));
            webBrowseThread.SetApartmentState(ApartmentState.STA);
            webBrowseThread.Start();
            //check every second if it got the screenshot yet.
            //i know, the thread sleep is terrible, but it's the secure section, don't judge...
            int numChecks = 20;
            for (int k = 0; k < numChecks; k++)
            {
                Thread.Sleep(1000);
                if (_bytes != null)
                {
                    return _bytes;
                }
            }
            return null;
        }

        private static void TakeScreenshot()
        {
            try
            {
                //load the webpage into a WebBrowser control.
                using (WebBrowser wb = new WebBrowser())
                {
                    wb.ScrollBarsEnabled = false;
                    wb.ScriptErrorsSuppressed = true;
                    wb.Navigate(_url);
                    while (wb.ReadyState != WebBrowserReadyState.Complete) { Application.DoEvents(); }
                    //set the size of the WebBrowser control.
                    //take Screenshot of the web pages full width.
                    wb.Width = wb.Document.Body.ScrollRectangle.Width;
                    //take Screenshot of the web pages full height.
                    wb.Height = wb.Document.Body.ScrollRectangle.Height;
                    //get a Bitmap representation of the webpage as it's rendered in the WebBrowser control.
                    var bitmap = new Bitmap(wb.Width, wb.Height);
                    wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
                    //resize.
                    var height = _width * (bitmap.Height / bitmap.Width);
                    var thumbnail = bitmap.GetThumbnailImage(_width, height, null, IntPtr.Zero);
                    //convert to byte array.
                    var ms = new MemoryStream();
                    thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                    _bytes = ms.ToArray();
                }
            }
            catch(Exception exc)
            {//TODO: why did screenshot fail?
                string message = exc.Message;
            }
        }

This works fine for the first screenshot that I take, however if I try to take subsequent screenshots of different URL's, it saves screenshots of the first url for the new url, or sometimes it'll save the screenshot from 3 or 4 url's ago. I'm creating a new instance of WebBrowser for each screenshot and am disposing of it properly with the "using" block, any idea why it's behaving this way?

Thanks, Justin

A: 

You're using a static field to store the current URL so multiple requests (and so multiple threads) have a chance to navigate to the same URL since you're not synchronizing any access. You should either use a synchronized queue that will process one screenshot request by one screenshot request, or consider making your whole class non-static so that you can instantiate one per request.

On a side note, use an AutoResetEvent or another signaling mechanism to perform the waiting, rather than sleeping in a loop.

Julien Lebosquain
Thanks that worked!
Justin