再次讨论,如何让程序只启动一个实例?

时间:2021-11-10 04:44:18
好象方法很多,有使用共享数据、有使用FindWindow、还有使用互斥变量等等,有热心人甚至写出了一个类,禁止启动第二实例,并且激活已启动的实例。

我想,把大家的方法都贴出来吧!OPEN 一把!

16 个解决方案

#1


关注:O===O

#2


(转的,呵呵)
用互斥(Mutex),Mutex本来是多线程同步用的,防止 
多个线程访问同一资源而引发冲突。用CreateMutex可以 
创建Mutex对象,实质上是一个kernel对象。 

在任何Win32程序(包括GDI和Console)的启动部分加上 
以下代码(Console程序的main函数、MFC程序的CWinApp 
派生类的InitInstance成员函数、RawSDK程序的WinMain 
函数): 
hMutex=CreateMutex( 
NULL,//nosecurityattributes 
FALSE,//initiallynotowned 
"RunOnlyOneInstance");//命名Mutex是全局对象 
//在所有的process都可以访问到 

if(hMutex==NULL|| 
ERROR_ALREADY_EXISTS==::GetLastError()){ 
//程序第二次或以后运行时,会得到Mutex已经创建的错误 
returnFALSE; 

嘿嘿,是不是不用FindWindow优雅简洁多了? 

另外,如果要限定程序同时可运行n个实例怎么办? 
Tips:用信号量(Semaphore)。 

#3


很容易的,查找第一个窗口,然后激活以前的。
HWND hOldInstance;
if((hOldInstance=::FindWindow(NULL,_T("志软个人信息管理")))!=NULL)
{
ShowWindow(hOldInstance,SW_RESTORE|SW_SHOWNORMAL);
SetForegroundWindow(hOldInstance);
return FALSE;
}

#4


我的方法和bodies一样,但就是不知道怎么激活以前的窗口。

#5


转贴的哦:
#define INSTANCEMAINWNDPROP _T("your special string")

BOOL CYourApp::InitInstance()
{
    if(CheckSingleInstance()==FALSE)
    {
        return FALSE;  //则程序安全退出
    }

    //......
}

BOOL CYourApp::CheckSingleInstance()
{
    ::CreateMutex(NULL,TRUE,INSTANCEMAINWNDPROP);
    if(::GetLastError()==ERROR_ALREADY_EXISTS)
    {
        //set focus to previous instance
        CWnd* pPrevWnd=CWnd::GetDesktopWindow()->GetWindow(GW_CHILD);
        while(pPrevWnd)
        {
            if(::GetProp(pPrevWnd->GetSafeHwnd(),INSTANCEMAINWNDPROP))
            {
                if(pPrevWnd->IsIconic())
                {
                    pPrevWnd->ShowWindow(SW_RESTORE);
                }
                
                pPrevWnd->SetForegroundWindow();
                pPrevWnd->GetLastActivePopup()->SetForegroundWindow();

                break;
            }

            pPrevWnd=pPrevWnd->GetWindow(GW_HWNDNEXT);
        }

        return FALSE;
    }

    return TRUE;
}

int CYourMainWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    ::SetProp(GetSafeHwnd(),INSTANCEMAINWNDPROP,(HANDLE)1);
    //......
}

void CYourMainWnd::OnDestroy() 
{
    ::RemoveProp(GetSafeHwnd(),INSTANCEMAINWNDPROP);    
    CWnd::OnDestroy();
}

http://www.csdn.net/expert/topic/78/78172.shtm
在这里啦

#6


我是这样做的,不过不是最好的办法
BOOL CThread01App::InitInstance()
{

CMutex vMutex(FALSE,MUTEXNAME,NULL);
BOOL bResult;
HWND vShowWindow;
DWORD err;

bResult = vMutex.Lock(0); //判断实例是否已经启动
if (!bResult)
{
                  //是,则显示实例
vShowWindow = ::FindWindow(LPCTSTR("#32770"),LPCTSTR("Thread01"));
if (vShowWindow == NULL)
{
err = GetLastError();
exit(0);
}
::ShowWindow((HWND)vShowWindow,SW_SHOWNORMAL); //设置窗口属性
::SetForegroundWindow((HWND)vShowWindow); //设置窗口在最前面
exit(0);
}
...
}
在显示该实例的处理上不是很好,不知道有什么更好的方法,请指教!

#7


Q243953 HOWTO: Limit 32-bit Applications to One Instance Using C++ 

