Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Automation Interfaces & .NET Applications


January, 2006: Automation Interfaces & .NET Applications

Scott is the author of several .NET books, a certified Microsoft trainer and developer, and a Microsoft MVP. He can be contacted at [email protected].


In the world of COM, many applications exposed automation interfaces. These interfaces allowed you to write one application that drives—or automates—another. For example, it is trivial to build an application that launches and drives Word, Excel, or Internet Explorer. Through automation, you can access the extensive functionality that those applications provide, as if those applications are reusable components. In fact, from the perspective of your application, Word, Excel, IE, and any other application that supports automation, just become uber COM objects.

Visual Studio .NET does not provide the same experience. You cannot simply add a reference to an executable and use it as a component. While Visual Studio does not provide something as simple as a checkbox to enable application automation, I show in this article how to build automation interfaces for .NET executables, and easily use those automation interfaces from other applications.

To illustrate, I present Line Count, a sample Windows Forms application that supports automation. When given a folder path, this application scans through all the C# and/or VB source files under that folder, and counts the number of lines of code for each file (see Figure 1). This application runs standalone or can be automated, so that its functionality is available to any other application that wishes to drive it. (The complete source code and related files for Line Count are available electronically; see "Resource Center," page 4.)

Note that I built this application for demonstration purposes only, and there are obviously other ways that reusable line-counting functionality could have been designed. For example, the bulk of the functionality could have been placed in DLLs, rather than in the executable. This would allow other applications to simply reference those DLLs, and also access the line-count functionality. Therefore, the techniques shown for automation are not put forward as a general practice for reusing functionality between applications. Instead, I assume that you already have a valid scenario where application automation is the solution you seek.

Step 1: Building an Automation Interface Assembly

The first step in automation is to create interfaces that expose specific application functionality. These interfaces should be placed in an assembly separate from the application executable. This assembly is referred to as the "automation interface assembly," and is referenced both by the automation application and by driver applications; see Figure 2.

In Listing One, the LineCountInterfaces project builds the automation interface assembly for the Line Count application. This project also contains a factory class that's used to instantiate the Line Count application. The driver application can then reference and use LineCountInterfaces to drive the Line Count application.

The Create method of the LineCountFactory class creates an instance of the Line Count application. With OLE automation, the automated application would be launched as a separate process, and interprocess communication would be used between the driver application and the automated application. The .NET Framework does not support this kind of seamless interprocess communication. One option would be to use remoting for interprocess communication. Remoting is a decent option for communicating with applications on remote machines, but it seems overly complex and cumbersome for accessing functionality in local applications.

Instead, the automation interface uses reflection to load the Line Count application into the same process as the application that's driving it. Line Count is loaded in as though it were any other assembly. This has certain advantages and drawbacks. One advantage is that calls into Line Count will be blazingly fast, as no process boundaries need to be crossed and no data needs to be marshaled. However, Line Count must be designed with this level of in-process intimacy in mind. If it does sufficiently bad things, it could adversely affect the driver process that's hosting it.

Once Line Count is loaded, reflection is used to locate the main form class. Reflection is then used to create an instance of this form and obtain a reference to the ILineCountApplication interface (Listing Two) that the form implements. The ILineCountApplication interface describes the functionality that's available through automation.

The SearchFolder property indicates the directory that should be recursively searched for .cs or .vb files. The Count method scans the search folder and returns an array of objects that contain filenames and line counts for the C# and VB source files. Finally, the Shutdown method closes the Line Count application.

Step 2: Implementing Automation Interfaces

The key to application automation is interfaces. Interfaces describe the functionality that Line Count exposes. Again, the interfaces are contained in the automation interface assembly, and the Line Count application references this assembly. Classes within the Line Count application that need to be accessible through automation then implement these interfaces. For example, the main form in Line Count implements the ILineCountApplication interface.

Step 3: Referencing Automation Interfaces and Driving the App

Any application that wishes to drive Line Count also references the automation interface assembly. This assembly provides strong typing, compile-time checks, and IntelliSense support for the driving application; see Listing Three.

The driver application calls the LineCountFactory.Create method in the automation interface assembly. This loads Line Count into memory and instantiates the main form. The Create method returns a reference to the ILineCountApplication interface. The driver application can use this interface to set the SearchFolder property and call the Count method. When Count is called, Line Count recurses through the search folder and counts the lines of code in any source files found. This information is returned as an ICountStatistics array. The driver application can then loop through this array and access the filename and line count for each file; see Figure 3.

Conclusion

With Visual Studio 2005, application automation becomes even easier. Visual Studio 2005 has the ability to add a reference to an executable. This means that the automation interface will not need to be housed in a separate executable but can instead be compiled into the executable. By default, anything in the executable with a public accessor is callable by the driver application. This means that by default, the driver application can directly access all forms and classes in the automated application. To prevent this, all accessors should be changed from public to internal, and only members of the automation API should be marked public.

With a little forethought, Windows Forms applications can be designed to support automation. The first critical step is to design interfaces to expose specific application functionality. These interfaces should be placed in a DLL assembly, separate from the executable (referred to as the "automation interface assembly"). The automation interface assembly should also contain code to instantiate the automated application through reflection.

The automated application should implement the interfaces contained in the automation interface assembly. Then, when someone wants to drive the application, they need only reference the automation interface assembly and use it to instantiate the application. Through the interfaces in the automation interface assembly, the application functionality can be accessed.

Application automation is useful for a number of scenarios. First, it may open an additional avenue to automate the testing for your application. Also, many customers used to COM applications may want automation support for your application as well.

DDJ



Listing One

public class LineCountFactory
{
    public static ILineCountApplication Create(
                  string appPath, bool visible)
    {
        Assembly assembly = Assembly.LoadFile(appPath);
        Type t = assembly.GetType("LineCount.Form1");
        Object instance = Activator.CreateInstance(t);
        ILineCountApplication app = (
            ILineCountApplication)instance;
        app.Start(visible);

        return app;
    }
}
Back to article


Listing Two
public interface ILineCountApplication
{
    string SearchFolder {get; set;}
    void Start(bool visible);
    ICountStatistics[] Count();
    void Shutdown();
}
Back to article


Listing Three
ILineCountApplication app = LineCountFactory.Create(
    Path.GetFullPath(
       @"..\..\..\LineCount\bin\debug\LineCount.exe"), 
       true);
app.SearchFolder = Path.GetFullPath(@"..\..\");
foreach (ICountStatistics stat in app.Count())
{
    textBox1.Text += stat.LineCount + 
        ":" + stat.FileName + "\r\n";
}
app.Shutdown();
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.