Home > Articles > Home & Office Computing > Microsoft Windows Desktop

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Object Based

Most shells operate in a text-based environment, which means you typically have to manipulate the output for automation purposes. For example, if you need to pipe data from one command to the next, the output from the first command usually must be reformatted to meet the second command's requirements. Although this method has worked for years, dealing with text-based data can be difficult and frustrating.

Often, a lot of work is necessary to transform text data into a usable format. Microsoft has set out to change the standard with PowerShell, however. Instead of transporting data as plain text, PowerShell retrieves data in the form of .NET Framework objects, which makes it possible for commands (cmdlets) to access object properties and methods directly. This change has simplified shell use. Instead of modifying text data, you can just refer to the required data by name. Similarly, instead of writing code to transform data into a usable format, you can simply refer to objects and manipulate them as needed.

Understanding the Pipeline

The use of objects gives you a more robust method for dealing with data. In the past, data was transferred from one command to the next by using the pipeline, which makes it possible to string a series of commands together to gather information from a system. However, as mentioned previously, most shells have a major disadvantage: The information gathered from commands is text based. Raw text needs to be parsed (transformed) into a format the next command can understand before being piped. To see how parsing works, take a look at the following Bash example:

$ ps -ef | grep "bash" | cut -f2
               

The goal is to get the process ID (PID) for the bash process. A list of currently running processes is gathered with the ps command and then piped to the grep command and filtered on the string "bash". Next, the remaining information is piped to the cut command, which returns the second field containing the PID based on a tab delimiter.

Based on the man information for the grep and cut commands, it seems as though the ps command should work. However, the PID isn't returned or displayed in the correct format.

The command doesn't work because the Bash shell requires you to manipulate text data to display the PID. The output of the ps command is text based, so transforming the text into a more usable format requires a series of other commands, such as grep and cut. Manipulating text data makes this task more complicated. For example, to retrieve the PID from the data piped from the grep command, you need to provide the field location and the delimiter for separating text information to the cut command. To find this information, run the first part of the ps command:


$ ps -ef | grep "bash"
   bob    3628       1 con  16:52:46 /usr/bin/bash

The field you need is the second one (3628). Notice that the ps command doesn't use a tab delimiter to separate columns in the output; instead, it uses a variable number of spaces or a whitespace delimiter, between fields.

The cut command has no way to tell that spaces should be used as a field separator, which is why the command doesn't work. To get the PID, you need to use the awk scripting language. The command and output in that language would look like this:

                  
$ ps -ef | grep "bash" | awk '{print $2}'
3628

The point is that although most UNIX and Linux shell commands are powerful, using them can be complicated and frustrating. Because these shells are text-based, often commands lack functionality or require using additional commands or tools to perform tasks. To address the differences in text output from shell commands, many utilities and scripting languages have been developed to parse text.

The result of all this parsing is a tree of commands and tools that make working with shells unwieldy and time consuming, which is one reason for the proliferation of management interfaces that rely on GUIs. This trend can be seen among tools Windows administrators use, too; as Microsoft has focused on enhancing the management GUI at the expense of the CLI.

Windows administrators now have access to the same automation capabilities as their UNIX and Linux counterparts. However, PowerShell and its use of objects fill the automation need Windows administrators have had since the days of batch scripting and WSH in a more usable and less parsing intense manner. To see how the PowerShell pipeline works, take a look at the following PowerShell example:

PS C:\> get-process bash | format-table id -autosize

  Id
  --
3628



PS C:\>
               

Like the Bash example, the goal of this PowerShell example is to display the PID for the bash process. First, information about the bash process is gathered by using the Get-Process cmdlet. Second, the information is piped to the Format-Table cmdlet, which returns a table containing only the PID for the bash process.

The Bash example requires complex shell scripting, but the PowerShell example simply requires formatting a table. As you can see, the structure of PowerShell cmdlets is much easier to understand and use.

Now that you have the PID for the bash process, take a look at the following example, which shows how to kill (stop) that process:

