PowerShell always works with objects. Whenever you output objects into the PowerShell console, PowerShell automatically converts the rich objects into readable text. In this chapter, you will learn what objects are and how to get your hands on PowerShell objects before they get converted to simple text.
Topics Covered:
Objects = Properties + Methods
In real life, you probably already know what an object is: everything you can touch. Objects in PowerShell are actually quite similar. Let's turn a typical real-world object like a pocketknife into a PowerShell object.
How would you describe this object to someone, let's say over a phone line? You would probably carefully examine the object and then describe what it is and what it can do:
- Properties: a pocketknife has particular properties, such as its color, manufacturer, size, or number of blades. The object is red, weights 55 grams, has three blades, and is made by the firm Idera. So properties describe what an object is.
- Methods: in addition, you can do things with this object, such as cut, turn screws, or pull corks out of wine bottles. The object can cut, screw, and remove corks. Everything that an object can is called its methods.
In the computing world, an object is very similar: its nature is described by properties, and the actions it can perform are called its methods. Properties and methods are called members.
Creating a New Object
Let's turn our real-life pocketknife into a virtual pocketknife. Using New-Object, PowerShell can generate new objects, even a virtual pocketknife. First you need a new and empty object:
$pocketknife = New-Object Object
This new object is actually pretty useless right now. If you call it, PowerShell will literally return "nothing":
$pocketknife
Adding Properties
Next, let's start describing what our object is. To do that, add properties to the object.
Add-Member -memberType NoteProperty -name Color -value Red -inputObject $pocketknife
Use the Add-Member cmdlet to add properties. Here, you added the property Color with the value Red to the object $pocketknife. If you call the object now, it suddenly has a first property telling the world that its color is red:
$pocketknife
Color
-----
Red
In the same way, you now add more properties to describe the object even better. Remember that you don't need to completely write out parameter names. It is enough to write only as much as to make the parameter name unambiguous:
Add-Member -Me NoteProperty -In $pocketknife -Na Weight -Value 55
In fact, you don't need to specify parameter names for some of the parameters at all because some of them are positional: provided you specify parameters in the right order, PowerShell can automatically assign your values to the correct parameter. Adding new properties to your object becomes even easier:
Add-Member -inputObject $pocketknife NoteProperty Manufacturer Idera
Most PowerShell cmdlets can receive their input objects either by parameter (-inputObject) or via the pipeline, so you can add properties to your object in yet another way:
$pocketknife | Add-Member NoteProperty Blades 3
By now, you've described the object in $pocketknife with a total of four properties. If you output the object in $pocketknife in the PowerShell console, PowerShell automatically converts the object into readable text:
$pocketknife
Color Weight Manufacturer Blades
----- ------ ------------ -------
Red 55 Idera 3
Outputting an object to the console gets you a quick overview over its properties. To access the value of a specific property, add a dot and then the property name:
$pocketknife.manufacturer
Idera
Adding Methods
With every new property you added to your object, $pocketknife has been gradually taking shape, but it still really can't do anything. Properties only describe what an object is, not what it can do.
The actions your object can do are called its methods. So let's teach your object a few useful methods:
Add-Member -memberType ScriptMethod -In $pocketknife `
-name cut -Value { "I'm whittling now" }
Add-Member -in $pocketknife ScriptMethod screw { "Phew...it's in!" }
$pocketknife | Add-Member ScriptMethod corkscrew { "Pop! Cheers!" }
Again, you used the Add-Member cmdlet, but this time you added a method instead of a property (in this case, a ScriptMethod). The value is a scriptblock marked by braces, which contains the PowerShell instructions you want the method to perform. If you output your object, it will still look the same because PowerShell only visualizes object properties, not methods:
$pocketknife
Color Weight Manufacturer Blades
----- ------ ------------ -------
Red 55 Idera 3
To use any of the three newly added methods, add a dot and then the method name followed by two parentheses, which are what distinguish properties from methods. For example, if you'd like to remove a cork with your virtual pocketknife, enter this instruction:
$pocketknife.corkscrew()
Pop! Cheers!
Your object really does carry out the exact script commands you assigned to the corkscrew() method. So, methods perform actions, while properties merely provide information. Always remember to add parentheses to method names. If you forget them, something interesting happens:
$pocketknife.corkscrew
Script : "Pop! Cheers!"
OverloadDefinitions : {System.Object corkscrew();}
MemberType : ScriptMethod
TypeNameOfValue : System.Object
Value : System.Object corkscrew();
Name : corkscrew
IsInstance : True
You just received a method description. What's interesting about this is mainly the OverloadDefinitions property. As you'll see later, it reveals the exact way to use a command for any method. In fact, the OverloadDefinitions information is in an additional object. For PowerShell, absolutely everything is an object so you could store the object in a variable and then specifically ask the OverloadDefinitions property for information:
$info = $pocketknife.corkscrew
$info.OverloadDefinitions
System.Object corkscrew();
The "virtual pocketknife" example reveals that objects are containers that contain data (properties) and actions (methods).
Our virtual pocketknife was a somewhat artificial object with no real use. Next, let's take a look at a more interesting object which PowerShell stores in the variable $host.
Properties: What an Object "Is"
Properties describe an object. Object properties are automatically converted into text when you output the object to the console. That's enough to investigate any object. Check out the properties in $host!
$host
Name : ConsoleHost
Version : 1.0.0.0
InstanceId : e32debaf-3d10-4c4c-9bc6-ea58f8f17a8f
UI : System.Management.Automation.Internal.
Host.InternalHostUserInterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
The object stored in the variable $host apparently contains seven properties. The properties' names are listed in the first column. So, if you want to find out which PowerShell version you're using, you could access and return the Version property:
$host.Version
Major Minor Build Revision
----- ----- ----- --------
1 0 0 0
It works—the version is displayed. However, the version isn't displayed as a single number. Rather, PowerShell displays four columns: Major, Minor, Build and Revision. Whenever you see columns, you know these are the object properties that PowerShell has just converted into text. Let's check out the data type that the Version property uses:
$version = $host.Version
$version.GetType().FullName
System.Version
The version is not stored as a String object but as a System.Version object. This object type is perfect for storing versions, allowing you to easily read all details about any given version:
$host.Version.Major
1
$host.Version.Build
0
Knowing an object type is very useful because once you know there is a type called System.Version, you can use it for your own purposes as well. Try and convert a simple string of your choice into a rich version object! To do that, simply make sure the string consists of four numbers separated by dots (the typical format for versions), then make PowerShell convert the string into a System.Version type. You convert things by adding the target type in square brackets in front of the string:
[System.Version]'12.55.3.28334'
Major Minor Build Revision
----- ----- ----- --------
12 55 3 28334
The CurrentCulture property is just another example of the same concept. Read this property and find out its type:
$host.CurrentCulture
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
$host.CurrentCulture.GetType().FullName
System.Globalization.CultureInfo
Country properties are again stored in a highly specialized type that describes a culture with the properties LCID, Name, and DisplayName. If you wanted to know which international version of PowerShell you are using, read the DisplayName property:
$host.CurrentCulture.DisplayName
English (United States)
$host.CurrentCulture.DisplayName.GetType().FullName
System.String
Likewise, you could convert any suitable string into a CultureInfo-object. So if you wanted to find out details about the 'de-DE' locale, do this:
[System.Globalization.CultureInfo]'de-DE'
LCID Name DisplayName
---- ---- -----------
1031 de-DE German (Germany)
You could also convert the LCID into a CultureInfo object by converting a suitable number:
[System.Globalization.CultureInfo]1033
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
Properties Containing Objects
The properties of an object store data, and this data is, in turn, stored in various other objects. Two properties in $host seem to be special: UI and PrivateData. When you output $host into the console, all other properties are converted into readable text - except for the properties UI and PrivateData:
$host
Name : ConsoleHost
Version : 1.0.0.0
InstanceId : e32debaf-3d10-4c4c-9bc6-ea58f8f17a8f
UI : System.Management.Automation.Internal.
Host.InternalHostUserInterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
The reason is that both these properties contain an object that, as the only property, provides, in turn, an object. If you'd like to find out what is actually stored in the UI property, read the property:
$host.UI
RawUI
-----
System.Management.Automation.Internal.
Host.InternalHostRawUserInterface
You see that the property UI contains only a single property called RawUI, in which yet another object is stored. Let's see what sort of object is stored in the RawUI property:
$host.ui.rawui
ForegroundColor : DarkYellow
BackgroundColor : DarkMagenta
CursorPosition : 0,136
WindowPosition : 0,87
CursorSize : 25
BufferSize : 120,3000
WindowSize : 120,50
MaxWindowSize : 120,62
MaxPhysicalWindowSize : 140,62
KeyAvailable : False
WindowTitle : PowerShell
"RawUI" stands for "Raw User Interface" and exposes the raw user interface settings your PowerShell console uses. You can read all of these properties, but can you also change them?
Read-Only and Read-Write Properties
Can you actually change properties, too? And if you can, what happens then?
Properties need to accurately describe an object, so if you modify a property, the underlying object has to also be modified to reflect that change. If that is not possible, the property cannot be changed and is called "read-only."
Console background and foreground colors are a great example of properties you can easily change. If you do, the console will change colors accordingly. Your property changes are reflected by the object, and the changed properties still accurately describe the object.
$host.ui.rawui.BackgroundColor = "Green"
$host.ui.rawui.ForegroundColor = "White"
Type cls so the entire console adopts this color scheme.
Other properties cannot be changed. If you try anyway, you'll get an error message:
$host.ui.rawui.keyavailable = $true
"KeyAvailable" is a ReadOnly-property.
At line:1 char:16
+ $host.ui.rawui.k <<<< eyavailable = $true
Whether the console receives key press input, or not, depends on whether you pressed a key or not. You cannot control that by changing a property, so this property refuses to be changed. You can only read it.
| Property |
Description |
| ForegroundColor |
Text color. Optional values are Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, and White. |
| BackgroundColor |
Background color. Optional values are Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, and White. |
| CursorPosition |
Current position of the cursor |
| WindowPosition |
Current position of the window |
| CursorSize |
Size of the cursor |
| BufferSize |
Size of the screen buffer |
| WindowSize |
Size of the visible window |
| MaxWindowSize |
Maximally permissible window size |
| MaxPhysicalWindowSize |
Maximum possible window size |
| KeyAvailable |
Makes key press input available |
| WindowTitle |
Text in the window title bar |
Table 6.1: Properties of the RawUI object
Property Types
Some properties accept numeric values. For example, the size of a blinking cursor is specified as a number from 0 to 100 and corresponds to the fill percentage. The next line sets a cursor size of 75%. Values outside the 0-100 numeric range generate an error:
$host.ui.rawui.cursorsize = 75
$host.ui.rawui.cursorsize = 1000
Exception setting "CursorSize": "Cannot process "CursorSize"
because the cursor size specified is invalid.
Parameter name: value
Actual value was 1000."
At line:1 char:16
+ $host.ui.rawui.c <<<< ursorsize = 1000
Other properties expect color settings. You cannot specify any color that comes to your mind, though. Instead, PowerShell expects a "valid" color, and if your color is unknown, you receive an error message listing the colors you can use:
$host.ui.rawui.ForegroundColor = "yellow"
$host.ui.rawui.ForegroundColor = "pink"
Exception setting "ForegroundColor": "Cannot convert value "pink" to
type "System.ConsoleColor" due to invalid enumeration values. Specify
one of the following enumeration values and try again. The possible
enumeration values are "Black, DarkBlue, DarkGreen, DarkCyan, DarkRed,
DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red,
Magenta, Yellow, White."
At line:1 char:16
+ $host.ui.rawui.F <<<< oregroundColor = "pink"
If you assign an invalid value to the property ForegroundColor, the error message lists the possible values. If you assign an invalid value to the property CursorSize, you get no hint. Why?
Every property expects a certain object type. Some object types are more specific than others. Use Get-Member to find out which object types a given property expects:
$host.ui.RawUI | Get-Member -memberType Property
TypeName: System.Management.Automation.Internal.Host.
InternalHostRawUserInterface
Name MemberType Definition
---- ---------- ----------
BackgroundColor Property System.ConsoleColor
BackgroundColor {get;set;}
BufferSize Property System.Management.Automation.
Host.Size BufferSize
{get;set;}
CursorPosition Property System.Management.Automation.
Host.Coordinates
CursorPosition {get;set;}
CursorSize Property System.Int32 CursorSize {get;set;}
ForegroundColor Property System.ConsoleColor
ForegroundColor {get;set;}
KeyAvailable Property System.Boolean
KeyAvailable {get;}
MaxPhysicalWindowSize Property System.Management.Automation.
Host.Size MaxPhysicalWindowSize
{get;}
MaxWindowSize Property System.Management.Automation.
Host.Size MaxWindowSize
{get;}
WindowPosition Property System.Management.Automation.
Host.Coordinates
WindowPosition {get;set;}
WindowSize Property System.Management.Automation.
Host.Size WindowSize
{get;set;}
WindowTitle Property System.String WindowTitle
{get;set;}
As you see, ForegroundColor expects a System.ConsoleColor type. This type is a highly specialized type, a list of possible values, a so called enumeration:
[system.ConsoleColor].IsEnum
True
Whenever a type is an enumeration, you can use a special .NET method called GetNames() to list the possible values defined in that enumeration:
[System.Enum]::GetNames([System.ConsoleColor])
Black
DarkBlue
DarkGreen
DarkCyan
DarkRed
DarkMagenta
DarkYellow
Gray
DarkGray
Blue
Green
Cyan
Red
Magenta
Yellow
White
If you specify anything not contained in the enumeration, the error message will simply return the enumeration's contents.
CursorSize stores its data in a System.Int32 object, which is simply a 32bit number. So, if you try to set the cursor size to 1000, you are actually not violating the object boundaries because the value of 1000 can be stored in a System.Int32 object. You get an error message anyway because of the validation code that the CursorSize property executes internally. So, whether you get detailed error information really depends on the property's definition. In the case of CursorSize, you would receive only an indication that your value is invalid, but not the reason why.
Sometimes, a property expects a value wrapped in a specific object. For example, if you'd like to change the PowerShell window size, you could use the WindowSize property. As it turns out, the property expects a new window size wrapped in an object of type System.Management.Automation.Host.Size. Where can you get an object like that?
$host.ui.rawui.WindowSize = 100,100
Exception setting "WindowSize": "Cannot convert "System.Object[]"
to "System.Management.Automation.Host.Size"."
At line:1 char:16
+ $host.ui.rawui.W <<<< indowSize = 100,100
There are a number of ways to provide specialized objects for properties. The easiest approach: read the existing value of a property (which will get you the object type you need), change the result, and then write back the changes. For example, here's how you would change the PowerShell window size to 80 x 30 characters:
$value = $host.ui.rawui.WindowSize
$value
Width Height
----- ------
110 64
$value.Width = 80
$value.Height = 30
$host.ui.rawui.WindowSize = $value
Or, you can freshly create the object you need by using New-Object:
$value = New-Object `
System.Management.Automation.Host.Size(80,30)
$host.ui.rawui.WindowSize = $value
Or in a line:
$host.ui.rawui.WindowSize = New-Object `
System.Management.Automation.Host.Size(80,30)
Listing All Properties
Because properties and methods are all members of an object, Get-Member returns detailed information about them. Let's use Get-Member to examine all properties defined in $host. To limit Get-Member to only properties, use the memberType parameter and specify "property":
$host | Get-Member -memberType property
Name MemberType Definition
---- ---------- ----------
CurrentCulture Property System.Globalization.CultureInfo
CurrentCulture {get;}
CurrentUICulture Property System.Globalization.CultureInfo
CurrentUICulture {get;}
InstanceId Property System.Guid InstanceId {get;}
Name Property System.String Name {get;}
PrivateData Property System.Management.Automation.PSObject
PrivateData {get;}
UI Property System.Management.Automation.Host.
PSHostUserInterface UI {get;}
Version Property System.Version Version {get;}
In the column Name, you now see all supported properties in $host. In the column Definition, the property object type is listed first. For example, you can see that the Name property stores a text as System.String type. The Version property uses the System.Version type.
At the end of each definition, braces report whether the property is read-only ({get;}) or can also be modified ({get;set;}). You can see at a glance that all properties of the $host object are only readable. Now, take a look at the $host.ui.rawui object:
$host.ui.rawui | Get-Member -memberType property
BackgroundColor Property System.ConsoleColor BackgroundColor
{get;set;}
BufferSize Property System.Management.Automation.Host.
Size BufferSize {get;set;}
CursorPosition Property System.Management.Automation.Host.
Coordinates CursorPosition {get;set;}
CursorSize Property System.Int32 CursorSize {get;set;}
ForegroundColor Property System.ConsoleColor ForegroundColor {get;set;}
KeyAvailable Property System.Boolean KeyAvailable {get;}
MaxPhysicalWindowSize Property System.Management.Automation.Host.Size
MaxPhysicalWindowSize {get;}
MaxWindowSize Property System.Management.Automation.Host.Size
MaxWindowSize {get;}
WindowPosition Property System.Management.Automation.Host.
Coordinates WindowPosition {get;set;}
WindowSize Property System.Management.Automation.Host.Size
WindowSize {get;set;}
WindowTitle Property System.String WindowTitle {get;set;}
This result is more differentiated. It shows you that some properties could be changed, while others could not.
There are different "sorts" of properties. Most properties are of the Property type, but PowerShell can add additional properties like ScriptProperty. So if you really want to list all properties, use the memberType parameter and assign it a value of *Property. The wildcard in front of "property" will also select all specialized properties like "ScriptProperty".
Methods: What an Object "Can Do"
Methods are things that an object can do. When you output an object to the console, only its properties are converted into readable text. Its methods remain invisible. To list the methods of an object, use Get-Member and use the parameter "memberType" with the value "method":
$host | Get-Member -memberType Method
Name MemberType Definition
---- ---------- ----------
EnterNestedPrompt Method System.Void EnterNestedPrompt()
Equals Method System.Boolean Equals(Object obj)
ExitNestedPrompt Method System.Void ExitNestedPrompt()
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
get_CurrentCulture Method System.Globalization.CultureInfo
get_CurrentCulture()
get_CurrentUICulture Method System.Globalization.CultureInfo
get_CurrentUICulture()
get_InstanceId Method System.Guid get_InstanceId()
get_Name Method System.String get_Name()
get_PrivateData Method System.Management.Automation.PSObject
get_PrivateData()
get_UI Method System.Management.Automation.Host.
PSHostUserInterface get_UI()
get_Version Method System.Version get_Version()
NotifyBeginApplication Method System.Void NotifyBeginApplication()
NotifyEndApplication Method System.Void NotifyEndApplication()
SetShouldExit Method System.Void SetShouldExit(Int32
exitCode)
ToString Method System.String ToString()
Eliminating "Internal" Methods
Get-Member lists all methods defined by an object. Not all of them are really useful to you. Let's check out why some of the listed methods are really only of limited use.
Get_ and Set_ Methods
Any method that starts with "get_" is really a method to retrieve a property value. So the method "get_someInfo()" is getting you the very same information you could also have retrieved with the "someInfo" property.
$host.version
Major Minor Build Revision
----- ----- ----- --------
1 0 0 0
$host.get_Version()
Major Minor Build Revision
----- ----- ----- --------
1 0 0 0
The same is true for Set_ methods: they change a property value and exist for properties that are read/writeable. Note in this example: all properties of the $host object can only be read so there are no Set_ methods. There can be more internal methods like this, such as Add_ and Remove_ methods. Generally speaking, when a method name contains an underscore, it most likely is an internal method.
Standard Methods
In addition, nearly every object contains a number of "inherited" methods that are also not specific to the object but perform general tasks for every object:
| Method |
Description |
| Equals |
Verifies whether the object is identical to a comparison object |
| GetHashCode |
Retrieves an object's digital "fingerprint" |
| GetType |
Retrieves the underlying object type |
| ToString |
Converts the object into readable text |
Table 6.2: Standard methods of a .NET object
To sort out all methods that contain an underscore, you could use Where-Object and the comparison operator -notlike:
$host | Get-Member -memberType *method |
Where-Object { $_.Name -notlike '*_*' }
The $host object really only contains these unique and useful methods:
Name MemberType Definition
---- ---------- ----------
EnterNestedPrompt Method System.Void EnterNestedPrompt()
ExitNestedPrompt Method System.Void ExitNestedPrompt()
NotifyBeginApplication Method System.Void NotifyBeginApplication()
NotifyEndApplication Method System.Void NotifyEndApplication()
SetShouldExit Method System.Void SetShouldExit(Int32
exitCode)
Calling a Method
Watch out: before you invoke a method: make sure you know what the method will do. Methods are commands that do something, and what a command does can be dangerous. To call a method, add a dot to the object and then the method name. Add an opened and closed parenthesis, like this:
$host.EnterNestedPrompt()
The PowerShell prompt changes to ">>". You have used EnterNestedPrompt() to open a nested prompt. Nested prompts are not especially useful in a normal console, so exit it again using the exit command or call $host.ExitNestedPrompt().
Nested prompts are very useful in functions or scripts because they work like breakpoints and can temporarily stop a function or script so you can verify variable contents or make code changes, after which you continue the code by entering exit. You'll learn more about this in Chapter 11.
Call Methods with Arguments
There are a bunch of useful methods in the UI object. Here's how you get a good overview:
$host.ui | Get-Member -memberType Method
TypeName: System.Management.Automation.Internal.Host.
InternalHostUserInterface
Name MemberType Definition
---- ---------- ----------
Equals Method System.Boolean Equals(Object obj)
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
get_RawUI Method System.Management.Automation.Host.
PSHostRawUserInterface get_RawUI()
Prompt Method System.Collections.Generic.Dictionary
`2[[System.String, mscorlib, ...
PromptForChoice Method System.Int32 PromptForChoice(String
caption, String message, ...
PromptForCredential Method System.Management.Automation.
PSCredential PromptForCredential...
ReadLine Method System.String ReadLine()
ReadLineAsSecureString Method System.Security.SecureString
ReadLineAsSecureString()
ToString Method System.String ToString()
Write Method System.Void Write(String value),
System.Void Write(ConsoleColor...
WriteDebugLine Method System.Void WriteDebugLine(String
message)
WriteErrorLine Method System.Void WriteErrorLine(String
value)
WriteLine Method System.Void WriteLine(), System.Void
WriteLine(String value)...
WriteProgress Method System.Void WriteProgress(Int64
sourceId, ProgressRecord record)
WriteVerboseLine Method System.Void WriteVerboseLine(String
message)
WriteWarningLine Method System.Void WriteWarningLine(String
message)
Most methods require additional arguments from you, which are listed in the Definition column.
Which Arguments are Required?
Pick out a method from the list, and ask Get-Member to get you more info. Let's pick WriteDebugLine():
$info = $host.UI | Get-Member WriteDebugLine
$info
TypeName: System.Management.Automation.
Internal.Host.InternalHostUserInterface
Name MemberType Definition
---- ---------- ----------
WriteDebugLine Method System.Void
WriteDebugLine
(String message)
$info.Definition
System.Void WriteDebugLine(String message)
The Definition property tells you how to call the method. Every definition starts with the object type that a method returns. In this example it is System.Void, a special object type because it represents "nothing": the method doesn't return anything at all. A method "returning" System.Void is really a procedure, not a function.
Next, a method's name follows, which is then followed by required arguments. WriteDebugLine needs exactly one argument called message, which is of String type. Here is how you call WriteDebugLine():
$host.ui.WriteDebugLine("Hello!")
Hello!
Low-Level Functions
WriteDebugLine() really does nothing spectacular. In fact, most methods found in the $host object are really only low-level commands used by the standard PowerShell cmdlets. For example, you could also have output the debug notification by using the following cmdlet:
Write-Debug "Hello!"
However, there are differences: No matter what—WriteDebugText() always writes out yellow debug messages. The high-level Write-Debug cmdlet only outputs the debug message when the $DebugPreference variable is set to anything other than "SilentlyContinue" (which is the default).
The same applies to the WriteErrorLine, WriteVerboseLine, and WriteWarningLine methods, which are the low-level functions for the Write-Error, Write-Verbose, and Write-Warning cmdlets.
So, if you'd like to output error or warning messages that are independent of the various preference settings in PowerShell, use the low-level commands in $host.UI.RawUI instead of the cmdlets.
Several Method "Signatures"
Some methods accept different argument types or even different numbers of arguments. To find out which "signatures" a method supports, use Get-Member again and look at the Definition property:
$info = $host.UI | Get-Member WriteLine
$info.Definition
System.Void WriteLine(),
System.Void WriteLine(String value),
System.Void WriteLine(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
String value)
Unfortunately, the definition is hard to read at first. Make it more readable by using Replace() to add line breaks.
Remember the strange "backtick" character ("`"). It introduces special characters; "`n" stands for a line break.
$info.Definition.Replace("), ", ")`n")
System.Void WriteLine()
System.Void WriteLine(String value)
System.Void WriteLine(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
String value)
This definition tells you: You do not necessarily need to supply arguments:
$host.ui.WriteLine()
The result is an empty line.
To output text, you specify one argument only, the text itself:
$host.ui.WriteLine("Hello world!")
Hello world!
The third variant adds support for foreground and background colors:
$host.ui.WriteLine("Red", "White", "Alarm!")
WriteLine() actually is the low-level function of the Write-Host cmdlet:
Write-Host
Write-Host "Hello World!"
Write-Host -foregroundColor Red `
-backgroundColor White Alarm!
Playing with PromptForChoice
Most methods you examined so far turned out to be low-level commands for cmdlets. This is also true for the following methods: Write() (corresponds to Write-Host -noNewLine) or ReadLine()/ReadLineAsSecureString() (Read-Host -asSecureString) or PromptForCredential() (Get-Credential).
A new functionality is exposed by the method PromptForChoice(). Let's first examine which arguments this method expects:
$info = $host.UI | Get-Member PromptForChoice
$info.Definition
System.Int32 PromptForChoice(String caption,
String message, Collection`1 choices,
Int32 defaultChoice)
You can get the same information if you call the method without parentheses:
$host.ui.PromptForChoice
MemberType : Method
OverloadDefinitions : {System.Int32 PromptForChoice(
String caption, String message,
Collection`1 choices,
Int32 defaultChoice)}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : System.Int32 PromptForChoice(
String caption, String message,
Collection`1 choices,
Int32 defaultChoice)
Name : PromptForChoice
IsInstance : True
The definition reveals that this method returns a numeric value (System.Int32). It requires a heading and a message respectively as text (String). The third argument is a bit strange: Collection`1 choices. The fourth argument is a number (Int32), the standard selection. You should have noticed by now the limitations of PowerShell's built-in description.
This is how you could use PromptForChoice() to create a simple menu:
$yes = ([System.Management.Automation.Host.ChoiceDescription]"&yes")
$no = ([System.Management.Automation.Host.ChoiceDescription]"&no")
$selection = [System.Management.Automation.Host.ChoiceDescription[]] `
($yes,$no)
$answer = $host.ui.PromptForChoice('Reboot',
'May the system now be rebooted?',$selection,1)
$selection[$answer]
if ($selection -eq 0) {
"Reboot"
} else {
"OK, then not"
}
Working with Real-Life Objects
Every single PowerShell command returns objects, which is a good thing. However, it is not that easy to get your hands on objects because whenever objects hit the PowerShell console, they will be converted to text and lose a lot of their information.
Storing Results in Variables
Do not output command results to the console to prevent PowerShell from converting objects into simple strings. The console is a hostile place for objects because anything output to the PowerShell console will end up as text. Instead, save the command result in a variable, which is a safe place for objects.
$listing = Dir
However, variables are only safe places for objects until you dump their content to the console: the objects stored inside of your variable would again be converted to text.
$listing
Directory: Microsoft.PowerShell.Core\FileSystem::
C:\Users\Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 04.03.2009 11:37 Application Data
d---- 05.03.2009 11:03 Backup
d-r-- 13.02.2009 15:05 Contacts
d---- 28.01.2009 18:33 Debug
(...)
So, to get in touch with the real objects, you can directly access them inside of a variable. Dir has stored its directory listing in $listing. Since the listing consists of more than one entry, it is wrapped in an array. Access an array element to get your hands on a real object:
$object = $listing[0]
$object
Directory: Microsoft.PowerShell.Core\FileSystem::
C:\Users\Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 0.07.2007 11:37 Application Data
The object picked here happens to match the folder Application Data; so it represents a directory. If you would prefer to directly pick a particular directory or file, you can do this:
$object = Dir c:\autoexec.bat
$object = Get-Item $env:winDir
Using Object Properties
You can now use Get-Member again to produce a list of all available properties:
$object | Get-Member -memberType *property
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
PSChildName NoteProperty System.String PSChildName=
Application Data
PSDrive NoteProperty System.Management.Automation.
PSDriveInfo PSDrive=C
PSIsContainer NoteProperty System.Boolean PSIsContainer=
True
PSParentPath NoteProperty System.String PSParentPath=
Microsoft.PowerShell.Core\
FileSystem::C:\Users...
PSPath NoteProperty System.String PSPath=Microsoft.
PowerShell.Core\FileSystem::
C:\Users\Tobia...
PSProvider NoteProperty System.Management.Automation.
ProviderInfo PSProvider=
Microsoft.PowerShell...
Attributes Property System.IO.FileAttributes
Attributes {get;set;}
CreationTime Property System.DateTime CreationTime
{get;set;}
CreationTimeUtc Property System.DateTime CreationTimeUtc
{get;set;}
Exists Property System.Boolean Exists {get;}
Extension Property System.String Extension {get;}
FullName Property System.String FullName {get;}
LastAccessTime Property System.DateTime LastAccessTime
{get;set;}
LastAccessTimeUtc Property System.DateTime LastAccessTimeUtc
{get;set;}
LastWriteTime Property System.DateTime LastWriteTime
{get;set;}
LastWriteTimeUtc Property System.DateTime LastWriteTimeUtc
{get;set;}
Name Property System.String Name {get;}
Parent Property System.IO.DirectoryInfo
Parent {get;}
Root Property System.IO.DirectoryInfo
Root {get;}
Mode ScriptProperty System.Object Mode
{get=$catr = "";...
Properties marked with {get;set;} in the column Definition may also be modified:
$object.LastAccessTime
Wednesday, January 14, 2009 11:37:39
$object.LastAccessTime = Get-Date
$object.LastAccessTime
Saturday, March 7, 2009 15:31:41
PowerShell-Specific Properties
PowerShell can add additional properties to an object. Whenever that occurs, Get-Member labels the property accordingly in the MemberType column. Native properties are just called "Property." Properties added by PowerShell use a prefix, such as "ScriptProperty" or "NoteProperty."
A NoteProperty like PSChildName contains static data. PowerShell adds it to tag additional information to an object. A ScriptProperty like Mode executes PowerShell script code that calculates the property's value.
If you want to see the script code being executed when you call the ScriptProperty Mode, ask Get-Member to list the property definition:
$info = $object | Get-Member Mode
$info.Definition
System.Object Mode {get=$catr = "";
if ( $this.Attributes -band 16 ) { $catr += "d" }
else { $catr += "z" };
if ( $this.Attributes -band 32 ) { $catr += "a" }
else { $catr += "-" };
if ( $this.Attributes -band 1 ) { $catr += "r" }
else { $catr += "-" };
if ( $this.Attributes -band 2 ) { $catr += "h" }
else { $catr += "-" };
if ( $this.Attributes -band 4 ) { $catr += "s" }
else { $catr += "-" };
$catr;}
As it turns out, Mode evaluates the native Attributes property which is a bitmask. Binary bitmasks are hard to read so that is why the new Mode script property converts the binary information into a more user friendly format.
| MemberType |
Description |
| AliasProperty |
Alternative name for a property that already exists |
| CodeProperty |
Static .NET method returns property contents |
| Property |
Genuine property |
| NoteProperty |
Subsequently added property with set data value |
| ScriptProperty |
Subsequently added property whose value is calculated by a script |
| ParameterizedProperty |
Property requiring additional arguments |
Table 6.3: Different property types
Using Object Methods
Use Get-Member again to find out the methods that an object supports:
$object | Get-Member -memberType *method
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Create Method System.Void Create(),
System.Void Create(
DirectorySecurity DirectoryS...
CreateObjRef Method System.Runtime.Remoting.ObjRef
CreateObjRef(Type requestedType)
CreateSubDirectory Method System.IO.DirectoryInfo
CreateSubDirectory(String path),
System.IO.Di...
Delete Method System.Void Delete(), System.Void
Delete(Boolean recursive)
Equals Method System.Boolean Equals(Object obj)
GetAccessControl Method System.Security.AccessControl.
DirectorySecurity GetAccessCo...
GetDirectories Method System.IO.DirectoryInfo[]
GetDirectories(), System.IO.
DirectoryInfo[]...
GetFiles Method System.IO.FileInfo[] GetFiles(
String searchPattern), System.IO.
FileIn...
GetFileSystemInfos Method System.IO.FileSystemInfo[]
GetFileSystemInfos(String
searchPattern), ...
GetHashCode Method System.Int32 GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetObjectData Method System.Void GetObjectData(
SerializationInfo info,
StreamingContext co...
GetType Method System.Type GetType()
get_Attributes Method System.IO.FileAttributes
get_Attributes()
get_CreationTime Method System.DateTime get_CreationTime()
get_CreationTimeUtc Method System.DateTime get_CreationTimeUtc()
get_Exists Method System.Boolean get_Exists()
get_Extension Method System.String get_Extension()
get_FullName Method System.String get_FullName()
get_LastAccessTime Method System.DateTime get_LastAccessTime()
get_LastAccessTimeUtc Method System.DateTime
get_LastAccessTimeUtc()
get_LastWriteTime Method System.DateTime get_LastWriteTime()
get_LastWriteTimeUtc Method System.DateTime
get_LastWriteTimeUtc()
get_Name Method System.String get_Name()
get_Parent Method System.IO.DirectoryInfo get_Parent()
get_Root Method System.IO.DirectoryInfo get_Root()
InitializeLifetimeService Method System.Object
InitializeLifetimeService()
MoveTo Method System.Void MoveTo(String destDirName)
Refresh Method System.Void Refresh()
SetAccessControl Method System.Void SetAccessControl(
DirectorySecurity DirectorySecurity)
set_Attributes Method System.Void set_Attributes(
FileAttributes value)
set_CreationTime Method System.Void set_CreationTime(
DateTime value)
set_CreationTimeUtc Method System.Void set_CreationTimeUtc(
DateTime value)
set_LastAccessTime Method System.Void set_LastAccessTime(
DateTime value)
set_LastAccessTimeUtc Method System.Void set_LastAccessTimeUtc(
DateTime value)
set_LastWriteTime Method System.Void set_LastWriteTime(
DateTime value)
set_LastWriteTimeUtc Method System.Void set_LastWriteTimeUtc(
DateTime value)
ToString Method System.String ToString()
Again, standard methods are displayed in bold font so you can safely ignore them because they exist in every object or match properties.
You can apply methods just like you did in the previous examples. For example, use the CreateSubDirectory method if you'd like to create a new subdirectory. Find out first which arguments this method requires and what it returns:
$info = $object | Get-Member CreateSubDirectory
$info.Definition.Replace("), ", ")`n")
System.IO.DirectoryInfo CreateSubDirectory(String path)
System.IO.DirectoryInfo CreateSubDirectory(String path,
DirectorySecurity DirectorySecurity)
You can see that the method has two signatures. Use the first to create a subdirectory and the second to add access permissions.
The next line creates a subdirectory called "My New Directory" without any special access privileges:
$object.CreateSubDirectory("My New Directory")
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 03.07.200915:49 My New Directory
Because the method returns a DirectoryInfo object as result and you haven't caught and stored this object in a variable, the pipeline converts it into text and outputs it. You could just as well have stored the result of the method in a variable:
$subdirectory = $object.CreateSubDirectory("Another subdirectory")
$subdirectory.CreationTime = "September 1, 1980"
$subdirectory.CreationTime
Monday, September 1, 1980 00:00:00
Different Method Types
Similarly to properties, PowerShell can also add additional methods to an object.
| MemberType |
Description |
| CodeMethod |
Method mapped to a static .NET method |
| Method |
Genuine method |
| ScriptMethod |
Method invokes PowerShell code |
Table 6.4: Different types of methods
Using Static Methods
By now, you know that PowerShell stores information in objects, and objects always have a type. You know that simple text is stored in objects of type System.String and that a date, for example, is stored in an object of type System.DateTime. You also know by now that each .NET object has a GetType() method with a FullName property which tells you the name of the type this object was derived from:
$date = Get-Date
$date.GetType().FullName
System.DateTime
Every type can have its own set of private members called "static" members. Simply specify a type in square brackets, then pipe it to Get-Member and use the -static parameter to see the static members of a type.
[System.DateTime] | Get-Member -static -memberType *method
TypeName: System.DateTime
Name MemberType Definition
---- ---------- ----------
Compare Method static System.Int32 Compare(
DateTime t1, DateTime t2)
DaysInMonth Method static System.Int32 DaysInMonth(
Int32 year, Int32 month)
Equals Method static System.Boolean Equals(
DateTime t1, DateTime t2),
static Sys...
FromBinary Method static System.DateTime FromBinary(
Int64 dateData)
FromFileTime Method static System.DateTime
FromFileTime(Int64 fileTime)
FromFileTimeUtc Method static System.DateTime
FromFileTimeUtc(Int64 fileTime)
FromOADate Method static System.DateTime FromOADate(
Double d)
get_Now Method static System.DateTime get_Now()
get_Today Method static System.DateTime get_Today()
get_UtcNow Method static System.DateTime get_UtcNow()
IsLeapYear Method static System.Boolean IsLeapYear(
Int32 year)
op_Addition Method static System.DateTime
op_Addition(DateTime d,
TimeSpan t)
op_Equality Method static System.Boolean
op_Equality(DateTime d1,
DateTime d2)
op_GreaterThan Method static System.Boolean
op_GreaterThan(DateTime t1,
DateTime t2)
op_GreaterThanOrEqual Method static System.Boolean
op_GreaterThanOrEqual(DateTime t1,
DateTime t2)
op_Inequality Method static System.Boolean
op_Inequality(DateTime d1,
DateTime d2)
op_LessThan Method static System.Boolean
op_LessThan(DateTime t1,
DateTime t2)
op_LessThanOrEqual Method static System.Boolean
op_LessThanOrEqual(DateTime t1,
DateTime t2)
op_Subtraction Method static System.DateTime
op_Subtraction(DateTime d,
TimeSpan t), sta...
Parse Method static System.DateTime
Parse(String s), static System.
DateTime Par...
ParseExact Method static System.DateTime
ParseExact(String s, String
format, IFormat...
ReferenceEquals Method static System.Boolean
ReferenceEquals(Object objA,
Object objB)
SpecifyKind Method static System.DateTime
SpecifyKind(DateTime value,
DateTimeKind kind)
TryParse Method static System.Boolean
TryParse(String s, DateTime&
result), static...
TryParseExact Method static System.Boolean
TryParseExact(String s,
String format, IFo...
There are a lot of method names starting with "op_," with "op" standing for "operator." These are methods called internally whenever you use this data type with an operator. op_GreaterThanOrEqual is the method that does the internal work when you use the PowerShell comparison operator "-ge" with date values.
The System.DateTime class supplies you with a bunch of important date and time methods. For example, to convert a date string into a real DateTime object and use the current locale, use Parse():
[System.DateTime]::Parse("March 12, 1999")
Friday, March 12, 1999 00:00:00
You could easily find out whether a certain year is a leap year:
[System.DateTime]::isLeapYear(2010)
False
for ($x=2000; $x -lt 2010; $x++) {
if( [System.DateTime]::isLeapYear($x) )
{ "$x is a leap year!" } }
2000 is a leap year!
2004 is a leap year!
2008 is a leap year!
Or you'd like to tell your children with absolute precision how much time will elapse before they get their Christmas gifts:
[DateTime]"12/24/2007 18:00" - [DateTime]::now
Days : 74
Hours : 6
Minutes : 28
Seconds : 49
Milliseconds : 215
Ticks : 64169292156000
TotalDays : 74.2700140694444
TotalHours : 1782,48033766667
TotalMinutes : 106948,82026
TotalSeconds : 6416929,2156
TotalMilliseconds : 6416929215,6
Two dates are being subtracted from each other here so you now know what happened during this operation:
- The first time indication is actually text. For it to become a DateTime object, you must specify the desired object type in square brackets. Important: Converting a String to a DateTime this way always uses the US locale. To convert a String to a DateTime using your current locale, use the Parse() method as shown a couple of moments ago!
- The second time comes from the Now static property, which returns the current time as DateTime object. This is the same as calling the Get-Date cmdlet (which you'd then need to put in parenthesis because you wouldn't want to subtract the Get-Date cmdlet but rather the result of the Get-Date cmdlet).
- The two timestamps are subtracted from each other using the subtraction operator ("-"). This was possible because the DateTime class defined the op_Subtraction() static method, which is needed for this operator.
Of course, you could have called the static method yourself and received the same result:
[DateTime]::op_Subtraction("12/24/2007 18:00", [DateTime]::Now)
Now it's your turn. In the System.Math class, you'll find a lot of useful mathematical methods. Try to put some of these methods to work.
| Function |
Description |
Example |
| Abs |
Returns the absolute value of a specified number (without signs). |
[Math]::Abs(-5) |
| Acos |
Returns the angle whose cosine is the specified number. |
[Math]::Acos(0.6) |
| Asin |
Returns the angle whose sine is the specified number. |
[Math]::Asin(0.6) |
| Atan |
Returns the angle whose tangent is the specified number. |
[Math]::Atan(90) |
| Atan2 |
Returns the angle whose tangent is the quotient of two specified numbers. |
[Math]::Atan2(90, 15) |
| BigMul |
Calculates the complete product of two 32-bit numbers. |
[Math]::BigMul(1gb, 6) |
| Ceiling |
Returns the smallest integer greater than or equal to the specified number. |
[Math]::Ceiling(5.7) |
| Cos |
Returns the cosine of the specified angle. |
[Math]::Cos(90) |
| Cosh |
Returns the hyperbolic cosine of the specified angle. |
[Math]::Cosh(90) |
| DivRem |
Calculates the quotient of two numbers and returns the remainder in an output parameter. |
$a = 0 [Math]::DivRem(10,3,[ref]$a) $a |
| Exp |
Returns the specified power of e (2.7182818). |
[Math]::Exp(12) |
| Floor |
Returns the largest integer less than or equal to the specified number. |
[Math]::Floor(5.7) |
| IEEERemainder |
Returns the remainder of division of two specified numbers. |
[Math]::IEEERemainder(5,2) |
| Log |
Returns the natural logarithm of the specified number. |
[Math]::Log(1) |
| Log10 |
Returns the base 10 logarithm of the specified number. |
[Math]::Log10(6) |
| Max |
Returns the larger of two specified numbers. |
[Math]::Max(-5, 12) |
| Min |
Returns the smaller of two specified numbers. |
[Math]::Min(-5, 12) |
| Pow |
Returns a specified number raised to the specified power. |
[Math]::Pow(6,2) |
| Round |
Rounds a value to the nearest integer or to the specified number of decimal places. |
[Math]::Round(5.51) |
| Sign |
Returns a value indicating the sign of a number. |
[Math]::Sign(-12) |
| Sin |
Returns the sine of the specified angle. |
[Math]::Sin(90) |
| Sinh |
Returns the hyperbolic sine of the specified angle. |
[Math]::Sinh(90) |
| Sqrt |
Returns the square root of a specified number. |
[Math]::Sqrt(64) |
| Tan |
Returns the tangent of the specified angle. |
[Math]::Tan(45) |
| Tanh |
Returns the hyperbolic tangent of the specified angle. |
[Math]::Tanh(45) |
| Truncate |
Calculates the integral part of a number. |
[Math]::Truncate(5.67) |
Table 6.5: Mathematical functions from the [Math] library
Finding Interesting .NET Types
The .NET framework consists of thousands of types, and maybe you are getting hungry for more. Are there other interesting types? There are actually plenty! Here are the three things you can do with .NET types:
Converting Object Types
For example, use System.Net.IPAddress to work with IP addresses. This is an example of a .NET type conversion where a string is converted into a System.Net.IPAddress type:
[system.Net.IPAddress]'127.0.0.1'
IPAddressToString : 127.0.0.1
Address : 16777343
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
Using Static Type Members
Or use System.Net.DNS to resolve host names. This is an example of accessing a static type method like GetHostByAddress():
[system.Net.Dns]::GetHostByAddress("127.0.0.1")
HostName Aliases AddressList
-------- ------- -----------
PCNEU01 {} {127.0.0.1}
Using Dynamic Object Instance Members
Or you can derive an instance of a type and use its dynamic members. For example, to download a file from the Internet:
$address = "http://powershell.com/cs/media/p/467/download.aspx"
$target = "$home\chart_drive_space.V2.ps1"
$object = New-Object Net.WebClient
$object.DownloadFile($address, $target)
"File was downloaded!"
Listing Assemblies
The search for interesting types begins with assemblies as they contain the types. First, you need to get a list of all the assemblies that PowerShell has loaded. Use the AppDomain type to find out the loaded assemblies. Its CurrentDomain() static method will give you access to the internal PowerShell .NET framework where you'll find the GetAssemblies() dynamic method, which will enable you to get a list of the loaded assemblies:
[AppDomain]::CurrentDomain
FriendlyName : DefaultDomain
Id : 1
ApplicationDescription :
BaseDirectory : C:\WINDOWS\system32\WindowsPowerShell\v1.0\
DynamicDirectory :
RelativeSearchPath :
SetupInformation : System.AppDomainSetup
ShadowCopyFiles : False
[AppDomain]::CurrentDomain.GetAssemblies()
GAC Version Location
--- ------- --------
True v2.0.50727 C:\Windows\Microsoft.NET\Framework\
v2.0.50727\mscorlib.dll
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.ConsoleHost\...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System\
2.0.0.0__b77a5c561934e089\...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Management.Automation\1.0....
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Configuration.Install\2.0....
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Commands.Man...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Security\1.0...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Commands.Uti...
True v2.0 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.ConsoleHost....
True v2.0.50727 C:\Windows\assembly\GAC_32\System.
Data\2.0.0.0__b77a5c561934e0...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Xml\2.0.0.0__b77a5c561934e...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
DirectoryServices\2.0.0.0_...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Management\2.0.0.0__b03f5f...
True v2.0 C:\Windows\assembly\GAC_MSIL\System.
Management.Automation.reso...
True v2.0.50727 C:\Windows\Microsoft.NET\Framework\
v2.0.50727\mscorlib.dll
True v2.0 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Security.res...
True v2.0 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Commands.Uti...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Configuration\2.0.0.0__b03...
You may see more assemblies than listed above. Any PowerShell snap-in loads its own assemblies, and the System.Reflection.Assembly type provides methods to manually load additional assemblies from .NET DLL files or the global assembly cache.
Finding Interesting Classes (Types)
To find out which types are located in an assembly, use the GetExportedTypes() method provided by an assembly. Since most assemblies contain way too many types you could search for a specific keyword. This code list all types that include the search word "environment":
$searchtext = "*Environment*"
[AppDomain]::CurrentDomain.GetAssemblies() |
ForEach-Object { $_.GetExportedTypes() } |
Where-Object { $_ -like $searchtext } |
ForEach-Object { $_.FullName }
System.EnvironmentVariableTarget
System.Environment
System.Environment+SpecialFolder
System.Runtime.InteropServices.RuntimeEnvironment
System.Security.Permissions.EnvironmentPermissionAccess
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.EnvironmentPermissionAttribute
System.ComponentModel.Design.Data.IDataEnvironment
Microsoft.PowerShell.Commands.EnvironmentProvider
System.Web.Configuration.HostingEnvironmentSection
System.Web.Hosting.HostingEnvironment
One of the types that show up is the type System.Environment. The System.Environment type can do very useful things. Let's list its static members:
[System.Environment] | Get-Member -static
TypeName: System.Environment
Name MemberType Definition
---- ---------- ----------
Exit Method static System.Void
Exit(Int32 exitCode)
ExpandEnvironmentVariables Method static System.String
ExpandEnvironmentVariables...
FailFast Method static System.Void
FailFast(String message)
GetCommandLineArgs Method static System.String[]
GetCommandLineArgs()
GetEnvironmentVariable Method static System.String
GetEnvironmentVariable(...
GetEnvironmentVariables Method static System.Collections.
IDictionary GetEnvironmentV...
GetFolderPath Method static System.String
GetFolderPath(SpecialFolder...
GetLogicalDrives Method static System.String[]
GetLogicalDrives()
SetEnvironmentVariable Method static System.Void
SetEnvironmentVariable(...
CommandLine Property static System.String
CommandLine {get;}
CurrentDirectory Property static System.String
CurrentDirectory {get;set;}
ExitCode Property static System.Int32 ExitCode
{get;set;}
HasShutdownStarted Property static System.Boolean
HasShutdownStarted {get;}
MachineName Property static System.String
MachineName {get;}
NewLine Property static System.String
NewLine {get;}
OSVersion Property static System.
OperatingSystem OSVersion
{get;}
ProcessorCount Property static System.Int32
ProcessorCount {get;}
StackTrace Property static System.String
StackTrace {get;}
SystemDirectory Property static System.String
SystemDirectory {get;}
TickCount Property static System.Int32
TickCount {get;}
UserDomainName Property static System.String
UserDomainName {get;}
UserInteractive Property static System.Boolean
UserInteractive {get;}
UserName Property static System.String
UserName {get;}
Version Property static System.Version
Version {get;}
WorkingSet Property static System.Int64
WorkingSet {get;}
For example, the static methods of the System.Environment class will show you which user has executed the script on which machine:
[system.Environment]::UserDomainName +
"\" + [System.Environment]::UserName +
" on " + [System.Environment]::MachineName
Idera\Tobias Weltner on PC12
Using GetFolderPath(), the class will also reveal the paths to all important Windows folders. To find out the proper values for an argument, specify an invalid argument, and the error message will list all valid argument values:
[System.Environment]::GetFolderPath("HH")
Cannot convert argument "0", with value: "HH", for
"GetFolderPath" to type "System.Environment+SpecialFolder":
"Cannot convert value "HH" to type "System.Environment+
SpecialFolder" due to invalid enumeration values. Specify
one of the following enumeration values and try again.
The possible enumeration values are "Desktop, Programs,
Personal, MyDocuments, Favorites, Startup, Recent, SendTo,
StartMenu, MyMusic, DesktopDirectory, MyComputer,Templates,
ApplicationData, LocalApplicationData, InternetCache,
Cookies, History, CommonApplicationData, System,
ProgramFiles, MyPictures, CommonProgramFiles"."
So, if you'd like to know where the picture folder is on your computer, use MyPictures.
[System.Environment]::GetFolderPath("MyPictures")
C:\Users\Tobias Weltner\Pictures
Looking for Methods
Next, let's search for some interesting methods. Here's a way for you to find the GetFolderPath() method of the previous example:
$searchtext = "*getfolder*"
[AppDomain]::CurrentDomain.GetAssemblies() |
ForEach-Object { $_.GetExportedTypes() } |
ForEach-Object { $_.getmembers() } |
Where-Object { $_.isStatic} |
Where-Object { $_ -like $searchtext } |
ForEach-Object { "[{0}]::{1} --> {2}" -f `
$_.declaringtype, $_.toString().SubString( `
$_.toString().IndexOf(" ")+1), $_.ReturnType }
[System.Environment]::GetFolderPath(SpecialFolder)
--> System.String
The search can easily take a few minutes because PowerShell examines every single method in every type exposed by every loaded assembly.
Creating New Objects
You have seen that many .NET types contain useful static methods. In addition, you can create new objects (instances) that are derived from a specific type. To create new instances, you can either convert an existing object to a new type, or you can create a new instance using New-Object. In addition, you may be able to call some cmdlet or method that happens to return the object type you are after:
$datetime = [System.DateTime] '1.1.2000'
$datetime.GetType().Fullname
System.DateTime
$datetime = New-Object System.DateTime
$datetime.GetType().Fullname
System.DateTime
$datetime = Get-Date
$datetime.GetType().Fullname
System.DateTime
$datetime = [System.DateTime]::Parse('1.1.2000')
$datetime.GetType().Fullname
System.DateTime
Creating New Objects with New-Object
You can create a .NET object with New-Object, which gives you full access to all type "constructors." These are invisible methods that create the new object. To create a new instance of a type, the type needs to have at least one constructor. If it has none, you cannot create instances of this type.
The DateTime type has one constructor that takes no argument. If you create a new instance of a DateTime object, you get back a date set to the very first date a DateTime type can represent which happens to be January 1, 0001:
New-Object System.DateTime
Monday, January 01, 0001 12:00:00 AM
To create a specific date, you would use a different constructor. There is one that takes three numbers for year, month, and day:
New-Object System.DateTime(2000,5,1)
Monday, May 01, 2000 12:00:00 AM
If you simply add a number, yet another constructor is used which interprets the number as ticks, the smallest time unit a computer can process:
New-Object System.DateTime(568687676789080999)
Monday, February 07, 1803 7:54:38 AM
Using Constructors
When you create a new object using New-Object, you can submit additional arguments by adding argument values as a comma separated list enclosed in parentheses. New-Object in fact is calling a method called ctor which is the type constructor. Like any other method, it can support different argument signatures.
Let's check out how you can discover the different constructors a type supports. The next line creates a new instance of a System.String and uses a constructor that accepts a character and a number:
New-Object System.String(".", 100)
..................................................
..................................................
To list the available constructors for a type, use the GetConstructors() method available in each type. For example, you could find out which constructors are offered by the System.String type to produce System.String objects:
[System.String].GetConstructors() |
ForEach-Object { $_.toString() }
Void .ctor(Char*)
Void .ctor(Char*, Int32, Int32)
Void .ctor(SByte*)
Void .ctor(SByte*, Int32, Int32)
Void .ctor(SByte*, Int32, Int32,
System.Text.Encoding)
Void .ctor(Char[], Int32, Int32)
Void .ctor(Char[])
Void .ctor(Char, Int32)
In fact, there are eight different signatures to create a new object of the System.String type. You just used the last variant: the first argument is the character, and the second a number that specifies how often the character is to be repeated. PowerShell itself uses the next to last constructor so if you specify text in quotation marks, it will interpret text in quotation marks as a field with nothing but characters (Char[]).
New Objects by Conversion
Objects can often be created without New-Object by using type casting instead. You've already seen how it's done for variables in Chapter 3:
$date = "November 1, 2007"
$date.GetType().FullName
System.String
$date
November 1, 2007
[System.DateTime]$date = "November 1, 2007"
$date.GetType().FullName
System.DateTime
$date
Thursday, November 1, 2007 00:00:00
So, if you enclose the desired .NET type in square brackets and put it in front of a variable name, PowerShell will require you to use precisely the specified object type for this variable. If you assign a value to the variable, PowerShell automatically converts it to that type. That process is sometimes called "implicit type conversion." Explicit type conversion works a little different. Here, the desired type is put in square brackets again but placed on the right side of the assignment operator:
$value = [DateTime]"November 1, 2007"
$value
Thursday, November 1, 2007 00:00:00
PowerShell would first convert the text into a date because of the type specification and then assign it to the variable $value, which itself remains a regular variable without type specification. Because $value is not limited to DateTime types, you can assign other data types to the variable later on.
$value = "McGuffin"
Using the type casting, you can also create entirely new objects without New-Object. First, create an object using New-Object:
New-Object system.diagnostics.eventlog("System")
Max(K) Retain OverflowAction Entries Name
------ ------ -------------- ------- ----
20,480 0 OverwriteAsNeeded 64,230 System
You could have accomplished the same thing without New-Object:
[System.Diagnostics.EventLog]"System"
Max(K) Retain OverflowAction Entries Name
------ ------ -------------- ------- ----
20,480 0 OverwriteAsNeeded 64,230 System
In the second example, the string System was converted into the System.Diagnostics.Eventlog type: The result is an EventLog object representing the System event log.
So, when should you use New-Object and when type conversion? It is largely a matter of taste, but whenever a type has more than one constructor and you want to select the constructor, use New-Object and specify the arguments for the constructor of your choice. Type conversion automatically chooses one constructor, and you have no control over which constructor is picked.
New-Object System.String(".", 100)
..................................................
..................................................
[system.string](".",100)
. 100
[system.string]".", 100
.
100
Type conversion can also include type arrays (identified by "[]") and can be a multi-step process where you convert from one type over another type to a final type. This is how you would convert string text into a character array:
[char[]]"Hello!"
H
e
l
l
o
!
You could then convert each character into integers to get the character codes:
[Int[]][Char[]]"Hello World!"
72
97
108
108
111
32
87
101
108
116
33
Conversely, you could make a numeric list out of a numeric array, and turn that into a string:
[string][char[]](65..90)
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
$OFS = ","
[string][char[]](65..90)
A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
Just remember: if arrays are converted into a string, PowerShell uses the separator in the $OFS automatic variable as a separator between the array elements.
Loading Additional Assemblies: Improved Internet Download
To get access to even more functionality, you can load additional assemblies with more types and members. If you have ever written VBScript scripts, you may want to get back some of your beloved VisualBasic methods such as MsgBox() or InputBox(). Simply load the Microsoft.VisualBasic assembly, which is located in the global assembly cache:
[void][reflection.assembly]::`
LoadWithPartialName("Microsoft.VisualBasic")
Once you did that, you have access to a whole bunch of new types:
[Microsoft.VisualBasic.Interaction] | Get-Member -static
TypeName: Microsoft.VisualBasic.Interaction
Name MemberType Definition
---- ---------- ----------
AppActivate Method static System.Void
AppActivate(Int32 Proces...
Beep Method static System.Void
Beep()
CallByName Method static System.Object
CallByName(Object Obje...
Choose Method static System.Object
Choose(Double Index, P...
Command Method static System.String
Command()
CreateObject Method static System.Object
CreateObject(String Pr...
DeleteSetting Method static System.Void
DeleteSetting(String App...
Environ Method static System.String
Environ(Int32 Expressi...
Equals Method static System.Boolean
Equals(Object objA, O...
GetAllSettings Method static System.String[,]
GetAllSettings(Stri...
GetObject Method static System.Object
GetObject(String PathN...
GetSetting Method static System.String
GetSetting(String AppN...
IIf Method static System.Object
IIf(Boolean Expression...
InputBox Method static System.String
InputBox(String Prompt...
MsgBox Method static Microsoft.VisualBasic.
MsgBoxResult M...
Partition Method static System.String
Partition(Int64 Number...
ReferenceEquals Method static System.Boolean
ReferenceEquals(Objec...
SaveSetting Method static System.Void
SaveSetting(String AppNa...
Shell Method static System.Int32
Shell(String PathName, ...
Switch Method static System.Object
Switch(Params Object[]...
[microsoft.VisualBasic.Interaction]::`
InputBox("Enter Name", "Name", "$env:username")
Tobias
Or, you could use a much-improved download method, which shows a progress bar while downloading files from the Internet:
[void][reflection.assembly]::`
LoadWithPartialName("Microsoft.VisualBasic")
$address = "http://powershell.com/cs/" +
"media/p/467/download.aspx"
$target = "$home\chart_drive_space.V2.ps1"
$object = New-Object `
Microsoft.VisualBasic.Devices.Network
$object.DownloadFile(
$address, $target, "", "",
$true, 500, $true, "DoNothing")
Using COM Objects
In addition to .NET, PowerShell can also load and access COM objects which work similar to .NET types and objects but use an older technology.
Which COM Objects Are Available?
COM objects each have a unique name known as ProgID or Programmatic Identifier, which is stored in the registry. So, if you want to look up COM objects available on your computer, visit the registry:
Dir REGISTRY::HKEY_CLASSES_ROOT\CLSID `
-include PROGID -recurse |
foreach {$_.GetValue("")}
How Do You Use COM Objects?
Once you know the ProgID of a COM component, use New-Object to put it to work in PowerShell. Just specify the additional parameter -comObject:
$object = New-Object -comObject WScript.Shell
You'll get an object which behaves very similar to .NET objects. It will contain properties with data and methods that you can execute. And, as always, Get-Member finds all object members for you. Let's look at its methods:
$object | Get-Member -memberType *method
TypeName: System.__ComObject#{41904400-be18-
11d3-a28b-00104bd35090}
Name MemberType Definition
---- ---------- ----------
AppActivate Method bool AppActivate (Variant, Variant)
CreateShortcut Method IDispatch CreateShortcut (string)
Exec Method IWshExec Exec (string)
ExpandEnviron Method string ExpandEnvironmentStrings
mentStrings (string)
LogEvent Method bool LogEvent (Variant, string,
string)
Popup Method int Popup (string, Variant,
Variant, Variant)
RegDelete Method void RegDelete (string)
RegRead Method Variant RegRead (string)
RegWrite Method void RegWrite (string, Variant,
Variant)
Run Method int Run (string, Variant, Variant)
SendKeys Method void SendKeys (string, Variant)
The information required to understand how to use a method can be inadequate. Only the expected object types are given, but not why the arguments exist. If you want to know more about a COM command, the Internet can help you. Go to a search site of your choice and enter two keywords: the ProgID of the COM components (in this case, it will be WScript.Shell) and the name of the method that you want to use.
Some of the commonly used COM objects are WScript.Shell, WScript.Network, Scripting.FileSystemObject, InternetExplorer.Application, Word.Application, and Shell.Application.
Let's create a shortcut to powershell.exe using WScript.Shell Com object and its method CreateShorcut():
$wshell = New-Object -comObject WScript.Shell
$path = [system.Environment]::GetFolderPath('Desktop')
$link = $wshell.CreateShortcut("$path\PowerShell.lnk")
$link | Get-Member
TypeName: System.__ComObject#{f935dc23-1cf0-11d0-adb9-00c04fd58a0b}
Name MemberType Definition
---- ---------- ----------
Load Method void Load (string)
Save Method void Save ()
Arguments Property string Arguments () {get} {set}
Description Property string Description () {get} {set}
FullName Property string FullName () {get}
Hotkey Property string Hotkey () {get} {set}
IconLocation Property string IconLocation () {get} {set}
RelativePath Property {get} {set}
TargetPath Property string TargetPath () {get} {set}
WindowStyle Property int WindowStyle () {get} {set}
WorkingDirectory Property string WorkingDirectory () {get} {set}
$link.TargetPath = 'powershell.exe'
$link.Description = 'Launch Windows PowerShell console'
$link.WorkingDirectory = $profile
$link.IconLocation = 'powershell.exe'
$link.Save()
Summary
Everything in PowerShell is represented by objects that have exactly two aspects: properties and methods, which both form the members of the object. While properties store data, methods are executable commands.
Objects are the result of all PowerShell commands and are not converted to readable text until you output the objects to the console. However if you save a command's result in a variable, you will get a handle on the original objects and be able to evaluate their properties or call their commands. If you would like to see all of an object's properties, then pass the object to Format-List and type an asterisk after it. In this way, all—and not only the most important—properties will be output as text.
The Get-Member cmdlet retrieves even more data, enabling you to output detailed information on the properties and methods of any object.
All the objects with which you work in PowerShell originate from .NET framework, on which PowerShell is layered. Aside from the objects that PowerShell commands provide you as results, you can also invoke objects directly from the .NET framework and gain access to a powerful arsenal of new commands. Along with the dynamic methods furnished by objects, there are also static methods, which are provided directly by the class from which objects are also derived.
If you cannot perform a task either with the cmdlets, regular console commands, or methods of the .NET framework, you can resort to the unmanaged world outside the .NET framework. Either directly access the low-level API functions, the foundation of the .NET framework, or use COM components.
Posted
Mar 08 2009, 06:00 PM
by
ps1