views:

2656

answers:

4

I found a code of winform here: http://www.wincustomize.com/articles.aspx?aid=136426&c=1

And it works fine as a winform. But I want to run the code in a web app.

  1. I add the references of System.Windows.Forms and the Microsoft.mshtml.dll in the C:\Program Files\Microsoft.NET\Primary Interop Assemblies\ to my web app.

  2. I copy the WebPageBitmap.cs into my web app.

  3. I copy the Program.cs 's Main() to my web app as a Button_Click().

  4. When I click the button in my web app. It occurs an error:

ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.

How can I use System.Windows.Forms.WebBrowser in a web app to get a Web Site Thumbnail?


public partial class Capture01 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    public delegate void WebBrowserDocumentCompletedEventHandler(object sender, WebBrowserDocumentCompletedEventArgs e);

    [STAThread]
    protected void Button1_Click(object sender, EventArgs e)
    {           


        int width = 1024;
        int height = 900;

        int thumbwidth = width;
        int thumbheight = height;



        string fileName = "image01.jpg";
        string url = "http://www.iweixtest.cn/WE/Site/1647/index.aspx";          
        thumbwidth = 150;
        thumbheight = 100;

        //WebPageBitmap webBitmap = new WebPageBitmap(args[0], width, height, false, 10000);
        WebPageBitmap webBitmap = new WebPageBitmap(url, width, height, false, 10000);
        if (webBitmap.IsOk)
        {
            webBitmap.Fetch();
            Bitmap thumbnail = webBitmap.GetBitmap(thumbwidth, thumbheight);
            //thumbnail.Save(args[1], ImageFormat.Jpeg);
            thumbnail.Save(fileName, ImageFormat.Jpeg);
            thumbnail.Dispose();
        }
        else
        {
            MessageBox.Show(webBitmap.ErrorMessage);
        }

    }



}

--------------------------------- WebPageBitmap.cs -----------------------

using System.Windows.Forms;
using System.Drawing;
using System.Net;
using mshtml;
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using System.Drawing.Drawing2D;


namespace GetSiteThumbnail
{

    /// <summary>
    /// Thanks for the solution to the "sometimes not painting sites to Piers Lawson
    /// Who performed some extensive research regarding the origianl implementation.
    /// You can find his codeproject profile here:
    /// http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=39324
    /// </summary>
    [InterfaceType(1)]
    [Guid("3050F669-98B5-11CF-BB82-00AA00BDCE0B")]
    public interface IHTMLElementRender2
    {
        void DrawToDC(IntPtr hdc);
        void SetDocumentPrinter(string bstrPrinterName, ref _RemotableHandle hdc);
    }

    /// <summary>
    /// Code by Adam Najmanowicz
    /// http://www.codeproject.com/script/Membership/Profiles.aspx?mid=923432
    /// http://blog.najmanowicz.com/
    /// 
    /// Some improvements suggested by Frank Herget
    /// http://www.artviper.net/
    /// </summary>
    class WebPageBitmap
    {
        private WebBrowser webBrowser;
        private string url;
        private int width;
        private int height;
        private bool isOk;
        private string errorMessage;

        public string ErrorMessage
        {
            get { return errorMessage; }
        }

        public bool IsOk
        {
            get { return isOk; }
            set { isOk = value; }
        }

        public WebPageBitmap(string url, int width, int height, bool scrollBarsEnabled, int wait)
        {
            this.width = width;
            this.height = height;


            this.url = 
                url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) ? 
                url : this.url = "http://" + url;

            try
            // needed as the script throws an exeception if the host is not found
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(this.url);
                req.AllowAutoRedirect = true;
                //req.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; .NET CLR 3.5.21022; .NET CLR 1.0.3705; .NET CLR 1.1.4322)"; //成功
                req.UserAgent = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
                //req.Referer = "http://www.cognifide.com";
                req.ContentType = "text/html";
                req.Accept = "*/*";
                req.KeepAlive = false;

                using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
                {
                    string x = resp.StatusDescription;
                }

            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                isOk = false;
                return;
            }
            isOk = true;                                                      // public, to check in program.cs if the domain is found, so the image can be saved

