Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Previewing HTML with InternetExplorer.Application in C#


:P
On this page:

Ok, this is not a big thing, but I do this so frequently that today I wrote a small wrapper to handle bringing up the IE Automation object without having to create a full Interop assembly. If you just quickly need to preview some HTML this class comes in pretty handy and it provides a few useful features that the Interop assemblies do not provide natively.

 

The goal here is a quick and dirty class that can be used to browse some HTML with some control over window sizing. In West Wind Web Monitor I’m using this class to display a particular log entry. HTML is often a much better format to display a large amount of information especially if the information is not fixed.

 

 

while this display isn’t particularly snazzy (primarily because it should be all text as to avoid external dependencies), it is easy to see all the information provided. Providing the same sort of display in a Windows form ‘square’ interface is not easy, requiring either a rich text box or some creative arrangements with editboxes or custom scrolling text areas.

 

Anyway, since I do this sort of thing all the time and I have a little bit of interop code sprinkled throughout several apps I combined this stuff tonight into a small reusable class that some of you might find handy. What this class does is use IE with Late Binding at runtime so there’s no requirement for an Interop assembly. Interop Assemblies are easy to use but they usually are pretty large in size (a few hundred k) because the TLBIMP import basically retrieves the entire type library and creates classes out of it. Wrappering a few properties and methods is a lot leaner and cleaner. <g>

 

/// <summary>

/// Summary description for IEAutomation.

/// </summary>

public class IEAutomation : IDisposable

{

     

      /// <summary>

      /// Instance of the InternetExplorer.Application raw COM Object

      /// </summary>

      public object Application

      {

            get

            {

                  if (!this.IsAlive())         

                        this.Load();

                  return  this.IE;

            }

      }

      private object IE = null;

 

      private int IEHwnd = 0;

 

      public int Top

      {

            get { return (int) wwUtils.GetProperty(this.IE,"Top"); }

            set {  wwUtils.SetProperty(this.Application,"Top",value); }

      }

     

      public int Left

      {

            get { return (int) wwUtils.GetProperty(this.Application,"Left"); }

            set {  wwUtils.SetProperty(this.Application,"Left",value); }

      }

     

      public int Height

      {

            get { return  (int) wwUtils.GetProperty(this.Application,"Height"); }

            set { wwUtils.SetProperty(this.Application,"Height",value); }

      }

 

      public int Width

      {

            get { return (int) wwUtils.GetProperty(this.Application,"Width"); }

            set { wwUtils.SetProperty(this.Application,"Width",value); }

      }

 

      public bool AddressBar

      {

            get { return (bool) wwUtils.GetProperty(this.Application,"AddressBar"); }

            set {  wwUtils.SetProperty(this.Application,"AddressBar",value); }

      }

      public bool Toolbar

      {

            get { return (bool) wwUtils.GetProperty(this.Application,"Toolbar"); }

            set {  wwUtils.SetProperty(this.Application,"Toolbar",value); }

      }

 

      public bool StatusBar

      {

            get { return (bool) wwUtils.GetProperty(this.Application,"StatusBar"); }

            set {  wwUtils.SetProperty(this.Application,"StatusBar",value); }

      }

 

      public bool Visible

      {

            get { return (bool) wwUtils.GetProperty(this.Application,"Visible"); }

            set {  wwUtils.SetProperty(this.Application,"Visible",value); }

      }

 

      public IEAutomation()

      {

      }

 

      /// <summary>

      /// Internally loads the IE COM object

      /// </summary>

      private void Load()

      {

            Type loT = Type.GetTypeFromProgID("InternetExplorer.Application");

            this.IE =  Activator.CreateInstance(loT);

            this.IEHwnd = (int) wwUtils.GetProperty(Application,"hwnd");

      }

           

      /// <summary>

      /// Check whether the IE is still alive

      /// </summary>

      /// <returns></returns>

      public bool IsAlive()

      {

            if (this.IE == null)

                  return false;

 

            try

            {

                  wwUtils.GetProperty(this.IE,"Top");

            }

            catch

            {

                  return false;

            }

 

            return true;

      }

 

