程序只运行一个实例

作者:孙鑫 日期:2002-7-30

 

我们在使用《金山词霸》时发现,在《金山词霸》已经运行了的情况下,再次点击《金山词霸》的图标,那么它不会再运行另外一个《金山词霸》,而是将已有的《金山词霸》给激活,始终只能运行一个《金山词霸》的实例。 在我们的程序当中如果要实现类似《金山词霸》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实例激活,同时退出第二个应用程序实例。 对于第一个问题,我们可以通过设置命名互斥对象或命名信标对象,在程序启动的时候检测互斥对象或信标对象,如互斥对象或信标对象已存在,则可以判断此程序已有一个实例正在运行。 第二个问题是如何找到已经运行的应用程序实例,如果我们能够找到已运行实例主窗口的指针,即可调用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();

…………………………………… }

2、在框架类的OnCreate()函数中设置查找标记。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{ ……………………

//设置查找标记。

::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,"维新");

}

至此,使应用程序只运行一个实例的功能就完成了。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

 

 

另转:::

方法一:有时候在开发应用程序时,希望控制程序运行唯一的实例。例如,最常用的mp3播放软件Winamp,由于它需要独占计算机中的音频设备,因此该程序只允许自身运行唯一的一个例程。在Visual C++的开发实践中,对于16位的Windows系统,应用程序的hPrevInstance句柄保存了应用程序上一个运行的实例,可以用该值来检查是否有实例运行;然而在32位Windows系统下,这个值总是NULL,所以无法利用该值来实现程序运行唯一实例。本实例给出了解决这个问题的简单办法,只要将程序中稍微改动一下就可以了。   

一、 实现方法   对于具有窗口的应用程序,可以用静态函数CWnd::FindWindow()查找固定窗口,来判断程序是否已经运行。

函数原型为:

CWnd* PASCAL FindWindow( LPCTSTR lpszClassName, LPCTSTR lpszWindowName );   这个函数有两个参数,第一个是要找的窗口的类,第二个是要找的窗口的标题。在搜索的时候不一定两者都知道,但至少要知道其中的一个。有的窗口的标题是比较容易得到的,如"计算器",所以搜索时应使用标题进行搜索。但有的软件的标题不是固定的,如"记事本",如果打开的文件不同,窗口标题也不同,这时使用窗口类搜索就比较方便。如果找到了满足条件的窗口,这个函数返回该窗口的指针,否则返回值为NULL。   

考虑到程序的健壮性,我们还需要判断窗口是否处于最小化状态、是否有弹出式子窗口,这就需要使用CWnd:: GetLastActivePopup()、CWnd::IsIconic()函数,

它们的原型分别为:

CWnd* GetLastActivePopup( )   

该函数返回一个指定父窗口中最近激活过的弹出式窗口的指针。如果窗口本身是刚刚激活的,或窗口不包含任何弹出窗口,那么该函数返回指向父窗口自身的指针。

BOOL IsIconic( )   

该函数用来判断当前窗口是否处于最小化状态,如果窗口处于最小化状态,函数返回值为True,否则返回Flase。   

对于处于最小化状态的窗口,可以调用CWnd::ShowWindow( int nCmdShow )恢复窗口的正常状态,

该函数的原型为:

BOOL ShowWindow( int nCmdShow )   

如窗口之前是可见的,函数调用后返回True,否则返回False。参数nCmdShow的值可以为以下任意个常数:   

SW_HIDE:隐藏窗口,活动状态给令一个窗口;   

SW_MINIMIZE:最小化窗口,活动状态给另一个窗口;

SW_RESTORE:用原来的大小和位置显示一个窗口,同时令其进入活动状态;  

SW_SHOW:用当前的大小和位置显示一个窗口,同时令其进入活动状态;   

SW_SHOWMAXIMIZED:最大化窗口,并将其激活;   

SW_SHOWMINIMIZED:最小化窗口,并将其激活;   

SW_SHOWMINNOACTIVE:最小化一个窗口,同时不改变活动窗口;   

SW_SHOWNA:用当前的大小和位置显示一个窗口,不改变活动窗口;   

SW_SHOWNOACTIVATE:用最近的大小和位置显示一个窗口,不改变活动窗口;   