            webBrowser = new WebBrowser();
            webBrowser.DocumentCompleted +=
            new WebBrowserDocumentCompletedEventHandler(documentCompletedEventHandler);
            webBrowser.Size = new Size(width, height);
            webBrowser.ScrollBarsEnabled = false;
        }

        /// <summary>
        /// Fetches the image 
        /// </summary>
        /// <returns>true is the operation ended with a success</returns>
        public bool Fetch()
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.AllowAutoRedirect = true;
            //req.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; .NET CLR 3.5.21022; .NET CLR 1.0.3705; .NET CLR 1.1.4322)";
            req.UserAgent = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
            //req.Referer = "http://www.cognifide.com";
            req.ContentType = "text/html";
            req.AllowWriteStreamBuffering = true;
            req.AutomaticDecompression = DecompressionMethods.GZip;
            req.Method = "GET";
            req.Proxy = null;
            req.ReadWriteTimeout = 20;

            HttpStatusCode status;
            using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
            {
                status = resp.StatusCode;
            }

            if (status == HttpStatusCode.OK || status == HttpStatusCode.Moved)
            {
                webBrowser.Navigate(url);
                while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
                {
                    Application.DoEvents();

                }
                return true;
            }
            else
            {
                return false;
            }
        }

        private void documentCompletedEventHandler(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            ((WebBrowser)sender).Document.Window.Error +=
                new HtmlElementErrorEventHandler(SuppressScriptErrorsHandler);
        }

        public void SuppressScriptErrorsHandler(object sender, HtmlElementErrorEventArgs e)
        {
            e.Handled = true;
            MessageBox.Show("Error!");
        }

        internal Bitmap GetBitmap(int thumbwidth, int thumbheight)
        {
            IHTMLDocument2 rawDoc = (IHTMLDocument2)webBrowser.Document.DomDocument;
            IHTMLElement rawBody = rawDoc.body;
            IHTMLElementRender2 render = (IHTMLElementRender2)rawBody;

            Bitmap bitmap = new Bitmap(width, height);
            Rectangle bitmapRect = new Rectangle(0, 0, width, height);

            // Interesting thing that despite using the renderer later 
            // this following line is still necessary or 
            // the background may not be painted on some websites.
            webBrowser.DrawToBitmap(bitmap, bitmapRect);

            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                IntPtr graphicshdc = graphics.GetHdc();
                render.DrawToDC(graphicshdc);

                graphics.ReleaseHdc(graphicshdc);
                graphics.Dispose();

                if (thumbheight == height && thumbwidth == width)
                {
                    return bitmap;
                }
                else
                {
                    Bitmap thumbnail = new Bitmap(thumbwidth, thumbheight);
                    using (Graphics gfx = Graphics.FromImage(thumbnail))
                    {
                        // high quality image sizing
                        gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;                            
                        gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;                                                                       // make it look pretty 
                        gfx.DrawImage(bitmap, new Rectangle(0, 0, thumbwidth, thumbheight), bitmapRect, GraphicsUnit.Pixel);
                    }
                    bitmap.Dispose();
                    return thumbnail;
                }
            }
        }
    }
}
A: 

The short answer is that nothing in System.Windows is intended to be used in a web app.

System.Windows.Forms are client controls. However, web applications run on the server side, and have their own UI elements in System.Web.UI.

mshtml may be another stumbling block. I honestly couldn't say if it would be accessible from IIS.

There may be another way to do it, but I'll let someone who knows more about it than I do answer that part.

R. Bemrose
+2  A: 

You could create your own worker thread and call SetApartmentState to change it to an STA thread; this thread could do the work of rendering web pages. However, a lot of inter-thread communication would be required and, as R. Bemrose said, the System.Windows classes aren't really designed to be used inside a web app.

Another approach would be to rewrite the sample application (as a .EXE) to take two parameters: (1) the URL to download, and (2) the output location for the screenshot image. Your web app could create a temp path (for the output file), launch this program (with Process.Start), wait for it to finish, load the output file it created, then delete the temp file once it had been sent to the client or was otherwise no longer necessary. (It would, of course, need to have different error handling than displaying a message box if something went wrong.)

Bradley Grainger
A: 

I have successfully used System.Windows.Forms.WebBrowser in a web app.

Just follow the steps above and add AspCompat="true" in the webform page:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" AspCompat="true" %>

Thank you for all the answers.

Mike108
A: 

Hi Mike,

Thank you for sharing.

I use VSTO 2005 and trying to use System.Windows.Forms Webbrowser control for a while and found your post above.

I followed the steps you are explained but get the following error message while I debug the page. Error 1 'WebBrowserDocumentCompletedEventArgs' type or namespace cannot be found. (...)

Did you faced such an error while you are moving the application to the web page?

Maybe I am doing anything wrong, allthough I spend quite a lot of time I can't to overcome the problem.

If it doesn't put you out could you please share the working aspx project with us?

Thank you for your effort.

macro
Hi macro, WebBrowserDocumentCompletedEventArgs is a class within the namespace System.Windows.Forms. Maybe you did not add the System.Windows.Forms reference to your webApp correctly or vs2005 does not have the right version of System.Windows.Forms. Try vs2008.
Mike108