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:
Markdown Monster - The Markdown Editor for Windows

Automating a .NET WinForm through COM Interop


:P
On this page:

 

Somebody asked an interesting question today on how to automate a Windows Form from a COM client application. .NET doesn’t support building EXE COM servers so it’s not as easy as it was in say Visual FoxPro or VB to simply create a COM public form and have it exposed as a COM object and fire away at the methods and properties.

 

I was actually thinking about this scenario as it came up because I have been toying with the idea of moving Help Builder to .NET for a while now and keep hitting a few areas that are, uh, shall we say less than optimal for a port. This is another issue that complicates matters.

 

Well, while .NET can’t create COM Exes it can create Windows Forms in DLLs and you can actually access these forms directly if you compile your DLL to be COM creatable (Register for COM Interop):

 

[ClassInterface(ClassInterfaceType.AutoDual)]

[ProgId("WinFormCom.Test")]

public class WinFormCom : System.Windows.Forms.Form

 

The key here is that you HAVE TO stick this form into a DLL project, not into the EXE where it can’t be directly instantiated via COM. So what I did is create a second project for the WinForms app (Windows Control Project) and added the Windows Form to be automated into this project. This is the project that needs to be marked for COM interop (Register for COM Interop in the Build Properties of the project).

 

Once that’s done you can now instantiate your WinForm directly. From VFP:

 

o = CREATEOBJECT("WinformCom.Test")

o.Show()

 

This works fine if all you want to do is display the form MODALLY and run it. The problem is that you can’t run the form in the background this way, because the Show() method blocks the call when no event loop (Application.Run() and the like) is present.

 

If you want the form to run and keep active while control returns to your client COM application, you will have to resort to using a new thread in the server and starting a full event loop on that new thread. That way the form will run independently of a reference that you’ve created and you get a reference to the form passed back to you.

 

To do this you need two new methods on your form (actually I’d move these off to a controller COM object but for demonstration these methods are on the form).

 

private WinFormCom LiveForm = null;

 

public WinFormCom RunForm()

{

      this.LiveForm = new WinFormCom();

 

      Thread t = new Thread( new ThreadStart( this.RunThread ));

      t.ApartmentState = ApartmentState.STA;

      t.Start();

 

      return this.LiveForm;

}

 

public void RunThread()

{

      this.LiveForm.Show();

      Application.Run();

     

}

 

Note that you need an instance field for the LiveForm so that we have a persistent reference that the thread routine can work with.

 

You can now run the Windows form from the COM client like this:

 

o = CREATEOBJECT("WinformCom.Test")

loForm = o.RunForm()

loForm.Top = 0

loForm.Left = 0

 

Voila, you can now access the WinForm directly via COM. Note that you can’t get at the controls of the form directly because controls are defined private by default, so the public interface will be the form’s public interface of methods plus whatever public interface you slap onto it.

 

Surprisingly, when you get the WinForm reference returned to you from the factory method, Intellisense does not work. Not sure why that is as the reference is exposed to COM and of the same type as the master form reference. Odd…

 

A couple of things that you need to be aware of as well. You need be very careful about object lifetime in this scenario. Killing the top level reference will not automatically kill the factory created form reference. So the proper sequence of getting rid of the form is:

 

loForm.Close()

loForm = null

o = null

 

But even that will not really clean up everything. If you use Task Manager you can watch the number of threads created in the application. As you call RunForm() each time the thread count goes up. But when you close the form the thread count does not go away. The reason for this is that we called Application.Run() without a parameter, without a specific form it is watching for termination. So we need to explicitly tell the application to stop processing the message loop with this code in the form:

 

private void WinFormCom_Closing(object sender, System.ComponentModel.CancelEventArgs e)

{

      Application.ExitThread();

}

 

This will shut down the message loop and properly clean up if the above sequence is used.

 