      /// <summary>

      /// Navigates to a specific URL

      /// </summary>

      /// <param name="Url"></param>

      public void Navigate(string Url)

      {

            // *** Make sure user hasn't closed IE - if so reload

            if (!this.IsAlive())

            {

                  // Reload

                  this.Load();

                  this.Show();

            }

 

            wwUtils.CallMethod(this.Application,"Navigate",Url);

      }

 

      /// <summary>

      /// Make internet Explorer Visible

      /// </summary>

      public void Show()

      {

            wwUtils.SetProperty(Application,"visible",true);

            this.BringToFront();

      }

 

 

      [DllImport("user32.dll")]

      public static extern int SetForegroundWindow(int hwnd);

 

      /// <summary>

      /// Brings IE to the top of the Window Stack

      /// </summary>

      public  void BringToFront()

      {

            SetForegroundWindow( this.IEHwnd );

      }

 

      #region IDisposable Members

      public void Dispose()

      {

            if (this.IE != null)

            {

                  try

                  {

                        this.Visible = false;

                  }

                  catch{ ; }

 

                  Marshal.ReleaseComObject(this.Application);

                  this.IE = null;

            }

      }

      #endregion

}

 

The wrapper only wraps a few methods and properties I wanted to expose and that I actually use here, but it’s easy to see how you can expose additional methods and properties. Events are a bit more tricky and if hook IE  browser events – or worse (<g>) MSHTML events – you’ll want to look into the Interop Assemblies or some of the public domain wrappers available for them (mostly based on the Web Browser control).

 

The class also does a few useful things like making sure the instance is alive at all times. This covers the case where the user closes the browser window manually and the next hit checks explicitly to make sure the window is still there and if not recreates it which is a common scenario. It also has an option to force IE to top of the Window stack with the Show() or BringToFront() methods. By default IE only pops to the top the first time it’s loaded – after that it stays wherever it is in the stack when you navigate. This is a real PITA especially if you have a few IE windows open and are trying to find the active one. The Show() method makes sure IE pops to the top and becomes visible.

 

 

To use the control in code is pretty easy. To create the HTML I just capture the output to an HTML file on disk:

 

