【问题标题】:Passing argument to another instance of the same application将参数传递给同一应用程序的另一个实例
【发布时间】:2016-09-24 13:34:26
【问题描述】:

我的小应用程序出现严重问题;基本上很容易理解:

我的软件,当打开时,做它的事。

我想专注于打开另一个实例(我的意思是再次打开.exe),检查它是否已经打开。如果不是简单地启动应用程序,而是如果它已经在运行(也就是第二个或更多实例)“简单地”将输入参数(args 字符串数组)传递给第一个实例,则会适当地处理它。

这是我的程序.cs

static class Program
{
    static Mutex mutex = new Mutex(true, "{blabla}");

    [STAThread]
    static void Main(String[] args)
    {
        if (mutex.WaitOne(TimeSpan.Zero, true))
        {
            //First Instance!

            try
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                CALL A MY STATIC METHOD, DO SOME THINGS

                Application.Run(new Form1());
            }
            finally
            {
                mutex.ReleaseMutex();
            }
        }
        else
        {
            //Not-so-first instance!
            CALL A STATIC METHOD, 
            DO OTHER THINGS LIKE COMUNICATE WITH FIRST INSTANCE

            SIMPLY CLOSE.
        }
    }
}

只有当它已经打开但(当然)它不能与主实例通信时,它才会识别(使用互斥锁)。

我已经尝试了很多东西,但我无法让它发挥作用。

我试过this,但我真的不明白(在浪费了很多时间之后)如何放置我的“第一次”代码和“已经运行”的代码。

也试过MSMQ,但我不能让它工作。

有人可以帮帮我吗?这是一个基本的软件,可以做一些非常基本的事情,但我花了一天时间让它按我想要的方式工作!

【问题讨论】:

  • 您的第二个实例在不同的进程中运行。您必须实现某种通信机制——MSMQ、通过套接字进行序列化等。使事情static 不会这样做。您正在运行同一可执行文件的多个副本这一事实并不比在不同应用程序之间进行通信更简单。
  • 不幸的是,我今天了解到与另一个程序通信并不容易,即使它是同一个可执行文件。我知道不同的副本必须以不同的方式进行管理,而不是使所有内容都静态化;我编写的静态方法在另一个 .cs 文件中,并在我的程序逻辑中使用:与问题无关:)

标签: c# arguments pipe instance msmq


【解决方案1】:

最简单的通信方式之一是将 Windows 消息从第二个实例发送到第一个实例。第二个实例将使用 PostMessage 发送消息,第一个实例将覆盖 Form1 的 WndProc 方法来处理消息。

这里有一个例子: Send message to a Windows process (not its main window)

该问题也有一个使用管道(和 WCF)进行通信的答案。

进程间通信有多种形式。使用哪一个取决于您的需求,例如 Windows 消息不能携带大量信息,而管道可以非常高速地传输数据。

更新 一个简单的替代方案可能是 IPCChannel 。这允许您在第一个实例中创建一个可由第二个实例调用的对象。您可以在该对象上创建一个方法并将您的数据作为参数传递

【讨论】:

  • 我已经尝试了 chitza 最终解决方案,它工作得很好,除了一件事:我们使用 PostMessage 将“输入”发送到主实例,然后我可以从那里管理启动功能或执行操作.问题是 PostMessage 只允许 Int 参数;我想发送某种结构化数据(如数组或对象),但我不知道是否可行。你说什么?有什么我可以尝试的吗?
  • 如果您需要发送更多数据,那么您需要更高级的进程间通信形式。 MSMQ 是一种方法,但您似乎遇到了麻烦。一个简单的替代方案可能是 msdn.microsoft.com/en-us/library/… 。这允许您在第一个实例中创建一个可由第二个实例调用的对象。您可以在该对象上创建一个方法并将您的数据作为参数传递
  • 谢谢!这解决了我的问题!我还有其他一些小问题(我不能从第二个实例中调用所有内容:有些事情不能很好地工作,至少在我的情况下,比如 FolderBrowserDialog 并且在调用某些方法的一些深层方法中抛出异常(Visual Studio调试器也告诉我不能从外部调用)但这已经解决了我的问题。所以我将 IpcChannel 与 InteropMessage(Chitza 解决方案)结合起来,现在我基本上可以做任何我想做的事情了!你成就了我的一天,我的软件:)
  • 好东西。我很高兴它帮助了你。我已将 IPC 频道添加到我的答案中。如果它解决了您的问题,请接受它。
  • 对不起,如果我回复晚了,但我有一个问题......我已经创建了自己。在主要实例中,我在 WndProc 中侦听消息。在第二个实例中,我发送消息,即“触发”主实例中的 WndProc 方法。在我安装了这个link 之前,一切都很好。要安装它,我必须将我的主表单方法声明从 'public partial class Form1 : Form' 重命名为 'public partial class Form1 : MetroFramework.Forms.MetroForm' 。这样做 IPC 根本不会在第一个实例中被捕获。你能给我一些建议吗?
【解决方案2】:

使用Mutex

    // Generate your GUID and put it here
    private const string ProgramRunningGuid = "BD2023EE-F7B3-47B8-8C76-32668196E4D3";
    private Mutex _mutex;

    private bool IsProgramRunning()
    {
        bool createdNew;
        // It returns a bool value determining if a Mutex that created is new
        // If the program is already running mutex wouldn't be new
        _mutex = new Mutex(true, ProgramRunningGuid, out createdNew);
        return !createdNew;
    }

在程序退出时,您需要释放互斥锁:

    _needlesRunningMutex.ReleaseMutex();

如果你想关注你的应用程序已经运行的实例,你可以在这里查看我的答案和工作解决方案:https://stackoverflow.com/a/35018042/3731444

如果您只需要共享一些 bool 参数来确定是打开还是关闭,您也可以为它们中的每一个使用互斥锁。

如果您需要在启动时将参数传递给您的 .exe,请检查以下问题:How to pass parameters to an exe?

如果您需要在正在运行的应用程序的两个实例之间进行持续通信,请使用 MSMQ。如果有任何问题,我会尽力帮助您。

在这里查看如何使用 MSMQ:https://github.com/IvanYurchenko/MSMQSample

【讨论】:

  • 但这并不能解决我的主要问题:让我的软件实例与主实例通信。使用您的解决方案,我可以理解它是否已经开始,但我已经在我的代码中完成了。或者我不明白你的解决方案?
  • 添加了对答案的更新。如果还不清楚,请告诉我。
  • 我是 MSMQ 的初学者(一般来说可能是 C#)。我不需要“原始实例”和其他实例之间的持续通信。我只需要新实例向主实例发送一条消息,然后它们就关闭了。我正在阅读代码,但我不明白一些新概念:我在我的项目中包含了对 System.Messaging 的引用,但我不明白在哪里将“接收者消息逻辑”放在我的 Form.cs 文件中到 costant '扫描'传入的消息。变量 MessageAddress/QueueName 也可以是我想要的任何东西?我可以只传输一个字符串数组吗?
  • 我为您创建了一个使用 MSMQ 的示例项目。您需要通过添加引用菜单为您的项目添加对 System.Messaging 的引用。还要确保为您安装了 MSMQ 系统(程序和功能 - 打开或关闭 Windows 功能 - Microsoft 消息队列 (MSMQ) 服务器)
  • 谢谢 :) 我不认为 MSMQ 应该从程序和功能中启用。一旦启用,一切都开始像沙姆一样工作:)不幸的是,并非所有电脑都默认启用它,我尝试了一种更“简单”的方式。但是我肯定会在其他项目上使用,非常感谢:)
【解决方案3】:

我通过文本文件传递参数(我相信你可以从 VB.NET/伪代码翻译):

Private _uniqueEventName As String
Private _uniqueMutexName As String
Private _eventWaitHandle As EventWaitHandle
Private _mutex As Mutex

Private Sub ensureSingleInstance()
    _uniqueEventName = "{0ae64101-e630-4221-bf10-123fdddd5ab2}" + Assembly.GetEntryAssembly().GetName().Name
    _uniqueMutexName = "{03169a07-793b-48c6-8ceb-1232388cb69a}" + Assembly.GetEntryAssembly().GetName().Name

    Dim isOwned As Boolean
    _mutex = New Mutex(True, _uniqueMutexName, isOwned)
    _eventWaitHandle = New EventWaitHandle(False, EventResetMode.AutoReset, _uniqueEventName)

    GC.KeepAlive(_mutex)

    If isOwned Then
        Dim thread As Thread = New Thread(
                               Sub()
                                   While _eventWaitHandle.WaitOne()
                                       Application.Dispatcher.BeginInvoke(
                                           Sub()
                                               ' ****************************************************
                                               ' READ AND PROCESS THE ARGUMENTS FROM C:\MYAPPARGS.TXT
                                               ' ****************************************************

                                               If Not Application.MainWindow Is Nothing Then
                                                   _loggingDAL.Log("Activating main window")

                                                   If (Application.MainWindow.WindowState = WindowState.Minimized Or Application.MainWindow.Visibility = Visibility.Hidden) Then
                                                       Application.MainWindow.Show()
                                                       Application.MainWindow.WindowState = WindowState.Normal
                                                   End If

                                                   Application.MainWindow.Activate()
                                                   Dim topMost As Boolean = Application.MainWindow.Topmost
                                                   Application.MainWindow.Topmost = True
                                                   Application.MainWindow.Topmost = topMost
                                                   Application.MainWindow.Focus()
                                               End If
                                           End Sub)
                                   End While
                               End Sub)
        thread.IsBackground = True
        thread.Start()
    Else
        _loggingDAL.Log("There is already an instance running -> switching and quitting")

        ' ***************************************
        ' WRITE THE ARGUMENTS TO C:\MYAPPARGS.TXT
        ' ***************************************

        _eventWaitHandle.Set()
        Application.Shutdown()
    End If
End Sub

【讨论】:

    猜你喜欢
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    相关资源
    最近更新 更多