Note that COM clients have very little control as it is over the lifetime of the .NET COM  subsystem. Even with the above code and a CLEAR ALL, CLOSE ALL, CLEAR DLLS etc in VFP, the DLL remains loaded in the VFP process. As far as I know there’s no way to completely unload the DLL once it’s been loaded. This means if you make changes to the DLL (as I did while testing) you have to shut down the client app and restart it frequently. There’s more info on this and other issues when dealing with COM interop from a COM client in an article I put out a while back.

 

So summing up, yeah it’s a bit more work to call a Windows Form from a COM client application, but it’s definitely possible. Note that if you already have an EXE and all of your WinForms are compiled into it, you can easily expose those forms through a wrapper DLL. The DLL can then simply set a reference to the EXE and expose forms using this same approach mentioned above using a thread method.

 


The Voices of Reason


 

Jay Schumacher
August 03, 2004

# re: Automating a .NET WinForm through COM Interop

Rick,

Great posting... One thing I wanted to note was that Intellisense worked fine for me (using VFP 8). When I got my reference to the form through the factory, I could access and see all public members as expected.

Jay

Ketema Harris
August 20, 2004

# re: Automating a .NET WinForm through COM Interop

I like your explanation, and I was wondering if it lies along the same lines I was thinking....I like the GUI builder that VS.Net provides. It is easy and straight forward, but I don't like coding in VB.net or C#, in fact I Prefer PERL. I have this great book by OI(Object Innovations) Called Programming Perl in the .Net Environment, and it has been wonderful. But, the graphical programming is tedious because although you can use the WinForms you can't build them throught the GUI builder, it has to be done by hand using the PerlNET pragma syntax. Not hard, but can get long if you are bbuilding a GUI with a lot of components. So to get to my question. Is it possible to make a Winform using Vb.Net or C#(the GUI builder) but have the form actually be an interface so that it can be imported into another project where the events & methods can then be implemented?

Rick Strahl
October 10, 2004

# re: Automating a .NET WinForm through COM Interop

Sure you can using the same sort of approach. You can simply design your UI in the designer, using C# or VB.NET and storing the class in a separate assembly. From Perl you can then subclass the form and implement the relevant event handlers to hook up your code.

I have no idea why you would want to use PERL for this sort of thing, but you can do that. Perl maybe a good string manipulation language but to build business or front end logic it seems like that's a REALLY BAD choice.

Peter's Gekko
June 03, 2005

# 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...

Tom Sage
July 19, 2005

# re: Automating a .NET WinForm through COM Interop

Rick,

Excelllent article.

I am especially interested in the last two sentences in your article. Would you explain this concept further when you have time? This is exactly what I would like to do, but I am not sure how to do it.

Thanks

Vishal Thakur
August 12, 2005

# re: Automating a .NET WinForm through COM Interop

Dear Sir,
I am a VB.NET/VB-6 guy, and never worked with VFP. But still now I am in need of calling a VB.NET component from VFP-9. I am a newbee in foxpro. I came across your article "CALLING .NET COMPONENTS VIA COM FROM VFP". I also have downloaded the sample code. But I am still unable to get it done through the VFP. Can you please put more light on it ?
It will be really of great help to me?
Thanks a lot.

Regards,
Vishal Thakur
vsthakur78@rediffmail.com

Boas
November 06, 2005

# re: Automating a .NET WinForm through COM Interop

Where can I download the sample code?

Thanks
Boas

Ashok
September 12, 2006

# re: Automating a .NET WinForm through COM Interop

Thanx a lot Rick. i was searching for winform interop from Vb 6.0 and couldnt find the solution for accessing keyboard shortcut of a winform from vb6.0. Your article gave me exactly what i need.Thanks a ton.

sharn
March 07, 2007

# re: Automating a .NET WinForm through COM Interop

great article, solved lot of my Qs

I want to automate one .net winform application by another like automating MSWord. both are MDI applications. i am using remoting to det it done. can any one of u give a guide or point to a similar resourse.

regards,
chathura.

Sahaj
January 28, 2008

# re: Automating a .NET WinForm through COM Interop

Nice.

Dennis
September 30, 2010

# re: Automating a .NET WinForm through COM Interop

Thanks Rick--this answered a lot of questions.

How would you go about calling VFP code from the WinForm?

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