Enable Clock on Task Bar for Windows XP and 2003

by Jeremy Saunders on May 10, 2009

Microsoft made it a real challenge to automate the enabling of the Clock on the taskbar. Some clever people worked out how this can be achieved several years ago by manipulating certain bits of the Settings binary value under the “Stuckrects2” registry key. However, it would only take effect on the 2nd logon, because the Stuckrects2 key is only created on the first logoff for a new user profile. I’ve simply extended an earlier script, written by Jeff Durbin, to create the Stuckrects2 key structure and set the Settings value to a default value during the first logon. This then allows us to manipulate the value as intended so that we can ensure the task bar Clock is displayed for all new user logons.

Enjoy!

SetTaskBarOptions.vbs


'================================================================================
'
' Release 1.2
' Modified by Jeremy@jhouseconsulting.com 23rd April 2009.
'
' Original Script written by jeffdurbin at gmail dot com on 24th January 2003.'
'
' COMMENT:   In a Terminal/Citrix Server environment, the administrator usually
'          tightly controls the user environment using System or Group Policies.
'          Policy settings make changes to registry values that are reflected in
'          the user's environment. Unfortunately, some of the common settings,
'          such as those that affect the Taskbar, are not controllable via policy.
'          Examining the registry value that determines the Taskbar's configuration
'          reveals the problem: The settings are lumped into a binary key. In the
'          case of the Taskbar, 4 bits of byte 9 (for Win2K and above) determine
'          which of the Taskbar's options are enabled. This is a problem, as System
'          and Group Policies do not give us a mechanism for changing a single bit
'          within a byte. Take the clock, for example. I routinely have customers
'          that ask me to either turn the clock ON for all users, or turn the clock
'          OFF for all users. This is not a problem for users that have never
'          logged on and don't have a Terminal Server profile. I modify the
'          Default User profile on the Citrix servers using REGEDT32 and set
'          the options the way I want them. I'm out of luck for users with roaming
'          profiles that already exist, however; because their profiles already
'          exist, they never receive the settings I've made in the Default User
'          profile. So the question becomes: how do you change the settings for
'          users with existing profiles?
'            One way would be to use REGEDT32 to load the NTUSER.DAT registry
'          hive for each user and make the change. That's fine if you have 10
'          users, but if you've got 500, that's not practical. Obviously, you
'          want to make a change as the user logs in. The first question to
'          resolve is, what bits need to be changed? The bits and their functions
'          are as follows:
'          
'                 Bit 1 (from right): on = auto hide
'                 Bit 2 (from right): on = always on top
'                 Bit 3 (from right): on = show small icons in start menu
'                 Bit 4 (from right): on = hide clock
'          
'                                    Auto   Always  Show Small  Hide
'                   Binary  Dec Hex  Hide   On Top    Icons     Clock
'                 ----------------------------------------------------
'                 | 00000000 00 00 |      |        |           |     |
'                 | 00000001 01 01 |  x   |        |           |     |
'                 | 00000010 02 02 |      |   x    |           |     |
'                 | 00000011 03 03 |  x   |   x    |           |     |
'                 | 00000100 04 04 |      |        |    x      |     |
'                 | 00000101 05 05 |  x   |        |    x      |     |
'                 | 00000110 06 06 |      |   x    |    x      |     |
'                 | 00000111 07 07 |  x   |   x    |    x      |     |
'                 | 00001000 08 08 |      |        |           |  x  |
'                 | 00001001 09 09 |  x   |        |           |  x  |
'                 | 00001010 10 0A |      |   x    |           |  x  |
'                 | 00001011 11 0B |  x   |   x    |           |  x  |
'                 | 00001100 12 0C |      |        |    x      |  x  |
'                 | 00001101 13 0D |  x   |        |    x      |  x  |
'                 | 00001110 14 0E |      |   x    |    x      |  x  |
'                 | 00001111 15 0F |  x   |   x    |    x      |  x  |
'                 | --------------------------------------------------
'
'                 SINGLE OPTIONS:
'                 08 = No Settings Enabled
'                 09 = Auto Hide
'                 00 = Show Clock
'                 0A = Always on Top
'                 0C = Show small icons in Start Menu
'                
'                 MULTIPLE OPTIONS:
'                 01 = Auto Hide and Show Clock
'                 02 = Always on Top and Show Clock
'                 03 = Always on Top and Auto Hide and Show Clock
'                 04 = Show small icons in Start Menu and Show Clock
'                 0E = Always on Top and Show small icons in Start Menu
'                 0F = Always on Top and Auto Hide and Show small icons in Start Menu
'                 06 = Always on Top and Show small icons in Start Menu and Show Clock
'                 07 = All Options Enabled
'                 0B = Always on Top and Auto Hide
'                 0D = Auto Hide and Show small icons in Start Menu
'                
'            Writing a script to change those bits is simple. Unfortunately,
'          if you run the script during logon, the changes aren't reflected
'          on the Taskbar because Explorer has already loaded. You might think
'          that the changes would show up at the next login, but that's not
'          the case; it seems that that the settings revert to those stored
'          in the profile. You could kill Explorer and restart it, but that
'          wouldn't be a very nice login for the users.
'            The answer is to run the script -before- Explorer starts. To do
'          this, follow these instructions*:
'          
'          1. Save this script to your Winnt\system32 folder
'          2. Open winnt\system32\usrlogon.cmd
'          3. Add the following line after the @Echo off line:
'          
'             %systemroot%\system32\SetTaskBarOptions.vbs
'          
'            That's it. Now, when a user logs in, the script will run and set
'          the options before Explorer runs, so the settings will show up
'          immediately.
'
'          * These instructions are for Terminal Servers. A similar technique
'            can be used on non-TS machines (which don't use USRLOGON.CMD).
'            At HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon,
'            there is a value called 'UserInit' which references an executable
'            called USERINIT.EXE. You can replace the reference to USERINIT.EXE
'            with a reference to a batch file (say, USERINIT.CMD). The batch
'            file contents could be:
'
'            @echo off
'            cls
'            SetTaskBarOptions.vbs
'            userinit
'          
'   USAGE: This script relies on WMI, which is included with Win2K and above.
'
'          To configure the options, modify the variables below the line that
'          reads: '*************** OPTIONS ARE SET HERE ***************
'          For example, I prefer the following configuration: show clock, always
'          on top, do not hide taskbar, show small icons. To achieve this result,
'          I need to set 1 bit (always on top), and clear 3 bits (auto hide
'          taskbar, hide clock, show small icons). You simply add the 4 constants
'          to the appropriate variable: intBitsToSet or intBitsToClear.
'            The registry key is called StuckRects2 in Windows 2000 and above.
'
' IMPORTANT: Usually the Stuckrects2 key is only created when the user logs off for
'            the first time with a new profile. Therefore these settings would only
'            take place after the second login. However, as you will see, this
'            script will create a default key and value, and populate it with a
'            typical default value to start with.
'
' This is typically what the default value is after the first logoff...
' Settings = hex:28,00,00,00,ff,ff,ff,ff,0a,00,00,00,03,00,00,00,3c,00,00,00,1e,00,00,00,fe,ff,ff,ff,e4,03,00,00,02,05,00,00,02,04,00,00