PS C:\> get-process bash | stop-process
PS C:\>
               

.NET Framework Tips

Before continuing, you need to know a few points about how PowerShell interacts with the .NET Framework. This information is critical to understanding the scripts you review in later chapters.

New-Object cmdlet

You use the New-Object cmdlet to create an instance of a .NET object. To do this, you simply provide the fully qualified name of the .NET class you want to use, as shown:

PS C:\> $Ping = new-object Net.NetworkInformation.Ping
PS C:\>
               

By using the New-Object cmdlet, you now have an instance of the Ping class that enables you to detect whether a remote computer can be reached via Internet Control Message Protocol (ICMP). Therefore, you have an object-based version of the Ping.exe command-line tool.

If you're wondering what the replacement is for the VBScript CreateObject method, it's the New-Object cmdlet. You can also use the comObject switch with this cmdlet to create a COM object, simply by specifying the object's programmatic identifier (ProgID), as shown here:

                  
PS C:\> $IE = new-object -comObject InternetExplorer.Application
PS C:\> $IE.Visible=$True
PS C:\> $IE.Navigate("www.cnn.com")
PS C:\>
        
               

Square Brackets

Throughout this book, you'll notice the use of square brackets ([ and ]), which indicate that the enclosed term is a .NET Framework reference. These references can be one of the following:

  • A fully qualified class name[System.DirectoryServices.ActiveDirectory.Forest], for example
  • A class in the System namespace[string], [int], [boolean], and so forth
  • A type accelerator[ADSI], [WMI], [Regex], and so on

Defining a variable is a good example of when to use a .NET Framework reference. In this case, the variable is assigned an enumeration value by using an explicit cast of a .NET class, as shown in this example:

PS C:\> $SomeNumber = [int]1
PS C:\> $Identity = [System.Security.Principal.NTAccount]"Administrator"
PS C:\>
               

If an enumeration can consist of only a fixed set of constants, and you don't know these constants, you can use the System.Enum class's GetNames method to find this information:

PS C:\>
[enum]::GetNames([System.Security.AccessControl.FileSystemRights])
ListDirectory
ReadData
WriteData
CreateFiles
CreateDirectories
AppendData
ReadExtendedAttributes
WriteExtendedAttributes
Traverse
ExecuteFile
DeleteSubdirectoriesAndFiles
ReadAttributes
WriteAttributes
Write
Delete
ReadPermissions
Read
ReadAndExecute
Modify
ChangePermissions
TakeOwnership
Synchronize
FullControl
PS C:\>
               

Static Classes and Methods

Square brackets are used not only for defining variables, but also for using or calling static members of a .NET class. To do this, just use a double colon (::) between the class name and the static method or property, as shown in this example:

PS C:\> [System.DirectoryServices.ActiveDirectory.Forest]::
GetCurrentForest()


Name                  : taosage.internal
Sites                 : {HOME}
Domains               : {taosage.internal}
GlobalCatalogs        : {sol.taosage.internal}
ApplicationPartitions : {DC=DomainDnsZones,DC=taosage,DC=internal,
DC=ForestDns
                        Zones,DC=taosage,DC=internal}
ForestMode            : Windows2003Forest
RootDomain            : taosage.internal
Schema                :
CN=Schema,CN=Configuration,DC=taosage,DC=internal
SchemaRoleOwner       : sol.taosage.internal
NamingRoleOwner       : sol.taosage.internal



PS C:\>

Reflection

Reflection is a feature in the .NET Framework that enables developers to examine objects and retrieve their supported methods, properties, fields, and so on. Because PowerShell is built on the .NET Framework, it provides this feature, too, with the Get-Member cmdlet. This cmdlet analyzes an object or collection of objects you pass to it via the pipeline. For example, the following command analyzes the objects returned from the Get-Process cmdlet and displays their associated properties and methods:

PS C:\> get-process | get-member

Developers often refer to this process as "interrogating" an object. It's a faster way to get information about objects than using the Get-Help cmdlet (which at the time of this writing provides limited information), reading the MSDN documentation, or searching the Internet.