SW_SHOWNORMAL:与SW_RESTORE相同;   

最后不要忘记了用CWnd:: SetForegroundWindow()函数将弹出窗口设置为桌面的最前端。   

有了上面的知识,我们就可以修改程序中应用程序类的InitInstance()函数,如果程序已经运行,也即是可以发现相应的程序窗口,那么就显示该窗口,InitInstance()函数就返回False,程序提前退出,否则就正常运行。   

二、 编程步骤   

1、 启动Visual C++6.0,生成一个基于对话框的应用程序,程序命名为"Instance";    2、 修改程序的InitInstance()函数;   

3、 添加代码,编译运行程序;   

三、 程序代码

/ // CInstanceApp initialization

BOOL CInstanceApp::InitInstance()

{

if (!FirstInstance())   

return FALSE;  

AfxEnableControlContainer();  

#ifdef _AFXDLL  Enable3dControls(); // Call this when using MFC in a shared DLL  

#else   Enable3dControlsStatic(); // Call this when linking to MFC statically   #endif  

CInstanceDlg dlg;  

m_pMainWnd = &dlg;  

int nResponse = dlg.DoModal();  

if (nResponse == IDOK)  

{    // TODO: Place code here to handle when the dialog is   

// dismissed with OK   }  

else if (nResponse == IDCANCEL)  

{    // TODO: Place code here to handle when the dialog is   

// dismissed with Cancel   }  

// Since the dialog has been closed, return FALSE so that we exit the  

// application, rather than start the application's message pump.  

return FALSE; }

BOOL CInstanceApp::FirstInstance() {  

CWnd *pWndPrev, *pWndChild;  

// Determine if another window with our class name and Window title exists...  

// The title "Instance " is set up latter, in the InitDialog function.  

if (pWndPrev = CWnd::FindWindow(NULL, "Instance "))  

{   

pWndChild = pWndPrev- >GetLastActivePopup();   

// if so, does it have any popups?   

if (pWndPrev- >IsIconic())    

pWndPrev- >ShowWindow(SW_RESTORE);    

// If iconic, restore the main window    

pWndChild- >SetForegroundWindow();    

// Bring the window to the foreground   

return FALSE;   }  

else   

return TRUE; // First instance. Proceed as normal. }   

四、 小结   上述方法虽然实现起来很简单,但是它对于无窗口的应用程序却无能为力。为了解决这个问题,可以通过动态连接库DLL实现更通用的控制程序运行的方法。在 DLL中使用#pragma data_seg指令实现共享数据段,在该数据段中定义一个变量long m_nRun,并设置其初始值为-1,同时还要在DLL的入口点函数DllMain返回成功值的语句前添加语句m_nRun++,意思是在应用程序启动连接DLL成功时对已经运行的实例进行计数,然后在DLL中导出一个函数来返回该变量的值。最后将应用程序的工程设置为依赖于该DLL的工程,在应用程序根据DLL中的m_nRun变量的值来判断是否程序已经运行了。

方法二:

#define UNIQE_NAME "{1AB792D6-EAF2-3267-9A84-9135681127A4}"

#define GIS_MSG "{D48CA993-4925-41cb-8F59-ABAAAFCEF797}"

const UINT ID_GIS_INSTANCE = ::RegisterWindowMessage(GIS_MSG);

//注册消息

// 确保程序只有一个实例在运行

m_hOneInstance = ::CreateMutex(NULL, FALSE, UNIQE_NAME);

if (GetLastError() == ERROR_ALREADY_EXISTS )

//如果存在前一个

{ //发送消息,激活实例

DWORD dwRecipients = BSM_APPLICATIONS; ::BroadcastSystemMessage(BSF_NOHANG, &dwRecipients, ID_GIS_INSTANCE, 0, 0);

// registered window message

// user defined parameters return FALSE;

}

ON_REGISTERED_MESSAGE(ID_GIS_INSTANCE, OnGISInstance)

LRESULT CMainFrame::OnGISInstance(WPARAM wParam, LPARAM lParam) {

theApp.m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);

theApp.m_pMainWnd->SetForegroundWindow();

::AfxMessageBox("本程序已经有一个实例在运行了。");

return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值