C++ MFC 只启动一个程序实例 唤醒之前的实例(完整源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客

很多时候我们希望只允许启动一个程序实例,如果再次运行,就唤醒之前的实例。

目录

[1 概述](#1 概述)

[2 相关技术介绍](#2 相关技术介绍)

[2.1 互斥对象](#2.1 互斥对象)

[2.2 查找窗口](#2.2 查找窗口)

[2.3 唤醒窗口](#2.3 唤醒窗口)


1 概述

技术上并不难,涉及到以下几个技术细节:

  1. 使用互斥对象,确保检查程序是否已经启动时没有并发错误。
  2. 查找之前已经运行的程序,可以通过窗口名称查找,也可以通过共享文件交互
  3. 控制之前已经运行的程序,可以通过发送窗口消息,也可以通过共享文件交互

窗口程序通过窗口机制,查找和发消息都是操作系统的标准功能,如果 使用了配置文件,通过文件来交互也是可以的,只不过要定时查询,实时性没有发消息好。

2 相关技术介绍

下面介绍相关代码:

2.1 互斥对象

cpp 复制代码
	HANDLE hOneInstance;
	hOneInstance = ::CreateMutex(NULL, FALSE, L"自定义的名字");
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
        //在这里处理已经存在的情形
        //查找窗口、唤醒窗口,然后退出
    }
    //正常启动

互斥对象是系统对象,本例中我们并不需要更多功能,只要检测创建是否成功即可。因为,互斥对象所有句柄被释放后就会被销毁,程序不退出,下一个程序创建会失败,程序退出后下一个就可以成功创建。对于我们的目的而言锁定互斥对象是不必要的。

CreateMutes原型如下:

cpp 复制代码
HANDLE CreateMutexA(
  [in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,
  [in]           BOOL                  bInitialOwner,
  [in, optional] LPCSTR                lpName
);

第一个参数涉及到权限,可以设置为NULL,第二个参数指示创建完成是不是要立即锁定,第三个参数是名字,可以不设置,设置的话不能和已经存在的内核对象同名,所有种类的内核对象共享同一个名字空间。

2.2 查找窗口

cpp 复制代码
		HWND hWnd = FindWindow(NULL, TEXT("窗口标题"));
		if (IsWindow(hWnd))
		{
			//遍历处理所有同名窗口!!!
			while(IsWindow(hWnd))
			{
				//在这里处理窗口

                //查找下一个
				hWnd = FindWindowEx(NULL,hWnd,NULL,TEXT("窗口标题"));
			}
		}

查找窗口的关键点是窗口标题必须正确,所以最好把窗口标题单独定义出来,并在窗口初始化里面专门设定一次,而且不要在程序里面修改窗口标题。这其实没法从技术上预防,程序的任何地方都可以随意修改窗口标题,如果没法掌控全部源代码,那就只好走文件控制的方式了。

文件控制就是把窗口句柄写入文件,每次启动去文件里面读,读不到就是还没有程序实例在运行。

这里写成循环是因为理论上可能有相同标题的窗口,需要这两个函数配合才能遍历所有同名窗口。

cpp 复制代码
HWND FindWindowA(
  [in, optional] LPCSTR lpClassName,
  [in, optional] LPCSTR lpWindowName
);

FindWindow用窗口类名和/或窗口标题查找窗口,大部情况下我们只用窗口标题来查找。

cpp 复制代码
HWND FindWindowExA(
  [in, optional] HWND   hWndParent,
  [in, optional] HWND   hWndChildAfter,
  [in, optional] LPCSTR lpszClass,
  [in, optional] LPCSTR lpszWindow
);

FindWindowEx查找子窗口。

2.3 唤醒窗口

cpp 复制代码
				// 显示
				if(!::ShowWindow(hWnd, SW_NORMAL))
				{
                    //出错
				} 
				// 激活
				if(!::SetForegroundWindow(hWnd))
				{
                    //出错
				}

这两句代码嵌入到之前查找窗口的循环里面即可将之前的实例的窗口显示到前端。

cpp 复制代码
BOOL SetForegroundWindow(
  [in] HWND hWnd
);

这个函数的效果是把窗口放到前端并激活,输入焦点将设置到这个窗口中。

如果想让窗口的标题栏闪烁,用这个API即可:

cpp 复制代码
BOOL FlashWindow(
  [in] HWND hWnd,
  [in] BOOL bInvert
);

第二个参数为FALSE将保持之前的活动或非活动状态,为TRUE则切换状态。这个调用只闪烁一次,想多闪烁几次要用定时器来控制(不要用Sleep,会导致界面僵死)。

(这里是文档结束)

相关推荐
·薯条大王2 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
hansang_IR7 小时前
【题解】LC:倍增 / 区间并查集(Range Parallel Unionfind)
c++·算法·并查集
东东最爱敲键盘7 小时前
day7.c++继承
c++
沫璃染墨8 小时前
《从零入门Linux系统篇(十五):系统工具篇·六——GDB调试器详解:从程序执行控制到高级调试技巧》
linux·运维·服务器·c语言·c++
吃着火锅x唱着歌8 小时前
Effective C++ 学习笔记 条款40 明智而审慎地使用多重继承
c++·笔记·学习
醉城夜风~9 小时前
(超详细)C++ STL list容器详解:从使用方法到双向链表底层原理全面解析
c++·windows
烟花落o9 小时前
C++ 从 C 到进阶 ②:引用、inline 、nullptr
c++·引用·inline·nullptr·c++入门
liulilittle9 小时前
MOE路由:路由(logits: top-k/8)
c++·人工智能·算法·机器学习·llm
旖旎夜光9 小时前
LeetCode 11:盛最多水的容器(双指针问题) —— 题解
数据结构·c++·算法·leetcode·双指针
我不会插花弄玉12 小时前
15.map,set下:AVL树,红黑树和map,set的封装【由浅入深-C++】
c++·stl