StreamWriter sw = new StreamWriter(Directory.GetCurrentDirectory() + "\\__preview.htm");

      sw.Write(

@"<html>

<head>

<title>Error: " + SiteName + @"</title>

</head>");

 

                  sw.Write(

            string.Format(

@"<body>

<h1>Error: {0}</h1>

<hr>

<table width='99%'><tr><td>

<b style='color:Red'>" + MsgType + @"</b>

</td><td align='right'><small>Time Stamp: {1} </small></td></tr></table>         

<p>

<b>Error Message:</b>

<hr>

{2}

<p>

<b>Expected as part of the Response:</b>

<hr>

{4}

<p>

<b>Full Html Response:</b> &nbsp;<small><a href='__preview2.htm'>View as Html</a></small>

<hr>

<pre>{3}</pre>

</body>

</html>",SiteName,Time,Message,HtmlResponse,SearchFor ) );

                       

      sw.Close();

 

// *** Load IE or reuse existing instance

if (this.IE == null || !this.IE.IsAlive())

this.LoadIe();

 

this.IE.Show();  // show and bring to front

this.IE.Navigate(Directory.GetCurrentDirectory() + "\\__preview.htm" );

 

 

where LoadIe looks like this:

 

private void LoadIe()

{

      if (this.IE == null)

            this.IE = new IEAutomation();

 

      this.IE.Top = this.Top + 50;

      this.IE.Left = this.Left + 25;

      this.IE.Width = this.Width;

      this.IE.Height = this.Height;

      this.IE.AddressBar = false;

      this.IE.Toolbar = false;

}

 

Then when it’s all said and done in the Form close:

 

IE.Dispose()

IE = null

 

and it’s all done. The call to Dispose() makes sure IE goes invisible before releasing the instance which is needed to unload the instance – otherwise stays open even if the COM instance is released.

 

Other Options

I should mention that there are a couple of other ways you can use IE if you don't care about the appearance of the Window. The ShellExecute API is the easiest and you can use System.Runtime.Process.Start(). This method has the unfriendly side effect of always firing up a new window for each navigation - usually that's not what I want. You can also use ShellExecute using PInvoke:

 

[DllImport("Shell32.dll")]

private static extern int ShellExecute(int hwnd, string lpOperation,

      string lpFile, string lpParameters,

      string lpDirectory, int nShowCmd);

 

public static int GoUrl(string lcUrl)

{

      string lcTPath = Path.GetTempPath();

 

      string Username = Environment.UserName;

       

      int lnResult = ShellExecute(0,"OPEN",lcUrl, "",lcTPath,1);

      return lnResult;

}

 

which lets you easily preview any type of document that Windows supports. Unlike Process.Run() it reuses the same window for URLs. This works but you have no control over the IE window that pops up because there’s no link between your app and the window. This means you can’t shut the window down or manipulate the appearance of the window. It does have the advantage that it’s one line of code and requires no extra classes or depencies. It does however require PInvoke which might have security implications in your application.

 

Finally there's also the Web Browser control which gives you the most control because you can embed and control it in your own forms. But that's a topic for another day <g>...

 

For completeness sake here are also the wwUtils Reflection routines using InvokeMember:

 

/// <summary>

/// Retrieve a dynamic 'non-typelib' property

/// </summary>

/// <param name="loObject"></param>

/// <param name="lcProperty"></param>

/// <returns></returns>

public static object GetProperty(object loObject,string lcProperty)

{

      return loObject.GetType().InvokeMember(lcProperty,BindingFlags.GetProperty,null,loObject,null);

}

 

/// <summary>

/// Set Property dynamically

/// </summary>

/// <param name="loObject">The object to set this property on.</param>

/// <param name="lcProperty">The name of the property to set</param>

/// <param name="loValue">The value to set it to</param>

/// <returns>nothing</returns>

public static void SetProperty(object loObject,string lcProperty,params object[] loValue)

{

      loObject.GetType().InvokeMember(lcProperty,BindingFlags.SetProperty,null,

                                      loObject,loValue);

}

 

/// <summary>

/// Wrapper method to call a 'dynamic' (non-typelib) method

/// on a COM object

/// </summary>

/// <param name="loParams"></param>

/// 1st - Method name, 2nd - 1st parameter, 3rd - 2nd parm etc.

/// <returns></returns>

public static object CallMethod(object loObject,string lcMethod, params object[] loParams)

{

      return loObject.GetType().InvokeMember(lcMethod,

            BindingFlags.InvokeMethod,null,loObject,loParams);

}

 


The Voices of Reason


 

Andrew Robinson
April 29, 2005

# re: Previewing HTML with InternetExplorer.Application in C#

Rick,

Can you tell me a bit more about the following code. Are you using reflection to set properties in the parent application? Clever.

wwUtils.GetProperty(this.Application,"Left");

-Andrew

Chris
June 10, 2005

# re: Previewing HTML with InternetExplorer.Application in C#

This is great! And works well, too.

rahul shirodkar
August 01, 2005

# re: Previewing HTML with InternetExplorer.Application in C#

Hi

I want to ceate a HTML file from a C# application. But the format is such that the user enters the
data in text boxes which is in an HTML format( along with tags like <p>data</p>. Now I have to create a html file(may be a template) with exisiting head body tags and then add this data in HTML format to this file.
So that i create A HTML file (.html ) file which can be opened in any browser.

Thanks
Rahul Shirodkar

Tristan
July 18, 2006

# re: Previewing HTML with InternetExplorer.Application in C#

Hi,

This is a very useful piece of code, thanks!

I was wondering how I would go about hooking into the Document DOM

Example
Object Doc = this.IE.Document;

And to access a collection within that object, for example
this.IE.Document.all.tags("A")

Any ideas would be greatly appreciated.

Kind regards,
tristan

Brian
August 03, 2007

# re: Previewing HTML with InternetExplorer.Application in C#

This is great stuff. My only question is can it be modified to reuse an already open IE window/process if one is available, then if not, create a new instance?

TIA!

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024