自動關機&初試多線程(6月28日)

感覺要搞個定時關機的小軟件方便點,比如說晚上要開着下BT,電驢什麼的,有時候可以預測一兩個小時候就可以下好,所以不想機器整晚開着。但是它本身不提供這個功能,於是今天抽了一下午和晚上的時間纔給弄了出來,我暈(本以爲一兩小時就可以搞定的)。

原因是這樣的,本來打算用設置定時器SetTimer來不斷的和系統時間比較,如果系統時間超過了我設定的時間就讓它執行關機的代碼。但是我忽然想起來以前看《windows 核心編程》的時候提到過一個函數:SetWaitableTimer,這個函數可以當其設定的時間(當然是在其參數裏面設置,第二個參數)到了後就執行其第四個參數所設定的回調函數。

  • 提到回調函數要記住一點,回調函數的參數一般都是固定的,不要修改。比如說這裏的回調函數PCshutdown(LPVOID lpArgToCompletionRoutine,DWORD dwTimerLowValue,DWORD dwTimerHighValue),當然,函數名是可以隨意取的,我用的這個回調函數的原型是MSDN裏面的TimerAPCProc。還有比如說創建線程的_beginthreadex,我設定的回調函數是waitableThreadFunc(void* pArguments),當然其參數是在MSDN裏面就規定好了的,這可以看_beginthreadex的相關參數即可。

但是既然已經這麼想了,我就想試試用這個函數,練練手,反正那個SetTimer用的很熟了。現在想起來,其實還是SetTimer簡單點,因爲畢竟不需要用多線程。不過也好,給了我一個機會去自己動手寫第一個多線程函數,而且並不複雜,適合我這樣的多線程編程初學者。下面具體的來說說這個程序。

於我而言,這裏的難點有一個,就是關機的關鍵代碼是什麼,這個很簡單,網上一搜一大把的。但是做到後來發現用我的這個SetWaitableTimer還有第二個難點,就是多線程問題。從來沒有自己寫過,接觸過一點,感覺比較容易出錯,所以對這個東東有點後怕。

1. 關機的關鍵代碼

其實真正其關機的就是一個函數:ExitWindowsEx(EWX_SHUTDOWN|EWX_POWEROFF,0);但是我們看到了,去查MSDN,會發現EWX_SHUTDOWN要求進程有SE_SHUTDOWN_NAME權限。所以如何取得這個權限是個問題。關鍵代碼如下所示:

 

其步驟是清晰的:先獲取訪問令牌--再查詢並複製到結構體TOKEN_PRIVILEGES tkp中--修改該結構體---重新使訪問令牌權限生效。但是依然不是很清晰的是該結構體的具體含義是什麼(希望有達人來指教和討論),現在只能從網絡上找到點具體例子和不完整的介紹貼到這裏,如下:

 typedef   struct   _TOKEN_PRIVILEGES   {    
            DWORD   PrivilegeCount;    
            LUID_AND_ATTRIBUTES   Privileges[];    
  }   TOKEN_PRIVILEGES,   *PTOKEN_PRIVILEGES;    
  PrivilegeCount
指的數組原素的個數,接着是一個LUID_AND_ATTRIBUTES類型的數組,再來看一下LUID_AND_ATTRIBUTES這個結構的內容,聲明如下:  
   
  typedef   struct   _LUID_AND_ATTRIBUTES   {    
            LUID       Luid;    
            DWORD     Attributes;    
  }   LUID_AND_ATTRIBUTES,   *PLUID_AND_ATTRIBUTES  

   
  第二個參數就指明瞭我們要進行的操作類型,有三個可選項:   SE_PRIVILEGE_ENABLED、SE_PRIVILEGE_ENABLED_BY_DEFAULT、SE_PRIVILEGE_USED_FOR_ACCESS。要使能一個權限就指定Attributes爲SE_PRIVILEGE_ENABLED

2.多線程的原因

  hTimer=CreateWaitableTimer(NULL,FALSE,NULL);
  SetWaitableTimer(hTimer,&liUTC,0,PCshutdown,NULL,FALSE);
  SleepEx(INFINITE,TRUE);
//第二個參數一定要設爲TRUE,否則只有設定的時間值到了後函數纔會返回,而這裏設置的是無限

看一下這個代碼,你會發現,當我執行完第二句後,接着執行第三句,即創建該等待計時器內核對象的線程被掛起,這意味着一旦用戶設定好時間並按下確認要關機的按鈕後該對話框就無法再用了。因爲這裏創建該線程和創建該主窗口是同一個,即主線程。所以就需要把以上的代碼放到另外一個線程裏面去執行,這樣當執行 SleepEx,線程就掛起來,但是我程序界面主窗口還是可以正常響應的。

3.多線程的流程

創建線程,使用_beginthreadex,一般在C++裏面就用這個函數,當然也可以用_beginthread,和MFC中的AfxBeginThread

結束線程,使用_endthreadex。這裏這些相關函數的參數使用依然不是很清楚,只是仿照MSDN裏面的例子使用,相信還要經過好多次的練習才行。

 

最後再說下遇到的問題,首先是如果不用多線程那麼就會出現點擊按鈕後,程序就掛起來了,界面上的任何一個東西都無法響應;其次遇到錯誤提示“非法的本地函數”,這是由於少些了大括號的原因(一般這個錯誤都是這個原因);最後,那個多線程的地方爲什麼要把返回值作爲HANDLE類型然後再close掉,原因不是很清楚,不是已經在其回調函數中已經調用_endthreadex(0);了嗎?具體的以後想明白了再回來修改

HANDLE hThread=(HANDLE)_beginthreadex(NULL,0,&waitableThreadFunc,NULL,0,&threadID);CloseHandle(hThread);

好了,今天就這樣吧。(更加具體的代碼見E:/c++/Projects/AutoShutDown)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章