PS C:\> get-process | get-member


   TypeName: System.Diagnostics.Process

Name                          MemberType     Definition
----                          ----------     ----------
Handles                       AliasProperty  Handles = Handlecount
Name                          AliasProperty  Name = ProcessName
NPM                           AliasProperty  NPM = NonpagedSystemMemorySize
PM                            AliasProperty  PM = PagedMemorySize
VM                            AliasProperty  VM = VirtualMemorySize
WS                            AliasProperty  WS = WorkingSet
add_Disposed                  Method         System.Void add_Disposed(Event...
add_ErrorDataReceived         Method         System.Void add_ErrorDataRecei...
add_Exited                    Method         System.Void add_Exited(EventHa...
add_OutputDataReceived        Method         System.Void add_OutputDataRece...
BeginErrorReadLine            Method         System.Void BeginErrorReadLine()
BeginOutputReadLine           Method         System.Void BeginOutputReadLine()
CancelErrorRead               Method         System.Void CancelErrorRead()
CancelOutputRead              Method         System.Void CancelOutputRead()
Close                         Method         System.Void Close()
CloseMainWindow               Method         System.Boolean CloseMainWindow()
CreateObjRef                  Method         System.Runtime.Remoting.ObjRef...
Dispose                       Method         System.Void Dispose()
Equals                        Method         System.Boolean Equals(Object obj)
get_BasePriority              Method         System.Int32 get_BasePriority()
get_Container                 Method         System.ComponentModel.IContain...
get_EnableRaisingEvents       Method         System.Boolean get_EnableRaisi...
...
__NounName                    NoteProperty   System.String __NounName=Process
BasePriority                  Property       System.Int32 BasePriority {get;}
Container                     Property       System.ComponentModel.IContain...
EnableRaisingEvents           Property       System.Boolean EnableRaisingEv...
ExitCode                      Property       System.Int32 ExitCode {get;}
ExitTime                      Property       System.DateTime ExitTime {get;}
Handle                        Property       System.IntPtr Handle {get;}
HandleCount                   Property       System.Int32 HandleCount {get;}
HasExited                     Property       System.Boolean HasExited {get;}
Id                            Property       System.Int32 Id {get;}
MachineName                   Property       System.String MachineName {get;}
MainModule                    Property       System.Diagnostics.ProcessModu...
MainWindowHandle              Property       System.IntPtr MainWindowHandle...
MainWindowTitle               Property       System.String MainWindowTitle ...
MaxWorkingSet                 Property       System.IntPtr MaxWorkingSet {g...
MinWorkingSet                 Property       System.IntPtr MinWorkingSet {g...
...
Company                       ScriptProperty System.Object Company {get=$th...
CPU                           ScriptProperty System.Object CPU {get=$this.T...
Description                   ScriptProperty System.Object Description {get...
FileVersion                   ScriptProperty System.Object FileVersion {get...
Path                          ScriptProperty System.Object Path {get=$this....
Product                       ScriptProperty System.Object Product {get=$th...
ProductVersion                ScriptProperty System.Object ProductVersion {...



PS C:\>

This example shows that objects returned from the Get-Process cmdlet have additional property information that you didn't know. The following example uses this information to produce a report about Microsoft-owned processes and their folder locations. An example of such a report would be as follows:

PS C:\> get-process | where-object {$_.Company -match ".*Microsoft*"} |
format-table Name, ID, Path -Autosize


Name         Id Path
----         -- ----
ctfmon     4052 C:\WINDOWS\system32\ctfmon.exe
explorer   3024 C:\WINDOWS\Explorer.EXE
iexplore   2468 C:\Program Files\Internet Explorer\iexplore.exe
iexplore   3936 C:\Program Files\Internet Explorer\iexplore.exe
mobsync     280 C:\WINDOWS\system32\mobsync.exe
notepad    1600 C:\WINDOWS\system32\notepad.exe
notepad    2308 C:\WINDOWS\system32\notepad.exe
notepad    2476 C:\WINDOWS\system32\NOTEPAD.EXE
notepad    2584 C:\WINDOWS\system32\notepad.exe
OUTLOOK    3600 C:\Program Files\Microsoft Office\OFFICE11\OUTLOOK.EXE
powershell 3804 C:\Program Files\Windows PowerShell\v1.0\powershell.exe
WINWORD    2924 C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE



PS C:\>

You wouldn't get nearly this much process information by using WSH with only a single line of code.

The Get-Member cmdlet isn't just for objects generated from PowerShell cmdlets. You can also use it on objects initialized from .NET classes, as shown in this example:

PS C:\> new-object System.DirectoryServices.DirectorySearcher

The goal of using the DirectorySearcher class is to retrieve user information from Active Directory, but you don't know what methods the returned objects support. To retrieve this information, run the Get-Member cmdlet against a variable containing the mystery objects, as shown in this example.

PS C:\> $Searcher = new-object System.DirectoryServices.DirectorySearcher
PS C:\> $Searcher | get-member


   TypeName: System.DirectoryServices.DirectorySearcher

Name                        MemberType Definition
----                        ---------- ----------
add_Disposed                Method     System.Void add_Disposed(EventHandle...
CreateObjRef                Method     System.Runtime.Remoting.ObjRef Creat...
Dispose                     Method     System.Void Dispose()
Equals                      Method     System.Boolean Equals(Object obj)
FindAll                     Method     System.DirectoryServices.SearchResul... 
FindOne                     Method     System.DirectoryServices.SearchResul...
...
Asynchronous                Property   System.Boolean Asynchronous {get;set;}
AttributeScopeQuery         Property   System.String AttributeScopeQuery {g...
CacheResults                Property   System.Boolean CacheResults {get;set;}
ClientTimeout               Property   System.TimeSpan ClientTimeout {get;s...
Container                   Property   System.ComponentModel.IContainer Con...
DerefAlias                  Property   System.DirectoryServices.Dereference...
DirectorySynchronization    Property   System.DirectoryServices.DirectorySy...
ExtendedDN                  Property   System.DirectoryServices.ExtendedDN ...
Filter                      Property   System.String Filter {get;set;}         
PageSize                    Property   System.Int32 PageSize {get;set;}
PropertiesToLoad            Property   System.Collections.Specialized.Strin...
PropertyNamesOnly           Property   System.Boolean PropertyNamesOnly {ge...
ReferralChasing             Property   System.DirectoryServices.ReferralCha...
SearchRoot                  Property   System.DirectoryServices.DirectoryEn...
SearchScope                 Property   System.DirectoryServices.SearchScope...
SecurityMasks               Property   System.DirectoryServices.SecurityMas...
ServerPageTimeLimit         Property   System.TimeSpan ServerPageTimeLimit ...
ServerTimeLimit             Property   System.TimeSpan ServerTimeLimit {get...
Site                        Property   System.ComponentModel.ISite Site {ge...
SizeLimit                   Property   System.Int32 SizeLimit {get;set;}
Sort                        Property   System.DirectoryServices.SortOption ...
Tombstone                   Property   System.Boolean Tombstone {get;set;}
VirtualListView             Property   System.DirectoryServices.
                                       DirectoryVi...



PS C:\>
               

Notice the FindAll method and the Filter property. These are object attributes that can be used to search for information about users in an Active Directory domain. To use these attributes the first step is to filter the information returned from DirectorySearcher by using the Filter property, which takes a filter statement similar to what you'd find in a Lightweight Directory Access Protocol (LDAP) statement:

PS C:\> $Searcher.Filter = ("(objectCategory=user)")

Next, you retrieve all users from the Active Directory domain with the FindAll method:

PS C:\> $Users = $Searcher.FindAll()

At this point, the $Users variable contains a collection of objects holding the distinguished names for all users in the Active Directory domain:

PS C:\> $Users

Path                                    Properties
----                                    ----------
LDAP://CN=Administrator,CN=Users,DC=... {homemdb, samaccounttype, countrycod...
LDAP://CN=Guest,CN=Users,DC=taosage,... {samaccounttype, objectsid, whencrea...
LDAP://CN=krbtgt,CN=Users,DC=taosage... {samaccounttype, objectsid, whencrea...
LDAP://CN=admintyson,OU=Admin Accoun... {countrycode, cn, lastlogoff, usncre...
LDAP://CN=servmom,OU=Service Account... {samaccounttype, lastlogontimestamp,...
LDAP://CN=SUPPORT_388945a0,CN=Users,... {samaccounttype, objectsid, whencrea...
LDAP://CN=Tyson,OU=Acc... {msmqsigncertificates, distinguished...
LDAP://CN=Maiko,OU=Acc... {homemdb, msexchhomeservername, coun...
LDAP://CN=servftp,OU=Service Account... {samaccounttype, lastlogontimestamp,...
LDAP://CN=Erica,OU=Accounts,OU... {samaccounttype, lastlogontimestamp,...
LDAP://CN=Garett,OU=Accou... {samaccounttype, lastlogontimestamp,...
LDAP://CN=Fujio,OU=Accounts,O... {samaccounttype, givenname, sn, when...
LDAP://CN=Kiyomi,OU=Accounts,... {samaccounttype, givenname, sn, when...
LDAP://CN=servsql,OU=Service Account... {samaccounttype, lastlogon, lastlogo...
LDAP://CN=servdhcp,OU=Service Accoun... {samaccounttype, lastlogon, lastlogo...
LDAP://CN=servrms,OU=Service Account... {lastlogon, lastlogontimestamp, msmq...



PS C:\>
               

Now that you have an object for each user, you can use the Get-Member cmdlet to learn what you can do with these objects:

PS C:\> $Users | get-member


   TypeName: System.DirectoryServices.SearchResult

Name              MemberType Definition
----              ---------- ----------
Equals            Method     System.Boolean Equals(Object obj)
get_Path          Method     System.String get_Path()
get_Properties    Method     System.DirectoryServices.ResultPropertyCollecti...
GetDirectoryEntry Method     System.DirectoryServices.DirectoryEntry GetDire... 
GetHashCode       Method     System.Int32 GetHashCode()
GetType           Method     System.Type GetType()
ToString          Method     System.String ToString()
Path              Property   System.String Path {get;}
Properties        Property   System.DirectoryServices.ResultPropertyCollecti...



PS C:\>

To collect information from these user objects, it seems as though you need to step through each object with the GetDirectoryEntry method. To determine what data you can retrieve from these objects, you use the Get-Member cmdlet again, as shown here:

PS C:\> $Users[0].GetDirectoryEntry() | get-member -MemberType Property


   TypeName: System.DirectoryServices.DirectoryEntry

Name                            MemberType Definition
----                            ---------- ----------
accountExpires                  Property   System.DirectoryServices.Property...
adminCount                      Property   System.DirectoryServices.Property...
badPasswordTime                 Property   System.DirectoryServices.Property...
badPwdCount                     Property   System.DirectoryServices.Property...
cn                              Property   System.DirectoryServices.Property...
codePage                        Property   System.DirectoryServices.Property...
countryCode                     Property   System.DirectoryServices.Property...
description                     Property   System.DirectoryServices.Property...
displayName                     Property   System.DirectoryServices.Property...
distinguishedName               Property   System.DirectoryServices.Property...
homeMDB                         Property   System.DirectoryServices.Property...
homeMTA                         Property   System.DirectoryServices.Property...
instanceType                    Property   System.DirectoryServices.Property...
isCriticalSystemObject          Property   System.DirectoryServices.Property...
lastLogon                       Property   System.DirectoryServices.Property...
lastLogonTimestamp              Property   System.DirectoryServices.Property...
legacyExchangeDN                Property   System.DirectoryServices.Property...
logonCount                      Property   System.DirectoryServices.Property...
mail                            Property   System.DirectoryServices.Property...
mailNickname                    Property   System.DirectoryServices.Property...
mDBUseDefaults                  Property   System.DirectoryServices.Property...
memberOf                        Property   System.DirectoryServices.Property...
msExchALObjectVersion           Property   System.DirectoryServices.Property...
msExchHomeServerName            Property   System.DirectoryServices.Property...
msExchMailboxGuid               Property   System.DirectoryServices.Property...
msExchMailboxSecurityDescriptor Property   System.DirectoryServices.Property...
msExchPoliciesIncluded          Property   System.DirectoryServices.Property...
msExchUserAccountControl        Property   System.DirectoryServices.Property...
mSMQDigests                     Property   System.DirectoryServices.Property...
mSMQSignCertificates            Property   System.DirectoryServices.Property...
name                            Property   System.DirectoryServices.Property...
nTSecurityDescriptor            Property   System.DirectoryServices.Property...
objectCategory                  Property   System.DirectoryServices.Property...
objectClass                     Property   System.DirectoryServices.Property...
objectGUID                      Property   System.DirectoryServices.Property...
objectSid                       Property   System.DirectoryServices.Property...
primaryGroupID                  Property   System.DirectoryServices.Property...
proxyAddresses                  Property   System.DirectoryServices.Property...
pwdLastSet                      Property   System.DirectoryServices.Property...
sAMAccountName                  Property   System.DirectoryServices.Property...
sAMAccountType                  Property   System.DirectoryServices.Property...
showInAddressBook               Property   System.DirectoryServices.Property...
textEncodedORAddress            Property   System.DirectoryServices.Property...
userAccountControl              Property   System.DirectoryServices.Property...
uSNChanged                      Property   System.DirectoryServices.Property...
uSNCreated                      Property   System.DirectoryServices.Property...
whenChanged                     Property   System.DirectoryServices.Property...
whenCreated                     Property   System.DirectoryServices.Property...



PS C:\>
               

To use PowerShell effectively, you should make sure you're familiar with the Get-Member cmdlet. If you don't understand how it works, figuring out what an object can and can't do may be at times difficult.

Now that you understand how to pull information from Active Directory, it's time to put together all the commands used so far:

PS C:\> $Searcher = new-object System.DirectoryServices.DirectorySearcher
PS C:\> $Searcher.Filter = ("(objectCategory=user)")
PS C:\> $Users = $Searcher.FindAll()
PS C:\> foreach ($User in $Users){$User.GetDirectoryEntry().sAMAccountName}
Administrator
Guest
krbtgt
admintyson
servmom
SUPPORT_388945a0
Tyson
Maiko
servftp
Erica
Garett
Fujio
Kiyomi
servsql
servdhcp
servrms
PS C:\>
               

Although the list of users in this domain isn't long, it shows that you can interrogate a set of objects to understand their capabilities.

The same is true for static classes, however, when attempting to use the Get-Member cmdlet in the same manner as before creates the following error:

PS C:\> new-object System.Net.Dns
New-Object : Constructor not found. Cannot find an appropriate constructor for
type System.Net.Dns.
At line:1 char:11
+ New-Object  <<<< System.Net.Dns
PS C:\>

As you can see, the System.Net.Dns class doesn't have a constructor, which poses a challenge when you're trying to find out what this class does. However, the Get-Member cmdlet can handle this challenge. With the Static parameter, you can gather information from static classes, as shown in this example:

PS C:\> [System.Net.Dns] | get-member -Static


   TypeName: System.Net.Dns


Name                  MemberType Definition
----                  ---------- ----------
BeginGetHostAddresses Method     static System.IAsyncResult BeginGetHostAddr...
BeginGetHostByName    Method     static System.IAsyncResult BeginGetHostByNa...
BeginGetHostEntry     Method     static System.IAsyncResult BeginGetHostEntr...
BeginResolve          Method     static System.IAsyncResult BeginResolve(Str...
EndGetHostAddresses   Method     static System.Net.IPAddress[] EndGetHostAdd...
EndGetHostByName      Method     static System.Net.IPHostEntry EndGetHostByN...
EndGetHostEntry       Method     static System.Net.IPHostEntry EndGetHostEnt...
EndResolve            Method     static System.Net.IPHostEntry EndResolve(IA...
Equals                Method     static System.Boolean Equals(Object objA, O...
GetHostAddresses      Method     static System.Net.IPAddress[] GetHostAddres...
GetHostByAddress      Method     static System.Net.IPHostEntry GetHostByAddr...
GetHostByName         Method     static System.Net.IPHostEntry GetHostByName...
GetHostEntry          Method     static System.Net.IPHostEntry GetHostEntry(...
GetHostName           Method     static System.String GetHostName()
ReferenceEquals       Method     static System.Boolean ReferenceEquals(Objec...
Resolve               Method     static System.Net.IPHostEntry Resolve(Strin...



PS C:\>

Now that you have information about the System.Net.Dns class, you can put it to work. As an example, use the GetHostAddress method to resolve the IP address for the Web site www.digg.com:

PS C:\> [System.Net.Dns]::GetHostAddresses("www.digg.com")


IPAddressToString : 64.191.203.30
Address           : 516669248
AddressFamily     : InterNetwork
ScopeId           :
IsIPv6Multicast   : False
IsIPv6LinkLocal   : False
IsIPv6SiteLocal   : False



PS C:\>
               

Extended Type System (ETS)

You might think that scripting in PowerShell is typeless because you rarely need to specify the type for a variable. PowerShell is actually type driven, however, because it interfaces with different types of objects from the less than perfect .NET to Windows Management Instrumentation (WMI), Component Object Model (COM), ActiveX Data Objects (ADO), Active Directory Service Interfaces (ADSI), Extensible Markup Language (XML), and even custom objects. However, you typically don't need to be concerned about object types because PowerShell adapts to different object types and displays its interpretation of an object for you.

In a sense, PowerShell tries to provide a common abstraction layer that makes all object interaction consistent, despite the type. This abstraction layer is called the PSObject, a common object used for all object access in PowerShell. It can encapsulate any base object (.NET, custom, and so on), any instance members, and implicit or explicit access to adapted and type-based extended members, depending on the type of base object. Furthermore, it can state its type and add members dynamically. To do this, PowerShell uses the Extended Type System (ETS), which provides an interface that allows PowerShell cmdlet and script developers to manipulate and change objects as needed.

Therefore, with ETS, you can change objects by adapting their structure to your requirements or create new ones. One way to manipulate objects is to adapt (extend) existing object types or create new object types. To do this, you define custom types in a custom types file, based on the structure of the default types file, Types.ps1xml.

In the Types.ps1xml file, all types are contained in a <Type></Type> node, and each type can contain standard members, data members, and object methods. Using this structure as a basis, you can create your own custom types file and load it into a PowerShell session by using the Update-TypeData cmdlet, as shown here:

PS C:\> Update-TypeData D:\PS\My.Types.Ps1xml

You can run this command manually during each PowerShell session or add it to your profile.ps1 file.

The second way to manipulate an object's structure is to use the Add-Member cmdlet to add a user-defined member to an existing object instance, as shown in this example:

PS C:\> $Procs = get-process
PS C:\> $Procs | add-member -Type scriptProperty "TotalDays" {
>> $Date = get-date
>> $Date.Subtract($This.StartTime).TotalDays}
>>
PS C:\>

This code creates a scriptProperty member called TotalDays for the collection of objects in the $Procs variable. The scriptProperty member can then be called like any other member for those objects, as shown in the next example:

PS C:\> $Procs | where {$_.name -Match "WINWORD"} | ft Name,
TotalDays -AutoSize


Name             TotalDays
----             ---------
WINWORD 5.1238899696898148



PS C:\>
               

Although the new scriptProperty member isn't particularly useful, it does demonstrate how to extend an object. Being able to extend objects from both a scripting and cmdlet development context is extremely useful.

  • + Share This
  • 🔖 Save To Your Account