我们开发的.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) ;)。长度,然后做一些事情。
谢谢!
答案 0 :(得分:3)
我们已经看到有一些可用于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 );
调用程序与应用程序的现有实例进行交互时,这只是需要,但它无法确保应用程序正在运行。
包装类可以使您更容易找到/启动您的应用程序。
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 );
}
它有点冗长,但它可以让你更好地控制清理和你想要控制的任何其他选项。
所有这些代码都可能存在于一个程序集中。但是,将包装器放在单独的程序集中并且服务契约与包装器和主应用程序引用的另一个程序集接口可能更清晰。