Windows程序互斥锁 - 一个程序同时仅允许运行一个实例
前言
鉴于应用逻辑需要,有些Windows应用同时只能运行一个实例。例如:一个电脑只能同时运行一个微信(手速快了当我没说,不信你去试试)。
怎么实现呢?大致有两种办法:系统自带的互斥锁 或 (手动实现或系统自带)文件锁。
想要使用系统提供的锁(不论是程序锁还是文件锁),不同系统调用的API不同(需要适配系统);想要手写文件锁,则十分麻烦(甚至很难做到完美)。
接下来以Windows系统为例,创建一个“同时只能运行一个实例”的程序。若第二次打开这个程序,则向正在运行的程序发送消息并退出。
How
推荐的方法:系统自带的锁功能
Windows中程序锁函数为CreateMutex
函数。例如:
1
| HANDLE mutexHandle = CreateMutex(NULL, TRUE, MUTEX_NAME);
|
所有程序中,系统只允许同时存在一个名为MUTEX_NAME
的锁。程序借此可判断自己是否为第一个实例,若是(得到了锁)则继续运行,否则找到正在运行的程序并向起发送消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #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; }
|
编译:
1
| gcc tryLock.c -o tryLock
|
运行:双击或使用命令行(可传参)
首次运行可以看到一个简单的窗口:
再次运行时,可以看到第二个程序并没有启动,反而是第一个程序接受到了第二个程序传来的参数(消息)并弹出了弹窗:
这样,同时只运行一个实例的功能就实现了。结合上URL Scheme(详情可见这篇博客),便可以实现通过浏览器控制本地的程序了。
向下载下来玩玩也可以点击这里下载。
不传参仅限制单个实例同时运行的简单版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <windows.h> #include <bits/stdc++.h> using namespace std; int main(int argc, char* argv[]) { HANDLE m_hMutex = CreateMutex(NULL, TRUE, "my_app_name"); DWORD dwRet = GetLastError(); if (m_hMutex) { if (ERROR_ALREADY_EXISTS == dwRet) { printf("Another Instance Running!\n"); CloseHandle(m_hMutex); return 0; } } else { printf("Creating Lock Failed!\n"); CloseHandle(m_hMutex); return 0; } system("pause"); return 0; }
|
通过文件(锁)实现 —— 完美的程序中不推荐
这种方法的思路是:程序启动时创建一个文件,程序结束时删除这个文件。若程序启动时发现这个文件已经存在,则认为有实例正在运行,自己退出。
若是使用系统提供的文件锁,和方法一中直接使用互斥锁没有过多区别,不如直接使用系统提供的互斥锁;
若是手动创建文件,则很难实现原子操作。(一个程序以写文件的方式打开一个文件,这个文件没有正在进行写操作时,另一个文件也是可以写的)。并且意外产生的程序退出可能导致文件没被删除(用户强制关闭、系统断电关机等)。
为了防止程序意外退出造成的文件未被删除,可以:
程序启动时写文件并在文件中写入自己的pid,第二个程序读到这个文件时监测这个pid是否还在运行,若没在运行则认为是意外退出并写入自己的pid,否则向这个正在运行的实例传参。
但是判断锁文件是否存在
、打开写文件的句柄
和实际写入内容
不是原子的,快速启动两个程序可能会导致:
两个程序判断锁文件是否存在时,还未存在;
两个程序依次写入内容;
两个程序同时运行。
End
原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/136139822
The End, thanks!