Link to home
Start Free TrialLog in
Avatar of infotechelg
infotechelgFlag for United States of America

asked on

.NET Image Creation Rendering a Black Box

Hello,

I've had code that creates a .JPG based off of the contents of a webpage running for 6-8 months now. All of a sudden, a black box is being rendered rather than the usual image, despite the fact that NO CODE HAS BEEN CHANGED.

This just, seemingly, randomly started happening. And it's only happening on our live server; our staging server and my local machine works just fine.

Any idea as to why this would happen?

User generated image
Here, we are getting the URL of the page we want to make an image out of. Going directly to the URL on the live server properly shows the image.
WebsiteToImage site = new WebsiteToImage(Request.Url.AbsoluteUri + "?rand=" + rInt, pathToUrl + rInt + ".jpg", divWidth + 75, divHeight + 300);
site.Generate();

Open in new window

        using System.Drawing;
        using System.Drawing.Imaging;

        public WebsiteToImage(string url, string fileName, int width, int height)
        {
            // With file 
            m_Url = url;
            m_FileName = fileName;
            m_Width = width;
            m_Height = height;
        }

        public Bitmap Generate()
        {
            // Thread 
            var m_thread = new Thread(_Generate);
            m_thread.SetApartmentState(ApartmentState.STA);
            m_thread.Start();
            m_thread.Join();
            return m_Bitmap;
        }
        private void _Generate()
        {
            var browser = new WebBrowser { ScrollBarsEnabled = false };
            browser.ScriptErrorsSuppressed = true;
            browser.Navigate(m_Url);
            browser.DocumentCompleted += WebBrowser_DocumentCompleted;

            while (browser.ReadyState != WebBrowserReadyState.Complete)
            {
                Application.DoEvents();
            }

            browser.Dispose();
        }
        private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // Capture 
            var browser = (WebBrowser)sender;
            browser.ClientSize = new Size(m_Width, browser.Document.Body.ScrollRectangle.Height + 100);
            browser.ScrollBarsEnabled = false;
            m_Bitmap = new Bitmap(m_Width, browser.Document.Body.ScrollRectangle.Height + 100);
            browser.BringToFront();
            browser.DrawToBitmap(m_Bitmap, browser.Bounds);

            // Save as file? 
            if (m_FileName.Length > 0)
            {
                // Save 
                m_Bitmap.SaveJPG100(m_FileName);
            }

        public static void SaveJPG100(this Bitmap bmp, string filename)
        {
            var encoderParameters = new EncoderParameters(1);
            encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            bmp.Save(filename, GetEncoder(ImageFormat.Jpeg), encoderParameters);
        }

Open in new window

Avatar of infotechelg
infotechelg
Flag of United States of America image

ASKER

Please let me know if you need more information.
Anyone?
So, I tried adding the following and the result is the same:

            browser.Document.BackColor = Color.Transparent;
            browser.Document.ForeColor = Color.Transparent;

Open in new window


I also added this to no avail:
            m_Bitmap.MakeTransparent();

Open in new window

Avatar of it_saige
First bitmaps do not support transparency which is why the box is rendered as black.  Second, it looks as if you are running into a timing issue (as a webpage loading can trigger the page completed event multiple times).  Give me a few and I'll throw a project together for you.

-saige-
Using ideas from multiple sources:

http://www.codeproject.com/Articles/16422/How-To-Get-A-Website-Thumbnail-in-a-C-Application
http://www.codeproject.com/Articles/58605/HTML-to-Image-in-C?msg=4059086#xx4059086xx

Try this code -
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;

