让应用程序只运行一个实例的实现方法
发布时间:2023-03-19 22:20:52
在我们的程序当中如果要实现类似《360软件管家》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实例激活,同时退出第二个应用程序实例。
对于第一个问题,我们可以通过设置命名互斥对象或命名信标对象,在程序启动的时候检测互斥对象或信标对象,如互斥对象或信标对象已存在,则可以判断此程序已有一个实例正在运行。
第二个问题是如何找到已经运行的应用程序实例,如果我们能够找到已运行实例主窗口的指针,即可调用SetForegroundWindow来激活该实例。我们可以通过两种形式找到已运行实例的主窗口,一种形式是通过调用FindWindowEx去查找正在运行的窗口的句柄,这种方式用得比较多一些,而本文通过另一种形式去查找正在运行的窗口的句柄。通过调用SetProp给应用程序主窗口设置一个标记,用GetDesktopWindow 可以获取Windows环境下的桌面窗口的句柄,所有应用程序的主窗口都可以看成该窗口的子窗口,接着我们就可以用GetWindow函数来获得这些窗口的句柄。然后再用Win32 SDK函数GetProp查找每一个应用程序的主窗口是否包含有我们设置的标记,这样就可以找到我们要找的第一个实例主窗口。
下面演示代码是以一个单文档应用程序为例,工程名字是Mutex。
1、在应用程序类InitInstance()函数中判断是否已有一个应用程序实例正在运行。
BOOL CMutexApp::InitInstance()
{
//创建命名信标对象。
HANDLE hSem=CreateSemaphore(NULL,1,1,"维新");
if(hSem) //信标对象创建成功。
{
//信标对象已经存在,则程序已有一个实例在运行。
if(ERROR_ALREADY_EXISTS==GetLastError())
{
CloseHandle(hSem); //关闭信号量句柄。
//获取桌面窗口的一个子窗口。
HWND hWndPrev=::GetWindow(::GetDesktopWindow(),GW_CHILD);
while(::IsWindow(hWndPrev))
{
//判断窗口是否有我们预先设置的标记,如有,则是我们寻找的窗口,并将它激活。
if(::GetProp(hWndPrev,"维新"))
{
//如果主窗口已最小化,则恢复其大小。
if (::IsIconic(hWndPrev))
::ShowWindow(hWndPrev,SW_RESTORE);
//将应用程序的主窗口激活。
::SetForegroundWindow(hWndPrev);
return FALSE; //退出实例。
}
//继续寻找下一个窗口。
hWndPrev = ::GetWindow(hWndPrev,GW_HWNDNEXT);
}
AfxMessageBox("已有一个实例在运行,但找不到它的主窗口!");
}
}
else
{
AfxMessageBox("创建信标对象失败,程序退出!");
return FALSE;
}
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
// Change the registry key under which our settings are stored.
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization.
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(); // Load standard INI file options (including MRU)
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views.
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMutexDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CMutexView));
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it.
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
2、在框架类的OnCreate()函数中设置查找标记。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar/n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar/n");
return -1; // fail to create
}
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
//设置查找标记。
::SetProp(m_hWnd,"维新",(HANDLE)1);
return 0;
}
3、在程序退出是删除设置的标记,在框架类中响应WM_DESTROY消息,进行处理。
void CMainFrame::OnDestroy()
{
CFrameWnd::OnDestroy();
// TODO: Add your message handler code here
//删除所设置的标记。
::RemoveProp(m_hWnd,"维新");
}
至此,使应用程序只运行一个实例的功能就完成了。


猜你喜欢
- 本文实例讲述了C#使用linq对数组进行筛选排序的方法。分享给大家供大家参考。具体如下:using System;using System.
- 一、在java中遍历一个文件夹里边的所有文件,可以有两种方式:1.递归遍历,通常也是开发者第一时间能想到的方法,递归遍历的优点是:实现起来相
- 利用链如下其中LazyMap.get()->ChainedTransformer.transform()-InvokerTransfo
- 背景在开发需求当中,当有总收益、总用户数等数字要显示时,为了更好的给用户提供展示效果,往往会想加入炫酷的数字滚动动画,使呆板平静的数字变得灵
- C语言字符串大小比较#include <stdio.h>#include <string.h>int fun(cha
- RecyclerView目前来说对大家可能不陌生了。由于在公司的项目中,我们一直用的listview和gridview。某天产品设计仿照美团
- 日常使用中spring的 @Cacheable 大家一定不陌生,基于aop机制的缓存实现,并且可以选择cacheManager具体提供缓存的
- 多继承指一个子类能同时继承于多个父类,从而同时拥有多个父类的特征,但缺点是显著的。1.若子类继承的父类中拥有相同的成员变量,子类在引用该变量
- 本文实例讲述了WPF弹出自定义窗口的方法。分享给大家供大家参考,具体如下:测试环境:[1]VS2010SP1[2]WPF(.NET Fram
- 刚学完JDBC不久,做了一个简单的学生管理系统,可能还有不完善的地方,望各路大神见谅。废话不多说,我先贴个图让大家让大家瞅瞅,觉得是你想要的
- 本文实例为大家分享了springboot实现基于aop的切面日志的具体代码,供大家参考,具体内容如下通过aop的切面方式实现日志通切面拦截所
- 此方案适用于解决springboot项目运行时动态添加数据源,非静态切换多数据源!!!一、多数据源应用场景:1.配置文件配置多数据源,如默认
- 前言人类建造迷宫已有5000年的历史。在世界的不同文化发展时期,这些奇特的建筑物始终吸引人们沿着弯弯曲曲、困难重重的小路吃力地行走,寻找真相
- 这篇文章主要介绍了通过实例了解spring使用构造器注入的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
- 这篇文章主要介绍了Java如何基于ProcessBuilder类调用外部程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 现在网上很多应用都是用二维码来分享网址或者其它的信息。尤其在移动领域,二维码更是有很大的应用场景。因为项目的需要,需要在网站中增加一个生成二
- 场景MyBaitsPlus3.x与2.x是不一样的。这里使用3.0.1版本。官方文档https://mp.baomidou.com/guid
- 前章知识: 点此跳转HTML简介:超文本是一种组织信息的方式,它通过超级链接方法将文本中的文字、图表与其他信息媒体相关联。这些相互关联的信息
- 介绍本文实现的功能有:1、播放音乐2、自定义流星数量、飞行速度、光晕大小、流星大小3、自定义表白话语 运用到的知识点有:GUI:j
- 前言《英文猜词游戏》代码行数没有超过200行,是之前为了背英语单词,特意研发的小游戏。主要设计1.事先准备单词文本。2.为了让玩家能与程序互