3

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!

Fefone
  • 55
  • 1
  • 5
  • Unclear(for me). How can the user call `New YourApplication`(They refer to your assembly)? Post some code to show what do you mean? Also couple of times you mentioned API, what API? Your own? Third party? Post that usage to make it clear please. – Sriram Sakthivel Aug 16 '14 at 08:07
  • 5
    How strange, three upvotes for a totally unclear question. Could someone that find this worthing an upvote explain? – Steve Aug 16 '14 at 08:08
  • Oh I do get it but still me first reaction is "WAT"? ... You want to somehow link new instances of your app to use the same process as the already running ... not a good idea - I think what your really need is either to just say "already running" and close the new instance or use some kind of server/client architecture ... just to give you some thought: if two windows are running using the same process ... where should the output go? Where should the (different) input come from? – Random Dev Aug 16 '14 at 08:09
  • 1
    @Steve (gonna be an OT and meta answer...): if users see a question they cannot answer then there are 3 (I think) possible reactions: 1. ignore, 2. downvote/flag to close 3. upvote :D – Random Dev Aug 16 '14 at 08:10
  • 4
    @Steve no problem. I found this question interesting because I can imagine very well what author wants to do. The idea is to get an instance of the class that is running on the separate process. This is what `.NET Remoting` was for somewhere around .NET 1.1 with all its `MarshalByRefObject` and transparent proxy classes. So this question is interesting for me from the point of how things changed over time. At least now remoting is not praised anymore and it is widely suggested to use self hosted services. Maybe I would see something different here, so my upvote is to encourage ppl to answer. – Alexander Manekovskiy Aug 16 '14 at 08:14
  • 3
    Based on the limited information in the question, I'd suggest a wrapper that a developer can freely instantiate as many times as they want. That wrapper would then be responsible for locating a single running instance of the application or creating a new one. Once the application was located/launched, the wrapper would then forward calls via a cross-process mechanism (along the same lines as @AlexanderManekovskiy just mentioned). This would probably work fine with WCF, although the pattern is a bit unusual. – Tim M. Aug 16 '14 at 08:16
  • Tim Medora thanks for suggestion however I tried to google how to create a wrapper but I don't exactly understand how to make it work. But, more important: isn't a wrapper something for COM components? As I mentioned in my question, to make a .net user application by Visual Studio access a .net process as our application, do we have to use COM methods? No other simpler way to have .net communicate with .net? If I miss anything, please let me know thanks – Fefone Aug 16 '14 at 12:14
  • @Fefone - I've expanded my comment into an answer. "Wrapper" is just a generic term; nothing to do with COM. You're probably looking for a WCF implementation, so I've detailed that. – Tim M. Aug 16 '14 at 23:23

1 Answers1

3

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
Tim M.
  • 53,671
  • 14
  • 120
  • 163
  • WOW, it looks maybe overkilled from what I wanted to get but, reading also previous comment by Alexander Manekovskiy, this seems the way to go instead of trying to register the Application in the ROT and get something out of it. Some questions though, please: Step 1 seems to work though I don't plan to input any OperationContract actually. Step 2: So far I'm not able to create a channel to connect the WCF endpoint through GetChannel: any further suggestion please? Step 3: yes, our APIs will manipulate the GUI. Again, thank you for your precious advice. – Fefone Aug 19 '14 at 15:02
  • For #2, this question may help you get further: http://stackoverflow.com/questions/2943148/how-to-programmatically-connect-a-client-to-a-wcf-service. WCF is a complicated topic, but it's intended for situations like this, which means that you should be able to find good documentation and troubleshooting help. Feel free to open another question if you continue to have trouble getting your connection to work. – Tim M. Aug 19 '14 at 15:09