namespace EE_Q28693274
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void OnLoad(object sender, EventArgs e)
		{
			WebSiteToImage site = new WebSiteToImage(new Uri("http://www.experts-exchange.com/").AbsoluteUri, "experts-exchange.jpg");
			if (site.IsOk)
			{
				site.Generate();
				pictureBox1.Image = site.Bitmap;
			}
		}
	}

	[ComVisible(true), ComImport()]
	[GuidAttribute("0000010d-0000-0000-C000-000000000046")]
	[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IViewObject
	{
		[return: MarshalAs(UnmanagedType.I4)]
		[PreserveSig]
		int Draw(
			[MarshalAs(UnmanagedType.U4)] UInt32 dwDrawAspect, 
			int lindex, IntPtr pvAspect, [In] IntPtr ptd, 
			IntPtr hdcTargetDev, IntPtr hdcDraw, 
			[MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcBounds, 
			[MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcWBounds, 
			IntPtr pfnContinue, 
			[MarshalAs(UnmanagedType.U4)] UInt32 dwContinue
			);

		[PreserveSig]
		int GetColorSet([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hicTargetDev, [Out] IntPtr ppColorSet);

		[PreserveSig]
		int Freeze([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze);

		[PreserveSig]
		int Unfreeze([In, MarshalAs(UnmanagedType.U4)] int dwFreeze);

		void SetAdvise([In, MarshalAs(UnmanagedType.U4)] int aspects, [In, MarshalAs(UnmanagedType.U4)] int advf, [In, MarshalAs(UnmanagedType.Interface)] IAdviseSink pAdvSink);

		void GetAdvise(
			[In, Out, MarshalAs(UnmanagedType.LPArray)] int[] paspects, 
			[In, Out, MarshalAs(UnmanagedType.LPArray)] int[] advf, 
			[In, Out, MarshalAs(UnmanagedType.LPArray)] IAdviseSink[] pAdvSink
			);
	}

	class WebSiteToImage
	{
		readonly WebBrowser browser;
		bool fIsGenerated = false;
		public string Url { get; private set; }
		public string FileName { get; private set; }
		public int Width { get; private set; }
		public int Height { get; private set; }
		public Bitmap Bitmap { get { return GetBitmap(); } }
		public string ErrorMessage { get; private set; }
		public bool IsOk { get; private set; }

		public WebSiteToImage(string url, string fileName, int width = -1, int height = -1)
		{
			Url = url;
			FileName = fileName;
			Width = (width != -1) ? width : 1024;
			Height = (height != -1) ? height : 768;

			try
			{
				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.Referer = "http://www.google.com/";
				req.ContentType = "text/html";
				req.Accept = "*/*";
				req.KeepAlive = false;

				using (HttpWebResponse response = (HttpWebResponse)req.GetResponse())
				{
					string x = response.StatusDescription;
				}
			}
			catch (Exception ex)
			{
				ErrorMessage = ex.Message;
				IsOk = false;
				return;
			}
			IsOk = true;

			browser = new WebBrowser();
			browser.DocumentCompleted += OnDocumentCompleted;
			browser.Size = new Size(Width, Height);
			browser.ScrollBarsEnabled = false;
			browser.ScriptErrorsSuppressed = true;
		}

		public bool Generate()
		{
			bool result = false;
			if (!fIsGenerated)
			{
				try
				{
					HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
					request.AllowAutoRedirect = true;
					request.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)";
					request.Referer = "http://www.google.com";
					request.ContentType = "text/html";
					request.AllowWriteStreamBuffering = true;
					request.AutomaticDecompression = DecompressionMethods.GZip;
					request.Method = "GET";
					request.Proxy = null;
					request.ReadWriteTimeout = 20;

					HttpStatusCode status;
					using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
						status = response.StatusCode;

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

						result = true;
					}
					else
						result = false;

					fIsGenerated = true;
				}
				catch (Exception ex)
				{
					ErrorMessage = ex.Message;
					result = fIsGenerated = false;
				}
				return result;
			}
			else
				return true;
		}

		private void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
		{
			if (sender is WebBrowser)
				((WebBrowser)sender).Document.Window.Error += SuppressScriptErrorsHandler;
		}

		private void SuppressScriptErrorsHandler(object sender, HtmlElementErrorEventArgs e)
		{
			e.Handled = true;
		}

		private Bitmap GetBitmap(int width = -1, int height = -1)
		{
			Bitmap result = default(Bitmap);
			if (Generate())
			{
				IViewObject ivo = browser.Document.DomDocument as IViewObject;
				Rectangle rect = new Rectangle(0, 0, Width, Height);
				Bitmap bitmap = new Bitmap(rect.Width, rect.Height);

				browser.DrawToBitmap(bitmap, rect);

				using (Graphics g = Graphics.FromImage(bitmap))
				{
					IntPtr ghdc = g.GetHdc();
					ivo.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ghdc, ref rect, ref rect, IntPtr.Zero, 0);
					g.ReleaseHdc(ghdc);
					g.Dispose();

					if (height == -1 || width == -1)
						result = bitmap;
					else if (height == Height && width == Width)
						result = bitmap;
					else
					{
						Bitmap thumbnail = new Bitmap(width, height);
						using (Graphics gfx = Graphics.FromImage(thumbnail))
						{
							// high quality image sizing
							gfx.SmoothingMode = SmoothingMode.HighQuality;
							gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
							gfx.DrawImage(bitmap, new Rectangle(0, 0, width, height), rect, GraphicsUnit.Pixel);
						}
						bitmap.Dispose();
						result = thumbnail;
					}
				}

				if (FileName.Length > 0)
					result.SaveJPG100(FileName);
			}
			return result;
		}
	}

	static class Extensions
	{
		private readonly static ImageCodecInfo[] fEncoders = ImageCodecInfo.GetImageEncoders();

		private static ImageCodecInfo GetEncoder(ImageFormat format)
		{
			return (from codec in fEncoders where codec.FormatID.Equals(format.Guid) select codec).FirstOrDefault();
		}

		public static void SaveJPG100(this Bitmap bmp, string filename)
		{
			var encoderParameters = new EncoderParameters(1);
			encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
			bmp.Save(filename, GetEncoder(ImageFormat.Jpeg), encoderParameters);
		}
	}
}

Open in new window


Which produces the following output -User generated image-saige-
Saige,

Thank you for the reply!

I just added the transparent code to see if anything would happen; this was not in my original code.

Also, like I mentioned, this just randomly started happening, and I have no idea why (it was working fine for months).

Anyway, I'll take a look at your code and see what I can do. Thanks.
User generated imageA possible lead into the cause of the issue. I emailed our leasing company to ask if any updates were done to the Web server. And, indeed, on 6/20 (about the time this started happening), there were many .NET updates made to the server. Is it possible that one or more of these updates caused the issue?
Anything is possible, but as I stated, it could also be a timing issue; e.g. - The page is not fully rendered when the bitmap image is created.

-saige-
Saige,

Thanks again for your assistance.

I implemented your code and received this error:

User generated image
Give me a few minutes.  I'll change this.

-saige-
Ok, thank you!
ASKER CERTIFIED SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Seriously, no apologies. You're taking time out of your day to help me, and I really appreciate it.

Anywho, I implemented the updated code, and am getting an error in line 98 of your code above (83 in the attached image). It's almost like it's not finding the URL, but the URL is active and it exists. Any ideas?

User generated image
I've been working out of a winforms project.  Let me whip up a quick local web project.  Just checking but did you change the request.Referer from google.com?

-saige-
I did not. However, I think I may know why this is happening. Let me do a test and I'll get back to you.
Saige, so the error was happening because of custom code from the front-end that had to be modified to adjust for the new code you created. Once I fixed that, it worked locally.

Now for the real test: publish live and see if it fixes the issue.

I'll let you know the results!
YOU ARE THE BEST!!!! It totally worked. Thank you SO MUCH. You have no idea how much of a help you have been, and how much time you saved me.

Thanks again!!
Not a problem at all.  Glad you got it sorted.

-saige-