Work around limitations of VS 2005 Deployment Projects with Windows Services

I needed to deploy a Windows Service using an MSI. The project was built in VS 2005 using .Net 2.0; so far, so good.

The problem arose when I wanted to have the Service write to its own Event Log, rather than the default Application log. The standard Installer class from System.Configuration.Install, which otherwise does a fine job of installing the Windows Service, proved less than ideal because it automatically creates an EventLogSource with the same name as the Service executable, and sets its event log to be Application.

The solution lies in the way in which the Installer class contains a series of other installers in the Installer.Installers collection, which in turn contain other installer objects.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;

namespace MyService
{
  [RunInstaller(true)]
  public partial class ProjectInstaller : Installer
  {
    public ProjectInstaller()
    {
      InitializeComponent();

      //
      // ProjectInstaller
      //
      this.Installers.AddRange(new System.Configuration.Install.Installer[] {
            this.serviceProcessInstaller1,
            this.serviceInstaller1});
    }

    private void serviceInstaller1_BeforeInstall(object sender, InstallEventArgs e)
    {
      RemoveEventLogInstallers(this, 0);
    }

    ///
    /// Recursive method to remove EventLogInstaller Installers from the Installers collection.
    /// This is needed to avoid automatic setting up of the event logs
    private static void RemoveEventLogInstallers(Installer installer, int depth)
    {
      Console.Out.WriteLine(String.Format("Number of installers at depth {0}: {1}", installer.Installers.Count, depth));
      for (int i = installer.Installers.Count - 1; i >= 0; --i)
      {
        Installer childInstaller = installer.Installers[i];
        Console.Out.WriteLine(String.Format("Installer {0} at depth {1} is of type: {2}", i, depth, installer.GetType().Name));

        RemoveEventLogInstallers(childInstaller, depth + 1); //recurse

        if (childInstaller is EventLogInstaller)
        {
          Console.Out.WriteLine(String.Format("Removing Installer {0} at depth {1} because it is of type: {2}", i, depth, childInstaller.GetType().Name));
          installer.Installers.Remove(childInstaller);
        }

      }
    }

    private void serviceInstaller1_BeforeUninstall(object sender, InstallEventArgs e)
    {
      RemoveEventLogInstallers(this, 0);
    }
  }
}

Search recursively through the entire tree of Installer objects and remove any installer whose type is EventLogInstaller, and the problem is solved! Make sure to do this before Install and before Uninstall, otherwise the (un)installation does not complete successfully.

Calling Javascript from C#

Call JavaScript from C# code, passing parameters and returning results.

I came across a need to call JavaScript methods on HTML code inside a web page from a C# WinForms application. How could that language gap be bridged?

It turns out that Type.InvokeMember() does the trick when using a [Internet Explorer] WebBrowser control to load a web page.

using mshtml;
...
private AxSHDocVw.AxWebBrowser axWebBrowser1;
...
///
 /// Retrieves a reference to the HTML element containing a DIV. /// JavaScript methods are implemented on this element itself. /// 

/// A reference to the HTML element holding the JavaScript methods
private HTMLDivElementClass GetContainer()
{
 HTMLDivElementClass container = null;
 IHTMLDocument2 doc = axWebBrowser1.Document as IHTMLDocument2;

 if (null != doc)
 {
  foreach (IHTMLElement element in doc.all)
  {
   if (element.id == "My_Container")
   {
    container = element as HTMLDivElementClass;
    break;
   }
  }
 }

 return container;
}

///
 /// Gets the text from the container /// 

/// A string containing the text of the container
private string GetText()
{
 string result = null;
 HTMLDivElementClass div = GetContainer();
 if (null != div)
 {
  Type type = div.GetType();
  result = (string) type.InvokeMember(
    "GetText",
    BindingFlags.InvokeMethod,
    null,
    div,
    null);
 }   

 return result;
}

The HTML page loaded in the WebBrowser control has a DIV element with an id of “My_Container”, attached to which is a method called GetText(), which returns some arbitrary text.

Having acquired a reference to the DIV on which the JavaScript method is declared (using GetContainer()), the JavaScript method is called using the InvokeMember() method of the Type class instance; the return value is cast to a string.

Discussion

There is every reason why this should NOT work, but the implementers of the WebBrowser control decided that JavaScript methods living within the DOM should be accessible as first class object members at runtime (in this case, via IDispatch). Nice!

Result

C# code can call arbitrary JavaScript within an HTML page! The only limitation is that the return types of the JavaScript methods can be only simple types (even DateTime is too complex).

Integrating Blogger and PHP

As part of my ’10 pages or less’ website framework, I needed to integrate Blogger with PHP. Blogger can publish to an FTP server (instead of BlogSpot, and so news and blog content can be included within a ‘real’ site, but the management overhead solved by Blogger.

Here is how to get Blogger working with PHP:

  1. Set up your blog to publish via FTP (on Publishing tab)
  2. Change the ‘Blog Filename’ to index.php
  3. Change ‘Archive Filename’ to archive.php (on Archiving tab)
  4. Finally, delete the old index.html file from the blog directory (otherwise it will probably take precedence over the new index.php)

You can then edit the blog Template to include all the standard PHP includes to match the rest of the site.

Generating unique IDs for XML nodes

I needed to assign a guaranteed unique ID to nodes in a tree. A simple way to do this is to use the sequence number of a node when creating the ID:

<xsl:number format="1" count="//Node" level="any" />

This takes the sequence number (provided by xsl:number) of any node matching “Node”; the level=”any” directive means no hierarchy/sibling information is used; all nodes matching “Node” are taken as being sequentially numbered.

Obviously this works only for the initial set of nodes, but it satisfied the requirements here.

Disappearing text in IE6

I have just found a work-around for a strange bug in IE6, when rendering empty paragraph elements. It seems to be related to this ‘Peekaboo’ bug, involving SPAN elements within styled containers, but only the pathology is similar, not any obvious causes.

Basically, an empty <p></p> would cause IE6 to mis-render entire sections of the page, making them flicker in and out of visibility when the chrome was resized, or when the text was highlighted. Firefox rendered the original text without problem.