#8


利用注册表、互斥对象、原子、FindWindow、都可以!

#9


继续?

#10


关注!

#11


继续?

#12


贴上我的笔记:

方案一: 查找窗口类 
方案二: 设置窗口属性 
方案三: 使用文件映像 
方案四: 使用原子 
方案五: 使用同步对象 


方案一: 使用注册类查找的方法实现单实例运行 
例: 
①C???App::InitInstance 
BOOL C???App::InitInstance()
{
    if (!FirstInstance())
      return FALSE;
    beRegisted=TRUE;
   m_pMainWnd =new CMainWnd();
    m_pMainWnd->CreateEx(0,"fmdmainwnd",
            _T("CodeHelper"),WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,NULL);
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    return TRUE;
}
其中beRegisted为C???App成员 
public:
    BOOL beRegisted;
②C???App::FirstInstance 
BOOL CMainApp::FirstInstance()
{
    CWnd *PrevCWnd, *ChildCWnd;
    if (PrevCWnd = CWnd::FindWindow("fmdmainwnd",NULL))
    {
      ChildCWnd=PrevCWnd->GetLastActivePopup();
      PrevCWnd->BringWindowToTop();
      if (PrevCWnd->IsIconic())
          PrevCWnd->ShowWindow(SW_RESTORE);
      if (PrevCWnd != ChildCWnd)
          ChildCWnd->BringWindowToTop();
      return FALSE;
    }
    else
      return TRUE;  // First instance. Proceed as normal.
}
③C???App::InitApplication 
BOOL CMainApp::InitApplication() 
{
    CWinApp::InitApplication();
    WNDCLASS wndcls;
    //注册自定义类
      memset(&wndcls, 0, sizeof(WNDCLASS));
    //窗口基本类型
      wndcls.style=CS_DBLCLKS ¦ CS_HREDRAW ¦ CS_VREDRAW;
    wndcls.lpfnWndProc=::DefWindowProc;
      wndcls.hInstance=AfxGetInstanceHandle();
      wndcls.hIcon=LoadIcon(IDR_MAIN);
      wndcls.hCursor=LoadStandardCursor(IDC_ARROW);
      wndcls.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
      wndcls.lpszMenuName=MAKEINTRESOURCE(IDR_MAIN);
      wndcls.lpszClassName="fmdmainwnd";
    return AfxRegisterClass(&wndcls);
}
④C???App::ExitInstance 
int CMainApp::ExitInstance() 
{
    if(beRegisted)
        ::UnregisterClass("fmdmainwnd", AfxGetInstanceHandle());
    return CWinApp::ExitInstance();
}

方案二:设置窗口附加属性 int C???Dlg::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    ......
    ::SetProp(m_hWnd, AfxGetApp()->m_pszExeName, (HANDLE)1);
    ......
}
void C???Dlg::OnDestroy() 
{
    ......
    ::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);    
    ......
}
BOOL CEllipseWndApp::InitInstance()
{
    // Create a Semaphore with a name of application exename
    HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszExeName);
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        CloseHandle(hSem);
        HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(), GW_CHILD);
        while (::IsWindow(hWndPrevious))
        {
            if (::GetProp(hWndPrevious, m_pszExeName))
            {
                if (::IsIconic(hWndPrevious))
                    ::ShowWindow(hWndPrevious, SW_RESTORE);
                ::SetForegroundWindow(hWndPrevious);
                ::SetForegroundWindow(::GetLastActivePopup(hWndPrevious));
                return FALSE;
            }
            hWndPrevious = ::GetWindow(hWndPrevious, GW_HWNDNEXT);
        }
        return FALSE;
    }
    C????Dlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: Place code here to handle when the dialog is
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: Place code here to handle when the dialog is
    }
    return FALSE;
}

方案三:使用文件映像 
另文有述

方案四:使用原子 
BOOL C???App::InitInstance()
{
    .....
    if(GlobalFindAtom("AdBreaker"))  //找原子
        return false;
    ATOM GlobalAtom=GlobalAddAtom("AdBreaker"); //添加原子
    CAdBreakerDlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
    .....    
    GlobalDeleteAtom(GlobalAtom);
    return FALSE;
}

方案五:使用同步对象 InitInstance中 
    HANDLE m_hMutex = 
    CreateMutex(NULL,TRUE,m_pszAppName);
    if (GetLastError()==ERROR_ALREADY_EXISTS){
        return FALSE;
    }



另:在
http://www.csdn.net/expert/topic/159/159112.shtm
中liuqiannan(二迷糊) 的方案也不错:

回复人:liuqiannan(二迷糊) (2001-6-14 22:28:00)  得0分 
用一个共享数据段,定义一个用来计数的长整形
#pragma data_seg(".OnlyOne")
    long 1Count=-1;
#pragma data_seg()

用DEF文件定义共享属性
SECTIONS
    .OnlyOne READ WRITE SHARED

在初始化阶段,如InitInstance开始处,加入
BOOL bFirstInit=(InterlockedIncrement($1InstanceCount)==0)
if(!bFirstInit)
{
      AfxMessageBox("该程序已经运行",MB_OK¦MB_ICONSTOP);
      InterlockedDecrement($1InstanceCount);
      return FALSE;
}
程序结束后,调用:
InterlockedDecrement(&1InstanceCount);  


综合,以上朋友的发言,应该有不少了,但只有少数能作到,将前一个实例激活

#13


用GUID做命名内核对象的名字,这样比较好

#14


呀,来晚了,那再说一种吧,
可以用内存映射文件实现:
MutexRuning为程序名
初始化阶段:
InitInstance中:

HANDLE hMap=CreateFileMapping((HANDLE)0xffffffff,NULL,PAGE_READWRITE,0,128,
                              "MutexRuning");
if(hMap==NULL)
{
     AfxMessageBox("创建内存映射文件失败",MB_OK|MB_ICONSTOP);
     return FALSE;
}
else if(GetLasError()==ERROR_ALREADY_EXISTS)
{
        LPVOID lpMem=MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);
       CString str=(char *)lpMem;
       UnmapViewOfile(lpMem);
        CloseHandle(hMap);

       AfxMessageBox(str,MB_OK|MB_ICONSTOP);
       return FALSE;
}
else
{
     //是第一次运行
     LPVOID lpMem=MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);
     //程序运行描述信息
     strcpy((char *) lpMem,"xxxx正在运行!");
     UnmapViewOfFile(lpMem);
}
  

#15


Mutex可以做互斥,看看操作系统的书吧

#16


BOOL CXXXApp::InitInstance()
{
...
HANDLE m_hMutex=CreateMutex(NULL,TRUE, m_pszAppName); 
if(GetLastError()==ERROR_ALREADY_EXISTS) { 
     AfxMessageBox("已经运行!");
     return FALSE; }
...
}

#1


关注:O===O

#2


(转的,呵呵)
用互斥(Mutex),Mutex本来是多线程同步用的,防止 
多个线程访问同一资源而引发冲突。用CreateMutex可以 
创建Mutex对象,实质上是一个kernel对象。 

在任何Win32程序(包括GDI和Console)的启动部分加上 
以下代码(Console程序的main函数、MFC程序的CWinApp 
派生类的InitInstance成员函数、RawSDK程序的WinMain 
函数): 
hMutex=CreateMutex( 
NULL,//nosecurityattributes 
FALSE,//initiallynotowned 
"RunOnlyOneInstance");//命名Mutex是全局对象 
//在所有的process都可以访问到 

if(hMutex==NULL|| 
ERROR_ALREADY_EXISTS==::GetLastError()){ 
//程序第二次或以后运行时,会得到Mutex已经创建的错误 
returnFALSE; 

嘿嘿,是不是不用FindWindow优雅简洁多了? 

另外,如果要限定程序同时可运行n个实例怎么办? 
Tips:用信号量(Semaphore)。 

#3


很容易的,查找第一个窗口,然后激活以前的。
HWND hOldInstance;
if((hOldInstance=::FindWindow(NULL,_T("志软个人信息管理")))!=NULL)
{
ShowWindow(hOldInstance,SW_RESTORE|SW_SHOWNORMAL);
SetForegroundWindow(hOldInstance);
return FALSE;
}

#4


我的方法和bodies一样,但就是不知道怎么激活以前的窗口。

#5


转贴的哦:
#define INSTANCEMAINWNDPROP _T("your special string")

BOOL CYourApp::InitInstance()
{
    if(CheckSingleInstance()==FALSE)
    {
        return FALSE;  //则程序安全退出
    }

    //......
}

BOOL CYourApp::CheckSingleInstance()
{
    ::CreateMutex(NULL,TRUE,INSTANCEMAINWNDPROP);
    if(::GetLastError()==ERROR_ALREADY_EXISTS)
    {
        //set focus to previous instance
        CWnd* pPrevWnd=CWnd::GetDesktopWindow()->GetWindow(GW_CHILD);
        while(pPrevWnd)
        {
            if(::GetProp(pPrevWnd->GetSafeHwnd(),INSTANCEMAINWNDPROP))
            {
                if(pPrevWnd->IsIconic())
                {
                    pPrevWnd->ShowWindow(SW_RESTORE);
                }
                
                pPrevWnd->SetForegroundWindow();
                pPrevWnd->GetLastActivePopup()->SetForegroundWindow();

                break;
            }

            pPrevWnd=pPrevWnd->GetWindow(GW_HWNDNEXT);
        }

        return FALSE;
    }

    return TRUE;
}

int CYourMainWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    ::SetProp(GetSafeHwnd(),INSTANCEMAINWNDPROP,(HANDLE)1);
    //......
}

void CYourMainWnd::OnDestroy() 
{
    ::RemoveProp(GetSafeHwnd(),INSTANCEMAINWNDPROP);    
    CWnd::OnDestroy();
}

http://www.csdn.net/expert/topic/78/78172.shtm
在这里啦

#6


我是这样做的,不过不是最好的办法
BOOL CThread01App::InitInstance()
{

CMutex vMutex(FALSE,MUTEXNAME,NULL);
BOOL bResult;
HWND vShowWindow;
DWORD err;

bResult = vMutex.Lock(0); //判断实例是否已经启动
if (!bResult)
{
                  //是,则显示实例
vShowWindow = ::FindWindow(LPCTSTR("#32770"),LPCTSTR("Thread01"));
if (vShowWindow == NULL)
{
err = GetLastError();
exit(0);
}
::ShowWindow((HWND)vShowWindow,SW_SHOWNORMAL); //设置窗口属性
::SetForegroundWindow((HWND)vShowWindow); //设置窗口在最前面
exit(0);
}
...
}
在显示该实例的处理上不是很好,不知道有什么更好的方法,请指教!

#7


Q243953 HOWTO: Limit 32-bit Applications to One Instance Using C++ 

#8


利用注册表、互斥对象、原子、FindWindow、都可以!

#9


继续?

#10


关注!

#11


继续?

#12


贴上我的笔记:

方案一: 查找窗口类 
方案二: 设置窗口属性 
方案三: 使用文件映像 
方案四: 使用原子 
方案五: 使用同步对象 


方案一: 使用注册类查找的方法实现单实例运行 
例: 
①C???App::InitInstance 
BOOL C???App::InitInstance()
{
    if (!FirstInstance())
      return FALSE;
    beRegisted=TRUE;
   m_pMainWnd =new CMainWnd();
    m_pMainWnd->CreateEx(0,"fmdmainwnd",
            _T("CodeHelper"),WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,NULL);
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    return TRUE;
}
其中beRegisted为C???App成员 
public:
    BOOL beRegisted;
②C???App::FirstInstance 
BOOL CMainApp::FirstInstance()
{
    CWnd *PrevCWnd, *ChildCWnd;
    if (PrevCWnd = CWnd::FindWindow("fmdmainwnd",NULL))
    {
      ChildCWnd=PrevCWnd->GetLastActivePopup();
      PrevCWnd->BringWindowToTop();
      if (PrevCWnd->IsIconic())
          PrevCWnd->ShowWindow(SW_RESTORE);
      if (PrevCWnd != ChildCWnd)
          ChildCWnd->BringWindowToTop();
      return FALSE;
    }
    else
      return TRUE;  // First instance. Proceed as normal.
}
③C???App::InitApplication 
BOOL CMainApp::InitApplication() 
{
    CWinApp::InitApplication();
    WNDCLASS wndcls;
    //注册自定义类
      memset(&wndcls, 0, sizeof(WNDCLASS));
    //窗口基本类型
      wndcls.style=CS_DBLCLKS ¦ CS_HREDRAW ¦ CS_VREDRAW;
    wndcls.lpfnWndProc=::DefWindowProc;
      wndcls.hInstance=AfxGetInstanceHandle();
      wndcls.hIcon=LoadIcon(IDR_MAIN);
      wndcls.hCursor=LoadStandardCursor(IDC_ARROW);
      wndcls.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
      wndcls.lpszMenuName=MAKEINTRESOURCE(IDR_MAIN);
      wndcls.lpszClassName="fmdmainwnd";
    return AfxRegisterClass(&wndcls);
}
④C???App::ExitInstance 
int CMainApp::ExitInstance() 
{
    if(beRegisted)
        ::UnregisterClass("fmdmainwnd", AfxGetInstanceHandle());
    return CWinApp::ExitInstance();
}

方案二:设置窗口附加属性 int C???Dlg::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    ......
    ::SetProp(m_hWnd, AfxGetApp()->m_pszExeName, (HANDLE)1);
    ......
}
void C???Dlg::OnDestroy() 
{
    ......
    ::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);    
    ......
}
BOOL CEllipseWndApp::InitInstance()
{
    // Create a Semaphore with a name of application exename
    HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszExeName);
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        CloseHandle(hSem);
        HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(), GW_CHILD);
        while (::IsWindow(hWndPrevious))
        {
            if (::GetProp(hWndPrevious, m_pszExeName))
            {
                if (::IsIconic(hWndPrevious))
                    ::ShowWindow(hWndPrevious, SW_RESTORE);
                ::SetForegroundWindow(hWndPrevious);
                ::SetForegroundWindow(::GetLastActivePopup(hWndPrevious));
                return FALSE;
            }
            hWndPrevious = ::GetWindow(hWndPrevious, GW_HWNDNEXT);
        }
        return FALSE;
    }
    C????Dlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: Place code here to handle when the dialog is
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: Place code here to handle when the dialog is
    }
    return FALSE;
}

方案三:使用文件映像 
另文有述

方案四:使用原子 
BOOL C???App::InitInstance()
{
    .....
    if(GlobalFindAtom("AdBreaker"))  //找原子
        return false;
    ATOM GlobalAtom=GlobalAddAtom("AdBreaker"); //添加原子
    CAdBreakerDlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
    .....    
    GlobalDeleteAtom(GlobalAtom);
    return FALSE;
}

方案五:使用同步对象 InitInstance中 
    HANDLE m_hMutex = 
    CreateMutex(NULL,TRUE,m_pszAppName);
    if (GetLastError()==ERROR_ALREADY_EXISTS){
        return FALSE;
    }



另:在
http://www.csdn.net/expert/topic/159/159112.shtm
中liuqiannan(二迷糊) 的方案也不错:

回复人:liuqiannan(二迷糊) (2001-6-14 22:28:00)  得0分 
用一个共享数据段,定义一个用来计数的长整形
#pragma data_seg(".OnlyOne")
    long 1Count=-1;
#pragma data_seg()

用DEF文件定义共享属性
SECTIONS
    .OnlyOne READ WRITE SHARED

在初始化阶段,如InitInstance开始处,加入
BOOL bFirstInit=(InterlockedIncrement($1InstanceCount)==0)
if(!bFirstInit)
{
      AfxMessageBox("该程序已经运行",MB_OK¦MB_ICONSTOP);
      InterlockedDecrement($1InstanceCount);
      return FALSE;
}
程序结束后,调用:
InterlockedDecrement(&1InstanceCount);  


综合,以上朋友的发言,应该有不少了,但只有少数能作到,将前一个实例激活

#13


用GUID做命名内核对象的名字,这样比较好

#14


呀,来晚了,那再说一种吧,
可以用内存映射文件实现:
MutexRuning为程序名
初始化阶段:
InitInstance中:

HANDLE hMap=CreateFileMapping((HANDLE)0xffffffff,NULL,PAGE_READWRITE,0,128,
                              "MutexRuning");
if(hMap==NULL)
{
     AfxMessageBox("创建内存映射文件失败",MB_OK|MB_ICONSTOP);
     return FALSE;
}
else if(GetLasError()==ERROR_ALREADY_EXISTS)
{
        LPVOID lpMem=MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);
       CString str=(char *)lpMem;
       UnmapViewOfile(lpMem);
        CloseHandle(hMap);

       AfxMessageBox(str,MB_OK|MB_ICONSTOP);
       return FALSE;
}
else
{
     //是第一次运行
     LPVOID lpMem=MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);
     //程序运行描述信息
     strcpy((char *) lpMem,"xxxx正在运行!");
     UnmapViewOfFile(lpMem);
}
  

#15


Mutex可以做互斥,看看操作系统的书吧

#16


BOOL CXXXApp::InitInstance()
{
...
HANDLE m_hMutex=CreateMutex(NULL,TRUE, m_pszAppName); 
if(GetLastError()==ERROR_ALREADY_EXISTS) { 
     AfxMessageBox("已经运行!");
     return FALSE; }
...
}