吳淑華
01-5-31 下午 01:56:54
實際應用中,程式設計人員有時希望某一時刻只執行應用程式的單個例項。或許是因為應用程式需要訪問專用的特殊資源,如訪問數據機或是cd-rom驅動器,或許是因為應用程式需占用大量的系統資源,為保證其工作正常只能執行單一例項。
無論何種原因,如果在執行第二個例項時,簡單地終止先前的例項,那麼程式就會顯得十分粗糙。為避免這個問題,一般要求在第二個例項終止前,把第乙個例項的視窗送到棧頂。一種win16的方法是簡單地檢測應用程式先前例項的控制代碼,如果其值不為null,就可以標識其他的例項。然而,在win32中,這個值總為null。因此,只能選用其他方法。
這裡提供的方法不依賴於應用程式具體的視窗標題也不依賴於登入視窗類。不但可以終止第二個例項,而且可以把第乙個例項帶到前台。下面本文結合具體例項,詳細介紹一下實現步驟,並總結實現這些步驟的技術要點。
一、實現步驟
1.程式功能描述
該程式除主視窗mainform外,還包括使用者登入子視窗(密碼檢查視窗)loginform。程式啟動後,首先需通過使用者登入後才能進入應用程式主視窗。如果程式不包括使用者登入子視窗,即程式啟動後直接進入主視窗,實現步驟中需去掉步驟3和步驟5,會更簡單一些。本例項之所以包括登入視窗,是考慮到實際應用中有時會遇到類似情況,如果了解了後者如何實現,當然也就掌握了前者的實現方法。
2.編輯tmainform.formcreate(sender: tobject)
首先檢查應用程式其他的例項是否啟動。方法是建立乙個mutex(mutex很象臨界區,除了在訪問多程序時能同步資料外。)如果所命名的mutex已經存在,就說明應用程式的另乙個例項在執行。通過將唯一的乙個名字傳送給createmutex可以命名mutex。在此以應用程式名作為第三個引數。**如下:
procedure tmainform.formcreate(sender: tobject);
varmutexname: string;//互斥元名
hprevwnd, hdata: hwnd; //hprevwnd---視窗控制代碼, hdata---getprop返回的屬性值
result: tmodalresult;
logindlg: tloginform;
begin
//建立互斥元.如果互斥元已經存在,這就是應用程式的第二個事例.
//注意:當應用程式結束時,互斥元自動關閉.
createmutex(nil, true, pchar(mutexname));
if (getlasterror() = error_already_exists) then
begin
...end;
...end;
3.編輯tloginform.formcreate
由於該例項剛執行時要先呼叫登入子窗體loginform,只有通過了密碼檢查後才能啟動應用程式主窗體mainform。為了避免該程式的前乙個例項剛執行到登入視窗時,就執行程式的第二個例項,採用標記登入視窗的方法,並在步驟4中對該標記進行判斷。**如下:
procedure tloginform.formcreate(sender: tobject);
begin
//進行視窗初始化
......
//設定視窗屬性,以便在該應用程式系統啟動時進行判斷
end;
4.尋找先前例項
當斷定應用程式的另乙個例項正在執行後,首先把先前的例項調到前台並給予焦點,並最大化顯示該視窗。
在此,通過呼叫sdk的函式setprop新增乙個和視窗控制代碼組合成對的字串/資料控制代碼來標記乙個視窗。當檢查出其他例項正在執行時,可通過搜尋所有頂層視窗的標記,找到先前應用程式的主視窗。對每乙個視窗來說,通過呼叫sdk的getprop函式,可以找到先前例項的標記。如果視窗包含該標記,則找到了主視窗。視窗找到後,最大化並送到前台。具體**如下:
procedure tmainform.formcreate(sender: tobject);
varmutexname: string;//互斥元名
hprevwnd, hdata: hwnd; //hprevwnd---視窗控制代碼, hdata---getprop返回的屬性值
result :tmodalresult;
logindlg :tloginform;
begin
//建立互斥元.如果互斥元已經存在,這就是應用程式的第二個事例.
//注意:當應用程式結束時,互斥元自動關閉.
createmutex(nil, true, pchar(mutexname));
if (getlasterror() = error_already_exists) then
begin
//查詢該應用程式的前乙個主視窗控制代碼.
hprevwnd := getdesktopwindow();
hprevwnd := getwindow(hprevwnd, gw_child);
while (iswindow(hprevwnd)) do
begin
//判斷此視窗屬性標誌是否與設定的主視窗或登入視窗標誌相符.
hdata := getprop(hprevwnd, pchar(mutexname));
if (hdata = 1) or (hdata = 2) then
begin
//判斷是主視窗還是登入視窗,並將視窗獲得焦點.
if hdata = 1 then //主視窗
showwindow(hprevwnd, sw_maximize)
else //登入視窗
showwindow(hprevwnd,sw_restore);
setforegroundwindow(hprevwnd);
//如果此視窗有彈出視窗,設定焦點到彈出視窗.
setforegroundwindow(getlastactivepopup(hprevwnd));
break;
end else
//沒有找到視窗,轉到視窗列表中下乙個視窗.
hprevwnd := getwindow(hprevwnd, gw_hwndnext);
end;
exit;
end;
5.刪除登入視窗先前例項識別符號
刪除步驟3中建立的視窗標記。
procedure tloginform.formdestroy(sender: tobject);
begin
end;
6.刪除主窗體先前例項表示符
刪除步驟4中建立的視窗標記。
procedure tmainform.formdestroy(sender: tobject);
begin
end;
二、技術要點
1.定位先前視窗,使用mutex比用findwindow更安全,因為在例項完成建立主視窗之前,應用程式的第二個例項有可能啟動。使用mutex可以防止此類情況發生。
2.要尋找應用程式先前例項主視窗,可用findwindow尋找有標題的視窗。該方法需要知道主程式視窗的標題,但如果應用程式動態更新標題,則該方法不適用。也可用findwindow來尋找具有具體註冊視窗類的視窗。但該方法需要註冊使用者自己的視窗類,而且以後版本公升級時,可能需要修改**。而採用sdk的setprop函式來「標記」視窗可以避免上述問題。
3.通過呼叫api函式getdesktopwindow和getwindow可以搜尋所有的頂層視窗。再通過判斷視窗標記找到先前例項視窗。
如何防止乙個程式執行多次
讓程式只執行乙個例項 delphi篇 windows 下乙個典型的特徵就是多工,我們可以同時開啟多個視窗進行操作,也可以同時執行程式的多個例項,比如可以開啟許多個資源管理器進行檔案的移動複製操作。但有時出於某種考慮 比如安全性 我們要做出一些限制,讓程式只能夠執行乙個例項。在delphi程式設計中,...
如何防止乙個類被繼承?
c view第一期 此處一定要宣告,否則在nonderivablehelper類中將nonderivable定義為友元類時,會認為是private中的某個類 class nonderivable namespace private friend class nonderivable ifdef nd...
乙個類如何防止被拷貝
在c 中,對於類我們如何防止被拷貝?首先,我們知道,在c 類中,我們是如何實現對類例項化物件的拷貝。是通過該類中的拷貝建構函式和賦值運算子的過載來實現的,那麼我們可以通過禁止在類外使用這兩種方法來達到防止該類被拷貝的目的。具體怎麼做 將拷貝建構函式和賦值運算子的過載,宣告為private 私有,類外...