方法1.VC 实现程序只运行一个实例,并激活已运行的程序
http://blog.sina.com.cn/s/blog_4b44e1c00100bh69.html
进程的互斥运行:CreateMutex函数实现只运行一个程序实例
正常情况下,一个进程的运行一般是不会影响到其他正在运行的进程的。但是对于某些有特殊要求的如以独占方式使用串行口等硬件设备的程序就要求在其进程运行期间不允许其他试图使用此端口设备的程序运行的,而且此类程序通常也不允许运行同一个程序的多个实例。这就引出了进程互斥的问题。
实现进程互斥的核心思想比较简单:进程在启动时首先检查当前系统是否已经存在有此进程的实例,如果没有,进程将成功创建并设置标识实例已经存在的标记。此后再创建进程时将会通过该标记而知晓其实例已经存在,从而保证进程在系统中只能存在一个实例。具体可以采取内存映射文件、有名事件量、有名互斥量以及全局共享变量等多种方法来实现。下面就分别对其中具有代表性的有名互斥量和全局共享变量这两种方法进行介绍:
// 创建互斥量
HANDLE m_hMutex = CreateMutex(NULL, FALSE, "Sample07");
// 检查错误代码
// 如果程序已经存在并且正在运行
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
// 如果已有互斥量存在则释放句柄并复位互斥量
CloseHandle(m_hMutex);
m_hMutex = NULL;
// 程序退出
return FALSE;
}
上面这段代码演示了有名互斥量在进程互斥中的用法。代码的核心是CreateMutex()对有名互斥量的创建。CreateMutex()函数可用来创建一个有名或无名的互斥量对象,其函数原型为:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
BOOL bInitialOwner, // 初始化互斥对象的所有者
LPCTSTR lpName // 指向互斥对象名的指针
);
如果函数成功执行,将返回一个互斥量对象的句柄。如果在CreateMutex()执行前已经存在有相同名字的互斥量,函数将返回这个已经存在互斥量的句柄,并且可以通过GetLastError()得到错误代码ERROR_ALREADY_EXIST。可见,通过对错误代码ERROR_ALREADY_EXIST的检测可以实现CreateMutex()对进程的互斥。
建立互斥体,用来同步。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
参数
lpMutexAttributes
指向一个SECURITY_ATTRIBUTES结构的指针,这个结构决定互斥体句柄是否被子进程继承。
bInitialOwner
布尔类型,决定互斥体的创建者是否为拥有者
lpName
指向互斥体名字字符串的指针。互斥体可以有名字。
互斥体的好处是可以在进程间共享
将 CreateMutex 代码加到 ****App::InitInstance() 函数中即可实现只运行一个实例的效果。
以上文章转载自:我的程序员生涯 http://steveq.blog.sohu.com/71112121.html
通过上面文章我们知道:使用CreateMutex可以防止一个实例多次运行。
下面介绍如何实现当再次运行程序时如何激活已经运行的程序,比如此程序只是已被最小化。
方法如下:
BOOL C**App::InitInstance()
{
//创建进程互斥体Sample07
m_hMutex = CreateMutex(NULL,TRUE,_T("Sample07"));
if (m_hMutex == NULL)
{
return FALSE;
}
//如果程序已经存在并且正在运行
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
HWND hProgramWnd = ::FindWindow(NULL,L"Sample07");
if (hProgramWnd)
{
WINDOWPLACEMENT* pWndpl = NULL;
WINDOWPLACEMENT wpm;
pWndpl =&wpm;
GetWindowPlacement(hProgramWnd,&wpm);
if (pWndpl)
{
//将运行的程序窗口还原成正常状态
pWndpl->showCmd = SW_SHOWNORMAL;
::SetWindowPlacement(hProgramWnd,pWndpl);
SetWindowPos(hProgramWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
}
}
//关闭进程互斥体
CloseHandle(m_hMutex);
m_hMutex = NULL;
return FALSE;
}
}
缺点:窗口标题必须固定,这是由FindWindow函数造成的。
参考:FindWindow函数详解http://websky18.com/wlbc/vcbc/2010-04-12/609.html
方法2.程序只运行一个实例,并激活前一个实例 http://www.vckbase.com/code/relateddoc.asp?id=1931
实现程序只运行一次的方法很多,但是原理都是一样的,就是运行第一次的时候设置一个标记,每次运行的时候检查该标记,如果有就说明已经运行了。
具体实现:
1、在程序初始化的时候 (InitInstance()) 枚举所有的窗口,查找本程序的实例是否存在
2、在主窗口初始化的时候在本窗口的属性列表中添加一个标记,以便程序查找.
部分关键代码
1、在App的InitInstance()中枚举所有窗口,查找本程序实例
HWND oldHWnd = NULL;
EnumWindows(EnumWndProc,(LPARAM)&oldHWnd); //枚举所有运行的窗口
if(oldHWnd != NULL)
{
AfxMessageBox("本程序已经在运行了");
::ShowWindow(oldHWnd,SW_SHOWNORMAL); //激活找到的前一个程序
::SetForegroundWindow(oldHWnd); //把它设为前景窗口
return false; //退出本次运行
}
2、添加EnumWndProc窗口过程函数:
//添加的标识只运行一次的属性名
BOOL CALLBACK EnumWndProc(HWND hwnd,LPARAM lParam)
{
CString g_szPropName = "Your Prop Name"; //自己定义一个属性名
HANDLE g_hValue = (HANDLE)1; //自己定义一个属性值
HANDLE h = GetProp(hwnd,g_szPropName);
if( h == g_hValue)
{
*(HWND*)lParam = hwnd;
return false;
}
return true;
}
3、在主窗口的 OnInitDialog()中添加属性
//设置窗口属性
CString g_szPropName = "Your Prop Name"; //自己定义一个属性名
HANDLE g_hValue = (HANDLE)1; //自己定义一个属性值
SetProp(m_hWnd,g_szPropName,g_hValue);
参考资料: EnumWindows()有两个参数,一个是指向回调函数的指针,一个是用户定义的 LPARAM 值, 针对每个桌面窗口(或者顶层窗口)它调用回调函数一次。
VC中枚举所有的任务,任务管理器的一些资料的整理 http://soft.zdnet.com.cn/software_zone/2008/0323/779901.shtml
枚举进程参考:http://blog.csdn.net/andylin02/archive/2007/11/09/1876506.aspx
http://dev.firnow.com/course/3_program/c++/cppsl/200855/113063.html
http://blog.csdn.net/jmshl/archive/2007/05/18/1614607.aspx
Powered by Zoundry Raven