XP系統(tǒng)中屏蔽Ctrl+Esc鍵序列的方法
發(fā)布時間:2013-11-11 10:04:57 作者:佚名
我要評論
在Windows XP中如何實現(xiàn)屏蔽CTRL+ALT+DEL組合鍵,也就是任務(wù)管理器,任務(wù)切換組合鍵(Alt+Tab),任務(wù)欄和“開始”菜單Ctrl+Esc,VK_LWIN,VK_RWIN
對于用過Windows的人,幾乎沒有人不知道Ctrl+Alt+Del組合鍵,尤其是在使用經(jīng)常死機的Windows9x時,使用它的頻率更高,這一組合鍵是專門為了系統(tǒng)安全起見提供的緊急出口。VC知識庫在線雜志第11期,ac952_z_cn在他的個人專欄中寫過一篇關(guān)于這方面的文章:“WINDOWS NT/2000下如何屏蔽CTRL+ALT+DEL”。因此本文側(cè)重于介紹在Windows XP中如何實現(xiàn)屏蔽CTRL+ALT+DEL組合鍵,也就是任務(wù)管理器,任務(wù)切換組合鍵(Alt+Tab),任務(wù)欄和“開始”菜單(Ctrl+Esc,VK_LWIN,VK_RWIN)。這個方法也能應(yīng)用于Windows 2000環(huán)境。
在Windows 9x/Me系統(tǒng)中,屏蔽Ctrl+Alt+Del和各種任務(wù)開關(guān)鍵的方法是通過下面的方法實現(xiàn)的:
BOOL bOldState;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &bOldState, 0);
MS大佬認為這種方法很業(yè)余,所以在Windows NT/2000/XP中對此進行了修改。在這些較新的Windows版本中用戶登陸使用Winlogon和GINA——Graphical Identification and Authentication,意思是圖形化的身份認證,挺嚇唬人的是不是!其實就那么回事。Winlogon是Windows系統(tǒng)的一部分,它專門提供交互式登陸支持,而GINA則是Winlogon用來實現(xiàn)認證的一個DLL——這個DLL就是msgina.dll。WlxInitialize、WlxActivateUserShell便是其中輸出,當(dāng)然不知這兩個,還有別的。前者進行自身的初始化,后者激活用戶的外殼程序。Windows就是用這個DLL來實現(xiàn)用戶名+口令的身份認證的,但是開發(fā)人員可以用自己的GINA代替msgina.dll。例如,實現(xiàn)智能卡、視網(wǎng)膜掃描儀、DNA檢查等等認證機制來代替輸入用戶名+口令形式的身份檢查。 下面的表格中列出了與GINA有關(guān)的全部函數(shù)。其中有一個是WlxLoggedOnSAS,當(dāng)按下Ctrl+Alt+Del 鍵時,Winlogon便調(diào)用這個函數(shù)。
(表一)GINA 函數(shù)一覽表 函數(shù) 描述
WlxActivateUserShell激活用戶外殼程序
WlxDisplayLockedNotice允許GINA DLL 顯示鎖定信息
WlxDisplaySASNotice 當(dāng)沒有用戶登陸時,Winlogon調(diào)用此函數(shù)
WlxDisplayStatusMessageWinlogon 用一個狀態(tài)信息調(diào)用此函數(shù)進行顯示
WlxGetConsoleSwitchCredentials Winlogon調(diào)用此函數(shù)讀取當(dāng)前登陸用戶的信任信息,并透明地將它們傳到目標會話
WlxGetStatusMessage Winlogon 調(diào)用此函數(shù)獲取當(dāng)前狀態(tài)信息
WlxInitialize 針對指定的窗口位置進行GINA DLL初始化
WlxIsLockOk 驗證工作站正常鎖定
WlxIslogoffOk 驗證注銷正常
WlxLoggedOnSAS 用戶已登陸并且工作站沒有被加鎖,如果此時接收到SAS事件,則Winlogon 調(diào)用此函數(shù)
WlxLoggedOutSAS 沒有用戶登陸,如果此時收到SAS事件,則Winlogon 調(diào)用此函數(shù)
WlxLogoff 請求注銷操作時通知GINA DLL
WlxNegotiate 表示當(dāng)前的Winlogon版本是否能使用GINA DLL
WlxNetworkProviderLoad 在加載網(wǎng)絡(luò)服務(wù)提供程序收集了身份和認證信息后,Winlogon 調(diào)用此函數(shù)
WlxRemoveStatusMessage Winlogon 調(diào)用此函數(shù)告訴GINA DLL 停止顯示狀態(tài)信息
WlxScreensaverNotify 允許GINA與屏幕保護操作交互
WlxShutdown 在關(guān)閉之前Winlogon 調(diào)用此函數(shù),允許GINA實現(xiàn)任何關(guān)閉任務(wù),例如從讀卡器中退出智能卡
WlxStartApplication 當(dāng)系統(tǒng)需要在用戶的上下文中啟動應(yīng)用程序時調(diào)用此函數(shù)
WlxWkstaLockedSAS當(dāng)工作站被鎖定,如果接收到一個SAS,則Winlogon 調(diào)用此函數(shù)
在默認情況下,GINA顯示登陸對話框,用戶輸入用戶名及口令。所以要想屏蔽掉Ctrl+Alt+Del,則可以寫一個新的MyGina.dll,其中提供接口調(diào)用msgina.dll的函數(shù)WlxLoggedOnSAS,從而實現(xiàn)Ctrl+Alt+Del屏蔽。或者編寫一個鍵盤驅(qū)動程序來實現(xiàn)。
難道屏蔽Ctrl+Alt+Del真的象上述所說的那么麻煩嗎?有沒有更好的方法呢?答案是肯定的。所以忘掉GINA吧,使用操作系統(tǒng)的策略設(shè)置完全可以搞掂這個問題。方法是進入"開始"菜單,選擇"運行",然后在運行對話框中輸入"gpedit.msc",啟動Windows系統(tǒng)的組策略編輯器。在左邊窗格查看"用戶配置|管理模板|系統(tǒng)|登錄/注銷",則在右邊窗格策略里不難發(fā)現(xiàn)"禁用任務(wù)管理器"一項。
組策略編輯器
通過對這個策略的設(shè)置可以屏蔽掉Ctrl+Alt+Del。如果要通過編寫代碼來實現(xiàn),則必須操作下面的注冊表項:
HKCU\
Software\
Microsoft\
Windows\
CurrentVersion\
Policies\
System\DisableTaskMgr = dword:1
如此設(shè)置之后,則在Windows XP中,如果用戶按下Ctrl+Alt+Del,則會彈出一個出錯對話框,
注意這里假設(shè)在控制面板中“用戶帳號”管理的“選擇登錄和注銷選項”設(shè)置啟用了“使用歡迎屏幕”一項。
否則,XP將使用Windows的傳統(tǒng)登錄模式,要求用戶輸入帳戶名。并且Ctrl+Alt+Del組合鍵的 行為也和傳統(tǒng)的行為一樣,注冊表中DisableTaskMgr的設(shè)置也只是將登錄/注銷對話框中的任務(wù)管理器按鈕屏蔽或置灰。 有人可能會問,有關(guān)任務(wù)管理器的文檔又沒有明確說明,那你是怎么知道DisableTaskMgr是用來禁用任務(wù)管理器的呢?告訴你吧, 我是在使用GPEDIT時發(fā)現(xiàn)的。GPEDIT是一個非常有用的工具,不僅可以用它來編輯策略,還可以用它來發(fā)現(xiàn)策略。利用這個工具可以輕松控制Windows的許多東西,從許可權(quán)限的存取到是否使用IE的傳統(tǒng)外觀,從是否顯示對話框中的Places Bar到是否用Ctrl+Alt+Del 啟動任務(wù)管理器。總之用它可以配置上百個界面行為,因此它是一個足以讓系統(tǒng)管理員垂延三尺的工具。一旦找到了感興趣的策略,那如何知道相應(yīng)的注冊表位置呢?有兩種方法。第一種是比較粗魯?shù)霓k法:在修改策略的前后將注冊表輸出到一個.reg文件,然后比較它們有什么不同。所有的策略無外乎以下的四個注冊表鍵:
// 用戶指定
HKEY_CURRENT_USER\Software\Policies
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies
// 機器指定
HKEY_LOCAL_MACHINE\Software\Policies
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies
第二種方法是直搗信息源頭--檢查描述策略的管理模板文件(.adm)。下面是Windows XP的system.adm文件對 DisableTaskMgr的描述:(Windows 2000對此的描述稍有不同,其細節(jié)請參考Windows 2000的資源開發(fā)包)
CATEGORY !!CADOptions
#if version >= 4
EXPLAIN !!CADOptions_Help
#endif
KEYNAME "Software\Microsoft\Windows\CurrentVersion\Policies\System"
POLICY !!DisableTaskMgr
#if version >= 4
SUPPORTED !!SUPPORTED_Win2k
#endif
EXPLAIN !!DisableTaskMgr_Help
VALUENAME "DisableTaskMgr"
END POLICY
;
; More Ctrl+Alt+Del policies here...
;
END CATEGORY ; Ctrl+Alt+Del options
……
……
DisableTaskMgr_Help="防止用戶啟動''任務(wù)管理器''(Taskmgr.exe)。\n\n如果該設(shè)置被啟用,并且用戶試圖啟動任務(wù)管理器,系統(tǒng)
會顯示消息,解釋是一個策略禁止了這個操作。\n\n任務(wù)管理器讓用戶啟動或終止程序,監(jiān)視計算機性能,查看及監(jiān)視計算機上所有運行
中的程序 (包含系統(tǒng)服務(wù)), 搜索程序的執(zhí)行文件名,及更改程序運行的優(yōu)先順序。"
DisableTaskMgr="刪除任務(wù)管理器"
以上是DisableTaskMgr的描述片斷
正是在這段描述中KEYNAME 和VALUENAME指定了注冊表的鍵值對。利用這種方法,你可以為自己的應(yīng)用程序創(chuàng)建管理模板和策略,但編輯和瀏覽.adm模板文件的編輯器必須支持Unicode字符。如Notepad或者WordPad等都可以。此外,使用管理模板文件,系統(tǒng)管理員可以用它為整個組織配置需要的策略——由此可以看出,此文件在系統(tǒng)中的地位舉足輕重!有關(guān)模板管理文件格式的詳細信息請參考平臺SDK。最后需要強調(diào)的是DisableTaskMgr只是禁用Ctrl+Alt+Del的功能。下面我們來討論如何捕獲它的按鍵序列。要想截獲Ctrl+Alt+Del,有三種可選擇的方法:
1、 編寫一個GINA代理;此方法我們在以后的文章中介紹。實際上,ac952_z_cn的個人專欄文章:“WINDOWS NT/2000下如何屏蔽CTRL+ALT+DEL”使用的就是這種方法。
2、 編寫一個鍵盤驅(qū)動程序;本文例子程序使用的方法。
3、 用自己的程序代替任務(wù)管理器程序TaskMgr.exe。
屏蔽Ctrl+Alt+Del解決方案的具體實現(xiàn)細節(jié)請參考本文的例子代碼。
下面讓我們來解決屏蔽任務(wù)切換鍵序列的問題,這些鍵序列包括Alt+Tab、Ctrl+Esc、Alt+Esc、VK_LWIN/VK_RWIN以及任務(wù)欄。在很早以前的Window 3.1年代,處理這個問題的方法是通過WM_SYSKEYDOWN實現(xiàn)。到了Windows 9x時期,本文前面提到過對這個問題的處理方法,使用SPI_SETSCREENSAVERRUNNING。但是進入Windows NT 4.0 (SP3 +),Windows 2000以及Windows XP時代,對這個問題的處理已經(jīng)有所不同,必須寫一個低級的鍵盤驅(qū)動鉤子。不要怕,因為要實現(xiàn)這個鉤子并不是很難。本文下面會介紹如何實現(xiàn)這個鍵盤鉤子。一般來講,系統(tǒng)級鉤子必須是一個DLL。下面是本文提供的一個鍵盤鉤子DLL的源代碼片斷(TaskKeyHook.dll):
頭文件
////////////////////////////////////////////////////////////////
//TaskKeyHook.h
//
#define DLLIMPORT __declspec(dllimport)
DLLIMPORT BOOL DisableTaskKeys(BOOL bEnable, BOOL bBeep);
DLLIMPORT BOOL AreTaskKeysDisabled();
實現(xiàn)文件
////////////////////////////////////////////////////////////////
// TaskKeyHook.cpp
//
#define _WIN32_WINNT 0x0500 // for KBDLLHOOKSTRUCT
#include // MFC core and standard components
#define DLLEXPORT __declspec(dllexport)
//////////////////
// App (DLL) object
//
class CTaskKeyHookDll : public CWinApp {
public:
CTaskKeyHookDll() { }
~CTaskKeyHookDll() { }
} MyDll;
////////////////////////////////////////////////
// 下面的代碼表示這一部分在此DLL所有實例之間共享
// 低級鍵盤鉤子一定是系統(tǒng)級的鉤子
//
#pragma data_seg (".mydata")
HHOOK g_hHookKbdLL = NULL; // 鉤子句柄
BOOL g_bBeep = FALSE; // 按下非法鍵時蜂鳴響鈴
#pragma data_seg ()
#pragma comment(linker, "/SECTION:.mydata,RWS") // 告訴鏈接器:建立數(shù)據(jù)共享段
//////////////////////////////////
// 低級鍵盤鉤子
// 截獲任務(wù)轉(zhuǎn)換鍵:不傳遞直接返回
//
LRESULT CALLBACK MyTaskKeyHookLL(int nCode, WPARAM wp, LPARAM lp)
{
KBDLLHOOKSTRUCT *pkh = (KBDLLHOOKSTRUCT *) lp;
if (nCode==HC_ACTION) {
BOOL bCtrlKeyDown =
GetAsyncKeyState(VK_CONTROL)>>((sizeof(SHORT) * 8) - 1);
if ((pkh->vkCode==VK_ESCAPE && bCtrlKeyDown) || // Ctrl+Esc
// Alt+TAB
(pkh->vkCode==VK_TAB && pkh->flags & LLKHF_ALTDOWN) ||
// Alt+Esc
(pkh->vkCode==VK_ESCAPE && pkh->flags & LLKHF_ALTDOWN)||
(pkh->vkCode==VK_LWIN || pkh->vkCode==VK_RWIN)) { // 開始菜單
if (g_bBeep && (wp==WM_SYSKEYDOWN||wp==WM_KEYDOWN))
MessageBeep(0); // 蜂鳴
return 1; // 不再往CallNextHookEx傳遞,直接返回
}
}
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
}
////////////////////////////////////////////////
// 是否屏蔽任務(wù)鍵序列——也就是說鍵盤鉤子是否安裝?
// 注:這里假設(shè)沒有其它鉤子做同樣的事情
//
DLLEXPORT BOOL AreTaskKeysDisabled()
{
return g_hHookKbdLL != NULL;
}
////////////////////////////////////////////////
// 屏蔽任務(wù)鍵:安裝低級鍵盤構(gòu)
// 返回當(dāng)前是否屏蔽標志(TRUE/FALSE)
//
DLLEXPORT BOOL DisableTaskKeys(BOOL bDisable, BOOL bBeep)
{
if (bDisable) {
if (!g_hHookKbdLL) {
g_hHookKbdLL = SetWindowsHookEx(WH_KEYBOARD_LL,
MyTaskKeyHookLL, MyDll.m_hInstance, 0);
}
} else if (g_hHookKbdLL != NULL) {
UnhookWindowsHookEx(g_hHookKbdLL);
g_hHookKbdLL = NULL;
}
g_bBeep = bBeep;
return AreTaskKeysDisabled();
}
TaskKeyHook 輸出兩個函數(shù):DisableTaskKeys 和 AreTaskKeysDisabled。前者安裝WH_KEYBOARD_LL 鉤子;后者判斷這個鉤子是否安裝。此鍵盤鉤子的處理思路是截獲Alt+Tab,Ctrl+Esc,Alt+Esc以及Windows 鍵VK_LWIN/VK_RWIN,關(guān)于這兩個鍵,稍候會有詳細描述。當(dāng)鉤子碰到這些鍵時,它直接返回到調(diào)用者,而不是將處理傳遞給CallNextHookEx 。
LRESULT CALLBACK MyTaskKeyHookLL(...)
{
if (/* 任務(wù)鍵*)
return 1; // 立即返回
return CallNextHookEx(...);
}
TaskKeyHook的大部分實現(xiàn)都很簡單。只有一個地方用到了一點小技巧:既使用#pragma data_seg 命名包含全程數(shù)據(jù)的數(shù)據(jù)段,并且用#pragma comment (linker...)告訴鏈接器讓這個數(shù)據(jù)段為共享段。實現(xiàn)細節(jié)請參考源代碼。本文附帶的例子程序(TrapKeys.exe)匯集了上述幾個有關(guān)屏蔽鍵盤按鍵序列的功能,除此之外,它還有一個功能就是禁用任務(wù)欄。因為既然禁用了任務(wù)轉(zhuǎn)換鍵,那么一般來說,也必然要禁用任務(wù)欄,否則禁用任務(wù)轉(zhuǎn)換鍵就沒有意義了。禁用任務(wù)欄的具體方法如下:
HWND hwnd = FindWindow("Shell_traywnd", NULL);//找到任務(wù)欄
EnableWindow(hwnd, FALSE); // 禁用任務(wù)欄
如圖四是例子程序運行畫面:
圖四 TrapKeys程序運行畫面
以下是TrapKeys程序的實現(xiàn)代碼:
/////////////////////////////////////////////////
// TrapKeys.cpp
//
#include "stdafx.h"
#include "resource.h"
#include "StatLink.h"
#include "TaskKeyMgr.h"
////////////////////
// 主對話框
//
class CMyDialog : public CDialog {
public:
CMyDialog(CWnd* pParent = NULL) : CDialog(IDD_MYDIALOG, pParent) { }
protected:
HICON m_hIcon;
CStaticLink m_wndLink1;
CStaticLink m_wndLink2;
CStaticLink m_wndLink3;
virtual BOOL OnInitDialog();
// 命令/UI 的更新處理
afx_msg void OnDisableTaskMgr();
afx_msg void OnDisableTaskKeys();
afx_msg void OnDisableTaskbar();
afx_msg void OnUpdateDisableTaskMgr(CCmdUI* pCmdUI);
afx_msg void OnUpdateDisableTaskKeys(CCmdUI* pCmdUI);
afx_msg void OnUpdateDisableTaskbar(CCmdUI* pCmdUI);
afx_msg LRESULT OnKickIdle(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
};
///////////////////////////////////////////////////////
// 標準的MFC 對話框應(yīng)用類代碼。
//
class CMyApp : public CWinApp {
public:
virtual BOOL InitInstance() {
// 初始化app:運行對話框
CMyDialog dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
return FALSE;
}
virtual int ExitInstance() {
// 為了按全起見,在退出程序的時候,將所有禁用的項目復(fù)原
CTaskKeyMgr::Disable(CTaskKeyMgr::ALL, FALSE);
return 0;
}
} theApp;
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_COMMAND(IDC_DISABLE_TASKKEYS,OnDisableTaskKeys)
ON_COMMAND(IDC_DISABLE_TASKBAR, OnDisableTaskbar)
ON_COMMAND(IDC_DISABLE_TASKMGR, OnDisableTaskMgr)
ON_UPDATE_COMMAND_UI(IDC_DISABLE_TASKKEYS, OnUpdateDisableTaskKeys)
ON_UPDATE_COMMAND_UI(IDC_DISABLE_TASKBAR, OnUpdateDisableTaskbar)
ON_UPDATE_COMMAND_UI(IDC_DISABLE_TASKMGR, OnUpdateDisableTaskMgr)
ON_MESSAGE(WM_KICKIDLE,OnKickIdle)
END_MESSAGE_MAP()
///////////////////////////////////////////////
// 初始化對話框:子類化超鏈接柄加栽圖標
//
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// 初始化超鏈接
m_wndLink1.SubclassDlgItem(IDC_EMAIL,this);
m_wndLink2.SubclassDlgItem(IDC_VCKBASEURL,this);
m_wndLink3.SubclassDlgItem(IDC_VCKBASELINK,this);
// 自己設(shè)置對話框圖標。MFC不會為對話框應(yīng)用程序設(shè)置它
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
SetIcon(m_hIcon, TRUE); // 打圖標
SetIcon(m_hIcon, FALSE); // 小圖標
return TRUE;
}
////////////////////////////////////////////////////////
// 命令/UI 更新處理:寫這些東西應(yīng)該很輕松。
void CMyDialog::OnDisableTaskKeys()
{
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKKEYS,
!CTaskKeyMgr::AreTaskKeysDisabled(), TRUE); // 蜂鳴
}
void CMyDialog::OnUpdateDisableTaskKeys(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(CTaskKeyMgr::AreTaskKeysDisabled());
}
void CMyDialog::OnDisableTaskbar()
{
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKBAR,
!CTaskKeyMgr::IsTaskBarDisabled());
}
void CMyDialog::OnUpdateDisableTaskbar(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(CTaskKeyMgr::IsTaskBarDisabled());
}
void CMyDialog::OnDisableTaskMgr()
{
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKMGR,
!CTaskKeyMgr::IsTaskMgrDisabled());
}
void CMyDialog::OnUpdateDisableTaskMgr(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(CTaskKeyMgr::IsTaskMgrDisabled());
}
////////////////////////////////////////////////////////
// 要想讓ON_UPDATE_COMMAND_UI正常工作,這是必需的。
//
LRESULT CMyDialog::OnKickIdle(WPARAM wp, LPARAM lCount)
{
UpdateDialogControls(this, TRUE);
return 0;
}
按上述方法盡管禁用了任務(wù)欄,但是還有一個機關(guān)沒有處理,那就是按下Windows鍵仍然可以彈出“開始”菜單。顯然在處理VK_LWIN之前,任務(wù)欄不會檢查是否被啟用。一般來講,如果某個窗口被屏蔽掉,那么它就不再會處理用戶在這個窗口的輸入——這就是所謂的禁用(Disable)的含義。通常調(diào)用EnableWindow(FALSE)后自然就達到了這個目的。但是處理VK_LWIN/VK_RWIN按鍵的代碼決不會去檢查任務(wù)欄啟用/禁用狀態(tài)。對此,本文的處理辦法仍然是利用鍵盤鉤子。修改一下TaskKeyHook實現(xiàn),增加對Windows鍵的捕獲。這樣按下“開始”菜單鍵之后什么也不會發(fā)生。希望沒有漏掉其它的按鍵。如果哪位讀者發(fā)現(xiàn)漏掉了什么鍵,請和我聯(lián)系,以便把它加到鍵盤鉤子中去。為了簡單起見,我在類CTaskKeyMgr中封裝了所有禁用的函數(shù)。下面是這個類的定義擊實現(xiàn)文件:
TaskKeyMgr
////////////////////////////////////////
// TaskKeyMgr.h
//
#pragma once
#include "TaskKeyHook.h"
/////////////////////////////////////////////////////////////////////
// 使用這個類禁用任務(wù)鍵,任務(wù)管理器或任務(wù)欄。
// 用相應(yīng)的標志調(diào)用Disable,如:CTaskMgrKeys::Disable(CTaskMgrKeys::ALL);
//
class CTaskKeyMgr {
public:
enum {
TASKMGR = 0x01, // 禁用任務(wù)管理器(Ctrl+Alt+Del)
TASKKEYS = 0x02, //禁用任務(wù)轉(zhuǎn)換鍵(Alt-TAB, etc)
TASKBAR = 0x04, //禁用任務(wù)欄
ALL=0xFFFF //禁用所有東西L
};
static void Disable(DWORD dwItem,BOOL bDisable,BOOL bBeep=FALSE);
static BOOL IsTaskMgrDisabled();
static BOOL IsTaskBarDisabled();
static BOOL AreTaskKeysDisabled() {
return ::AreTaskKeysDisabled(); // 調(diào)用 DLL
}
};
CPP實現(xiàn)
////////////////////////////////////////////////////////////////
// TaskKeyMgr.cpp
//
#include "StdAfx.h"
#include "TaskKeyMgr.h"
#define HKCU HKEY_CURRENT_USER
// 用于禁用任務(wù)管理器策略的注冊表鍵值對
LPCTSTR KEY_DisableTaskMgr =
"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCTSTR VAL_DisableTaskMgr = "DisableTaskMgr";
///////////////////////////////////////////
// 禁用相關(guān)的任務(wù)鍵
//
// dwFlags = 表示禁用什么
// bDisable = 禁用為 (TRUE) ,否則為啟用 (FALSE)
// bBeep = 按下非法鍵是否蜂鳴(指針對任務(wù)鍵)
//
void CTaskKeyMgr::Disable(DWORD dwFlags, BOOL bDisable, BOOL bBeep)
{
// 任務(wù)管理器 (Ctrl+Alt+Del)
if (dwFlags & TASKMGR) {
HKEY hk;
if (RegOpenKey(HKCU, KEY_DisableTaskMgr,&hk)!=ERROR_SUCCESS)
RegCreateKey(HKCU, KEY_DisableTaskMgr, &hk);
if (bDisable) { // 禁用任務(wù)管理器(disable TM): set policy = 1
DWORD val=1;
RegSetValueEx(hk, VAL_DisableTaskMgr, NULL,
REG_DWORD, (BYTE*)&val, sizeof(val));
} else { // 啟用任務(wù)管理器(enable TM)
RegDeleteValue(hk,VAL_DisableTaskMgr);
}
}
// 任務(wù)鍵 (Alt-TAB etc)
if (dwFlags & TASKKEYS)
::DisableTaskKeys(bDisable,bBeep); // 安裝鍵盤鉤
// 任務(wù)欄
if (dwFlags & TASKBAR) {
HWND hwnd = FindWindow("Shell_traywnd", NULL);
EnableWindow(hwnd, !bDisable);
}
}
BOOL CTaskKeyMgr::IsTaskBarDisabled()
{
HWND hwnd = FindWindow("Shell_traywnd", NULL);
return IsWindow(hwnd) ? !IsWindowEnabled(hwnd) : TRUE;
}
BOOL CTaskKeyMgr::IsTaskMgrDisabled()
{
HKEY hk;
if (RegOpenKey(HKCU, KEY_DisableTaskMgr, &hk)!=ERROR_SUCCESS)
return FALSE; // 沒有此鍵,不禁用
DWORD val=0;
DWORD len=4;
return RegQueryValueEx(hk, VAL_DisableTaskMgr,
NULL, NULL, (BYTE*)&val, &len)==ERROR_SUCCESS && val==1;
}
這個類中的函數(shù)都是靜態(tài)的,實際上CTaskKeyMgr完全就是一個名字空間。你可以在自己的程序中隨心所欲地使用它。例如,禁用任務(wù)轉(zhuǎn)換按鍵和任務(wù)欄,但是不禁用Ctrl+Alt+Del:
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKKEYS |
CTaskKeyMgr::TASKBAR, TRUE);
此外,還有幾個函數(shù)是用來檢查當(dāng)前禁用了哪些東西,甚至可以在用戶按下禁用鍵時發(fā)出蜂鳴聲……自己去享受Paul的源代碼吧!
在Windows 9x/Me系統(tǒng)中,屏蔽Ctrl+Alt+Del和各種任務(wù)開關(guān)鍵的方法是通過下面的方法實現(xiàn)的:
BOOL bOldState;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &bOldState, 0);
MS大佬認為這種方法很業(yè)余,所以在Windows NT/2000/XP中對此進行了修改。在這些較新的Windows版本中用戶登陸使用Winlogon和GINA——Graphical Identification and Authentication,意思是圖形化的身份認證,挺嚇唬人的是不是!其實就那么回事。Winlogon是Windows系統(tǒng)的一部分,它專門提供交互式登陸支持,而GINA則是Winlogon用來實現(xiàn)認證的一個DLL——這個DLL就是msgina.dll。WlxInitialize、WlxActivateUserShell便是其中輸出,當(dāng)然不知這兩個,還有別的。前者進行自身的初始化,后者激活用戶的外殼程序。Windows就是用這個DLL來實現(xiàn)用戶名+口令的身份認證的,但是開發(fā)人員可以用自己的GINA代替msgina.dll。例如,實現(xiàn)智能卡、視網(wǎng)膜掃描儀、DNA檢查等等認證機制來代替輸入用戶名+口令形式的身份檢查。 下面的表格中列出了與GINA有關(guān)的全部函數(shù)。其中有一個是WlxLoggedOnSAS,當(dāng)按下Ctrl+Alt+Del 鍵時,Winlogon便調(diào)用這個函數(shù)。
(表一)GINA 函數(shù)一覽表 函數(shù) 描述
WlxActivateUserShell激活用戶外殼程序
WlxDisplayLockedNotice允許GINA DLL 顯示鎖定信息
WlxDisplaySASNotice 當(dāng)沒有用戶登陸時,Winlogon調(diào)用此函數(shù)
WlxDisplayStatusMessageWinlogon 用一個狀態(tài)信息調(diào)用此函數(shù)進行顯示
WlxGetConsoleSwitchCredentials Winlogon調(diào)用此函數(shù)讀取當(dāng)前登陸用戶的信任信息,并透明地將它們傳到目標會話
WlxGetStatusMessage Winlogon 調(diào)用此函數(shù)獲取當(dāng)前狀態(tài)信息
WlxInitialize 針對指定的窗口位置進行GINA DLL初始化
WlxIsLockOk 驗證工作站正常鎖定
WlxIslogoffOk 驗證注銷正常
WlxLoggedOnSAS 用戶已登陸并且工作站沒有被加鎖,如果此時接收到SAS事件,則Winlogon 調(diào)用此函數(shù)
WlxLoggedOutSAS 沒有用戶登陸,如果此時收到SAS事件,則Winlogon 調(diào)用此函數(shù)
WlxLogoff 請求注銷操作時通知GINA DLL
WlxNegotiate 表示當(dāng)前的Winlogon版本是否能使用GINA DLL
WlxNetworkProviderLoad 在加載網(wǎng)絡(luò)服務(wù)提供程序收集了身份和認證信息后,Winlogon 調(diào)用此函數(shù)
WlxRemoveStatusMessage Winlogon 調(diào)用此函數(shù)告訴GINA DLL 停止顯示狀態(tài)信息
WlxScreensaverNotify 允許GINA與屏幕保護操作交互
WlxShutdown 在關(guān)閉之前Winlogon 調(diào)用此函數(shù),允許GINA實現(xiàn)任何關(guān)閉任務(wù),例如從讀卡器中退出智能卡
WlxStartApplication 當(dāng)系統(tǒng)需要在用戶的上下文中啟動應(yīng)用程序時調(diào)用此函數(shù)
WlxWkstaLockedSAS當(dāng)工作站被鎖定,如果接收到一個SAS,則Winlogon 調(diào)用此函數(shù)
在默認情況下,GINA顯示登陸對話框,用戶輸入用戶名及口令。所以要想屏蔽掉Ctrl+Alt+Del,則可以寫一個新的MyGina.dll,其中提供接口調(diào)用msgina.dll的函數(shù)WlxLoggedOnSAS,從而實現(xiàn)Ctrl+Alt+Del屏蔽。或者編寫一個鍵盤驅(qū)動程序來實現(xiàn)。
難道屏蔽Ctrl+Alt+Del真的象上述所說的那么麻煩嗎?有沒有更好的方法呢?答案是肯定的。所以忘掉GINA吧,使用操作系統(tǒng)的策略設(shè)置完全可以搞掂這個問題。方法是進入"開始"菜單,選擇"運行",然后在運行對話框中輸入"gpedit.msc",啟動Windows系統(tǒng)的組策略編輯器。在左邊窗格查看"用戶配置|管理模板|系統(tǒng)|登錄/注銷",則在右邊窗格策略里不難發(fā)現(xiàn)"禁用任務(wù)管理器"一項。
組策略編輯器
通過對這個策略的設(shè)置可以屏蔽掉Ctrl+Alt+Del。如果要通過編寫代碼來實現(xiàn),則必須操作下面的注冊表項:
HKCU\
Software\
Microsoft\
Windows\
CurrentVersion\
Policies\
System\DisableTaskMgr = dword:1
如此設(shè)置之后,則在Windows XP中,如果用戶按下Ctrl+Alt+Del,則會彈出一個出錯對話框,
注意這里假設(shè)在控制面板中“用戶帳號”管理的“選擇登錄和注銷選項”設(shè)置啟用了“使用歡迎屏幕”一項。
否則,XP將使用Windows的傳統(tǒng)登錄模式,要求用戶輸入帳戶名。并且Ctrl+Alt+Del組合鍵的 行為也和傳統(tǒng)的行為一樣,注冊表中DisableTaskMgr的設(shè)置也只是將登錄/注銷對話框中的任務(wù)管理器按鈕屏蔽或置灰。 有人可能會問,有關(guān)任務(wù)管理器的文檔又沒有明確說明,那你是怎么知道DisableTaskMgr是用來禁用任務(wù)管理器的呢?告訴你吧, 我是在使用GPEDIT時發(fā)現(xiàn)的。GPEDIT是一個非常有用的工具,不僅可以用它來編輯策略,還可以用它來發(fā)現(xiàn)策略。利用這個工具可以輕松控制Windows的許多東西,從許可權(quán)限的存取到是否使用IE的傳統(tǒng)外觀,從是否顯示對話框中的Places Bar到是否用Ctrl+Alt+Del 啟動任務(wù)管理器。總之用它可以配置上百個界面行為,因此它是一個足以讓系統(tǒng)管理員垂延三尺的工具。一旦找到了感興趣的策略,那如何知道相應(yīng)的注冊表位置呢?有兩種方法。第一種是比較粗魯?shù)霓k法:在修改策略的前后將注冊表輸出到一個.reg文件,然后比較它們有什么不同。所有的策略無外乎以下的四個注冊表鍵:
// 用戶指定
HKEY_CURRENT_USER\Software\Policies
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies
// 機器指定
HKEY_LOCAL_MACHINE\Software\Policies
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies
第二種方法是直搗信息源頭--檢查描述策略的管理模板文件(.adm)。下面是Windows XP的system.adm文件對 DisableTaskMgr的描述:(Windows 2000對此的描述稍有不同,其細節(jié)請參考Windows 2000的資源開發(fā)包)
CATEGORY !!CADOptions
#if version >= 4
EXPLAIN !!CADOptions_Help
#endif
KEYNAME "Software\Microsoft\Windows\CurrentVersion\Policies\System"
POLICY !!DisableTaskMgr
#if version >= 4
SUPPORTED !!SUPPORTED_Win2k
#endif
EXPLAIN !!DisableTaskMgr_Help
VALUENAME "DisableTaskMgr"
END POLICY
;
; More Ctrl+Alt+Del policies here...
;
END CATEGORY ; Ctrl+Alt+Del options
……
……
DisableTaskMgr_Help="防止用戶啟動''任務(wù)管理器''(Taskmgr.exe)。\n\n如果該設(shè)置被啟用,并且用戶試圖啟動任務(wù)管理器,系統(tǒng)
會顯示消息,解釋是一個策略禁止了這個操作。\n\n任務(wù)管理器讓用戶啟動或終止程序,監(jiān)視計算機性能,查看及監(jiān)視計算機上所有運行
中的程序 (包含系統(tǒng)服務(wù)), 搜索程序的執(zhí)行文件名,及更改程序運行的優(yōu)先順序。"
DisableTaskMgr="刪除任務(wù)管理器"
以上是DisableTaskMgr的描述片斷
正是在這段描述中KEYNAME 和VALUENAME指定了注冊表的鍵值對。利用這種方法,你可以為自己的應(yīng)用程序創(chuàng)建管理模板和策略,但編輯和瀏覽.adm模板文件的編輯器必須支持Unicode字符。如Notepad或者WordPad等都可以。此外,使用管理模板文件,系統(tǒng)管理員可以用它為整個組織配置需要的策略——由此可以看出,此文件在系統(tǒng)中的地位舉足輕重!有關(guān)模板管理文件格式的詳細信息請參考平臺SDK。最后需要強調(diào)的是DisableTaskMgr只是禁用Ctrl+Alt+Del的功能。下面我們來討論如何捕獲它的按鍵序列。要想截獲Ctrl+Alt+Del,有三種可選擇的方法:
1、 編寫一個GINA代理;此方法我們在以后的文章中介紹。實際上,ac952_z_cn的個人專欄文章:“WINDOWS NT/2000下如何屏蔽CTRL+ALT+DEL”使用的就是這種方法。
2、 編寫一個鍵盤驅(qū)動程序;本文例子程序使用的方法。
3、 用自己的程序代替任務(wù)管理器程序TaskMgr.exe。
屏蔽Ctrl+Alt+Del解決方案的具體實現(xiàn)細節(jié)請參考本文的例子代碼。
下面讓我們來解決屏蔽任務(wù)切換鍵序列的問題,這些鍵序列包括Alt+Tab、Ctrl+Esc、Alt+Esc、VK_LWIN/VK_RWIN以及任務(wù)欄。在很早以前的Window 3.1年代,處理這個問題的方法是通過WM_SYSKEYDOWN實現(xiàn)。到了Windows 9x時期,本文前面提到過對這個問題的處理方法,使用SPI_SETSCREENSAVERRUNNING。但是進入Windows NT 4.0 (SP3 +),Windows 2000以及Windows XP時代,對這個問題的處理已經(jīng)有所不同,必須寫一個低級的鍵盤驅(qū)動鉤子。不要怕,因為要實現(xiàn)這個鉤子并不是很難。本文下面會介紹如何實現(xiàn)這個鍵盤鉤子。一般來講,系統(tǒng)級鉤子必須是一個DLL。下面是本文提供的一個鍵盤鉤子DLL的源代碼片斷(TaskKeyHook.dll):
頭文件
////////////////////////////////////////////////////////////////
//TaskKeyHook.h
//
#define DLLIMPORT __declspec(dllimport)
DLLIMPORT BOOL DisableTaskKeys(BOOL bEnable, BOOL bBeep);
DLLIMPORT BOOL AreTaskKeysDisabled();
實現(xiàn)文件
////////////////////////////////////////////////////////////////
// TaskKeyHook.cpp
//
#define _WIN32_WINNT 0x0500 // for KBDLLHOOKSTRUCT
#include // MFC core and standard components
#define DLLEXPORT __declspec(dllexport)
//////////////////
// App (DLL) object
//
class CTaskKeyHookDll : public CWinApp {
public:
CTaskKeyHookDll() { }
~CTaskKeyHookDll() { }
} MyDll;
////////////////////////////////////////////////
// 下面的代碼表示這一部分在此DLL所有實例之間共享
// 低級鍵盤鉤子一定是系統(tǒng)級的鉤子
//
#pragma data_seg (".mydata")
HHOOK g_hHookKbdLL = NULL; // 鉤子句柄
BOOL g_bBeep = FALSE; // 按下非法鍵時蜂鳴響鈴
#pragma data_seg ()
#pragma comment(linker, "/SECTION:.mydata,RWS") // 告訴鏈接器:建立數(shù)據(jù)共享段
//////////////////////////////////
// 低級鍵盤鉤子
// 截獲任務(wù)轉(zhuǎn)換鍵:不傳遞直接返回
//
LRESULT CALLBACK MyTaskKeyHookLL(int nCode, WPARAM wp, LPARAM lp)
{
KBDLLHOOKSTRUCT *pkh = (KBDLLHOOKSTRUCT *) lp;
if (nCode==HC_ACTION) {
BOOL bCtrlKeyDown =
GetAsyncKeyState(VK_CONTROL)>>((sizeof(SHORT) * 8) - 1);
if ((pkh->vkCode==VK_ESCAPE && bCtrlKeyDown) || // Ctrl+Esc
// Alt+TAB
(pkh->vkCode==VK_TAB && pkh->flags & LLKHF_ALTDOWN) ||
// Alt+Esc
(pkh->vkCode==VK_ESCAPE && pkh->flags & LLKHF_ALTDOWN)||
(pkh->vkCode==VK_LWIN || pkh->vkCode==VK_RWIN)) { // 開始菜單
if (g_bBeep && (wp==WM_SYSKEYDOWN||wp==WM_KEYDOWN))
MessageBeep(0); // 蜂鳴
return 1; // 不再往CallNextHookEx傳遞,直接返回
}
}
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
}
////////////////////////////////////////////////
// 是否屏蔽任務(wù)鍵序列——也就是說鍵盤鉤子是否安裝?
// 注:這里假設(shè)沒有其它鉤子做同樣的事情
//
DLLEXPORT BOOL AreTaskKeysDisabled()
{
return g_hHookKbdLL != NULL;
}
////////////////////////////////////////////////
// 屏蔽任務(wù)鍵:安裝低級鍵盤構(gòu)
// 返回當(dāng)前是否屏蔽標志(TRUE/FALSE)
//
DLLEXPORT BOOL DisableTaskKeys(BOOL bDisable, BOOL bBeep)
{
if (bDisable) {
if (!g_hHookKbdLL) {
g_hHookKbdLL = SetWindowsHookEx(WH_KEYBOARD_LL,
MyTaskKeyHookLL, MyDll.m_hInstance, 0);
}
} else if (g_hHookKbdLL != NULL) {
UnhookWindowsHookEx(g_hHookKbdLL);
g_hHookKbdLL = NULL;
}
g_bBeep = bBeep;
return AreTaskKeysDisabled();
}
TaskKeyHook 輸出兩個函數(shù):DisableTaskKeys 和 AreTaskKeysDisabled。前者安裝WH_KEYBOARD_LL 鉤子;后者判斷這個鉤子是否安裝。此鍵盤鉤子的處理思路是截獲Alt+Tab,Ctrl+Esc,Alt+Esc以及Windows 鍵VK_LWIN/VK_RWIN,關(guān)于這兩個鍵,稍候會有詳細描述。當(dāng)鉤子碰到這些鍵時,它直接返回到調(diào)用者,而不是將處理傳遞給CallNextHookEx 。
LRESULT CALLBACK MyTaskKeyHookLL(...)
{
if (/* 任務(wù)鍵*)
return 1; // 立即返回
return CallNextHookEx(...);
}
TaskKeyHook的大部分實現(xiàn)都很簡單。只有一個地方用到了一點小技巧:既使用#pragma data_seg 命名包含全程數(shù)據(jù)的數(shù)據(jù)段,并且用#pragma comment (linker...)告訴鏈接器讓這個數(shù)據(jù)段為共享段。實現(xiàn)細節(jié)請參考源代碼。本文附帶的例子程序(TrapKeys.exe)匯集了上述幾個有關(guān)屏蔽鍵盤按鍵序列的功能,除此之外,它還有一個功能就是禁用任務(wù)欄。因為既然禁用了任務(wù)轉(zhuǎn)換鍵,那么一般來說,也必然要禁用任務(wù)欄,否則禁用任務(wù)轉(zhuǎn)換鍵就沒有意義了。禁用任務(wù)欄的具體方法如下:
HWND hwnd = FindWindow("Shell_traywnd", NULL);//找到任務(wù)欄
EnableWindow(hwnd, FALSE); // 禁用任務(wù)欄
如圖四是例子程序運行畫面:
圖四 TrapKeys程序運行畫面
以下是TrapKeys程序的實現(xiàn)代碼:
/////////////////////////////////////////////////
// TrapKeys.cpp
//
#include "stdafx.h"
#include "resource.h"
#include "StatLink.h"
#include "TaskKeyMgr.h"
////////////////////
// 主對話框
//
class CMyDialog : public CDialog {
public:
CMyDialog(CWnd* pParent = NULL) : CDialog(IDD_MYDIALOG, pParent) { }
protected:
HICON m_hIcon;
CStaticLink m_wndLink1;
CStaticLink m_wndLink2;
CStaticLink m_wndLink3;
virtual BOOL OnInitDialog();
// 命令/UI 的更新處理
afx_msg void OnDisableTaskMgr();
afx_msg void OnDisableTaskKeys();
afx_msg void OnDisableTaskbar();
afx_msg void OnUpdateDisableTaskMgr(CCmdUI* pCmdUI);
afx_msg void OnUpdateDisableTaskKeys(CCmdUI* pCmdUI);
afx_msg void OnUpdateDisableTaskbar(CCmdUI* pCmdUI);
afx_msg LRESULT OnKickIdle(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
};
///////////////////////////////////////////////////////
// 標準的MFC 對話框應(yīng)用類代碼。
//
class CMyApp : public CWinApp {
public:
virtual BOOL InitInstance() {
// 初始化app:運行對話框
CMyDialog dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
return FALSE;
}
virtual int ExitInstance() {
// 為了按全起見,在退出程序的時候,將所有禁用的項目復(fù)原
CTaskKeyMgr::Disable(CTaskKeyMgr::ALL, FALSE);
return 0;
}
} theApp;
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_COMMAND(IDC_DISABLE_TASKKEYS,OnDisableTaskKeys)
ON_COMMAND(IDC_DISABLE_TASKBAR, OnDisableTaskbar)
ON_COMMAND(IDC_DISABLE_TASKMGR, OnDisableTaskMgr)
ON_UPDATE_COMMAND_UI(IDC_DISABLE_TASKKEYS, OnUpdateDisableTaskKeys)
ON_UPDATE_COMMAND_UI(IDC_DISABLE_TASKBAR, OnUpdateDisableTaskbar)
ON_UPDATE_COMMAND_UI(IDC_DISABLE_TASKMGR, OnUpdateDisableTaskMgr)
ON_MESSAGE(WM_KICKIDLE,OnKickIdle)
END_MESSAGE_MAP()
///////////////////////////////////////////////
// 初始化對話框:子類化超鏈接柄加栽圖標
//
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// 初始化超鏈接
m_wndLink1.SubclassDlgItem(IDC_EMAIL,this);
m_wndLink2.SubclassDlgItem(IDC_VCKBASEURL,this);
m_wndLink3.SubclassDlgItem(IDC_VCKBASELINK,this);
// 自己設(shè)置對話框圖標。MFC不會為對話框應(yīng)用程序設(shè)置它
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
SetIcon(m_hIcon, TRUE); // 打圖標
SetIcon(m_hIcon, FALSE); // 小圖標
return TRUE;
}
////////////////////////////////////////////////////////
// 命令/UI 更新處理:寫這些東西應(yīng)該很輕松。
void CMyDialog::OnDisableTaskKeys()
{
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKKEYS,
!CTaskKeyMgr::AreTaskKeysDisabled(), TRUE); // 蜂鳴
}
void CMyDialog::OnUpdateDisableTaskKeys(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(CTaskKeyMgr::AreTaskKeysDisabled());
}
void CMyDialog::OnDisableTaskbar()
{
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKBAR,
!CTaskKeyMgr::IsTaskBarDisabled());
}
void CMyDialog::OnUpdateDisableTaskbar(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(CTaskKeyMgr::IsTaskBarDisabled());
}
void CMyDialog::OnDisableTaskMgr()
{
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKMGR,
!CTaskKeyMgr::IsTaskMgrDisabled());
}
void CMyDialog::OnUpdateDisableTaskMgr(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(CTaskKeyMgr::IsTaskMgrDisabled());
}
////////////////////////////////////////////////////////
// 要想讓ON_UPDATE_COMMAND_UI正常工作,這是必需的。
//
LRESULT CMyDialog::OnKickIdle(WPARAM wp, LPARAM lCount)
{
UpdateDialogControls(this, TRUE);
return 0;
}
按上述方法盡管禁用了任務(wù)欄,但是還有一個機關(guān)沒有處理,那就是按下Windows鍵仍然可以彈出“開始”菜單。顯然在處理VK_LWIN之前,任務(wù)欄不會檢查是否被啟用。一般來講,如果某個窗口被屏蔽掉,那么它就不再會處理用戶在這個窗口的輸入——這就是所謂的禁用(Disable)的含義。通常調(diào)用EnableWindow(FALSE)后自然就達到了這個目的。但是處理VK_LWIN/VK_RWIN按鍵的代碼決不會去檢查任務(wù)欄啟用/禁用狀態(tài)。對此,本文的處理辦法仍然是利用鍵盤鉤子。修改一下TaskKeyHook實現(xiàn),增加對Windows鍵的捕獲。這樣按下“開始”菜單鍵之后什么也不會發(fā)生。希望沒有漏掉其它的按鍵。如果哪位讀者發(fā)現(xiàn)漏掉了什么鍵,請和我聯(lián)系,以便把它加到鍵盤鉤子中去。為了簡單起見,我在類CTaskKeyMgr中封裝了所有禁用的函數(shù)。下面是這個類的定義擊實現(xiàn)文件:
TaskKeyMgr
////////////////////////////////////////
// TaskKeyMgr.h
//
#pragma once
#include "TaskKeyHook.h"
/////////////////////////////////////////////////////////////////////
// 使用這個類禁用任務(wù)鍵,任務(wù)管理器或任務(wù)欄。
// 用相應(yīng)的標志調(diào)用Disable,如:CTaskMgrKeys::Disable(CTaskMgrKeys::ALL);
//
class CTaskKeyMgr {
public:
enum {
TASKMGR = 0x01, // 禁用任務(wù)管理器(Ctrl+Alt+Del)
TASKKEYS = 0x02, //禁用任務(wù)轉(zhuǎn)換鍵(Alt-TAB, etc)
TASKBAR = 0x04, //禁用任務(wù)欄
ALL=0xFFFF //禁用所有東西L
};
static void Disable(DWORD dwItem,BOOL bDisable,BOOL bBeep=FALSE);
static BOOL IsTaskMgrDisabled();
static BOOL IsTaskBarDisabled();
static BOOL AreTaskKeysDisabled() {
return ::AreTaskKeysDisabled(); // 調(diào)用 DLL
}
};
CPP實現(xiàn)
////////////////////////////////////////////////////////////////
// TaskKeyMgr.cpp
//
#include "StdAfx.h"
#include "TaskKeyMgr.h"
#define HKCU HKEY_CURRENT_USER
// 用于禁用任務(wù)管理器策略的注冊表鍵值對
LPCTSTR KEY_DisableTaskMgr =
"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCTSTR VAL_DisableTaskMgr = "DisableTaskMgr";
///////////////////////////////////////////
// 禁用相關(guān)的任務(wù)鍵
//
// dwFlags = 表示禁用什么
// bDisable = 禁用為 (TRUE) ,否則為啟用 (FALSE)
// bBeep = 按下非法鍵是否蜂鳴(指針對任務(wù)鍵)
//
void CTaskKeyMgr::Disable(DWORD dwFlags, BOOL bDisable, BOOL bBeep)
{
// 任務(wù)管理器 (Ctrl+Alt+Del)
if (dwFlags & TASKMGR) {
HKEY hk;
if (RegOpenKey(HKCU, KEY_DisableTaskMgr,&hk)!=ERROR_SUCCESS)
RegCreateKey(HKCU, KEY_DisableTaskMgr, &hk);
if (bDisable) { // 禁用任務(wù)管理器(disable TM): set policy = 1
DWORD val=1;
RegSetValueEx(hk, VAL_DisableTaskMgr, NULL,
REG_DWORD, (BYTE*)&val, sizeof(val));
} else { // 啟用任務(wù)管理器(enable TM)
RegDeleteValue(hk,VAL_DisableTaskMgr);
}
}
// 任務(wù)鍵 (Alt-TAB etc)
if (dwFlags & TASKKEYS)
::DisableTaskKeys(bDisable,bBeep); // 安裝鍵盤鉤
// 任務(wù)欄
if (dwFlags & TASKBAR) {
HWND hwnd = FindWindow("Shell_traywnd", NULL);
EnableWindow(hwnd, !bDisable);
}
}
BOOL CTaskKeyMgr::IsTaskBarDisabled()
{
HWND hwnd = FindWindow("Shell_traywnd", NULL);
return IsWindow(hwnd) ? !IsWindowEnabled(hwnd) : TRUE;
}
BOOL CTaskKeyMgr::IsTaskMgrDisabled()
{
HKEY hk;
if (RegOpenKey(HKCU, KEY_DisableTaskMgr, &hk)!=ERROR_SUCCESS)
return FALSE; // 沒有此鍵,不禁用
DWORD val=0;
DWORD len=4;
return RegQueryValueEx(hk, VAL_DisableTaskMgr,
NULL, NULL, (BYTE*)&val, &len)==ERROR_SUCCESS && val==1;
}
這個類中的函數(shù)都是靜態(tài)的,實際上CTaskKeyMgr完全就是一個名字空間。你可以在自己的程序中隨心所欲地使用它。例如,禁用任務(wù)轉(zhuǎn)換按鍵和任務(wù)欄,但是不禁用Ctrl+Alt+Del:
CTaskKeyMgr::Disable(CTaskKeyMgr::TASKKEYS |
CTaskKeyMgr::TASKBAR, TRUE);
此外,還有幾個函數(shù)是用來檢查當(dāng)前禁用了哪些東西,甚至可以在用戶按下禁用鍵時發(fā)出蜂鳴聲……自己去享受Paul的源代碼吧!
相關(guān)文章

WinXP登錄失敗提示:未授予用戶在此計算機上的請求登陸類型怎么辦?
WinXP登錄失敗提示:“未授予用戶在此計算機上的請求登陸類型”怎么辦?今天我們就來看看詳細的解決過程2023-08-16
xp系統(tǒng)我的文檔在哪? WinXP系統(tǒng)下我的文檔打不開怎么辦?
xp系統(tǒng)我的文檔在哪?winxp系統(tǒng)中的我的文檔打不開,可能是權(quán)限出現(xiàn)了變化,下面我們就來看看WinXP系統(tǒng)下我的文檔打不開怎么辦?2023-08-16
winXP系統(tǒng)如何快速升級到Windows8系統(tǒng)?
XP系統(tǒng)如何快速升級到Win8系統(tǒng)?當(dāng)我們使用XP系統(tǒng)的電腦時,想要升級到Win8系統(tǒng),這時我們該怎么做呢,讓我們一起看下文尋找解決的方法吧2020-12-24
如何解決WinXP系統(tǒng)LOL安全證書不可用?最近有不少XP系統(tǒng)的用戶,向小編反應(yīng)LOL安全證書不可用該怎么解決,下面就由小編帶領(lǐng)大家來解決問題2020-12-22
如何恢復(fù)XP系統(tǒng)本地連接?最近有不少XP系統(tǒng)的用戶,在使用電腦的時候遇到了這樣的問題本地連接不見了,那么如何恢復(fù)呢。下面就由小編為大家解決問題2020-12-11
如何解決WinXP系統(tǒng)記事本亂碼?解決WinXP系統(tǒng)記事本亂碼的教程
如何解決WinXP系統(tǒng)記事本亂碼?相信很多WinXP系統(tǒng)用戶都有因為設(shè)置不當(dāng)導(dǎo)致記事本亂碼而煩惱過,那么如何解決這一問題呢,讓我們一起來看看吧2020-12-08
WinXP系統(tǒng)網(wǎng)頁不能復(fù)制粘貼的教程
怎么解決WinXP系統(tǒng)網(wǎng)頁不能復(fù)制粘貼?windows xp系統(tǒng),在使用電腦的時需要復(fù)制粘貼這個功能,但是有時候我們在WinXP系統(tǒng)網(wǎng)頁不能完成復(fù)制粘貼,該如何解決這一問題,下面小編2020-12-07
如何解決winxp系統(tǒng)oracle無法使用?很多電腦用戶不知道oracle的話,就需要開啟oracle服務(wù),但是如何開啟oracle服務(wù)呢,下面小編帶領(lǐng)大家學(xué)習(xí)一下2020-12-07
怎么解決winXP出現(xiàn)“數(shù)據(jù)執(zhí)行保護”?
怎么解決XP出現(xiàn)"數(shù)據(jù)執(zhí)行保護"?最近有很多小伙伴向小編反應(yīng)使用xp系統(tǒng)過程中,總是會彈出"數(shù)據(jù)執(zhí)行保護",那么我們該如何解決這一問題呢,下面小編為2020-12-07
winxp超級管理員賬戶消失了怎么辦? winxp管理員賬戶恢復(fù)的技巧
winxp超級管理員賬戶消失了怎么辦?最近遇到一個問題,winxp創(chuàng)建新賬戶后超級管理員賬戶消失了,該怎么辦呢?下面我們就來看看winxp管理員賬戶恢復(fù)的技巧,需要的朋友可以2020-08-19










