摘要 本文給出了一種通過設置系統熱鍵來呼出在系統後台隱藏運行的服務程序的一種方法通過這種方法可以實現後台服務程序在必要的時候同用戶的交互設置
引言
通常情況下用於後台監控的服務程序(Service)是沒有界面的甚至也沒有提供任務欄圖標因此絕大多數情況下服務程序是無法同用戶進行交互的但是在實際應用中這些服務程序雖然絕大多數時間是在後台運行但是在某些必要的情況下還是需要用戶的干預並同用戶進行一些必要的交互操作但是由於服務程序沒有提供任何可供交互操作之用的界面因此如何將其從後台激活(即呼出)成為解決此問題的一個關鍵本文下面就給出一種通過設置系統熱鍵的方法來激活運行於後台的服務程序
設計思路
盡管從理論上可以有許多方法來激活後台運行的服務程序比如可以通過尋找服務程序的窗口標題名而得到其窗口指針然後再向此窗口發送消息使其出現到前台;也可以通過系統快照對當前系統進程進行枚舉然後再將其激活到前台但是以上這些方法都需要另外編寫應用程序對後台服務程序的激活實際是再這些應用程序中進行的這樣的處理方式顯然十分不便最好的方法是對程序的激活和隱藏處理均在服務程序內部完成因此可以考慮接收系統發出的消息如果通過設置全局鉤子對設置事件進行攔截捕獲顯然是相當煩瑣的在此考慮使用系統熱鍵來激活後台服務程序其實現過程非常簡單只需先向操作系統添加一個全局原子(Atom)然後再向操作系統登記一個熱鍵當程序在後台運行期間一旦有此熱鍵按下操作系統將會拋出系統消息WM_HOTKEY所以服務程序只需在 WM_HOTKEY消息響應函數中添加相應代碼即可實現服務程序的後台激活
系統熱鍵的注冊
根據前面的介紹不難寫出為後台服務程序添加對系統熱鍵響應的功能代碼首先通過函數GlobalFindAtom()查詢本服務程序所對應的全局原子是否已存在於全局原子表中如果發現則說明系統中已經存在有此服務程序退出如果沒有發現則通過GlobalAddAtom()函數向全局原子表添加一個字串並獲取得到一個唯一標識此字串的原子以上兩函數原型分別為
以下是引用片段
ATOM GlobalFindAtom(LPCTSTR lpString);
ATOM GlobalAddAtom(LPCTSTR lpString);
其中輸入參數為一個描述原子的字符串如果GlobalFindAtom()從全局原子表中找到了指定的字串那麼將返回此字串對應的原子否則返回GlobalAddAtom()如果創建成功將返回一個新創建的原子
接下來為了能在程序運行期間捕獲到系統熱鍵需要通過RegisterHotKey()定義一個系統范圍的熱鍵該函數原形如下
以下是引用片段
BOOL RegisterHotKey(HWND hWnd // 接收熱鍵響應的窗口句柄
int id // 熱鍵的標識
UINT fsModifiers // 控制鍵標志
UINT vk // 虛擬鍵值
);
其中熱鍵標識id必須是一個范圍在xC到xFFFF之間的全局唯一的值為了避免可能引起的熱鍵沖突通常把GlobalAddAtom ()返回的原子作為參數傳入而且GlobalAddAtom()返回值的范圍同id參數的允許范圍是完全一致的參數fsModifiers定義了同虛擬鍵值vk同時按下而產生出系統熱鍵消息WM_HOTKEY的控制鍵組合如MOD_ALTMOD_CONTROLMOD_SHIFT和 MOD_WIN等在本例中將要設定的系統熱鍵為Alt+Ctrl+R因此參數fsModifiers和vk分別設置為MOD_ALT| MOD_CONTROL和VK_R有關系統熱鍵的注冊實現方法可以整理如下
以下是引用片段
// 獲取當前窗口句柄
HWND handle = GetSafeHwnd();
// 尋找HotKey對應的原子是否存在於原子列表
if(GlobalFindAtom(Hotkey) == )
{
// 如果沒有存在於原子列表則創建一個原子
id = GlobalAddAtom(Hotkey);
//注冊全局熱鍵Ctrl + Alt + R
RegisterHotKey(handle id CONTROL + ALT R);
}
else // 如果HotKey已經存在於原子列表則終止程序運行
PostQuitMessage();
服務程序的隱藏與激活
服務程序除了被激活後同用戶的交互絕大部分時間都是在後台隱藏運行的不僅界面是不可視的而且在任務列表中也不應當出現關於界面的隱藏比較簡單可以通過向ShowWindow()函數設置SW_HIDE參數來實現而在任務列表中的隱身則一般的做法是通過調用系統內核KernelDLL的RegisterServiceProcess()函數將其設置成為一個服務進程這樣在任務列表中也實現了隱身但是RegisterServiceProcess()函數並非一個標准的API函數使用起來有點煩瑣首先要通過 GetModuleHandle()函數得到KernelDLL模塊的句柄並由此通過GetProcAddress()函數進一步得出 RegisterServiceProcess()函數在KernelDLL中的入口地址最後才能使用 RegisterServiceProcess()函數該函數原型聲明如下
以下是引用片段
DWORD RegisterServiceProcess(DWORD dwProcessIdDWORD dwType);
其第一個參數指定了將要注冊為服務進程的進程標識參數dwType指定是去注冊一個服務進程(為時)還是去卸載一個服務進程(為時)其具體服務注冊過程如下
typedef DWORD (WINAPI *RSP)(DWORD dwProcessIdDWORD dwType);
// 獲取KernelDLL模塊句柄
HMODULE m_hKernel = ::GetModuleHandle(KernelDLL);
// 得到RegisterServiceProcess()函數入口地址
RSP m_rsp = (RSP)::GetProcAddress(m_hKernel RegisterServiceProcess);
// 注冊當前進程為服務進程
m_rsp(::GetCurrentProcessId());
在服務程序後台運行期間一旦有系統熱鍵Alt+Ctrl+R按下將發出系統熱鍵消息WM_HOTKEY該消息的消息響應函數不能通過 ClassWizard來添加而只能手工完成消息映射在消息響應函數中通過對消息參數 wParam的判斷可以確定出是否是本服務程序所設定的系統熱鍵如果是通過ShowWindow(SW_SHOW)將程序界面顯示出來以進行同用戶的交互操作:
以下是引用片段
void CServiceDlg::OnHotKey(WPARAM wParam LPARAM lParam)
{
// 判斷是否是本服務程序設置的系統熱鍵
if (wParam == id)
{
……
// 在此發送WM_PAINT消息在OnPain()中通過
// ShowWindow(SW_SHOW)將界面設置為可視
PostMessage(WM_PAINT );
}
}
系統熱鍵的卸載
由於前面將系統熱鍵全局原子等都注冊到系統因此必須在服務程序退出之前將其卸載否則將導致下次注冊時的失敗函數UnregisterHotKey()負責完成對系統熱鍵的釋放GlobalDeleteAtom()將全局原子從全局原子列表刪除
小結
通過本文所述方法為後台運行的系統服務程序添加此熱鍵呼出功能可以真正實現程序的後台隱蔽運行熱鍵激活非常有利於管理員和用戶的管理與使用本文所述程序在Windows Professional下由Microsoft Visual C++ 編譯通過
From:http://tw.wingwit.com/Article/program/net/201311/13819.html