让程序只运行一个实例的5种方法

您所在的位置:网站首页 衣服吊牌拆了还能退货吗 让程序只运行一个实例的5种方法

让程序只运行一个实例的5种方法

2024-03-19 18:45| 来源: 网络整理| 查看: 265

综述:让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建前,有窗口的程序在窗口创建前,检查系统中是否已经设置了某些特定标志了,如果有说明已经有一个实例在运行了,则当前程序通知用户怎样怎样,然后程序退出,当然方法有这么多,各自也就有自己的优缺点了。 

方法一:我用得做多的方法是创建互斥体Mutex,使用Mutex代码比较简洁,但是此时不能取得已经启动的实例窗口局柄,因此无法激活已经启动的实例窗口,代码如下:

View Code // -------------------------------------------------------------------------// 函数 : CreateSendingWNDList// 功能 : 创建互斥量,用于保证只启动一个进程// 返回值 : int // 成功 0// 失败 -1// 存在进程实例 1// 附注 : // -------------------------------------------------------------------------int CreateSendingWNDList(const TCHAR *pstrKSCoreAppName){ //-------防止多次起动---------- HANDLE hMutex = ::CreateMutex(0, true, pstrKSCoreAppName); int nRet = 0; if (hMutex) { if(GetLastError() == ERROR_ALREADY_EXISTS) { nRet = 1; } else { nRet = 0; } } else { nRet = -1; }

return nRet;}

// 在创建窗口前调用下面代码switch(CreateSendingWNDList(g_strKSCoreAppName)){case 0: // 正常启动 // TODO…… break; case 1: // 已存在进程,退出 { ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK); }

case -1:// 无法创建,退出default: return FALSE;}

方法二:一般来说,使程序只运行一个实例的最简单的方法当然是使用FindWindow()查找主窗口,如果主窗口已经存在了,当然说明已经有一个实例运行了。代码如下:

View Code // 这种方法有缺陷,窗口名字改变之后就再也找不到了,FindWindow()的参数ClassName和Caption比较难取得。HWND hWnd = FindWindow(NULL, TEXT("SingleInstanceFW"));if(IsWindow(hWnd)){ ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK); ::ShowWindow(hWnd, SW_NORMAL); // 显示 ::SetForegroundWindow(hWnd); // 激活 return FALSE;}

方法三:

这种方法相比上面两种方法,避免上面两种方法的缺点,通过SetProp()为程序主窗口设置一个特殊的Property,然后在启动时遍历所有的窗口,找出包含着个Property的窗口局柄【这个附加的窗口属性在窗口销毁时也应该销毁】这个方法的缺点就是代码比较多一点,如下:

View Code // 声明全局的 属性 名和 属性值TCHAR g_strKSCoreAppName[] = _T("AFX_KSInstall_CPP__12036F8B_8301_46e2_ADC5_A14A44A85877__");HANDLE g_hValue = (HANDLE)1022;

// 定义枚举窗口回调函数BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam){ //TCHAR str[200] = {0}; //::GetWindowText(hwnd, str, 200); HANDLE h = GetProp(hwnd, g_strKSCoreAppName); if(h == g_hValue) { *(HWND*)lParam = hwnd; return FALSE; } return TRUE;}

// 主窗口创建前判断HWND oldHWnd = NULL;::EnumWindows(EnumWndProc,(LPARAM)&oldHWnd); //枚举所有运行的窗口if (oldHWnd != NULL){ ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK);

::ShowWindow(oldHWnd, SW_NORMAL);

// 显示 ::SetForegroundWindow(oldHWnd); // 激活 return FALSE;}

// 主窗口创建后设置,为窗口附加一个属性::SetProp(m_hWnd, g_strKSCoreAppName, g_hValue);

// 主窗口退出时移除该附加属性::RemoveProp(m_hWnd, g_strKSCoreAppName);

方法四:

上面的方法二和方法三都有一个弊病,不知道大家发现没,那就是依赖于窗口的存在,没有窗口的程序怎么办了,用方法一是可以的,不过方法一不太适合即时修改状态,譬如我想提供选项给用户,可以即时修改是否允许多实例,像KMP就提供了即时修改是否允许多实例,使用全局变量是一个比较好的解决方案,使用全局共享变量的方法则主要是在VC框架程序中通过编译器来实现的。通过#pragma data_seg预编译指令创建一个新节,在此节中可用volatile关键字定义一个变量,而且必须对其进行初始化。Volatile关键字指定了变量可以为外部进程访问。最后,为了使该变量能够在进程互斥过程中发挥作用,还要将其设置为共享变量,同时允许具有读、写访问权限。这可以通过#pragma comment预编译指令来通知编译器。下面给出使用了全局变量的进程互斥代码清单:

#pragma data_seg("Shared") int volatile g_lAppInstance = 0; #pragma data_seg() #pragma comment(linker,"/section:Shared,RWS")

if (0 == g_lAppInstance){ g_lAppInstance = 1;}else if (1 == g_lAppInstance){ ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK); return FALSE;}else{ // 直接启动}

方法五:

使用命名信标对象,这个方法是孙鑫老师讲的. 

在我们的程序当中如果要实现类似《金山词霸》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实例激活,同时退出第二个应用程序实例。

对于第一个问题,我们可以通过设置命名互斥对象或命名信标对象,在程序启动的时候检测互斥对象或信标对象,如互斥对象或信标对象已存在,则可以判断此程序已有一个实例正在运行。

第二个问题是如何找到已经运行的应用程序实例,如果我们能够找到已运行实例主窗口的指针,即可调用SetForegroundWindow来激活该实例。我们可以通过两种形式找到已运行实例的主窗口,一种形式是通过调用FindWindowEx去查找正在运行的窗口的句柄,这种方式用得比较多一些,而本文通过另一种形式去查找正在运行的窗口的句柄。通过调用SetProp给应用程序主窗口设置一个标记,用GetDesktopWindow 可以获取Windows环境下的桌面窗口的句柄,所有应用程序的主窗口都可以看成该窗口的子窗口,接着我们就可以用GetWindow函数来获得这些窗口的句柄。然后再用Win32 SDK函数GetProp查找每一个应用程序的主窗口是否包含有我们设置的标记,这样就可以找到我们要找的第一个实例主窗口。 

View Code 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;

} }

在主窗口的OnInitDialog中添加类似如下标记:

::SetProp(this->m_hWnd,"维新",(HANDLE)1);

-----------------------------------------end

【注意,代码应该放在程序的入口处】

其实上面的方法可以两种进行组合来实现一些比较特殊的需求,具体怎样就自己去想了。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3