Hey, Scripting Guy! Question

Hey, Scripting Guy! You know, it just dawned on me that Windows PowerShell uses the old-fashioned Fred Flintstone cmd console for its host application. Because this thing has been around forever, are there any registry settings I can use to configure this beast? The reason I am asking is that every time I go to a new machine, I need to right-click the little icon, open up the property page, and make my configuration settings. I would love to be able to run a script that would set things up the way I wish. Can you help?

- BP

SpacerHey, Scripting Guy! Answer

Hi BP,

Fred Flintstone does DOS (dinosaur operating system). You are, however, correct that Windows PowerShell uses the old-fashioned cmd console and as such can be configured via the registry. But before we get too far into answering your question, please stand by for a few words from our sponsors.

Standard disclaimer: The script today talks about editing the registry. Microsoft does not support editing the registry, because you can really mess stuff up if you are not careful. If your script causes your computer to jump off your desk and bite the postman, please send us the script because we would love to see something like that. If you gain massive amounts of weight and all your hair falls out, don’t send us your script; we already have enough problems in that arena. If your pet rabbit gets an unquenchable desire for Anzak biscuits and starts listening to Olivia Newton John songs, don't say we didn't warn you. Please back up your registry before proceeding to run the script. Back up everything you don't want to lose just to be on the safe side.

The first thing you should do before working with the registry is make sure you are either working in a virtual machine that allows you to roll back changes if you wish, or ensure you have a good backup of the registry. Jerry Honeycutt has written a number of good books about the registry for Microsoft Press, as well as a number of excellent articles such as this one. By the way, in case you do not know, you can download Microsoft Virtual PC 2007 for free.

For a good overview of the registry, you can refer to this article. To explore the registry, you can use the Tweakomatic, which produces scripts in VBScript. Here is a good Sesame Script article that gives an overview of the registry. And here is the Script Center registry archive.

Today's script allows us to retrieve and configure a number of console settings. In this script, we will also illustrate using a class from the .NET Framework. Here’s today’s script:

Param([switch]$get,[switch]$set)
Function Get-Settings()
{
 $hklm = "HKEY_CURRENT_USER"
 $key = "Console"
 $properties = "QuickEdit","FullScreen","InsertMode"
 Foreach ($property in $properties)
  {
   $property + ": " + [Microsoft.Win32.Registry]::GetValue("$hklm\$key",$property,$null)
  }
} #end Get-Settings

Function Set-Settings()
{
 $hklm = "HKEY_CURRENT_USER"
 $key = "Console"
 $hash = @{"QuickEdit" = 1 ; "FullScreen" = 0 ; "InsertMode" = 1 }
 Foreach($name in $hash.keys)
 {
  "Changing " + $name + " to " + $hash[$name] 
  [Microsoft.Win32.Registry]::SetValue("$hklm\$key",$name,$hash[$name])
 } 
} #end Set-Settings
# *** Entry Point To Script ***

if($get) {Get-Settings}
if($set) {Set-Settings}

As mentioned earlier, the cmd console has a number of properties that can be configured via the registry. These settings are seen here:

Image of cmd console properties that can be configured via the registry

 

In our script, the first thing we do is create a couple of command-line parameters. To do this, we use the Param statement. This statement must be the first non-commented line in the script. We use the switchparameter structure, which has a type alias of [switch] to convert our command-line parameter into a switched parameter. A switched parameter only has effect when it is present on the command line. Suppose, for example, we run the script with a command line such as the one seen here:

GetSetConsoleSettings.ps1 –get

Then the script will only retrieve the console settings. If we run it with the –set switch, it will only set the console settings. One of the cool things about command-line parameters is that you do not need to type the entire parameter name. You can, for instance, run the script as shown here:

GetSetConsoleSettings.ps1 –g -s

The line of code that creates the command-line parameters is shown here:

Param([switch]$get,[switch]$set)

We now need to create the Get-Settings function. To do this, we use the function key word as illustrated here:

Function Get-Settings()

We then create a few variables. The first variable, $hklm, represents the registry hive of HKEY_CURRENT_USER, although we could have called it anything we wish. The second variable is the registry key we wish to work with, which is the console key. These two variables are seen here:

 $hklm = "HKEY_CURRENT_USER"
 $key = "Console"

We also create an array of property names. This will allow us to minimize the amount of code that we need to write, and it is actually a pretty cool way to retrieve the property values. To create an array in Windows PowerShell, we need only assign more than one item to the variable. As seen here, when we assign multiple items to the variable, it is automatically an array. When we query the variable, it will spit out all of the items by default:

PS C:\> $a = 1,2,3
PS C:\> $a
1
2
3

If we need to retrieve a specific item from the array, we can do so by using the element number, beginning with 0. This is seen here:

PS C:\> $a[0]
1
PS C:\> $a[1]
2
PS C:\> $a[2]
3

If we are interested in seeing all of the items, one element at a time, we could use the For…Next loop (except that Windows PowerShell does not use a next at the end of the for loop; so I guess it is really only a for loop.) This is seen here:

for($i = 0 ; $i -le $a.length -1 ; $i++) {$a[$i]}

It might make more sense, if we used the foreach statement. This is because the foreach statement does not require us to increment a counter variable, as the for statement did. Notice how much simpler this code appears:

PS C:\> Foreach($i in $a) {$i}
1
2
3

The array of property names is shown here:

 $properties = "QuickEdit","FullScreen","InsertMode"

To work through the array of property names, we decide to use the foreach statement, as seen here:

 Foreach ($property in $properties)

Then we call the static GetValue method from the Microsoft.Win32.Registry .NET Framework class. When we do this, we need to give the GetValue the registry root name with the key name, the property name, and a default value for the registry key, if it is not present. Valid root names are listed here:

HKEY_CURRENT_USER

HKEY_LOCAL_MACHINE

HKEY_CLASSES_ROOT

HKEY_USERS

HKEY_PERFORMANCE_DATA

HKEY_CURRENT_CONFIG

HKEY_DYN_DATA

In our script, we are interested in printing out the value that is contained in the registry key property. We use the variable $property and concatenate the string with the value returned from the GetValue static method. This is shown here:

  {
   $property + ": " + [Microsoft.Win32.Registry]::GetValue("$hklm\$key",$property,$null)
  }
} #end Get-Settings

It is time to create the Set-Settings function. We once again use the function key word:

Function Set-Settings()

You can probably guess what comes next. Sure, we need to define some more variables and set their associated values. These are the same two variables we used in the previous function:

{
 $hklm = "HKEY_CURRENT_USER"
 $key = "Console"

Now let's get a bit more sophisticated. Yes, it is time for a spot of tea in my new tea pot the Scripting Wife gave me for Christmas, complete with an assortment of loose leaf teas in their own little tin canisters. I think that Earl Grey tea will do nicely on this rainy Charlotte, NC, USA, afternoon. If only I could steal some Anzak biscuits from the neighbor’s pet rabbit. I am back. The little dude ate all the biscuits. Oh, well.

When we create a hash table, we use a variable to hold it, and use the special ampersand character followed with a couple of curly brackets. Each name/value pair is assigned by using the equal sign and separated by a semicolon. As seen here, when you call the variable by itself, it prints out all the name/value pairs:

PS C:\> $hash = @{"QuickEdit" = 1 ; "FullScreen" = 0 ; "InsertMode" = 1 }
PS C:\> $hash

Name                           Value
----                           -----
InsertMode                     1
QuickEdit                      1
FullScreen                     0

Because this is Windows PowerShell and everything in Windows PowerShell is an object, this means that the $hash variable contains an object. In fact, it is the same object you would use in C# or in VB.NET: the System.Collections.Hashtable class. The System.Collections.Hashtable class has the members shown in Table 1.

Table 1 WMI registry tree values

Name Member Type Definition

Add

Method

System.Void Add(Object key, Object value)

Clear

Method

System.Void Clear()

Clone

Method

System.Object Clone()

Contains

Method

System.Boolean Contains(Object key)

ContainsKey

Method

System.Boolean ContainsKey(Object key)

ContainsValue

Method

System.Boolean ContainsValue(Object value)

CopyTo

Method

System.Void CopyTo(Array array, Int32 arrayIndex)

Equals

Method

System.Boolean Equals(Object obj)

GetEnumerator

Method

System.Collections.IDictionaryEnumerator GetEnumerator()

GetHashCode

Method

System.Int32 GetHashCode()

GetObjectData

Method

System.Void GetObjectData(SerializationInfo info, StreamingContext context)

GetType

Method

System.Type GetType()

get_Count

Method

System.Int32 get_Count()

get_IsFixedSize

Method

System.Boolean get_IsFixedSize()

get_IsReadOnly

Method

System.Boolean get_IsReadOnly()

get_IsSynchronized

Method

System.Boolean get_IsSynchronized()

get_Item

Method

System.Object get_Item(Object key)

get_Keys

Method

System.Collections.ICollection get_Keys()

get_SyncRoot

Method

System.Object get_SyncRoot()

get_Values

Method

System.Collections.ICollection get_Values()

On_Deserialization

Method

System.Void OnDeserialization(Object sender)

Remove

Method

System.Void Remove(Object key)

set_Item

Method

System.Void set_Item(Object key, Object value)

ToString

Method

System.String ToString()

Item

ParameterizedProperty

System.Object Item(Object key) {get;set;}

Count

Property

System.Int32 Count {get;}

IsFixedSize

Property

System.Boolean IsFixedSize {get;}

IsReadOnly

Property

System.Boolean IsReadOnly {get;}

IsSynchronized

Property

System.Boolean IsSynchronized {get;}

Keys

Property

System.Collections.ICollection Keys {get;}

SyncRoot

Property

System.Object SyncRoot {get;}

Values

Property

System.Collections.ICollection Values {get;}

By playing around with the methods and properties from the HashTable class, you can leverage this object to allow you to do some rather clever things. As an example, see how easy it is to find out if we have the value 5 in our hash table:

PS C:\> $hash.ContainsValue(1)
True
PS C:\> $hash.ContainsValue(5)
False 

At any rate, here is the line of code that creates the HashTable for our script:

$hash = @{"QuickEdit" = 1 ; "FullScreen" = 0 ; "InsertMode" = 1 }

To walk through the HashTable, we can use this trick from the VBScript days. We obtain a collection of all the keys in the HashTable by using the keys property, and we use the $name variable with the foreach statement as our enumerator. This is seen here:

 Foreach($name in $hash.keys)

We now print out just a bit of confirmation by indexing into the HashTable via the $name variable. We also concatenate the $name variable with the value associated with name from the hash table. This is seen here:

 {  "Changing " + $name + " to " + $hash[$name] 

Next we use the SetValue static method from the Microsoft.Win32.Registry .NET Framework class. The SetValue static method takes the registry hive and registry key in the first position. The second position is the name of the property, and the third position is the value to assign to that property. This is seen here:

  [Microsoft.Win32.Registry]::SetValue("$hklm\$key",$name,$hash[$name])
 } 
} #end Set-Settings

Now we need to check the command-line parameters. To do this, we use an if statement. If the variable exists, we call the appropriate function. This is seen here:

if($get) {Get-Settings}
if($set) {Set-Settings}

Well BP, I hope you have enjoyed registry week. I tried to show you different ways to do the same kinds of things. Along the way, we also picked up some decent tricks for working with Windows PowerShell. Join us tomorrow for Quick-Hits Friday where we will explore some more hot topics fresh from the scripter@microsoft.com inbox (I have finally caught it up by the way). Until then, take care and don't download any dancing babies.

Ed Wilson and Craig Liebendorfer, Scripting Guys