【问题标题】:How to access a running .net application如何访问正在运行的 .net 应用程序
【发布时间】:2014-10-09 21:10:44
【问题描述】:

我们开发的 .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("TheApplicationName").Length 和然后做点什么以防万一。

谢谢!

【问题讨论】:

  • 不清楚(对我来说)。用户如何调用New YourApplication(他们指的是您的程序集)?发布一些代码以显示您的意思?还有几次你提到 API,什么 API?你自己?第三者?请张贴该用法以说明清楚。
  • 多么奇怪,一个完全不清楚的问题投了三票。认为这值得赞成的人可以解释一下吗?
  • @Steve(将成为 OT 和元答案...):如果用户看到他们无法回答的问题,那么有 3(我认为)可能的反应:1. 忽略,2. 否决/标志关闭 3. 投票:D
  • @Steve 没问题。我发现这个问题很有趣,因为我可以很好地想象作者想要做什么。这个想法是获取在单独进程上运行的类的实例。这就是 .NET Remoting 在 .NET 1.1 附近的某个地方,它的所有 MarshalByRefObject 和透明代理类。所以这个问题对我来说很有趣,因为事情是如何随着时间变化的。至少现在远程处理不再受到赞扬,并且广泛建议使用自托管服务。也许我会在这里看到一些不同的东西,所以我赞成鼓励人们回答。
  • 基于问题中的有限信息,我建议开发人员可以随意实例化任意多次的包装器。然后,该包装器将负责定位应用程序的单个运行实例或创建一个新实例。一旦应用程序被定位/启动,包装器将通过跨进程机制转发调用(与刚才提到的@AlexanderManekovskiy 相同)。这可能适用于 WCF,尽管模式有点不寻常。

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


【解决方案1】:

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

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

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

第一步

让您的应用程序托管一个服务,通过 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();
    }
}

第三步

您的调用者可以从机器上的任何位置(或其他地方,如果您愿意)连接到您的应用程序:

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

【讨论】:

  • 哇,我想得到的东西看起来可能有点过头了,但是,阅读 Alexander Manekovskiy 之前的评论,这似乎是要走的路,而不是尝试在 ROT 中注册应用程序并得到一些东西其中。不过,请提出一些问题:尽管我实际上不打算输入任何 OperationContract,但第 1 步似乎可行。第 2 步:到目前为止,我无法创建一个通道来通过 GetChannel 连接 WCF 端点:请问有什么进一步的建议吗?第 3 步:是的,我们的 API 将操纵 GUI。再次感谢您的宝贵建议。
  • 对于#2,这个问题可能会帮助您进一步了解:stackoverflow.com/questions/2943148/…。 WCF 是一个复杂的主题,但它适用于此类情况,这意味着您应该能够找到良好的文档和故障排除帮助。如果您的连接仍然无法正常工作,请随时提出另一个问题。
猜你喜欢
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多