• <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++程序重復(fù)運(yùn)行的幾種方法

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

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

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

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

            PS:因?yàn)楸救耸褂肕FC,所以為了方便,所有代碼均為以MFC為基礎(chǔ)。大家可以根據(jù)自己需要更改

              1.查找窗體

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

            通常,我們能寫(xiě)出如下代碼:

            // 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;
            }

             

            以上的代碼可以簡(jiǎn)單的起到防止重復(fù)啟動(dòng)的效果,但是局限性很大。

            首先,由于在FindWindow中要指定窗體的標(biāo)題,如果窗體的標(biāo)題在程序運(yùn)行中是不斷變化的,那么就給搜索帶來(lái)了一定難度。

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

            看來(lái)這方法的局限性的確很大- -#

              2.額外窗體存儲(chǔ)

            此方法來(lái)源于對(duì)上面一種方法的補(bǔ)充,因?yàn)橥ㄟ^(guò)搜索MFC的窗體類(lèi)比較困難,而且準(zhǔn)確度不一定高。所以,我想到了使用額外窗體存儲(chǔ)(Extra Widnow Memory)的方法

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

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

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

            一般來(lái)說(shuō),我們可以使用SetWindowLong對(duì)額外窗體存儲(chǔ)進(jìn)行填充,然后用GetWindowLong獲取,最后配合FindWindow來(lái)檢驗(yàn)程序是否重復(fù)運(yùn)行。

            // 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將某個(gè)特定的字符串添加到全局原子列表(Global Atom Table),然后在程序運(yùn)行時(shí)檢查該字符串即可。

            但是這個(gè)方法有一個(gè)致命的弱點(diǎn),程序退出時(shí),Windows不會(huì)自動(dòng)為你刪除添加到列表中的Atom,而是需要你自己使用GlobalDeleteAtom進(jìn)行刪除。

            這就意味著,如果你的程序意外的退出了,沒(méi)有刪除添加的Atom,那么,你的程序?qū)o(wú)法運(yùn)行。

            所以,這并不是一個(gè)好方法。


              4.枚舉進(jìn)程

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

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

            //   關(guān)閉一個(gè)進(jì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是你的進(jìn)程名  
            CString   sProcesFilename   =   m_pPath->ExtractFilename(sFilename);  
            CLanguage   *   pLanguage   =   CLanguage::Instance();  

            //調(diào)整統(tǒng)一大小寫(xiě)格式,方便比較、  
            #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)進(jìn)程快照  
            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時(shí),要注意權(quán)限問(wèn)題,OpenProcess增加了一個(gè)新的權(quán)限常數(shù),僅限Vista。如果不增加這個(gè)參數(shù),很多進(jìn)程是無(wú)法被枚舉出來(lái)的(不過(guò)MS不印象我們自己的進(jìn)程- -#)。


              5.互斥對(duì)象

            使用互斥對(duì)象來(lái)防止程序重復(fù)運(yùn)行是一個(gè)很常用的做法,而且M$也推薦使用這種方法。和上面的幾種方法相比,需要寫(xiě)的代碼少,而且效率比較高。所謂方便易用~

            一般我們會(huì)使用CreateMutex來(lái)創(chuàng)建互斥體,當(dāng)?shù)诙蝿?chuàng)建相同的互斥體時(shí),這個(gè)API會(huì)返回前一個(gè)互斥體的Handle,而GetLastError則會(huì)返回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);

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

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

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

            詳情請(qǐng)看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ā) 閱讀(732) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Windows編程

            導(dǎo)航

            <2025年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            統(tǒng)計(jì)

            常用鏈接

            留言簿

            隨筆分類(lèi)

            文章分類(lèi)

            文章檔案

            搜索

            最新評(píng)論

            精品国产乱码久久久久久人妻| 久久久久久午夜成人影院 | 久久精品无码专区免费东京热| 亚洲va久久久噜噜噜久久男同 | 久久亚洲国产精品123区| 久久久噜噜噜久久中文字幕色伊伊| 久久婷婷午色综合夜啪| 91久久精一区二区三区大全| 老司机午夜网站国内精品久久久久久久久| 2020国产成人久久精品| 国产高潮久久免费观看| 无码国内精品久久人妻蜜桃 | 亚洲国产精品婷婷久久| 国产精品久久新婚兰兰| 国产 亚洲 欧美 另类 久久| 色欲综合久久中文字幕网| 国産精品久久久久久久| 国产亚洲婷婷香蕉久久精品| 亚洲日本va中文字幕久久| 欧美激情精品久久久久久| 久久99中文字幕久久| 久久精品国产亚洲AV无码偷窥| 色欲综合久久躁天天躁| 久久激情亚洲精品无码?V| 国产69精品久久久久777| 久久精品毛片免费观看| 亚洲精品无码久久久久久| 亚洲乱码日产精品a级毛片久久 | 久久久久亚洲AV无码去区首| 久久伊人精品青青草原高清| 久久99国内精品自在现线| 国产偷久久久精品专区| 狠狠色噜噜色狠狠狠综合久久| 久久性精品| 婷婷久久综合九色综合九七| 久久综合久久伊人| 欧美精品一区二区久久| 青青青青久久精品国产h久久精品五福影院1421| 88久久精品无码一区二区毛片 | 久久久亚洲欧洲日产国码二区 | 欧美日韩精品久久久免费观看|