Windows程序互斥锁 - 一个程序同时仅允许运行一个实例

Windows程序互斥锁 - 一个程序同时仅允许运行一个实例

前言

鉴于应用逻辑需要,有些Windows应用同时只能运行一个实例。例如:一个电脑只能同时运行一个微信(手速快了当我没说,不信你去试试)。

怎么实现呢?大致有两种办法:系统自带的互斥锁 或 (手动实现或系统自带)文件锁。

想要使用系统提供的锁(不论是程序锁还是文件锁),不同系统调用的API不同(需要适配系统);想要手写文件锁,则十分麻烦(甚至很难做到完美)。

接下来以Windows系统为例,创建一个“同时只能运行一个实例”的程序。若第二次打开这个程序,则向正在运行的程序发送消息并退出

How

推荐的方法:系统自带的锁功能

Windows中程序锁函数为CreateMutex函数。例如:

HANDLE mutexHandle = CreateMutex(NULL, TRUE, MUTEX_NAME);

所有程序中,系统只允许同时存在一个名为MUTEX_NAME的锁。程序借此可判断自己是否为第一个实例,若是(得到了锁)则继续运行,否则找到正在运行的程序并向起发送消息。

#include <windows.h>
#include <stdio.h>

#define MUTEX_NAME "Local\\MySingleInstanceMutex"
#define WINDOW_CLASS_NAME "MySingleInstanceAppWindowClass"
#define WINDOW_TITLE "MySingleInstanceApp"

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_COPYDATA: {
        PCOPYDATASTRUCT pCDS = (PCOPYDATASTRUCT)lParam;
        if (pCDS->cbData > 0) {
            char* messageText = (char*)pCDS->lpData;
            MessageBox(hWnd, messageText, "Message Received", MB_OK);
        }
        return 0;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    HANDLE mutexHandle = CreateMutex(NULL, TRUE, MUTEX_NAME);
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        // 实例已运行,找到窗口并发送消息
        HWND hWnd = FindWindow(WINDOW_CLASS_NAME, WINDOW_TITLE);
        if (hWnd) {
            COPYDATASTRUCT cds;
            cds.dwData = 1; // 可以使用不同的值来表示不同类型的消息
            cds.cbData = strlen(lpCmdLine) + 1;
            cds.lpData = lpCmdLine;
            SendMessage(hWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
        }
        return 0; // 退出程序
    }

    // 注册窗口类
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = WINDOW_CLASS_NAME;

    if (!RegisterClass(&wc)) return 0;

    // 创建窗口
    HWND hWnd = CreateWindow(WINDOW_CLASS_NAME, WINDOW_TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    if (!hWnd) return 0;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    ReleaseMutex(mutexHandle);
    CloseHandle(mutexHandle);
    return (int) msg.wParam;
}

编译:

gcc tryLock.c -o tryLock

运行:双击或使用命令行(可传参)

./tryLock.exe -t stop

首次运行可以看到一个简单的窗口:

首次运行

再次运行时,可以看到第二个程序并没有启动,反而是第一个程序接受到了第二个程序传来的参数(消息)并弹出了弹窗:

接收到了消息

这样,同时只运行一个实例的功能就实现了。结合上URL Scheme(详情可见这篇博客),便可以实现通过浏览器控制本地的程序了。

向下载下来玩玩也可以点击这里下载。

通过文件(锁)实现 —— 完美的程序中不推荐

这种方法的思路是:程序启动时创建一个文件,程序结束时删除这个文件。若程序启动时发现这个文件已经存在,则认为有实例正在运行,自己退出。

若是使用系统提供的文件锁,和方法一中直接使用互斥锁没有过多区别,不如直接使用系统提供的互斥锁;

若是手动创建文件,则很难实现原子操作。(一个程序以写文件的方式打开一个文件,这个文件没有正在进行写操作时,另一个文件也是可以写的)。并且意外产生的程序退出可能导致文件没被删除(用户强制关闭、系统断电关机等)。

为了防止程序意外退出造成的文件未被删除,可以:

程序启动时写文件并在文件中写入自己的pid,第二个程序读到这个文件时监测这个pid是否还在运行,若没在运行则认为是意外退出并写入自己的pid,否则向这个正在运行的实例传参。

但是判断锁文件是否存在打开写文件的句柄实际写入内容不是原子的,快速启动两个程序可能会导致:

两个程序判断锁文件是否存在时,还未存在;

两个程序依次写入内容;

两个程序同时运行。

End

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/136139822

The End, thanks!
  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值