二次啟動,這個名詞恐怕做過產品的都會了解吧。由於二次啟動程序,會產生許多的問題(這一般是根據程序的質量而言的),例如:對共享資源的操作、界面的更新顯示等等。
一般的產品(至少是我現在參與過的),都或多或少要求進行程序二次啟動的防止,實際的對策各種各樣,這裡只對於實際工作中遇到的一個比較典型的問題進行分析,找出其解決辦法。
我們是做鬼子外包程序的“大”公司,因此很多顯示、處理式樣是由鬼子決定的。對於二次啟動防止,鬼子的要求是(這裡指的程序是指能最小化的有顯示界面的窗口程序):如果程序已經在用戶A下啟動,
1、在用戶A下,再次啟動本程序,將程序置前顯示(即顯示在所有窗口最上面);
2、在用戶B下,再次啟動本程序,彈出二次啟動防止對話框(因為在用戶A下已經啟動過啦);
第二種情況下如何顯示實際是挺復雜的,不過不是這篇所討論的重點。這裡主要討論第一種情況下該如何完成相應的功能。
這裡還要分為更為詳細的情況(由於我們應客戶的要求,在Vista下開發,所以提到的都是Vista下的操作。XP下也有類似的操作,不過是否會產生同樣的現象,沒驗證過,:-D ):
1、首次直接啟動程序,第二次也直接啟動程序(這其中,可能已經通過切換,將程序顯示在其它窗口後面,下面也存在同樣情況,不再贅述);
2、首次直接啟動程序,將程序最小化,第二次也直接啟動程序;
3、首次直接啟動程序,第二次提升權限啟動程序;
4、首次直接啟動程序,將程序最小化,第二次提升權限啟動程序;
5、首次提升權限啟動程序,第二次直接啟動程序;
6、首次提升權限啟動程序,將程序最小化,第二次直接啟動程序;
7、首次提升權限啟動程序,第二次也提升權限啟動程序;
8、首次提升權限啟動程序,將程序最小化,第二次也提升權限啟動程序;
應該就這些吧,是不是感覺很啰嗦?俺也很無奈,誰讓現實情況就這樣呢,因為這些不同的情況,才會引出本文的。
產品是由Visual C# 2005開發的(實際跟版本關系不大,因為使用到的類和方法好像在.net Framework 1.0中也支持,沒仔細查過),使用的都是.Net Framework中提供的類和方法(當然還有一個WinAPI——SetForegroundWindow,用來將窗口置前顯示的,就不多描述了,可以查看MSDN),互斥量Mutex。基本邏輯是這樣的:進入程序後,創建一個命名互斥量。如果互斥量不存在,正常啟動程序;如果互斥量已存在,獲取窗口句柄,將窗口置前顯示。
說實話,這個邏輯基本上是沒什麼問題的。不過由於.Net Framework的局限性,以及Vista比較討厭的安全性,導致上面提到的第6種情況會無法做到,即將最小化的窗口再顯示出來。
問題出在哪裡呢?仔細調查發現是下面兩種原因所導致的:
1、Vista新的安全措施要求低權限用戶不能向高權限用戶發消息,實際上就是高權限用戶一般接收不到低權限用戶發出來的消息;
2、.Net Framework在創建互斥量等內核對象時,不能指定對象的安全性。
第一種原因,是系統為了增強安全性所做的必要的措施,看來我們是無法更改了(除非我們能說動MS,要不然就做病毒吧),只好在第二種原因上想辦法。而.net Framework又不支持,只好使用WinAPI了(感想:.net Framework所封裝的類雖然易用,不過還是WinAPI強大啊,好多功能是.Net Framework中沒有的)。更改後的程序啟動邏輯為(必要的清理資源工作就不敘述了):
1、進入程序後創建全局命名互斥量(mutex);
2、如果互斥量已存在,發送一個事件(event),退出程序;
3、使用GetLastError得到的錯誤不是ERROR_FILE_NOT_FOUND,退出;
4、創建全局命名互斥量;
5、創建具有指定安全屬性的事件(event),並創建將窗口置前顯示的等待線程;——注意:這裡是解決問題的關鍵!!!
6、正常啟動程序。
既然關鍵在於創建具有指定安全性的事件,那麼該如何創建呢?具體代碼如下:
Ok啦,由於創建的事件具有sa的安全屬性(所有用戶均可訪問),因此上面提到的第6種情況已經順利解決。