将参数传递给同一应用程序的另一个实例

7

我遇到了一个非常严重的问题,与我的小应用程序相关; 基本上很容易理解:

我的软件开启后,可以运行。

我想重点讲述打开另一个实例(我指重新打开.exe文件),检查它是否已经打开。 如果没有打开,只需启动应用程序,但如果已经运行(即第二个或更多实例),则将输入参数(args字符串数组)“简单地”传递给第一个实例,该实例将适当地处理它。

以下是我的program.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.
        }
    }
}

这个程序(使用互斥锁)只能识别已经打开的实例,但是它无法与主实例通信。

我尝试了很多东西但是无法让它工作。

我尝试了这个方法,但是我真的不明白(浪费了很多时间后)如何将我的“第一次”代码和“已运行”的代码放在一起。

也尝试了MSMQ但是无法让它工作。

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


你的第二个实例正在不同的进程中运行。你需要实现某种通信机制 - 无论是 MSMQ,还是通过套接字进行序列化等。将事物设为“静态”并不能解决问题。你运行多个相同可执行文件的事实并不比在不同应用程序之间进行通信更简单。 - 3Dave
今天我很遗憾地发现,即使是同一个可执行程序,与另一个程序进行通信也并不容易。我明白不同的副本必须以不同的方式进行管理,而不能将所有内容都静态化;我编写的静态方法位于另一个.cs文件中,并在我的程序逻辑中使用:这对问题来说并不是很重要 :) - Alex DG
3个回答

1

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

这里有一个示例: 向Windows进程发送消息(而不是其主窗口)

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

有许多形式的进程间通信。您使用哪种取决于您的需求,例如Windows消息无法携带大量信息,而管道可以以非常高的速度流数据。

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


我尝试了chitza的最终解决方案,它非常有效,除了一个问题:我们使用PostMessage将“输入”发送到主实例,然后我可以管理启动函数或执行操作。问题在于PostMessage仅允许Int参数;我想发送某种结构化数据(例如数组或对象),但我不知道是否可能。你怎么看?有什么我可以尝试的吗? - Alex DG
如果需要发送更多数据,则需要一种更高级的进程间通信方式。MSMQ是其中一种方法,但您似乎遇到了麻烦。一个简单的替代方案可能是https://msdn.microsoft.com/en-us/library/system.runtime.remoting.channels.ipc.ipcchannel.aspx。这使您可以在第一实例中创建一个可由第二实例调用的对象。您可以在该对象上创建一个方法并将您的数据作为参数传递。 - David J
谢谢!那解决了我的问题!我还有一些其他小问题(我不能从第二个实例调用所有东西:有些东西不太好用,至少在我的情况下,像FolderBrowserDialog和在某些深层方法调用中一些方法会抛出异常(Visual Studio Debugger也告诉我不能从外部调用),但是这个解决了我的问题。所以我结合了IpcChannel、InteropMessage(Chitza解决方案),现在我可以做任何我想做的事情!你让我的一天,也让我的软件 :) - Alex DG
太棒了。我很高兴它对你有所帮助。我已经在我的回答中添加了IPC通道。如果它解决了你的问题,请接受它。 - David J
抱歉回复晚了,但我有一个问题……是我自己创建的。在主实例中,我在WndProc函数中监听消息。在第二个实例中,我发送消息,触发主实例中的WndProc方法。一切都正常工作,直到我安装了这个链接。为了安装它,我不得不将主窗体方法声明从'public partial class Form1 : Form'改为'public partial class Form1 : MetroFramework.Forms.MetroForm'。做了这个更改后,IPC在第一个实例中就无法再捕捉到消息了。你能给我一些建议吗? - Alex DG
抱歉,我不熟悉那个库。它不能正常工作可能有很多原因。我认为这应该是一个单独的问题。 - David J

-1

我通过一个文本文件传递参数(我相信你可以从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

-1
使用互斥锁
    // 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://dev59.com/PWox5IYBdhLWcg3wmFaV#35018042

如果您只需要共享一些布尔参数来确定某些事物是否打开或关闭,您也可以为每个参数使用互斥锁。

如果您需要在启动 .exe 时传递参数,请查看此问题:如何将参数传递给 exe?

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

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


1
但这并不能解决我的主要问题:使我的软件实例与主要软件通信。通过您的解决方案,我可以了解它是否已经启动,但我已经在我的代码中完成了这个步骤。或者我没有理解您的解决方案? - Alex DG
已经更新了一个答案。如果还不清楚,请告诉我。 - Ivan Yurchenko
我对MSMQ(也许是C#总体)非常初学。我不需要“原始实例”和其他实例之间的持续通信。我只需要新实例向主实例发送一条消息,然后它们就会关闭。我正在阅读代码,但我不理解一些新概念:我已经在我的项目中包含了对System.Messaging的引用,但我不知道在Form.cs文件中放置“接收器消息逻辑”的位置以便不断“扫描”传入的消息。另外,MessageAddress/QueueName变量可以是任何我想要的吗?我能传输只有一个字符串数组吗? - Alex DG
我为您创建了一个使用MSMQ的示例项目。您需要通过“添加引用”菜单为您的项目添加对System.Messaging的引用。此外,请确保MSMQ系统已为您安装(程序和功能-打开或关闭Windows功能-Microsoft Message Queue(MSMQ)服务器)。 - Ivan Yurchenko
这里有一个答案:https://dev59.com/CHRB5IYBdhLWcg3wQFLu。目前,我只在用户需要时通过编程方式启用MSMQ,因此我采用了不同的方法。看起来人们建议将其与应用程序一起安装,以便您可以尝试它(也许有一天我也会使其工作)。 - Ivan Yurchenko
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接