Skip to content

添加单实例管理器 #121

Description

@Soar360

单实例互斥 + 通过命名管道激活已有窗口。

  • 补充文档
  • 修改全局锁的名称
  • 补充单元测试?(可选)
using System.IO.Pipes;
using System.Security.Cryptography;
using System.Text;

namespace Popcorn.Toolkit.Libs;

public static class SingleInstanceManager
{
    private const string Command_ActivateWindow = "ActivateWindow";
    private static Mutex? _processLock;
    private static bool _hasLock;

    public static event EventHandler? ActivateWindowRequested;

    /// <summary>
    /// 获取进程锁
    /// </summary>
    public static void EnsureSingleInstance()
    {
        // 全局锁,锁名称可以自定义。
        string key = $"Global\\Popcorn.Toolkit[{GetUid()}]";
        _processLock = new Mutex(false, key, out _hasLock);
        if (_hasLock)
        {
            Task.Factory.StartNew(() => ServerThread(key), TaskCreationOptions.LongRunning);
        }
        else
        {
            RequestActivateWindow(key);
            Environment.Exit(0);
        }
    }


    private static string GetUid()
    {
        var bytes = Encoding.UTF8.GetBytes(Environment.ProcessPath ?? AppContext.BaseDirectory);
        using (var md5 = MD5.Create())
        {
            bytes = md5.ComputeHash(bytes);
        }

        return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
    }


    /// <summary>
    /// 释放当前进程的锁
    /// </summary>
    /// <remarks>小心使用</remarks>
    public static void ReleaseLock()
    {
        if (_processLock != null && _hasLock)
        {
            _processLock.Dispose();
            _hasLock = false;
        }
    }

    private static async Task ServerThread(string name)
    {
        while (true)
        {
            using (var server = new NamedPipeServerStream(name))
            {
                await server.WaitForConnectionAsync();
                using (var reader = new StreamReader(server))
                {
                    var command = await reader.ReadLineAsync();
                    if (command == Command_ActivateWindow)
                    {
                        ActivateWindowRequested?.Invoke(null, EventArgs.Empty);
                    }
                }
            }
        }
    }

    private static void RequestActivateWindow(string key)
    {
        using (var client = new NamedPipeClientStream(key))
        {
            client.Connect(1000);
            using (var writer = new StreamWriter(client))
            {
                writer.WriteLine(Command_ActivateWindow);
                writer.Flush();
            }
        }
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions