• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            yehao's Blog

            防止C++程序重復運行的幾種方法

            轉(zhuǎn)自http://hi.baidu.com/dreamyguy/blog/item/aacc6f44086afe45500ffef9.html

            有時候,為了某些要求,我們希望程序?qū)嵗贿\行一次。而在VB6中,我們可以很輕易的根據(jù)App.hPreInstance來判斷程序是否已經(jīng)運行。但是在C++中,這一切就變得不是那么容易。

            雖然WinMain函數(shù)有hPreInstance參數(shù)來指示,但是那是在Win16位的前提下,到了32Bit時代,那個參數(shù)已經(jīng)完全成為擺設。

            而本文正好探討了如何防止C++程序重復運行的方法。

            PS:因為本人使用MFC,所以為了方便,所有代碼均為以MFC為基礎(chǔ)。大家可以根據(jù)自己需要更改

              1.查找窗體

            對于存在GUI窗體(CUI暫不討論)的程序來說,最容易想到的就是利用FindWindow,以標題作為參數(shù)進行查找主窗體,然后使其關(guān)閉即可。

            通常,我們能寫出如下代碼:

            // Find Window by Caption
            // Add this code in InitInstance function of class 
            // you have derived from the CWinApp class

            HWND hWnd 
            = ::FindWindow(NULL, "MFCDialog");

            if (hWnd)
            {
            AfxMessageBox(
            "Has been running");
            return FALSE;
            }

             

            以上的代碼可以簡單的起到防止重復啟動的效果,但是局限性很大。

            首先,由于在FindWindow中要指定窗體的標題,如果窗體的標題在程序運行中是不斷變化的,那么就給搜索帶來了一定難度。

            而且,如果其他程序也恰好是用相同的標題的話……- -#。當然,你可以通過在FindWindow中指定類名來減少錯誤。但是如果你看過我前面寫的文章的話,你就會發(fā)現(xiàn),MFC注冊窗口類并不是那么隨意,而是經(jīng)過N次陰謀籌劃之后……

            看來這方法的局限性的確很大- -#

              2.額外窗體存儲

            此方法來源于對上面一種方法的補充,因為通過搜索MFC的窗體類比較困難,而且準確度不一定高。所以,我想到了使用額外窗體存儲(Extra Widnow Memory)的方法

            PS:關(guān)于什么是額外窗體存儲,請自行g(shù)oogle或MSDN或查看我曾經(jīng)寫的The Analyses Of Windows Runnning Principle

            如果你使用SDK進行開發(fā),可以在創(chuàng)建窗體時填充這一屬性,然后用GetWindowLong獲取。

            而由于我使用MFC,所以我更關(guān)注如何在MFC中使用這一屬性。

            一般來說,我們可以使用SetWindowLong對額外窗體存儲進行填充,然后用GetWindowLong獲取,最后配合FindWindow來檢驗程序是否重復運行。

            // Add this code in InitDialog function
            // and you can specify any number you want
            BOOL bRet = ::SetWindowLong(GetSafeHwnd(), GWL_USERDATA, 256);

            // Add this code in InitInstance function
            // Find Window by using extra memory

            HWND hWnd 
            = FindWindow(NULL, "MFCDialog");

            if (hWnd)
            {
            BOOL bRet 
            = ::GetWindowLong(hWnd, GWL_USERDATA);

            if (256 == bRet)  // compare
                {
            AfxMessageBox(
            "Has been running");
            return FALSE;
            }
            }

             

             3.全局原子

            你可以使用GlobalAddAtom將某個特定的字符串添加到全局原子列表(Global Atom Table),然后在程序運行時檢查該字符串即可。

            但是這個方法有一個致命的弱點,程序退出時,Windows不會自動為你刪除添加到列表中的Atom,而是需要你自己使用GlobalDeleteAtom進行刪除。

            這就意味著,如果你的程序意外的退出了,沒有刪除添加的Atom,那么,你的程序?qū)o法運行。

            所以,這并不是一個好方法。


              4.枚舉進程

            這或許是一個畢竟正常,或者說相對穩(wěn)定的方法。

            我們可以使用CreateToolhelp32Snapshot或者EnumProcess來枚舉當前的進程,然后檢查是否已經(jīng)運行。如果擔心存在同名的進程,還可以檢查路徑。

            //   關(guān)閉一個進程  
            static   bool   CloseApplication(CString   sAppToClose,   CString   sAppToUpdate,    
            CString   sFilename,   bool   bAskUser   /*   =   true   */)  
            {  
            //   Declare   variables  
            HANDLE   hProcessSnap   =   NULL;  
            PROCESSENTRY32   pe32   =   {NULL};  
            CString   sCompare,   sTemp;  

                   //sProcessFileName是你的進程名  
            CString   sProcesFilename   =   m_pPath->ExtractFilename(sFilename);  
            CLanguage   *   pLanguage   =   CLanguage::Instance();  

            //調(diào)整統(tǒng)一大小寫格式,方便比較、  
            #if   (_MFC_VER   <   0x0700)  
            sProcesFilename.MakeLower();  
            #else  
            CString   sLowerTemp;  
            sLowerTemp   =   sProcesFilename;  
            sProcesFilename   =   sLowerTemp.MakeLower();  
            #endif  

            hProcessSnap   =   CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,   0);  

            pe32.dwSize   =   sizeof(PROCESSENTRY32);  
            //   系統(tǒng)進程快照  
            if (Process32First(hProcessSnap,   &pe32))  
            {  
            do  
            {  
            sCompare.Format("%s",   pe32.szExeFile);  

            #if   (_MFC_VER   <   0x0700)  
            sCompare.MakeLower();  
            #else  
            CString   sLowerTemp;  
            sLowerTemp   =   sCompare;  
            sCompare   =   sLowerTemp.MakeLower();  
            #endif  


            //   Check   if   we   found   the   right   process  
            if(sCompare   ==   sProcesFilename)  
            {  
            //   If   we   are   not   in   hidden   mode  
            if(bAskUser)  
            {  
            //Set   up   confirmation   text  
            sTemp.Format(pLanguage->GetString(IDS_CONFIRMATION_CLOSEAPPLICATION_EXPLANATION),  
            sAppToClose,   sAppToUpdate,   sAppToClose);  

            //Ask   confirmation  
            CConfirmationDlg   dlgConfirm(pLanguage->GetString(IDS_CONFIRMATION_CLOSEAPPLICATION_TITLE),  
            pLanguage->GetString(IDS_CONFIRMATION_TITLE),   sTemp,  
            pLanguage->GetString(IDS_GENERAL_YES),   pLanguage->GetString(IDS_GENERAL_NO));  
            if(dlgConfirm.DoModal()   ==   IDNO)  
            {  
            return   false;  
            }  
            }  

            //   Get   handle   to   process  
            HANDLE   hProcess   =   OpenProcess(PROCESS_ALL_ACCESS,   FALSE,   pe32.th32ProcessID);  

            //   Exit   process  
            DWORD   exCode;  
            GetExitCodeProcess(hProcess,   &exCode);  
            TerminateProcess(hProcess,   exCode);  

            //   Wait   until   process   is   finished  
            if   (WaitForSingleObject(hProcess,   30000)   ==   WAIT_TIMEOUT)  
            return   false;  
            }  
            }   while   (Process32Next(hProcessSnap,   &pe32));  
            }  
            else  
            {  
            return   false;  
            }  

            return   true;  
            }

            PS:在Vista下使用EnumProcess時,要注意權(quán)限問題,OpenProcess增加了一個新的權(quán)限常數(shù),僅限Vista。如果不增加這個參數(shù),很多進程是無法被枚舉出來的(不過MS不印象我們自己的進程- -#)。


              5.互斥對象

            使用互斥對象來防止程序重復運行是一個很常用的做法,而且M$也推薦使用這種方法。和上面的幾種方法相比,需要寫的代碼少,而且效率比較高。所謂方便易用~

            一般我們會使用CreateMutex來創(chuàng)建互斥體,當?shù)诙蝿?chuàng)建相同的互斥體時,這個API會返回前一個互斥體的Handle,而GetLastError則會返回ERROR_ALREADY_EXITS

            // Mutex Object 
            // Add this code in InistInstance function
              
            HANDLE hMutex 
            = NULL;
            TCHAR 
            *lpszName = "TestMutex";

            hMutex 
            = ::CreateMutex(NULL, FALSE, lpszName);
            DWORD dwRet 
            = ::GetLastError();

            if (hMutex)
            {
            if (ERROR_ALREADY_EXISTS == dwRet)
            {
            AfxMessageBox(
            "Has been running");
            CloseHandle(hMutex);  
            // should be closed
                  return FALSE;
            }
            }
            else
            {
            AfxMessageBox(
            "Create Mutex Error");
            }

            // Add this code in Destruction function
              
            ::CloseHandle(hMutex);

            使用互斥體時要注意幾個問題:
            在CreateMutex之后馬上GetLastError,GetLastError是一個很復雜的API,任何牽涉到GetLastError的操作在執(zhí)行之后,都會覆蓋先前的值。

            把正常的CloseHandle寫到窗體的析構(gòu)函數(shù)或者程序?qū)ο蟮奈鰳?gòu)函數(shù)里。不要在CreateMutex之后立刻CloseHandle,否則互斥對象會被清空。

            這也是我當初所犯的錯誤,(不知道網(wǎng)上那么多錯誤的代碼是不是經(jīng)過Debug的囧),當互斥對象的最后一個Handle被Close之后,互斥對象將被刪除。如果程序在退出時沒有清空互斥對象,Windows將會執(zhí)行這一操作。當然,把次操作交給OS不是一個好習慣。

            詳情請看MSDN的引用:
            引用:
            Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.
            轉(zhuǎn)自:http://bbs.cfan.com.cn/viewthread.php?tid=793295
            http://topic.csdn.net/t/20060424/09/4708145.html
            http://student.csdn.net/space.php?uid=110004&do=thread&id=3278

            posted on 2011-05-03 18:28 厚積薄發(fā) 閱讀(713) 評論(0)  編輯 收藏 引用 所屬分類: Windows編程

            導航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計

            常用鏈接

            留言簿

            隨筆分類

            文章分類

            文章檔案

            搜索

            最新評論

            精品久久久久久国产| 99久久综合国产精品免费| 久久精品国产亚洲av日韩| 精品久久亚洲中文无码| 久久国产精品无| 亚洲精品综合久久| 亚洲国产成人久久综合区| 日日狠狠久久偷偷色综合免费 | 久久天堂AV综合合色蜜桃网 | 一本一道久久a久久精品综合 | 久久亚洲国产精品成人AV秋霞| 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 国产午夜久久影院| 伊人热人久久中文字幕| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 一本色道久久88综合日韩精品| 伊人久久亚洲综合影院| 青青草原综合久久大伊人| 久久精品国产乱子伦| 久久综合精品国产二区无码| 日韩精品久久久肉伦网站 | 狠狠人妻久久久久久综合| 国产精自产拍久久久久久蜜| 内射无码专区久久亚洲| 国产成人精品综合久久久久| 久久ZYZ资源站无码中文动漫| 久久久综合九色合综国产| 久久夜色撩人精品国产小说| 中文字幕日本人妻久久久免费 | 国内精品久久久久久久涩爱| 久久午夜综合久久| 亚洲色大成网站WWW久久九九| 91精品国产91久久久久福利| 久久久久97国产精华液好用吗| 国产亚洲精品久久久久秋霞| 99久久99久久精品国产| 久久这里有精品| 国产精品一区二区久久精品无码 | 精品一久久香蕉国产线看播放| 久久成人小视频| 亚洲综合婷婷久久|