C++ Q&A

Typename, Disabling Keys in Windows XP with TrapKeys

Paul DiLascia

Code download available at: CQA0209.exe (57 KB)
Browse the Code Online

Q Can you explain the purpose of the typename keyword in C++? When should I use it instead of <class T>? Is there some difference between the two?

Q Can you explain the purpose of the typename keyword in C++? When should I use it instead of <class T>? Is there some difference between the two?

Hafiz Abid Qadeer

A The short answer is that typename and class are synonymous at the start of a template definition, but sometimes you need the typename to tell the compiler that a symbol in a template represents a type name. For example, suppose you have a template like the one shown here:

template<class T> class foo {
 void bar() {
   T::Bletch * p;
   •••
 }
}

What does T::Bletch * p mean in this code sample? Is this a multiplication expression or is it supposed to be a pointer declaration? Is T::Bletch a member or a type? As you've probably guessed by now, if you use typename you can easily resolve the ambiguity.

template <class T> class foo {
 void bar() {
   typename T::Bletch * p;
   •••
 }
}

A The short answer is that typename and class are synonymous at the start of a template definition, but sometimes you need the typename to tell the compiler that a symbol in a template represents a type name. For example, suppose you have a template like the one shown here:

template<class T> class foo {
 void bar() {
   T::Bletch * p;
   •••
 }
}

What does T::Bletch * p mean in this code sample? Is this a multiplication expression or is it supposed to be a pointer declaration? Is T::Bletch a member or a type? As you've probably guessed by now, if you use typename you can easily resolve the ambiguity.

template <class T> class foo {
 void bar() {
   typename T::Bletch * p;
   •••
 }
}

Now the compiler knows T::Bletch is a type, not a member name. You can also use <typename T> instead of <class T> in the template definition. The following statements are identical:

template<class X>...
template<typename X>...

This should answer the question.

Q Is there some way I can disable the Ctrl+Alt+Del key and Alt+Tab (task-switch) keys in Windows® XP? I don't want Ctrl+Alt+Del to bring up the Task Manager.

Q Is there some way I can disable the Ctrl+Alt+Del key and Alt+Tab (task-switch) keys in Windows® XP? I don't want Ctrl+Alt+Del to bring up the Task Manager.

Many readers

A Despite what you may have heard, it's pretty easy to disable these keys in Windows XP. But first let me warn you that outlawing Ctrl+Alt+Del kind of defeats the purpose of such a magic key sequence: namely, to provide an emergency escape hatch. On the other hand, if you're developing an Intergalactic World Control application, I can see where you might want to turn off the three-finger salute.

A Despite what you may have heard, it's pretty easy to disable these keys in Windows XP. But first let me warn you that outlawing Ctrl+Alt+Del kind of defeats the purpose of such a magic key sequence: namely, to provide an emergency escape hatch. On the other hand, if you're developing an Intergalactic World Control application, I can see where you might want to turn off the three-finger salute.

In the old days of Windows 95, Windows 98, and Windows Me, you could disable Ctrl+Alt+Del and the various task-switching keys by fooling the operating system into thinking the screen saver was running.

BOOL bOldState;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &bOldState, 0);

