Fefone
Fefone

Reputation: 55

How to access a running .net application

the .net Windows Form application we developed (vb.net but c# answer is ok too) has some APIs (edit: yes, our own) to allow users to automate some tasks. Everything is fine when the application is started through APIs by, say, Visual Studio. What we cannot get to work though is to assign an already running instance of our application to a new application object in visual studio. We have seen there are some methods available for COM objects (getojbect) to access a running instance of an application but how about .net applications?

Rephrasing the question, we would like that, when a user calls the New() constructor of our application, the new object points to the running instance of our application (if any) instead of trying to create a new one (which is not possible by the way because we have made it single instance by checking through Mutex that no other instance of our application is running).

EDIT: Sample code in the user application to automate some tasks

Imports TheApplication
Public Class WinFormByUser
    Private ApplicationObject As TheApplication.MainForm
    Public Sub OpenTheApplication()
        ApplicationObject = New TheApplication.MainForm
        Rem here theapplication should create a new instance if no instance of TheApplication is running. BUT, if an instance of the application 
        Rem is already running (in a different process, maybe started directly from the user), the ApplicationObject should point to the running 
        Rem instance from now on, instead of trying to create a new instance
        ApplicationObject.DoSomething()
    End Sub
 End Class

Sample code inside TheApplication

Imports System.Threading
Public Class MainForm
Private ApplicationOpenedThroughAPI As Boolean = False
Private Shared mtx As Mutex
Private firstInstance As Boolean = False
Dim AppName As String = "TheApplicationName"
Public Sub New()
    If Application.ProductName.ToString() <> AppName Then
        Rem if TheApplication is opened externally through API the name is different therefore we can determine the boolean value
        ApplicationOpenedThroughAPI = True
    End If
    mtx = New Mutex(True, AppName, firstInstance)
    If firstInstance Then
        InitializeComponent()
        DoAllTheNecessaryStuff()
    Else
        If ApplicationOpenedThroughAPI = False Then
            MsgBox("Application is running, can't open second instance")
        Else
            ReturnTheRunningInstance()
        End If
    End If
End Sub
Private Sub ReturnTheRunningInstance()
    Rem please help here. what to do?
End Sub
Public Sub DoSomething()
    Rem this does something and can be called by API user
End Sub
End Class

Please note that the solution could either be adding some code inside the application in the Sub ReturnTheRunningInstance() or in the user code, maybe checking if the application is running through something like Process.GetProcessesByName("TheApplicationName").Length and then do something in case.

Thanks!

Upvotes: 3

Views: 1814

Answers (1)

Tim M.
Tim M.

Reputation: 54387

We have seen there are some methods available for COM objects (getojbect) to access a running instance of an application but how about .net applications?

Let's start with this part. You essentially need to have one process access another process. .Net provides a variety of forms of cross-process communication. WCF seems the most appropriate here.

WCF is a large subject, but here's a basic architecture that might accomplish your goals.

Step 1

Have your application host a service, available to local callers over TCP.

Consider this pseudocode; there is plenty of documentation available on WCF once you know what to search for.

// the contract
[ServiceContract]
public interface IMyService
{
    [OperationContract]
    int Foo( int bar );
}

// the implementation
public MyService : IMyService
{
    public int Foo( int bar ){ return bar * 100; }
}

// hosting the service within your application
var baseUri = new Uri( "net.tcp://localhost:59999/" );
var serviceHost = new ServiceHost( typeof( MyService ), baseUri );

// many options can/should be set here, e.g. throttling, security, and serialization behavior

var binding = new NetTcpBinding();
var endpoint = serviceHost.AddServiceEndpoint( typeof( IMyService ), binding, baseUri );

This is all you need for a caller to interface with an existing instance of the application, but it doesn't address the need to ensure that the app is running.

Step 2

A wrapper class may make it easier to locate/launch your application.

public sealed class MyWrapper
{
    public IMyService GetService()
    {
        // TODO: perform appropriate OS-wide locking here

        // TODO: see if app is running

        // TODO: if not, launch it in a new process

        // create a channel to connect the WCF endpoint we just defined
        var channel = GetChannel();

        // TODO: release lock

        // return the channel to the caller
        return channel;
    }

    public GetChannel( Binding binding, EndpointAddress endpointAddress )
    {
        var channelFactory = new ChannelFactory<IMyService>( binding, endpointAddress );
        return _channelFactory.CreateChannel();
    }
}

Step 3

Your callers can connect to your application from anywhere on the machine (or beyond, if you wish):

var wrapper = new Wrapper();
var service = wrapper.GetService();
int result = service.Foo( 123 );

While a bit unusual, your service code could also manipulate the GUI. For example:

var wrapper = new Wrapper();
var service = wrapper.GetService();

// call a method, the implementation of which launches a "contact form"
// with data preloaded for the specified contact ID
service.ShowContactForm( 1 );

Cleanup

Note that this syntax I've shown so far is elegant, but it doesn't handle closing the channel or channel factory. There are a variety of ways to do this; I've used a pattern like this:

public sealed class ServiceClient
{
    private readonly ChannelFactory<IMyService> _channelFactory;

    public ServiceClient( Binding binding, EndpointAddress endpointAddress )
    {
        _channelFactory = new ChannelFactory<IMyService>( binding, endpointAddress );
        Channel = _channelFactory.CreateChannel();
    }

    public IMyService Channel { get; private set; }

    public void Dispose()
    {
        if( Channel != null )
        {
            // TODO: check the state of the channel and close/abort appropriately
        }

        if( _channelFactory != null )
        {
            _channelFactory.Close();
        }
    }
}

public sealed class MyWrapper
{
    public ServiceClient GetClient()
    {
        // Similar setup to the previous example, except the service client wraps
        // the channel factory.
    }
}

var wrapper = new Wrapper();
using( var client = wrapper.GetClient() )
{
    client.Channel.Foo( 123 );
}

It's a bit more verbose, but it gives you much more control over cleanup and any other options you wish to control.

Solution Structure

All of this code can potentially live in one assembly. However, it may be cleaner to place the wrapper in a separate assembly and the service contract(s) interfaces into another assembly referenced by the wrapper and the main application.

  • Assembly 1: service contracts (interfaces)
  • Assembly 2: GUI application, references assembly 1 and implements its service contracts
  • Assembly 3: wrapper class, references assembly 1

Upvotes: 3

Related Questions