C# 互斥:如何停止我的控制台应用多次运行

C# Mutex how to stop my console app running multiple times

提问人:Nick 提问时间:2/5/2022 更新时间:2/5/2022 访问量:390

问:

我有一个控制台程序,不是那么复杂,但同时又不是一个hello world程序。我只有两个项目,第一个项目有几个类。它不是多线程的,而是关于调用 restful API 等的。 事情是这样的:我正在尝试让我的应用程序检查它是否运行了两次(这非常重要)。我以为这在某种程度上是可行的,但结果却非常复杂。 不幸的是,我发现的唯一例子并没有说明如何使用这项技术。在代码下方,这对我来说非常复杂,我不知道如何将其插入我的 Main。因此,问题来了。 在代码下面(不是我的,我在这里找到的:https://www.codeproject.com/Articles/3014/Single-Process-Instance-Object)

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;

namespace SpecialServices
{
    //SingleProgamInstance uses a mutex synchronization 
    //object to ensure that only one copy of process is running 
    //at a particular time.  It also allows for UI identification
    // of the intial process by bringing that window to the foreground.
public class SingleProgramInstance : IDisposable
{

    //Win32 API calls necesary to raise an unowned processs main window
    [DllImport("user32.dll")] 
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")] 
    private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);
    [DllImport("user32.dll")] 
    private static extern bool IsIconic(IntPtr hWnd);

    private const int SW_RESTORE = 9;

    //private members 
    private Mutex _processSync;
    private bool _owned = false;


    public SingleProgramInstance()
    {   
        //Initialize a named mutex and attempt to
        // get ownership immediately 
        _processSync = new Mutex(
            true, // desire intial ownership
            Assembly.GetExecutingAssembly().GetName().Name,
            out _owned);
    }

    public SingleProgramInstance(string identifier)
    {   
        //Initialize a named mutex and attempt to
        // get ownership immediately.
        //Use an addtional identifier to lower
        // our chances of another process creating
        // a mutex with the same name.
        _processSync = new Mutex(
            true, // desire intial ownership
            Assembly.GetExecutingAssembly().GetName().Name + identifier,
            out _owned);
    }

    ~SingleProgramInstance()
    {
        //Release mutex (if necessary) 
        //This should have been accomplished using Dispose() 
        Release();
    }

    public bool IsSingleInstance
    {
        //If we don't own the mutex than
        // we are not the first instance.
        get {return _owned;}
    }

    public void RaiseOtherProcess()
    {
        Process proc = Process.GetCurrentProcess();
        // Using Process.ProcessName does not function properly when
        // the actual name exceeds 15 characters. Using the assembly 
        // name takes care of this quirk and is more accruate than 
        // other work arounds.
        string assemblyName = 
            Assembly.GetExecutingAssembly().GetName().Name;
        foreach (Process otherProc in 
            Process.GetProcessesByName(assemblyName))
        {
            //ignore "this" process
            if (proc.Id != otherProc.Id)
            {
                // Found a "same named process".
                // Assume it is the one we want brought to the foreground.
                // Use the Win32 API to bring it to the foreground.
                IntPtr hWnd = otherProc.MainWindowHandle;
                if (IsIconic(hWnd))
                {
                    ShowWindowAsync(hWnd,SW_RESTORE);
                }
                SetForegroundWindow(hWnd);
                break;
            }
        }
    }

    private void Release()
    {
        if (_owned)
        {
            //If we own the mutex than release it so that
            // other "same" processes can now start.
            _processSync.ReleaseMutex();
            _owned = false;
        }
    }

#region Implementation of IDisposable
    public void Dispose()
    {
        //release mutex (if necessary) and notify 
        // the garbage collector to ignore the destructor
        Release();
        GC.SuppressFinalize(this);
    }
#endregion
}
}

使用上述内容的任何帮助将不胜感激;非常感谢。

C# 控制台-应用程序 互斥单 实例

评论

0赞 Nick 2/5/2022
我希望我不必把我的公开课程序的数千行放在某个班级或其他东西中;但话又说回来,我不知道......我希望能有一些简单的方法来声明这个程序运行一次 - 所以如果有人试图运行第二个实例,会自动返回当前正在运行的控制台窗口。先谢谢你。
0赞 pm100 2/5/2022
您想确保程序的两个实例没有同时运行吗?那么第二个应该终止吗?或者你想让第二个等待第一个完成?
0赞 Nick 2/5/2022
@pm100 我想确保我的程序运行一次。这意味着,如果它正在运行并且您尝试再次运行它,则它不应启动第二个实例。

答:

0赞 Jonathan Barraone 2/5/2022 #1

试试这个:

private void CloseDuplicateApplications()
    {
        string ProgramTitle = System.Diagnostics.Process.GetCurrentProcess().MainWindowTitle;
        System.Diagnostics.Process[] Processes = System.Diagnostics.Process.GetProcesses();

        for (int i = 0; i < Processes.Length; i++)
        {
            if (Processes[i].MainWindowTitle == ProgramTitle)
            {
                Processes[i].CloseMainWindow();
            }
        }
    }

目前,该过程只是关闭任何重复的程序,但是如果您需要杀死重复的程序,请替换以下行:

Processes[i].CloseMainWindow();

跟:

Processes[i].Kill();

评论

0赞 pm100 2/5/2022
这里的问题是,如果两者同时开始,比赛,
0赞 Nick 2/5/2022
@jonathan-barraone 感谢您的贡献。我会检查的
1赞 pm100 2/5/2022 #2

如何使用示例代码。

获取示例代码(其一个文件)并将其添加到项目中(作为单独的 .cs 文件)

现在,在启动程序时,主添加

   using(var spi = new SpecialServices.SingleProgramInstance("x5k6yz"))
    {
        if (!spi.IsSingleInstance){
            Console.WriteLine("another copy is running");
            return;
        }
    }

警告,我没有尝试过示例中的代码,我认为它有效。

编辑。 好的测试,它工作正常

评论

0赞 Nick 2/5/2022
谢谢;正如我所看到的那样,我立即将其放在静态 void Main(string[] args) 下,我的下一行代码(我的前第一行代码,在添加之前)是 DateTime utcDateTime = DateTime.UtcNow;我建造,没有错误,一切都很好。我跑了,似乎它立即关闭了 - 顺便说一句,我的行 DateTime 等有绿色卷曲下划线,警告“检测到无法访问的代码”。我做错了什么??
0赞 Nick 2/5/2022
做!(我不得不用我的例外替换您的退货)。完美工作!谢谢!
0赞 pm100 2/5/2022
@Nick从 CodeProject 中读取代码并锻炼它的作用。你会发现它很有趣,而且你不想依赖你不了解的魔法
0赞 Nick 2/5/2022
@pim100 你是绝对正确的!实际上,我在互斥锁上下载了几个项目,我开始理解了。您的消息来源完美无缺!非常感谢!