' This is what we change it to. Notice the 9th hex value.
' Settings = hex:28,00,00,00,ff,ff,ff,ff,02,00,00,00,03,00,00,00,3c,00,00,00,1e,00,00,00,fe,ff,ff,ff,e4,03,00,00,02,05,00,00,02,04,00,00
'
' This "simple" change enables the clock on the task bar.
'
'================================================================================

Option Explicit

Dim objReg, intBitsToClear, intBitsToSet, strComputer, strKeyPath, strKeyRoot
Dim strValue, strValueData, arrTemp, item, arrValueData, Return, i, blnDebug
Dim intDecimalValue, StdOut

Const HKEY_CURRENT_USER = &H80000001
Const cAutoHideTaskbar = 1
Const cAlwaysOnTop = 2
Const cShowSmallIcons = 4
Const cHideClock = 8

'*************** OPTIONS ARE SET HERE ***************

intBitsToSet = cAlwaysOnTop
intBitsToClear = cAutoHideTaskbar & cHideClock & cShowSmallIcons
' These options will set the 9th value to 02 (00000010), which is
' Always on Top and Show Clock

'****************************************************

Set StdOut = WScript.StdOut
strComputer = "."
strKeyRoot = "HKCU\"
strKeyPath ="SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Stuckrects2"
strValue = "Settings"
strValueData = "28,00,00,00,ff,ff,ff,ff,0a,00,00,00,03,00,00,00,3c,00,00,00,1e,00,00,00,fe,ff,ff,ff,e4,03,00,00,02,05,00,00,02,04,00,00"
blnDebug = False

Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\default:StdRegProv")

' New profiles will not contain the key and value, so we need to create them, using
' typical default data to start with.
If NOT RegKeyExists(strKeyRoot & strKeyPath) Then
  Return = objReg.CreateKey (HKEY_CURRENT_USER,strKeyPath)
  If blnDebug Then
    If (Return = 0) And (Err.Number = 0) Then
      Wscript.Echo "Create the " & strKeyPath & " key."
    Else
      Wscript.Echo "Could not create the " & strKeyPath & " key."
    End If
  End If
