Introduction
By integration of application it's often possible to archive more functionality with less cost than implementing all of it in single application. There are a lot of existing applications, which are created for COM platform. They are still in heavy use, but current tendency on Microsoft Windows is migration to .NET as development platform. So arises task of integration of existing COM-applications with new applications being developing with .NET.
There is good sample of application automation in COM world: Microsoft Excel. It's possible to run the application as standalone full-fledged application, but also to control it from outer script.
Working on integration between .NET and Visual Basic applications there is arisen idea to use similar mechanism to allow obligatory integration between two applications created with two different frameworks. It's known that .NET platform provides tools for calling it from COM and vise-versa. But attempt to find analogue for Visual Basic ActiveX-exe in .NET world was not successful. By finding some clue on starting application by manual loading assembly and using Reflection was found the way to implement required integration ability.
To investigate the possibility was created presented in the article prototype.
Proof of concept prototype
The common architecture of the solution is next:
The core of the integration is interface library: it exposes application interface to clients and provides factory to load the automated application.
namespace icontrollable_app
{
[Guid("63B13299-D126-488f-8CCD-807C00456EDB")]
public interface IControllableApp
{
void Start();
void Finish();
void AddLine(string line);
}
}
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace icontrollable_app
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[Guid("027BD25E-DDA9-4152-9430-59EC342347B4")]
[ProgId("ControllableApp.ControllableAppFactory")]
public class ControllableAppFactory
{
public IControllableApp CreateApp(string appPath)
{
return ControllableAppFactory.Create(appPath);
}
[ComVisible(false)]
public static IControllableApp Create(string appPath)
{
Assembly assembly = Assembly.LoadFile(appPath);
Type t = assembly.GetType("controllable_app.AppMainForm");
Object instance = Activator.CreateInstance(t);
IControllableApp app = (IControllableApp)instance;
return app;
}
}
}
The automated application must have the class that implements exposed automation interface. The simplest way to do it is implementation of the interface by main form.
namespace controllable_app
{
public partial class AppMainForm : Form, IControllableApp
{
public AppMainForm()
{
InitializeComponent();
}
#region IControllableApp Members
public void Start()
{
this.Show();
}
public void Finish()
{
this.Hide();
}
public void AddLine(string line)
{
listBox1.Items.Add(line);
}
#endregion
}
}
Access from both .NET and Visual Basic clients performed through interface library. Simplest way to do it from VB6 - use late binding to access factory and automated application.
namespace net_driver
{
public partial class TestDriverForm : Form
{
IControllableApp m_app = null;
public TestDriverForm()
{
InitializeComponent();
}
private void btnFinish_Click(object sender, EventArgs e)
{
if (m_app != null)
{
m_app.Finish();
m_app = null;
}
}
private void btnStart_Click(object sender, EventArgs e)
{
const string PATH = @"..\..\..\controllable_app\bin\Debug\controllable_app.exe";
string test_app_path = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), PATH);
m_app = ControllableAppFactory.Create(test_app_path);
m_app.Start();
}
private void btnAddLine_Click(object sender, EventArgs e)
{
if (m_app != null)
{
m_app.AddLine(textBox1.Text);
}
}
}
}
'FILE: Form1.frm
Private m_app As Object
Private Sub btnFinish_Click()
If Not m_app Is Nothing Then
m_app.Finish
Set m_app = Nothing
End If
End Sub
Private Sub cmdAddLine_Click()
If Not m_app Is Nothing Then
m_app.AddLine txtLine.Text
End If
End Sub
Private Sub cmdStart_Click()
Dim factory As Object
Set factory = CreateObject("ControllableApp.ControllableAppFactory")
Dim appPath As String
appPath = App.Path + "\..\controllable_app\bin\Debug\controllable_app.exe"
Set m_app = factory.CreateApp(appPath)
Set factory = Nothing
m_app.Start
End Sub
Benefits
The proposed approach has several benefits:
- Accessibility to automated application both from COM and .NET clients.
- Migration to .NET platform without loosing existing functionality.
- Possibility to independly create separate parts of the system.
- Possibility to gradually update old applications with reworked .NET versions.
Visual Studio 2005 specific
VS2005 have one quirk related to exposing .NET to COM. After application of required attributes to classes and interfaces it need to open AssemblyInfo.cs and manually modify next line:
[assembly: ComVisible(true)]
Without this line all attempts to access COM interfaces will be unsuccessful.