如何访问正在运行的.net应用程序

时间:2014-08-16 07:59:18

标签: c# .net vb.net visual-studio

我们开发的.net Windows窗体应用程序(vb.net但c#的答案也可以)有一些API(编辑:是的,我们自己的),允许用户自动执行某些任务。 当应用程序通过API(例如Visual Studio)启动时,一切都很好。我们无法工作的是将已经运行的应用程序实例分配给visual studio中的新应用程序对象。 我们已经看到有一些方法可用于COM对象(getojbect)来访问应用程序的运行实例但是.net应用程序怎么样?

重新解释这个问题,我们希望当用户调用我们应用程序的New()构造函数时,新对象指向我们应用程序的运行实例(如果有的话),而不是尝试创建一个新的(这是不可能的,因为我们通过检查Mutex没有运行其他应用程序实例来使其成为单个实例。

编辑: 用户应用程序中的示例代码,用于自动执行某些任务

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

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

请注意,解决方案可能是在Sub ReturnTheRunningInstance()或用户代码中的应用程序中添加一些代码,也许可以检查应用程序是否正在运行像Process.GetProcessesByName(&#34; TheApplicationName&#34) ;)。长度,然后做一些事情。

谢谢!

1 个答案:

答案 0 :(得分:3)

  

我们已经看到有一些可用于COM对象的方法   (getojbect)访问应用程序的运行实例但是如何访问   关于.net应用程序?

让我们从这部分开始吧。您基本上需要让一个进程访问另一个进程。 .Net提供了多种形式的跨进程通信。 WCF似乎最合适。

WCF是一个很大的主题,但这是一个可以实现目标的基本架构。

第1步

让您的应用程序托管服务,通过TCP为本地呼叫者提供服务。

考虑这个伪代码;一旦知道要搜索什么,就可以在WCF上获得大量文档。

// 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 );

调用程序与应用程序的现有实例进行交互时,这只是需要,但它无法确保应用程序正在运行。

第2步

包装类可以使您更容易找到/启动您的应用程序。

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();
    }
}

第3步

您的来电者可以从机器上的任何地方(或更远,如果您愿意)连接到您的应用程序:

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

虽然有点不寻常,但您的服务代码也可能会操纵GUI。例如:

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 );

清理

请注意,到目前为止我所显示的这种语法很优雅,但它不会处理关闭频道或频道工厂的问题。有很多种方法可以做到这一点;我使用过这样的模式:

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 );
}

它有点冗长,但它可以让你更好地控制清理和你想要控制的任何其他选项。

解决方案结构

所有这些代码都可能存在于一个程序集中。但是,将包装器放在单独的程序集中并且服务契约与包装器和主应用程序引用的另一个程序集接口可能更清晰。

  • 装配1:服务合同(接口)
  • 程序集2:GUI应用程序,引用程序集1并实现其服务合同
  • 程序集3:包装类,引用程序集1