Turn a .NET winforms application into a (COM) automation server

As a start to the next 2 years of blogging I want to look back on an “ancient” technology : COM, which is still the glue between software parts created with a great diversity of tools. COM brought me to .NET, exploring the world of COM in Delphi I discovered the .NET 1.0 betas. Never to leave again.


COM support in .NET is good. You can make your .NET code accessible to any COM client (for instance VBscript) by setting the “Register for COM Interop” property in a project.



Having done that all the public classes in your assembly are available as COM server classes. Public methods and properties are exposed as COM callable methods and props. You can finetune this by setting the ComVisible attribute on a class or method


using System;
using System.Runtime.InteropServices;


namespace MyAutomationLibrary
{


   [ClassInterface(ClassInterfaceType.AutoDual)]
   [ProgId(“MyComClass.Test”)]
   public class MyComClass
   {


      [ComVisible(false)]
      public void MyNonComMethod()
      {


This makes building COM servers with .NET a snap.


But there is one huge drawback: you can register only a dll for COM interop. And not an exe like a WinForms application. Making a Winforms application (COM) automatable requires a little work on the architecture of your app.



  • Creat a windows forms application

  • Add a class library to the solution

  • Add System.Windows.Forms to the references of the lib

  • Add a form to the class library

  • Add the classlibrary to the references of the winforms app

  • Delete the main form of the windows app

  • Add a new class to the winform application

Your solution should now look like this



By deleting the mainform from the windows app we have also removed the entry point of the application. By default on startup a .net executable searches for a static main method. VS generates this method for you in the mainform. The new entry point will now be in the MainClass.


using System;
using System.Windows.Forms;


namespace MyAutomatableWinformApp
{



   public class MainClass
   {


       [STAThread]
       static void Main()
      {
         Application.Run(new MyWinFormClassLibrary.MainForm());
      }


   }
}


It does exactly the same as the original main method, but it creates a form which is housed in the class library instead of the exe itself. Now you still have your winform app and have moved all “real” code into a class library.


The next step is to make the application automatable. Add a new class libary to the solution and register it for COM interop. The library references the windows form class library and System.Windows.Forms. The Show method of the COM class will fire up the mainform found in the MyWinFormClassLibrary. Just creating the form and running it, like the Main method did, has one problem: the method will not return to the calling COM client untill the application has stopped running the mainform. That is when the winform app has terminated. To circumvent this the form has to be started in a separate thread. Given the great threading support in .NET this no big deal either.


using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;



[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId(“MyWinComServer.Test”)]
public class MyComClass
{


   private void formThread()
   {
      Application.Run(new MyWinFormClassLibrary.MainForm());
   }


   public void Show()
   {
      Thread t = new Thread(new ThreadStart(formThread));
      t.ApartmentState = ApartmentState.STA;
      t.Start();
   }


}


The actual code to run the mainform is in the private formThread method. Which is wrapped up in a ThreadStart delegate. A more detailed article on threading in winforms is here.


This snippet of VBscript will test your code


set o = CreateObject(“MyWinComServer.Test”)
o.Show()
msgBox(“Click to stop”)


It creates the mainform. You can toy around with that until you click the script-messagebox. Which will terminate your main app.


As the thread in the server interacts with the UI you have to set the thread’s appartmentstate to STA. Another thing to watch out for is when you want to access the form from other methods in your COM class. You will cross a thread boundary and have to Invoke methods and properties on the form. More details are in this article.


That should do the trick. Of a very great value was this blogpost by Rick Strahl on automating winforms in general. This result is just a couple of lines code but .NET is doing some very powerfull things here. More than enough to get you into trouble, so feel free to chime in.


 

This entry was posted in Coding. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • dralinusa

    Thanks!

  • cucumber
  • PeterGekko

    Thanks, feedback almost 10 years after writing :) Als, the images got lost in a server migration :( Sorry

  • horvath

    Hello, really nice article, but the images aren’t available. Could you please update the links on the images? Thanks, in advance.

  • http://codebetter.com/blogs/peter.van.ooijen/ pvanooijen

    Glad yu found your way around. A windows form is also a control on itself. So you can turn every winform into an (embedable) ActiveX control.

  • Adrian

    I guess what I was trying to do is described at http://www.codeproject.com/cs/miscctrl/exposingdotnetcontrols.asp

    I didn’t realize that the ‘control’ could consist of multiple items.

  • Adrian

    Never mind about how to add a class library to the solution–I figured out what you meant.

    I created a solution based on your instructions, but apparently the COM component that this creates does not have the same interface as an ActiveX control. Trying to view an ActiveX control with the ProgID I specified fails.

    How does one go about creating an ActiveX control from a WinForm (preferably in C#)?

    Thanks in advance.

  • Adrian

    I’m new to the .NET stuff. Does this describe how to convert a Winforms application to an ActiveX control? If not, how would I go about doing that?

    Also, what do you mean when say to “Add a class library to the solution?”

    Thanks in advance :)

  • http://srsmoot.blogspot.com Sam

    Very nice writeup!