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

news2025/3/16 3:48:59

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!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1453169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++中的拷贝构造函数

一、拷贝构造函数的概念 拷贝构造函数用于创建一个与已有对象相同的对象&#xff0c;本质上也是构造函数的重载 拷贝构造函数只有一个类型为 const 类类型引用的形参&#xff0c;当我们要创建一个与已存在对象相同的对象时&#xff0c;由编译器自动调用拷贝构造函数。 clas…

Java Lambda表达式:简化编程,提高效率

Java Lambda表达式&#xff1a;简化编程&#xff0c;提高效率 1. 使用Lambda表达式进行集合遍历1.1 未使用Lambda表达式&#xff1a;1.2 使用Lambda表达式&#xff1a; 2. 使用Lambda表达式进行排序2.1 未使用Lambda表达式&#xff1a;2.2 使用Lambda表达式&#xff1a; 3. 使用…

智能摄像头prv文件恢复案例

家用智能摄像头一般采用的是mp4或者mov视频方案&#xff0c;常见的是mp4&#xff0c;对于部分有开发能力的厂商可能会采用自定义方案&#xff08;如360的bin文件&#xff09;,今天我们来看一个小厂的PRV自定义文件的恢复案例。 故障存储: 32G TF卡/fat32/ 簇&#xff08;块)大…

单片机移植Lua(STM32H743移植Lua-5.4.6)

文章目录 目的移植演示示例链接更多说明合理设置内存大小按需加载标准库实现系统和IO接口设置引用路径 总结 目的 通常单片机都是使用C/C来开发的&#xff0c;任何修改都需要重新编译固件然后下载运行。在一些需要灵活性更强的场合中可以内嵌Lua解释器实现动态更新应用程序的功…

算法——数论——同余

目录 同余 一、试题 算法训练 同余方程 同余 同余使人们能够用等式的形式简洁地描述整除关系同余&#xff1a;若 m&#xff08;正整数&#xff09;&#xff0c;a 和 b 是整数&#xff0c;a%mb%m&#xff0c;或(a-b)%m0&#xff0c;记为 a b(mod m)求解一元线性同余方程等价于…

Swift Combine 使用 ObservableObject 与 SwiftUI 模型作为发布源 从入门到精通二十

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

PLC_博图系列☞GRAPH

PLC_博图系列☞GRAPH 文章目录 PLC_博图系列☞GRAPH背景介绍GRAPH优势局限性 顺序控制系统的模块 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 GRAPH 背景介绍 这是一篇关于PLC编程的文章&#xff0c;特别是关于西门子的博图软件。我并不是专业的PLC编程人员&am…

【Redis实战】有MQ为啥不用?用Redis作消息队列!?Redis作消息队列使用方法及底层原理高级进阶

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Redis实战与进阶》 本专栏纯属为爱发电永久免费&#xff01;&a…

报文鉴别、实体鉴别

目录 鉴别 1 报文鉴别 1.1 用数字签名进行鉴别&#xff08;原理&#xff09; 可保证机密性的数字签名 1.2 密码散列函数 MD5 算法 MD5 算法计算步骤 安全散列算法 SHA-1 1.3 用报文鉴别码实现报文鉴别 用报文鉴别码 MAC 鉴别报文 使用已签名的报文鉴别码 MAC 对报…

工程师日常:海丰县附城镇鹿境元宵开灯活动

海丰县附城镇鹿境元宵开灯活动 &#xff08;蔡惠进搜集整理&#xff09; 鹿境乡春节正月初十大老热&#xff0c;全县家喻户晓。为纪念先祖功德&#xff0c;在本乡车地建立蔡氏“济阳堂”大祖祠&#xff0c;并定年初十为开灯日&#xff0c;大祖开灯代代相传。凡移居外乡裔孙、“…

机器学习8-决策树

决策树&#xff08;Decision Tree&#xff09;是一种强大且灵活的机器学习算法&#xff0c;可用于分类和回归问题。它通过从数据中学习一系列规则来建立模型&#xff0c;这些规则对输入数据进行递归的分割&#xff0c;直到达到某个终止条件。 决策树的构建过程&#xff1a; 1.…

java 宠物医院系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 宠物医院系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

463. Island Perimeter(岛屿的周长)

问题描述 给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向相连&#xff08;对角线方向不相连&#xff09;。整个网格被水完全包围&#xff0c;但其中恰好有…

希捷与索尼集团合作生产HAMR写头激光二极管

最近有报道指出&#xff0c;希捷&#xff08;Seagate&#xff09;在生产其采用热辅助磁记录&#xff08;HAMR&#xff09;技术的大容量硬盘时&#xff0c;并非所有组件都在内部制造。根据日经新闻的一份新报告&#xff0c;希捷已与索尼集团合作&#xff0c;由索尼为其HAMR写头生…

qml中边界图片BorderImage的使用

1、基本概念 2、案例 原图&#xff0c;120*120像素 &#xff08;1&#xff09;水平和垂直方向上都设置为拉伸模式 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12ApplicationWindow {id: windowvisible: truew…

【c语言】人生重开模拟器

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

【学习笔记】一文打通Docker!(项目部署orCTF)

Docker What is Docker? 利用Docker 可以快速安装应用&#xff0c;Docker会自动搜索并下载应用镜像(image)。镜像不仅包含应用本身&#xff0c;还包含应用运行所需要的环境&#xff0c;配置&#xff0c;系统函数库。 注意这个系统函数库&#xff0c;相当于在不同的操作版本…

反向迭代器------封装的力量

目录 一、list封装中模板参数Ref和Ptr的理解 二、反向迭代器的实现 一、list封装中模板参数Ref和Ptr的理解 对于反向迭代器&#xff0c;是我们在前面STL模拟实现中留下的一个问题。在之前的文章中&#xff0c;我们极大程度上的利用了模板&#xff0c;从而减少了许多的代码&…

【半监督图像分割 2023 】BHPC

【半监督图像分割 2023 】BHPC 论文题目&#xff1a;Semi-supervised medical image segmentation via hard positives oriented contrastive learning 中文题目&#xff1a;通过面向硬阳性的对比学习进行半监督医学图像分割 论文链接&#xff1a; 论文代码&#xff1a;https:/…

CCF编程能力等级认证GESP—C++6级—20231209

CCF编程能力等级认证GESP—C6级—20231209 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)闯关游戏工作沟通 答案及解析单选题判断题编程题1编程题2 单选题…