But that was too easy and a bit of a kludge, so the friendly Redmondtonians canned that feature in Windows NT®, Windows 2000, and Windows XP. These versions of Windows use Winlogon and GINA, short for Graphical Identification and Authentication. Wow, that's a mouthful! Winlogon is the part of Windows that provides interactive logon support, and GINA is the DLL that Winlogon uses to perform authentication. The GINA DLL exports several functions like WlxInitialize to initialize itself and WlxActivateUserShell to activate a user's shell program. Windows comes with msgina.dll to perform the usual enter-a-name-and-password authentication, but developers (that's you) can replace msgina.dll with their own GINA. For example, you might implement smartcard, retinal-scan, DNA-check, or Divine Awareness authentication mechanisms in lieu of the boring user name and password.

Figure 1 shows the full list of GINA functions. One of them is WlxLoggedOnSAS. Winlogon calls this function when it receives a "secure attention sequence," better known as Ctrl+Alt+Del. The default GINA displays the logon dialog and awaaaay you go.... So one way to disable Ctrl+Alt+Del is to write a new MyGina.dll with stubs that call the old msgina.dll and a WlxLoggedOnSAS function that gobbles the key. Or, you could write a keyboard driver.

Figure 1 GINA Functions

Function Description
WlxActivateUserShell Activates the user shell program
WlxDisplayLockedNotice Allows the GINA DLL to display lock information
WlxDisplaySASNotice Winlogon calls this function when no user is logged on
WlxDisplayStatusMessage Winlogon calls this function with a status message to display
WlxGetConsoleSwitchCredentials Winlogon calls this function to read the currently logged on user's credentials to transparently transfer them to a target session
WlxGetStatusMessage Winlogon calls this function to get the current status message
WlxInitialize Initializes the GINA DLL for a specific window station
WlxIsLockOk Verifies that workstation lock is okay
WlxIslogoffOk Verifies that logoff is okay
WlxLoggedOnSAS Winlogon calls this function when it receives a secure attention sequence (SAS) event while the user is logged on and the workstation is not locked
WlxLoggedOutSAS Winlogon calls this function when it receives an SAS event while no user is logged on
WlxLogoff Notifies the GINA DLL that a logoff operation was requested
WlxNegotiate Indicates whether the current version of Winlogon can be used with the GINA DLL
WlxNetworkProviderLoad Winlogon calls this function after it loads a network provider to collect valid authentication and identification information
WlxRemoveStatusMessage Winlogon calls this function to tell the GINA DLL to stop displaying the status message
WlxScreensaverNotify Allows the GINA to interact with the screen saver operation
WlxShutdown Winlogon calls this function just before shutting down, allowing the GINA to perform any shutdown tasks, such as ejecting a smart card from a reader
WlxStartApplication Winlogon calls this function when the system needs an application started in the user's context
WlxWkstaLockedSAS Winlogon calls this function when it receives an SAS while the workstation is locked

Gosh, that sure seems like a lot of work just to disable a measly key. There has to be a better way. In fact, there is. You can forget about GINA because all you have to do to disable Ctrl+Alt+Del is set a policy. Really. Go to the Start menu, select Run, and type "gpedit.msc" to run the Group Policy editor. Look in User Configuration | Administrative Templates | System and you'll find a section called Ctrl+Alt+Del Options (see Figure 2). "Remove Task Manager" is just the ticket to make Ctrl+Alt+Del go bye-bye. From code, you have to set the registry entry:

HKCU\
 Software\
  Microsoft\
   Windows\
    CurrentVersion\
     Policies\
      System\DisableTaskMgr = dword:1

Figure 2 Setting a Policy to Disable Ctrl+Alt+Del

Now if Joe User types Ctrl+Alt+Del, he'll get a message like the one in Figure 3. So the short answer to the first half of your question—how to disable Ctrl+Alt+Del in Windows XP?—is: enable the policy "Remove Task Manager," either in GPEDIT or by setting the registry entry. I assume you've already disabled Ctrl+Alt+Del for logon by checking "Use the Welcome screen" in Control Panel | User Accounts (see Figure 4), since otherwise you wouldn't be asking about Task Manager. If you aren't using the Welcome screen, then setting DisableTaskMgr disables the Task Manager button from the logon/logoff dialog.

Figure 3 Error Message

Figure 3** Error Message **

Some of you might be wondering how I discovered DisableTaskMgr when neither it nor Remove Task Manager seems to be documented. I found it by poking around with GPEDIT. GPEDIT is an incredibly useful tool, not so much for editing policies but for finding them. GPEDIT lets you control many aspects of Windows, from access permissions to whether Microsoft® Internet Explorer has the classic look; from whether to display the Places bar in dialogs to whether Ctrl+Alt+Del invokes the Task Manager. You can configure hundreds of user interface behaviors, enough, in fact, to make a system administrator drool.

Figure 4 Logon Options

Figure 4** Logon Options **

Once you find the policy that does what you want, how do you find the corresponding registry voodoo? There are two ways. The first is the brute-force approach: export your registry keys to a .reg file before and after modifying the policy, then look for differences. All policies live in one of four keys.

// user-specific
HKEY_CURRENT_USER\Software\Policies 
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies

// computer-specific 
HKEY_LOCAL_MACHINE\Software\Policies 
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies

The second way is to go directly to the source by examining the administrative templates (.adm files) that describe the policies. Figure 5 shows the snippet from \windows\system32\GroupPolicy\Adm\system.adm that describes DisableTaskMgr. The magic commands to specify the registry key and value are KEYNAME and VALUENAME. You can create your own administrative templates and policies for your app, but to edit and explore them, you need an editor that can handle Unicode. My favorite is Epsilon 11.0, but Notepad or WordPad will do. Administrative templates let sys admins configure systems for entire organizations—what power! To learn more, see "Administrative Template File Format" in the Platform SDK.

Figure 5 DisableTaskMgr .adm File

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="Prevents users from starting Task Manager 
(Taskmgr.exe).\n\nIf this setting is enabled and users try to start
Task Manager, a message appears explaining that a policy prevents the 
action.\n\nTask Manager lets users start and stop programs; monitor the 
performance of their computers; view and monitor all programs running 
on their computers, including system services; find the executable 
names of programs; and change the priority of the process in which 
programs run."

DisableTaskMgr="Remove Task Manager"

Before moving on, let me emphasize that DisableTaskMgr lets you disable Ctrl+Alt+Del, not trap it. To trap Ctrl+Alt+Del, you have three options: write a GINA stub, write a keyboard driver, or replace TaskMgr.exe with your own program.

OK, whew! So much for Ctrl+Alt+Del. What about Alt+Tab and the other task-switching keys? In the really old days (Windows 3.1), you could handle WM_SYSKEYDOWN. In Windows 95 and Windows 98 you could pull the same SPI_SETSCREENSAVERRUNNING trick I mentioned earlier. But once again, Windows NT 4.0 (SP3 and later), Windows 2000, and Windows XP are different. You have to write a low-level keyboard hook. Sorry. Fortunately, it's not hard. Figure 6 shows a DLL I wrote, TaskKeyHook.dll, that does the job. In general, systemwide hooks must be implemented as DLLs. TaskKeyHook exports two functions, DisableTaskKeys and AreTaskKeysDisabled. The former installs the WH_KEYBOARD_LL hook; the latter tells whether it's installed. The hook procedure itself traps Alt+Tab, Ctrl+Esc, Alt+Esc, and also the Windows keys (VK_LWIN and VK_RWIN, which I'll describe later). If it sees one of these keys, it returns immediately. Go directly to caller; do not pass CallNextHookEx.

LRESULT CALLBACK MyTaskKeyHookLL(...)
{
   if (/* task key *)
      return 1; // gobble gobble
   return CallNextHookEx(...);
}

Figure 6 TaskKeyHook

TaskKeyHook.h

////////////////////////////////////////////////////////////////
// MSDN Magazine — September 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio 6.0 and Visual Studio .NET on Windows XP.
//
#define DLLIMPORT __declspec(dllimport)

DLLIMPORT BOOL DisableTaskKeys(BOOL bEnable, BOOL bBeep);
DLLIMPORT BOOL AreTaskKeysDisabled();

Figure 6 TaskKeyHook

TaskKeyHook.cpp

////////////////////////////////////////////////////////////////
// MSDN Magazine — September 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio 6.0 and Visual Studio .NET on Windows XP.
//
// This file implements the low-level keyboard hook that traps the task 
// keys.
//
#define _WIN32_WINNT 0x0500 // for KBDLLHOOKSTRUCT
#include <afxwin.h>         // MFC core and standard components

#define DLLEXPORT __declspec(dllexport)

//////////////////
// App (DLL) object
//
class CTaskKeyHookDll : public CWinApp {
public:
   CTaskKeyHookDll()  { }
   ~CTaskKeyHookDll() { }
} MyDll;

////////////////
// The section is SHARED among all instances of this DLL.
// A low-level keyboard hook is always a system-wide hook.
// 
#pragma data_seg (".mydata")
HHOOK g_hHookKbdLL = NULL; // hook handle
BOOL  g_bBeep = FALSE;     // beep on illegal key
#pragma data_seg ()
#pragma comment(linker, "/SECTION:.mydata,RWS") // tell linker: make it 
                                                // shared

/////////////////
// Low-level keyboard hook:
// Trap task-switching keys by returning without passing along.
//
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)) { // Start Menu
         if (g_bBeep && (wp==WM_SYSKEYDOWN||wp==WM_KEYDOWN))
            MessageBeep(0); // only beep on downstroke if requested
         return 1; // gobble it: go directly to jail, do not pass go
      }
   }
   return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
}

//////////////////
// Are task keys disabled—ie, is hook installed?
// Note: This assumes there's no other hook that does the same thing!
//
DLLEXPORT BOOL AreTaskKeysDisabled()
{
   return g_hHookKbdLL != NULL;
}

//////////////////
// Disable task keys: install low-level kbd hook.
// Return whether currently disabled or not.
//
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 is mostly straightforward. The only tricks are the use of #pragma data_seg to name the data segment that contains global data, and #pragma comment (linker...) to tell the linker to make this segment shared. See the code for details.

Figure 7 TrapKeys

Figure 7** TrapKeys **

I wrote a little program, TrapKeys (see Figure 7 and Figure 8), that puts everything together: DisableTaskMgr, keyboard hook, and a third function, disable taskbar. If you want to disable task-switching, you probably want to disable the taskbar as well:

HWND hwnd = FindWindow("Shell_traywnd", NULL);
EnableWindow(hwnd, FALSE); // disable it

Figure 8 TrapKeys.cpp

////////////////////////////////////////////////////////////////
// MSDN Magazine — September 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio 6.0 and Visual Studio .NET on Windows XP.
//
#include "stdafx.h"
#include "resource.h"
#include "StatLink.h"
#include "TaskKeyMgr.h"

//////////////////
// Main dialog
//
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();

   // command/UI update handlers
   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()
};

//////////////////
// Standard MFC dialog app class in 14 lines + comments
//
class CMyApp : public CWinApp {
public:
   virtual BOOL InitInstance() {
      // init app: run dialog
      CMyDialog dlg;
      m_pMainWnd = &dlg;
      dlg.DoModal();
      return FALSE;
   }
   virtual int ExitInstance() {
      // For safety, re-enable everything on exit
      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()

//////////////////
// Initialize dialog: subclass hyperlinks and load icon
//
BOOL CMyDialog::OnInitDialog()
{
   CDialog::OnInitDialog();

   // init my hyperlinks
   m_wndLink1.SubclassDlgItem(IDC_PDURL,this);
   m_wndLink2.SubclassDlgItem(IDC_MSDNURL,this);
   m_wndLink3.SubclassDlgItem(IDC_MSDNLINK,this);

   // Set the icon for this dialog. MFC doesn't do it for dialog apps.
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
   SetIcon(m_hIcon, TRUE);       // Set big icon
   SetIcon(m_hIcon, FALSE);      // Set small icon
   
   return TRUE;
}

////////////////////////////////////////////////////////////////
// Command/UI update handlers. You can write this in your sleep—Zzzz.

void CMyDialog::OnDisableTaskKeys()
{
   CTaskKeyMgr::Disable(CTaskKeyMgr::TASKKEYS,
      !CTaskKeyMgr::AreTaskKeysDisabled(), TRUE); // beep
}

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());
}

//////////////////
// This is required to make ON_UPDATE_COMMAND_UI work properly.
// Cf. my column in the July 1997 Microsoft Systems Journal.
// 
LRESULT CMyDialog::OnKickIdle(WPARAM wp, LPARAM lCount)
{
   UpdateDialogControls(this, TRUE);
   return 0;
}

There's one minor quirk here. If you disable the taskbar and then press the Windows key, you'll get a Start menu as normal. Oops. Apparently, the taskbar doesn't check to see if it's enabled before processing VK_LWIN. If a window is disabled, it shouldn't process input—that's what it means to be disabled. Usually this happens automatically because when you call EnableWindow(FALSE), Windows stops processing keystrokes. But no doubt VK_LWIN arrives through some other path and whoever wrote the code to handle it never thought to check whether the taskbar is in fact enabled. No big deal. I modified TaskKeyHook to trap the Windows keys as well. So now nothing happens if you press the Start menu key. If there are any other keys I left out, by all means, add them yourself.

To make life easy, I encapsulated all the disabling functions in a class, CTaskKeyMgr (see Figure 9). All the functions are static, so CTaskKeyMgr is really just a namespace. You can use it in your own apps to disable the task key functions you want. For example, to disable task-switching and the taskbar but not Ctrl+Alt+Del:

CTaskKeyMgr::Disable(CTaskKeyMgr::TASKKEYS |
                     CTaskKeyMgr::TASKBAR, TRUE);

There are also functions to find out which features are currently disabled, and you can even tell CTaskKeyMgr to beep when the user presses one of the taboo keys. Happy programming!

Figure 9 TaskKeyMgr

TaskKeyMgr.h

////////////////////////////////////////////////////////////////
// MSDN Magazine — September 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio 6.0 and Visual Studio .NET on Windows XP.
//
#pragma once
#include "TaskKeyHook.h"

//////////////////
// Use this class to disable task keys, task manager and/or the taskbar.
// Call Disable with flags for items you want to disable; for example:
//
// CTaskMgrKeys::Disable(CTaskMgrKeys::ALL);
// 
class CTaskKeyMgr {
public:
   enum {
      TASKMGR  = 0x01,  // disable task manager (Ctrl+Alt+Del)
      TASKKEYS = 0x02,  // disable task keys (Alt-TAB, etc)
      TASKBAR  = 0x04,  // disable task bar
      ALL=0xFFFF        // disable everything :(
   };
   static void Disable(DWORD dwItem,BOOL bDisable,BOOL bBeep=FALSE);

   static BOOL IsTaskMgrDisabled();
   static BOOL IsTaskBarDisabled();
   static BOOL AreTaskKeysDisabled() {
      return ::AreTaskKeysDisabled(); // call DLL 
   }
};

Figure 9 TaskKeyMgr

TaskKeyMgr.cpp

////////////////////////////////////////////////////////////////
// MSDN Magazine — September 2002
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio 6.0 and Visual Studio .NET on Windows XP.
//
#include "StdAfx.h"
#include "TaskKeyMgr.h"

#define HKCU HKEY_CURRENT_USER

// Magic registry key/value for "Remove Task Manager" policy.
LPCTSTR KEY_DisableTaskMgr =
   "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCTSTR VAL_DisableTaskMgr = "DisableTaskMgr";

//////////////////
// Disable task-key related stuff.
// 
// dwFlags  = what to disable
// bDisable = disable (TRUE) or enable (FALSE)
// bBeep    = whether to beep for illegal keys (TASKKEYS only)
//
void CTaskKeyMgr::Disable(DWORD dwFlags, BOOL bDisable, BOOL bBeep)
{
   // task manager (Ctrl+Alt+Del)
   if (dwFlags & TASKMGR) {
      HKEY hk;
      if (RegOpenKey(HKCU, KEY_DisableTaskMgr,&hk)!=ERROR_SUCCESS)
         RegCreateKey(HKCU, KEY_DisableTaskMgr, &hk);

      if (bDisable) { // disable TM: set policy = 1
         DWORD val=1;
         RegSetValueEx(hk, VAL_DisableTaskMgr, NULL,
            REG_DWORD, (BYTE*)&val, sizeof(val));

      } else { // enable TM: remove policy 
         RegDeleteValue(hk,VAL_DisableTaskMgr);
      }
   }

   // task keys (Alt-TAB etc)
   if (dwFlags & TASKKEYS)
      ::DisableTaskKeys(bDisable,bBeep); // install keyboard hook

   // task bar
   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; // no key ==> not disabled

   DWORD val=0;
   DWORD len=4;
   return RegQueryValueEx(hk, VAL_DisableTaskMgr,
      NULL, NULL, (BYTE*)&val, &len)==ERROR_SUCCESS && val==1;
}

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLasciais a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++(Addison-Wesley, 1992). Paul can be reached at askpd@pobox.com or https://www.dilascia.com.