End If
If NOT RegValueExists(strKeyRoot & strKeyPath & "\" & strValue) Then
' This is tricky to add because we need to present the data to the SetBinaryValue method
' as an array of strings, each representing an integer between 0 and 255. So we first
' need to convert the string of hex values into an array of integers.
  arrTemp = Split(strValueData,",")
  For i = 0 to ubound(arrTemp)
    intDecimalValue = HexToDec(arrTemp(i))
    If blnDebug Then
      wscript.echo "Array Element " & i & ": " & intDecimalValue
    End If
    If IsArray (arrValueData) Then
      ReDim Preserve arrValueData(uBound(arrValueData) + 1)
    Else
      ReDim arrValueData(i)
    End If
    arrValueData(i) = intDecimalValue
  Next
  If blnDebug Then
    For i = 0 to uBound(arrValueData) - 1
      StdOut.WriteLine "Array Element " & i & ": " & arrValueData(i)
    Next
  End If
  Return = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValue, arrValueData)
  If blnDebug Then
    If (Return = 0) And (Err.Number = 0) Then
      Wscript.Echo "SetBinaryValue succeeded."
    Else
      Wscript.Echo "SetBinaryValue failed. Error = " & Err.Number
    End If
  End If
End If

' Read in the Binary value and set the required options.
Return = objReg.GetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValue, arrValueData)
If blnDebug Then
For i = 0 to uBound(arrValueData)
   StdOut.WriteLine "Array Element " & i & ": " & arrValueData(i)
Next
End If

If (Return = 0) And (Err.Number = 0) Then

  If ubound(arrValueData) > 0 Then
    arrValueData(8) = arrValueData(8) OR intBitsToSet
    arrValueData(8) = arrValueData(8) AND (NOT(intBitsToClear))

    Return = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValue, arrValueData)
    If blnDebug Then
      If (Return = 0) And (Err.Number = 0) Then
        Wscript.Echo "SetBinaryValue succeeded"
      Else
        Wscript.Echo "SetBinaryValue failed. Error = " & Err.Number
      End If
    End If

' Set: Byte = Byte OR cShowSmallIcons
' Clear: Byte = Byte AND (NOT(cShowSmallIcons))
' Check: if ((Byte AND cShowSmallIcons) = cShowSmallIcons)

  End If
End If

Set objReg = Nothing
Set StdOut = Nothing

WScript.Quit(0)

Function RegKeyExists(ByVal sRegKey)
' Returns True or False based on the existence of a registry key.
  Dim sDescription, oShell
  Set oShell = CreateObject("WScript.Shell")
  RegKeyExists = True
  sRegKey = Trim (sRegKey)
  If Not Right(sRegKey, 1) = "\" Then
    sRegKey = sRegKey & "\"
  End If
  On Error Resume Next
  oShell.RegRead "HKEYNotAKey\"
  sDescription = Replace(Err.Description, "HKEYNotAKey\", "")
  Err.Clear
  oShell.RegRead sRegKey
  RegKeyExists = sDescription <> Replace(Err.Description, sRegKey, "")
  On Error Goto 0
  Set oShell = Nothing
End Function

Function RegValueExists(sRegValue)
' Returns True or False based of the existence of a registry value.
  Dim oShell, RegReadReturn
  Set oShell = CreateObject("WScript.Shell")
  RegValueExists = True  ' init value
  On Error Resume Next
  RegReadReturn = oShell.RegRead(sRegValue)
  If Err.Number <> 0 Then
    RegValueExists = False
  End if
  On Error Goto 0
  Set oShell = Nothing
End Function

Function HexToDec(strHex)
' This function converts a hexadecimal value represented by a string into a decimal value.
' http://www.sonofsofaman.com/hobbies/code/hextodec.asp
  dim lngResult
  dim intIndex
  dim strDigit
  dim intDigit
  dim intDecimalValue
  lngResult = 0
  for intIndex = len(strHex) to 1 step -1
    strDigit = mid(strHex, intIndex, 1)
    intDigit = instr("0123456789ABCDEF", ucase(strDigit))-1
    if intDigit >= 0 then
      intDecimalValue = intDigit * (16 ^ (len(strHex)-intIndex))
      lngResult = lngResult + intDecimalValue
    else
      lngResult = 0
      intIndex = 0 ' stop the loop
    end if
  next
  HexToDec = lngResult
End Function
Jeremy Saunders

Jeremy Saunders

Independent Consultant | Contractor | Microsoft & Citrix Specialist | Desktop Virtualization Specialist at J House Consulting
Jeremy is a highly respected, IT Professional, with over 30 years’ experience in the industry. He is an independent IT consultant providing expertise to enterprise, corporate, higher education and government clients. His skill set, high ethical standards, integrity, morals and attention to detail, coupled with his friendly nature and exceptional design and problem solving skills, makes him one of the most highly respected and sought after Microsoft and Citrix technical resources in Australia. His alignment with industry and vendor best practices puts him amongst the leaders of his field.
Jeremy Saunders
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: