Welcome to MSDN Blogs Sign in | Join | Help

Exclude, Include, Filter Parameters - How to make sense of these

So how come we have three ways to filter a path in most of the core cmdlets such as in the get-item cmdlet.  It is important to understand that exclude, include, and filter offer different levels of filtering.

 

So let’s give a few examples of how get-item cmdlet works with these parameters individually and then in conjunction.

 

MSH C:\monad> get-item file*

    Directory: Microsoft.Management.Automation.Core\FileSystem::C:\monad

 

Mode

LastWriteTime

Length

Name

-a---         

3/3/2006   4:00 PM

600

fileA

-a---         

3/3/2006   5:00 PM

700

fileB

 

We have two items and we are only interested in retrieving fileA. There are two ways to achieve this.

 

One way is to use the exclude parameter which removes (or excludes) item(s) that are not of interest.

 

MSH C:\monad> get-item file* -exclude fileB

    Directory: Microsoft.Management.Automation.Core\FileSystem::C:\monad

 

Mode

LastWriteTime

Length

Name

-a---         

3/3/2006   4:00 PM

600

fileA

 

The other approach is to use the include parameter which only allows item(s) of interest to be displayed.

 

MSH C:\monad> get-item file* -include fileA

    Directory: Microsoft.Management.Automation.Core\FileSystem::C:\monad

 

Mode

LastWriteTime

Length

Name

-a---         

3/3/2006   4:00 PM

600

fileA

 

The above two scenarios are quite straightforward, but say you were trying to search for a file in the Windows directory that started with the letter ‘a’ and did not end with the letter ‘z’.

 

The below command shows how easily that can be done with the use of exclude and include parameters.

 

MSH C:\monad> get-item C:\Windows\* -include a* -exclude *z

 

The filter parameter gives the ability for the provider to perform additional filtering based upon some provider-specific string.  This is provider specific and hence not all providers do something special given a filter parameter input.  In the File System Provider case, the filter parameter performs an inclusive filter similar to the include parameter but it is implemented natively. Hence, you would notice significant performance boost when trying to perform searches within a very large set of files using the filter parameter versus the include parameter.  The both achieve the same result but filter parameter can be a time saver during instances of large loads.

One thing I forgot to mention is that exclude, include parameters work on the leaf items and not on the absolute path. What does this mean?

In our previous example, where we were trying to extract fileA from a list of files, we cannot specify the absolute path

MSH C:\monad> get-item C:\monad\file* -include C:\monad\fileA  //will not work

 

However, just specifying the leaf item will work as expected.

MSH C:\monad> get-item C:\monad\file* -include fileA

    Directory: Microsoft.Management.Automation.Core\FileSystem::C:\monad

Mode

LastWriteTime

Length

Name

-a---         

3/3/2006   4:00 PM

600

fileA

 

 

-Satish

How to Access or Modify StartUp Items in the Window Registry

Some applications launch themselves whenever you start your computer and load Windows.  In most cases, this is the desired behavior.  However in some instances, malicious programs such as spyware, Trojans, worms, viruses load in this manner and hijack your computer. It is important to stay vigilant and periodically monitor your startup registry keys and delete keys that are unwarranted.

 

REGEDIT.EXE is the program you run to enter into the windows registry

You can find ALOT of the startup programs which are running in the background in your Windows Registry.  For those who enjoy managing Windows via the command line, you don’t need to launch a GUI application such as REGEDIT and use a pesky mouse. Monad offers a portal to the Registry world via a cmdlet provider called Registry Provider.

 

So, how do we access the Registry Provider? Think of the provider as very similar to how you would navigate a File System.  The registry keys are treated equivalent to folders in the File System and registry values are treated equivalent to files in the File System.

 

So let’s explore a bit by starting MSH and then set the location to the root of the Registry Provider.

 

MSH C:\monad> cd Registry::

MSH Microsoft.Management.Automation.Core\Registry::> dir

Hive:

SKC

VC

Name

Property

5

0

HKEY_LOCAL_MACHINE

{}

15

0

HKEY_CURRENT_USER             

{}

535

1

HKEY_CLASSES_ROOT

{EditFlags}

0

2

HKEY_CURRENT_CONFIG           

{GLOBAL, COSTLY}

 

10

0

HKEY_USERS

{}

The following are the two most common registry keys which load applications at start up.

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
– These programs automatically start when any user is logged in. It is used for all users on this computer

 

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
– The programs here automatically start when the current user logs in. It is used only for current logoned user.

So let’s navigate to the HKEY_LOCAL_MACHINE folder. 

MSH Microsoft.Management.Automation.Core\Registry::> cd HKLM:\

-OR-

MSH Microsoft.Management.Automation.Core\Registry::> cd HKey_Local_Machine

Note: Don’t worry about case sensitivity, since Monad is not a case sensitive language

 

Both operations will lead you to same location.

 

MSH HLKM:\> cd Software\Microsoft\Windows\CurrentVersion

Note: Don’t worry about case sensitivity, since Monad is not a case sensitive language

 

Now we want to view what is currently registered to startup on every Windows boot up.

 

MSH HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run> dir

 

 

   Hive: Microsoft.Management.Automation.Core\Registry::HKEY_LOCAL_MACHINE\SOFT

WARE\Microsoft\Windows\CurrentVersion\Run

SKC

VC

Name

Property

3

0

OptionalComponents

{}

 

So how come we are not seeing the applications that start up when Windows is loaded.  That is because the registry values are treated as properties on an existing item or registry key.  To view the applications loaded at startup, type the following command:

 

MSH HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run> get-itemproperty .

 

This will list all the registry values under this key.  The same steps can be repeated for the HKey_Current_User folder.

 

Once you identify any unwanted registry values, then you can perform a delete operation in Monad via the remove-itemproperty cmdlet.

 

MSH HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run> remove-itemproperty -path . –property [PropertyName]

Note: Be wary of using wildcard characters since you can accidentally delete all item properties by specifying “*” in the property parameter.

 

-Satish

Posted by Monad Team | 6 Comments

The worlds easiest way to create/install MshSnapins

Hopefully you've read the previous entries on how to create MshSnapins to include your own cmdlets/providers without having to create an entire separate executable (custom shell).

If you didn't or if you did and you don't feel like writing the extra MshSnapin class to be used by InstallUtil.exe, you're in luck. I wrote a little ditty(script) about taking any arbitrary assembly and setting the appropriate values in the Registry for use.And in using this little handy script, you'll be able to look at registry settings to see what needs to get added to add your mshsnapin.

This script takes 2 parameters. first parameter is the assembly containing your cmdlets and/or providers. The 2nd optional parameter is the "name" of the mshsnapin. If you don't supply a name, then the name of the mshsnapin will be the assemblyfile (1st parameter)
Lets assume that you created an assembly "myawesomecmdlets.dll" which contain the most awe-inspiring cmdlets possibly imaginable.

MSH> register-mshsnapin myawesomecmdlets.dll mysnapin
MSH> add-mshsnapin  mysnapin  # add your snapin to the current session
MSH> create-somethingwonderful # run one of your cmdlets :)

If you run regedit, you'll then be able to see the entries that get added for your mshsnapin "mysnapin":
HKLM\Software\Microsoft\MSH\1\MshSnapins\mysnapin has values for
ApplicationBase - directoryname where your assembly is located. This is also the dir where help files, types & format files are looked for
AssemblyName - Full strong name of assembly
Description - text describing your mshsnapin
ModuleName - full path of assembly file
MshVersion - "1.0"
Version - "1.0"

 

So use the script at end of this post to quickly take any assembly and configure it be added as an mshsnapin.

Hope this helps!

Scott

 

# Beginning of script 'register-mshsnapin'

param ([string] $assemblyfile, [string] $MshSnapinName = $assemblyFile)

# Make sure we can load assembly
################################
$assemblyInfo = [System.Reflection.Assembly]::LoadFrom($assemblyFile)
if($assemblyInfo -eq $null)
{
   "unable to load assembly $assemblyFile!"
   exit -1
}

# Based on MshSnapinName, lets create the Reg Key values
#################################################
$keyname = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Msh\1\MshSnapins\" + $MshSnapinName

[Microsoft.Win32.Registry]::SetValue($keyname, "ApplicationBase",
                 [System.IO.Path]::GetDirectoryName($assemblyInfo.Location) )
[Microsoft.Win32.Registry]::SetValue($keyname, "AssemblyName",$assemblyInfo.FullName)

[Microsoft.Win32.Registry]::SetValue($keyname,"Description","A Test MshSnapin")
[Microsoft.Win32.Registry]::SetValue($keyname,"ModuleName",$assemblyInfo.Location)
[Microsoft.Win32.Registry]::SetValue($keyname,"Version","1.0")
[Microsoft.Win32.Registry]::SetValue($keyname,"MshVersion","1.0")

# End of script

Posted by Monad Team | 4 Comments

Monad breaking change announcement: Approved verb names enforcement.

In order to prevent a mass usability nightmare with developers picking their own improvised cmdlet verbs, the Monad team, based on customer feedback, decided to enforce the use approved verbs.

 

The idea is to provide a more uniform and consistent interface to the end user. If a user needs to get something, he or she should know that what they are looking for is a get-something cmdlet. The user can then use the command discovery cmdlet (get-command get-*) to look for a getters and so forth...

With this change, if a non standard verb is to be used, the developer will be forced to add “__” to the beginning of the cmdlet name in order to make their cmdlet usable:

Example: in order to create a “play-nice” cmdlet where “play” is not an approved verb the developer will need to prepend the verb name with two underscore characters as follows “__play”.

Users can still get around this issue by simply aliasing the underscored cmdlet name as follows:

 

Set-Alias Play-Nice __Play-Nice

 

In order to ensure we have a complete list of valid verbs, we need to get your feedback on the list below to ensure we did not miss anything. Please speak now or forever hold your peace J

 

 

Ubiquitous Verbs

Definition

Common Parameters

Obsoletes

Pair

Add

Add, append or attach an element

At, After, Before, Create,  Filter, ID, Name, Value, Whatif

append, attach, concatenate, insert

Remove

Clear

Remove all the elements or content of a container

 

flush, erase, release, unmark, unset, nullify

 

Copy

Copy a resource to another name or another container

Acl, Overwrite, Recurse, Strict, Whatif

duplicate, clone, replicate

 

Get

/object /content /children

All, As, Compatible, Continuous, Count, Encoding, Exclude, Filter,  Include, ID, Interval, Name, Path, Property, Recurse, Scope, Sortby,

read, open, cat, type, dir, obtain, dump, acquire, examine, find, search

Set

Lock

To protect from changes or deletion

 

secure, fasten

Unlock

Move

Move a resource

 

transfer, name, migrate

 

New

create a new resource

Description, ID, Name, Value

create, generate, build, make, allocate

Remove

Remove

Remove a resource from a container

(Get), Drain, Erase, Force, Whatif

delete, disconnect, detach, drop, purge, flush, erase, release

Add/New

Rename

Give a resource a new name

 

 

 

Set

/object /content /children

Passthru,

write, reset, assign, configure

Get

Join

to unite so as to form one unit

 

combine, unite, link, 1connect, relate, associate

split

Split

To become separated into parts, especially to undergo lengthwise division. Divided into portions, parts, or fragments

 

Divide, separate, fragment, disunite

join

Unlock

To undo the Lock operation. To give access to; open

 

unsecure, unfasten

Lock

Data Verbs

Definition

 

Obsoletes

Pair

Backup

 

 

 

Restore

Compare

Compare this resource with another one and produce a set of differences

 

Diff

 

Convert

Change from one encoding to another or from one unit base to another (e.g. feet to meters)

 

 

 

Export

Make a copy of a set of resources using an interchange format

(get), Add, As, AsScript, Delete, Description, FileName, Location, Strict, Whatif

extract, backup

Import

Import

Create a set of resources using an interchange format

FileName, Location

bulk load, load

Export

Initialize

Prepare a resource for use. Assign a beginning value to something

 

erase, renew, rebuild, reinitialize, setup

 

Limit

Limit the consumption of a resource or apply a constraint on a resource

 

quota

 

Merge

Take multiple instances and create a single instance

 

coalesce

 

Restore

Rollback state to a predefined snapshot/checkpoint

 

 

Checkpoint

Update

update or refresh a resource from a source of truth

 

refresh, renew, recalculate, reindex

 

Mount

Attach a named entity to a hierarchy at the pathname location. To set in position

 

fix, organize, prepare

Dismount

Dismount

To get off. To detach.

 

detach, take-down

Mount

out

direct to a port. Output something to a port.

 

output.

 

 

 

 

 

 

Lifecycle Verbs

Definition

 

Obsoletes

Pair

Disable

Stop and/or configure something to be unavailable (e.g unable to not start again)

 

 

Enable

Enable

Configure to be available (e.g. able to start)

 

 

Disable

Install

Settle in an indicated place or condition (optionally initializing for use)

 

setup, load

Uninstall

Restart

Terminate existing activity and begin it again (with the same or checkpointed configuration)

 

recycle

 

Resume

Begin an activity again after it was suspended

 

 

Suspend

Start

Begin an activity

 

launch, initiate, boot

Stop

Stop

Discontinue or cease an activity

 

End, kill, terminate, cancel

Start

Suspend

Suspend an activity temporarily

 

Pause

Resume

Uninstall

 

 

 

Install

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Diagnostics verb

Definition

 

Obsoletes

Pair

Debug

Interactively interact with a resource or activity for the purpose finding a flaw or better understanding of what is occurring.

 

 

 

Measure

calculate/identify resources consumed by a specified operation or retrieve statistics about a resource

 

 

 

Ping

Determine whether a resource is alive and responding to requests

 

 

 

Resolve

Map a shorthand name will be bound to a longname

 

where, which

 

Test

Verify the operational validity or consistency of a resource

 

diagnose, verify, analyze, salvage, verify

 

Trace

Trace activities performed by a specified operation

 

 

 

 

 

 

 

 

Communications

Definition

 

Obsoletes

Pair

Send

Convey by an intermediary to a destination

 

put, broadcast, mail, fax,

receive

Receive

Take or acquire from a source

 

read, accept, peek,

send

Connect

Associate subsequent activities with a resource

 

 

disconnect

Disconnect

 

 

 

Connect

write

communicate or express. Display data.

 

display, communicate

read

read

To obtain (data) from a storage medium, such as a magnetic disk.

 

input

write

 

 

 

 

 

 

 

 

 

 

Security

Definition

 

Obsoletes

Pair

Grant

 

 

 

Revoke

Revoke

 

 

 

Grant

Block

prevent access to or usage of

 

 

unblock

Unblock

allow access to or usage of

 

 

block

 

 

 

 

 

Other Verbs

Definition

 

Obsoletes

Pair

Use

 

 

 

 

 

Wassim

Posted by Monad Team | 30 Comments

Write-Progress

Tony has a very cool series of Monad-oriented blog entries at: http://mshforfun.blogspot.com/ .  I was fascinated by this one: http://mshforfun.blogspot.com/2005/12/ncbi-blastn-under-msh-command-line.html  which exposes the NCBI BLAST tool via a command line.  Tony's log explains what this tool does but what I found so cool was that it submitted a query for a Website, waited for results, retrived those results and then displayed them.

Tony's script is a rewrite of a Perl Script using traditional concepts.  In particular, it outputs error, progress, and results all to the same channel on the console.  I did a light rewrite of the script to leverage Monad ability to put this information into different channels.  In particular, progress information can be displayed in a Console ProgressBar via Write-Progress.   This is the preferred way to display this information.  It provides a much better user experience and it forces you to think about what information you should be providing the user to set their expectations.  The other cool thing is that these Progress records are also available to GUI hosts so if this script where hosted in a GUI, it could use a GUI progressBar.

Run the original script and then try this one and compare the difference. 

I've attached the script.

Jeffrey P. Snover
Monad Architect

Posted by Monad Team | 3 Comments

Attachment(s): blastn-nr.msh

.NET types

In response to the recent Days till Xmas post, applepwc asked the question 
   >  where can I find more ".NET types"?I mean is there a list of ".NET type"  available in monad?

Excellent question but there are a number of aspects to it so let's break it down:

.NET is a developer platform.  That platform contains a number of libraries that developers can use to develope their code.  Reference information for the classes that ship with the .NET platform are available at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdatetimememberstopic.asp .

Other developers take that platform and produce their own .NET types and ship them as assemblies (DLLs) or included in their executables.  Each team will provide they own documentation for their types.  Monad makes this information avilable as part of its SDK.

Then there is the question: which of these types are available to Monad.  The answer is that Monad provides access to any public .NET type which is loaded in process.  The following sequence tells you what assemblies you have loaded in your current process (appdomain actually but we don't need to go there now).


MSH> [appdomain]::currentdomain.getassemblies() |ft fullname

FullName
--------
mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5...
System.Management.Automation.ConsoleHost, Version=1.0.490.0, Cul...
System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c5...
System.Management.Automation, Version=1.0.490.0, Culture=neutral...
System.Management.Automation.ConsoleHost.resources, Version=1.0....
System.Configuration.Install, Version=2.0.0.0, Culture=neutral, ...
System.Management.Automation.Commands.Management, Version=1.0.49...
System.Management.Automation.Security, Version=1.0.490.0, Cultur...
System.Management.Automation.Commands.Utility, Version=1.0.490.0...
System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyTo...
System.DirectoryServices, Version=2.0.0.0, Culture=neutral, Publ...
System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b7...
System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77...
System.Management.Automation.resources, Version=1.0.490.0, Cultu...
System.Management.Automation.Security.resources, Version=1.0.490...
System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKe...
System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken...
System.Management.Automation.Commands.Utility.resources, Version...
System.Management.Automation.Commands.Management.resources, Vers...

This call returns an assembly object.  This object has a method GetExportedTypes() which gets all the public types (as opposed to private types which you don't have access to) for that assemblies.  Thus if you wanted to get all the types available in process you would do the following: 

MSH> [appdomain]::currentdomain.getassemblies() |foreach {$_.getexpo
rtedtypes()}

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object
True     False    ICloneable
True     False    IEnumerable
True     False    ICollection
True     False    IList
True     True     Array                                    Syste...
....

I use this quite a bit actually.  I create a function Match-Type to find a set of .Net Types:

function Match-Type ($Name)
{  [Appdomain]::CurrentDomain.GetAssemblies() |
       foreach {$_.GetExportedTypes()} |
          where {$_.FullName -match $Name}
}


MSH>  Match-Type mshhost |ft FullName

FullName
--------
Microsoft.Management.Automation.MshHostMshSnapIn
System.Management.Automation.Host.MshHost
System.Management.Automation.Host.MshHostRawUserInterface
System.Management.Automation.Host.MshHostUserInterface

This is a good point to take a tangent.  In an earlier post, I pointe dou that some .NET types can parse strings and how wonderful that was.  You might reasonably ask yourself the question: Which types support the Parse() method.  Here is a function that helps address that:

function Match-Method ($Name)
{  [Appdomain]::CurrentDomain.GetAssemblies() |
     foreach {$_.GetExportedTypes()} |
      foreach {
        $Type = $_
        if ($Type.GetMethods() | where {$_.Name -match $Name}) {
          $Type
        }
      }
}
MSH> match-Method Parse

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Enum                                     Syste...
True     True     DateTime                                 Syste...
True     True     Boolean                                  Syste...
True     True     Byte                                     Syste...
True     True     Char                                     Syste...
...

The last issue is, how do I load types into my process if they aren't already there.  Lee Holmes has a great blog entry on that at: http://www.leeholmes.com/blog/HowDoIEasilyLoadAssembliesWhenLoadWithPartialNameHasBeenDeprecated.aspx

I hope that this was useful.

Enjoy!

Jeffrey P. Snover
Monad Architect

Posted by Monad Team | 3 Comments

Monad cmdlet cleanup update

As Jeffrey mentioned in an earlier Blog “Finding which parameters are used the most” The Monad team is in the process of cleaning up our cmdlets to promote a more consistent user experience. The following two areas which we are focused on might have impact on you:

 

Cmdlet Verb names consistency and Parameters naming consistency.

 

Cmdlet Verb names consistency

 

The changes we are introducing might cause you to have to modify many of your existing scripts and possibly some code. Among the changes that will have direct effect on you are the cmdlet verb names changes which are as follows:

 

·          Combine-Path will be renamed to Join-Path

·          Parse-Path will be renamed to Split-Path

·          Match-String will be renamed to Select-String

·          Time-Expression will be renamed to Measure-Command

·          Write-Object will be renamed to Write-Output

 

This change will ensure Monad core cmdlets conform with the verb naming guidelines described in the Monad SDK.

 

Parameters naming consistency

 

We are still in the process of implementing this cleanup phase and no concrete details are available as of yet to be publicly announced. The following shows a sample for some of the guidelines we will be using to complete this cleanup phase:

 

Issue

If a parameter has the same name across cmdlets, it should have the same type and semantics. You should use the same parameter name for the same concepts. 

 

For example:

 

Match-String and Tee-Object cmdlets should use a –Path parameter aliased to MshPath instead of –FileName to have a consistent user experience across the different cmdlets.

 

Parameter names should be verbose and allow you to easily disambiguate them by simply typing the first three characters or using aliases. You only have to provide enough of a parameter name to disambiguate it but you can also use parameter aliases. We must use parameter aliases when parameters cannot be disambiguated easily using the first three characters.

 

For example:

 

-OutBuffer (-ob) and -OutVariable (-ov)

 

Both OutBuffer and OutVariable start with “out” which can only be disambiguated when more then the first three characters are used. The –ob and –ov parameter aliases resolve this issue

 

This posting is simply a heads-up on what will possibly be in the next release. Any thoughts or feedback is appreciated.

Posted by Monad Team | 7 Comments

Days till Xmas

My daughter loves christmas.  She often asks me, "how long is it till christmas?"  The problem with that is that I'm one of those people that can barely remember what year it is much less the date.  Well, it is one thing to be a flawed person and its another thing to disappoint your daughter.  Monad to the rescue!

Here is a little date math routine I wrote to help me out:

function tillXmas ()
{
    $now = [DateTime]::Now
    [Datetime]("12/25/" + $now.Year) - $Now
}

MSH> tillxmas


Days              : 321
Hours             : 18
Minutes           : 8
Seconds           : 26
Milliseconds      : 171
Ticks             : 277997061718750
TotalDays         : 321.755858470775
TotalHours        : 7722.14060329861
TotalMinutes      : 463328.436197917
TotalSeconds      : 27799706.171875
TotalMilliseconds : 27799706171.875

Thanks to Monad, I can tell my daughter how many seconds to go till Xmas!  Now if I can only get her to stop asking me in the car.

Here is an exercise for you - calculate what time I ran this example (hint - think "date math" and use the Ticks value)

Enjoy!

Jeffrey P. Snover
Monad Architect

Posted by Monad Team | 6 Comments

Finding which parameters are used the most

We are in the process of cleaning up our Cmdlets and ensuring that they are consistent.  One of the things we want to do is the ensure that we provide Aliases for ParameterNames.  As a general rule, parameternames are not pithy.  That is great for reading scripts but can be a pain during interactive sessions.  You only have to provide enough of a parametername to disambiguate it but you can also use parameter aliases if they are defined.  For instance, you can type -OutVariable or you can type -ov because all the commands provide the alias "ov" to map to "OutVariable". 

 

The task at hand is to ensure that we make good use of parameter aliases.  As always, “to ship is to choose”.  That means that we don’t time to do everything so we have to focus on the things that are going to produce the biggest bang for the buck.  Here I walk through the steps to get a histogram of parameter use by all the Cmdlets.   Let’s break that down into steps:

1)    Get all the Cmdlets

2)    Get all the parameters of all the Cmdlets

3)    Create a histogram

 

Getting all the Cmdlets is pretty simple to do – we provide the “Get-Command” function which has the alias “gcm”

MSH> gcm

 

CommandType     Name                      Definition

-----------     ----                      ----------

Cmdlet          add-content               add-content [-Path] St...

Cmdlet          add-history               add-history [[-InputOb...

….

 

If you provide no parameters, gcm returns the list of all Cmdlets.  If you provide a name specifier (you can use wildcards), then it returns everything that can resolve a token.  You can type “gcm *” to see what that looks like.  I’ll group them to show you want I mean:

 

MSH> gcm * |Group CommandType |Sort name

 

Count Name                      Group

----- ----                      -----

  120 Alias                     {ac, alias, aliases, cat, cd, ch...

 2746 Application               {$ncsp$.inf, $winnt$.inf, _defau...

  128 Cmdlet                    {add-content, add-history, add-m...

   25 ExternalScript            {aliases.msh, aspen.msh, burn-co...

   64 Function                  {..., A:, Alias:, Aspen:, B:, C:...

 

For this work, we are only interested in Cmdlets so how do we find just the Cmdlets? 

 

The thing I love about Monad is that with a few concepts, you can do almost anything.  Since you know that gcm returns objects and you know that you can filter objects using “where”, the following gives you exactly what you want:

 

MSH> gcm * |where {$_.CommandType -eq "Cmdlet"}

 

With that as a baseline, let’s explore whether there is a better way to do things.  Type “gcm -?” and you’ll get help on “Get-Command” and it will tell you all sorts of wonderful things.  I’m one of those “less is more” type of readers so I often just want the examples. I do this to get just the examples:

 

MSH> (gcm -?).Examples

 

In this case it doesn’t tell me what I wanted to see (looks like we need to cleanup our help as well J) so let’s use gcm on Get-Command:

 

MSH> gcm Get-Command |fl *

DLL           : C:\WINDOWS\assembly\GAC_MSIL\System.Management.Auto

                mation\1.0.7487.0__31bf3856ad364e35\System.Manageme

                nt.Automation.dll

Verb          : get

Noun          : command

HelpFile      : System.Management.Automation.dll-Help.xml

MshSnapIn     : Microsoft.Management.Automation.Core

Type          : System.Management.Automation.Commands.GetCommand

ParameterSets : {CmdletSet, AllCommandSet}

Definition    : get-command [[-CommandArguments] Object[]] [-Verb S

                tring[]] [-Noun String[]] [-MshSnapin String[]] [-T

                otalCount Int32] [-Synopsis] [-Verbose] [-Debug] [-

                ErrorAction ActionPreference] [-ErrorVariable Strin

                g] [-OutVariable String] [-OutBuffer Int32]

                get-command [[-Name] String[]] [[-CommandArguments]

                 Object[]] [-Type CommandTypes] [-TotalCount Int32]

                 [-Synopsis] [-Verbose] [-Debug] [-ErrorAction Acti

                onPreference] [-ErrorVariable String] [-OutVariable

                 String] [-OutBuffer Int32]

 

Name          : get-command

CommandType   : Cmdlet

 

This tells me that I can use “-Type Cmdlet” to get just the items I want:

 

MSH> gcm –Type Cmdlet

 

Notice also that gcm shows you the ParameterSets.  Every command can have multiple sets of parameters.  For instance you can do a “get-process” specifying either an ID or a ProcessName or by pipelining a set of instance to the command.

 

MSH> (gcm get-process).parametersets |ft name,parameters -Auto

 

Name        Parameters

----        ----------

ProcessName {ProcessName, Verbose, Debug, ErrorAction, ErrorVari...

Id          {Id, Verbose, Debug, ErrorAction, ErrorVariable, Out...

Input       {Input, Verbose, Debug, ErrorAction, ErrorVariable, ...

 

So now what we want to do is to get all the parameters of all the ParameterSets of all the commands which are Cmdlets.  First let’s get all the ParameterSets.  We know that gcm returns all the properties of the Cmdlet and that “select” can be used to pick just those properties that we want so it seems like we should be able to do the following:

 

MSH> gcm -type Cmdlet |Select ParameterSets

 

Do this and you’ll discover that it doesn’t quite give you want you want because Parametersets is a Collection.  That command sequence returns a stream of collections when what we want is a stream of objects.  This is what “Select –Expand “ is designed to do.

 

MSH> gcm -type Cmdlet |Select -Expand ParameterSets |Select -Expand

Parameters

 

 

Name                            : Value

Type                            : System.Object[]

IsMandatory                     : True

IsDynamic                       : False

Position                        : 1

ValueFromPipeline               : True

ValueFromPipelineByPropertyName : True

ValueFromRemainingArguments     : False

HelpMessage                     :

Aliases                         : {}

Attributes                      : {System.Management.Automation.All

                                  owNullAttribute, System.Managemen

                                  t.Automation.AllowEmptyCollection

                                  Attribute, __AllParameterSets}

….

 

 

 

 

  

Now we are ready to party.  Let’s put these parameters in a variable so the subsequent commandlines are easier to read.

MSH> $p = gcm -type Cmdlet  |Select -Expand ParameterSets |Select -Expand Parameters

 

 

Now we want to produce a histogram:

 

MSH> $p |group Name

 

Count Name                      Group

----- ----                      -----

   12 Value                     {Value, Value, Value, Value, Val...

   47 PassThru                  {PassThru, Passthru, PassThru, P...

   50 Path                      {Path, Path, Path, Path, Path, P...

   26 Filter                    {Filter, Filter, Filter, Filter,...

   49 Include                   {Include, Include, Include, Incl...

 

We want to focus on the ones that matter the most so let’s find out which parameters are used the most:

 

MSH> $p |group Name |sort -Descending count

 

Count Name                      Group

----- ----                      -----

  174 ErrorAction               {ErrorAction, ErrorAction, Error...

  174 Debug                     {Debug, Debug, Debug, Debug, Deb...

  174 Verbose                   {Verbose, Verbose, Verbose, Verb...

  174 OutBuffer                 {OutBuffer, OutBuffer, OutBuffer...

  174 OutVariable               {OutVariable, OutVariable, OutVa...

 

Now notice that the most used parameters are the ubiquitous parameters.  Pretty obvious really, when you think about the word “ubiquitous” J .  <Digression – Cmdlets are subclasses of a base .Net class.  Ubiquitous parameters are parameters that we put on the base class so that all commands inherit them and whose behavior is implemented by the Monad Cmdlet hosting environment.  For example, -OutVariable allows you to take the results of a command and assign or append it to a variable [as well as passing it on].  When a Cmdlet writes its results, Monad looks to see if the user has specified –OutVariable and it does this work [not the Cmdlet].>

 

The command line is about to get large again so let’s put the results into another variable and for yucks let’s use –OutVariable.  Of course who wants to type –OutVariable so let’s use its alias –ov.  It turns out that we already have good aliases for the ubiquitous parameters so we want to find the top 20 non-ubiquitous parameters.

MSH> $p |group Name |sort -Descending count –ov p1

 

Count Name                      Group

----- ----                      -----

  174 ErrorAction               {ErrorAction, ErrorAction, Error...

  174 Debug                     {Debug, Debug, Debug, Debug, Deb...

  174 Verbose                   {Verbose, Verbose, Verbose, Verb...

  174 OutBuffer                 {OutBuffer, OutBuffer, OutBuffer...

  174 OutVariable               {OutVariable, OutVariable, OutVa...

 

 

There are 6 ubiquitous parameter so there are 2 ways to get the top 20:

MSH> $p1 |select -first 26 |select -Last 20 –ov p2

MSH> $p1[6..25]

 

Count Name                      Group

----- ----                      -----

   52 WhatIf                    {WhatIf, WhatIf, WhatIf, WhatIf,...

   52 Confirm                   {Confirm, Confirm, Confirm, Conf...

   50 Path                      {Path, Path, Path, Path, Path, P...

   50 Exclude                   {Exclude, Exclude, Exclude, Excl...

   49 Include                   {Include, Include, Include, Incl...

   47 PassThru                  {PassThru, Passthru, PassThru, P...

   43 Force                     {Force, Force, Force, Force, For...

   37 Credential                {Credential, Credential, Credent...

   32 InputObject               {InputObject, InputObject, Input...

   29 Name                      {Name, Name, Name, Name, Name, N...

   26 Filter                    {Filter, Filter, Filter, Filter,...

   20 Property                  {Property, Property, Property, P...

   13 Scope                     {Scope, Scope, Scope, Scope, Sco...

   12 Value                     {Value, Value, Value, Value, Val...

    8 Description               {Description, Description, Descr...

    8 DisplayName               {DisplayName, DisplayName, Displ...

    8 ServiceName               {ServiceName, ServiceName, Servi...

    8 Input                     {Input, Input, Input, Input, Inp...

    6 Type                      {Type, Type, Type, Type, Type, T...

    6 Encoding                  {Encoding, Encoding, Encoding, E...

 

Now let’s turn the dial to 11 and see if these things already have aliases and if so, whether there are inconsistent aliases.  I’m going to make the assumption that if a parameter has an alias, it only has 1 alias (I validated this assumption with the following command “$p2 |select -expand group |where {$_.aliases.count -gt 1}” ).

 

The Group command keeps the things it grouped in a property called group.  We grouped parameters so this command gets us back the parameters:

 

MSH> $p2 |select -expand group

 

We now want to group these parameters by Name AND alias.  The issue is that the parameter class does not have such a property.  No problem!  Remember – Monad is all about helping you cope with a world that doesn’t give you want you need.  (If it did, you wouldn’t need us J ).  Also remember that there are a small set of KEY MONAD CONCEPTS and TECHNIQUES that you need to learn and then the world is your oyster.  The technique that saves us here is ScriptBlock Parameters.

 

I mentioned that we are turning to dial to 11 didn’t I?  OK – put your seatbelt on.

 

When you pipeline data to a command, you can specify a ScriptBlock to a parameter that does not take a ScriptBlock.  The Monad engine detects this and for every pipelined object, it assigns that object to the variable $_, evaluates the ScriptBlock and assigns the results to the parameter.  Let’s provide a simple example.  Here we pipe 2 integers to the stop-process command.  We provide a ScriptBlock to the parameter –ID which only accepts INTs.  The ScriptBlock issues a prompt and asks the user to enter a process id.  Monad assigns the pipelined objects (1 and 2) to $_, evaluates the ScriptBlock and passes the parameter to –ProcessName.

 

MSH> 1..2 |Stop-Process -ProcessName {Read-host "Record $_  Name" } -whatif

Record 1  Name: lsass

What if: Performing operation "stop-process" on Target "lsass (1440)".

Record 2  Name: msh

What if: Performing operation "stop-process" on Target "msh (3428)".

 

 

 

Back to the problem at hand.  We want to group these parameters by Name and Alias so let’s use the fact that Group can group by multiple parameters and use a ScriptBlock as one of those parameters:

 

MSH> $p2|select -Expand Group |group Name,{$_.Aliases[0]} |sort name

 

Count Name                      Group

----- ----                      -----

   52 Confirm.cf                {Confirm, Confirm, Confirm, Conf...

   37 Credential                {Credential, Credential, Credent...

    8 Description               {Description, Description, Descr...

    8 DisplayName               {DisplayName, DisplayName, Displ...

    6 Encoding                  {Encoding, Encoding, Encoding, E...

   50 Exclude                   {Exclude, Exclude, Exclude, Excl...

   26 Filter                    {Filter, Filter, Filter, Filter,...

   43 Force                     {Force, Force, Force, Force, For...

   49 Include                   {Include, Include, Include, Incl...

    8 Input                     {Input, Input, Input, Input, Inp...

   32 InputObject               {InputObject, InputObject, Input...

   29 Name                      {Name, Name, Name, Name, Name, N...

   47 PassThru                  {PassThru, Passthru, PassThru, P...

    2 Path                      {Path, Path}

   48 Path.MshPath              {Path, Path, Path, Path, Path, P...

   12 Property                  {Property, Property, Property, P...

    8 Property.MshProperty      {Property, Property, Property, P...

   13 Scope                     {Scope, Scope, Scope, Scope, Sco...

    8 ServiceName.Name          {ServiceName, ServiceName, Servi...

    6 Type                      {Type, Type, Type, Type, Type, T...

   12 Value                     {Value, Value, Value, Value, Val...

   52 WhatIf.wi                 {WhatIf, WhatIf, WhatIf, WhatIf,...

 

Here it shows that most parameters don’t have an alias and that PATH uses the alias MSHPATH but that 2 commands don’t use this alias which gives us something to investigate.

 

 

Here is what I hope you’ve learned from this:

1)    Get-Command provides detailed information about anything that can be executed included parameter information for Cmdlets.

a.    Explore how Get-Command returns different data for different types of executables (e.g.
Get-Command ipconfig.exe |fl *
Get-Command prompt |fl *

2)    You can always use WHERE to filter the results of a command but it can be worthwhile investigating whether the command does its own filtering which can sometimes be faster or easier to specify.

a.    Explore how you can leverage the fact that help returns objects so you can craft your own view of help.  E.g.
get-help get-* |fl Name,Synopsis
get-help * |where {$_.Synopsis -match "process"} |fl name,Synopsis

3)    Commands can have multiple ParameterSets which define valid sets of parameters to perform an operation.

a.    Explore which command has the most ParameterSets.  Hint – use a ScriptBlock parameter on group.

4)    Select can pick properties of an object and/or expand the elements of a property if that property is a collection.

a.    Explore the power of both selecting properties and expanding properties. Take the example below and explore each step in the pipeline:
MSH> gps |select ProcessName -expand Modules -ea silentlycontinue |group ModuleName |sort -Descend count |select -first 20

5)    Group can produce a histogram based upon a property or set of properties and keep the original set of objects.

a.    Explore how you can use group to gain insights into your data.  E.g.
dir |group {$_.CreationTime.DayOfWeek} |sort Name
dir |group {$_.CreationTime.DayOfWeek},extension |sort Name

6)    ScriptBlock Parameters can be used in any pipelines to dynamically evaluate what data to pass to a parameter.

a.    Explore how you can use ScriptBlock parameters to do funky file tree manipulations. 
dir *zip |cpi -destination {$_.fullname.Replace("jps","BackupJps") } –whatif

 

Jeffrey P. Snover [MSFT]

Monad Architect

Posted by Monad Team | 4 Comments

Invoking Scriptblocks from C#

Last time somebody asked for an example of a C# method that took a scriptblock.  First of all, there’s one very important thing you have to know about scriptblocks as they are currently implemented.  They can’t be invoked outside of a runspace.  Any attempt to do so will result in an exception.  Some of you may already have found this out when using scriptblocks as delegates.  Here’s a quick demonstration of what happens when you try it:

 

        static void Main(string[] args)

        {

            try

            {

                RunspaceInvoke invoke = new RunspaceInvoke();

 

                ScriptBlock scriptblock = invoke.Invoke(

                  "{ return 'foo'; }")[0].BaseObject as ScriptBlock;

                scriptblock.Invoke();

            }

            catch (Exception e)

            {

                Console.WriteLine(e);

            }

            finally

            {

                Console.WriteLine("Press any key to exit...");

                Console.ReadKey();

            }

        }

 

Which results in the following exception message…

 

System.Management.Automation.MshInvalidOperationException: A script block delega

te was invoked from the wrong thread. It is permitted to pass a script block as

a delegate, but the delegate may only be invoked from a runspace thread. The scr

ipt block you attempted to invoke was: return 'foo';

   at System.Management.Automation.ScriptBlock.UpdateUsingInfoFromTLS()

   at System.Management.Automation.ScriptBlock.InvokeWithPipe(Boolean UseLocalSc

ope, Boolean writeErrors, Object dollarUnder, Object input, Object scriptThis, P

ipe outputPipe, ArrayList& resultList, Object[] args)

   at System.Management.Automation.ScriptBlock.Invoke(Object dollarUnder, Object

 input, Object[] args)

   at System.Management.Automation.ScriptBlock.Invoke(Object[] args)

   at Test.Program.Main(String[] args) in c:\Temp\ConsoleApplication15\Program.c

s:line 23

Press any key to exit...

 

 

So… creating scriptblocks and taking them out of the runspace for invocation is out of the question for now…  You could of course send the scriptblock back into a runspace and invoke it there.  Lets try that, and just for fun we’ll invoke the scriptblock in a different runspace than the one it was created in:

 

                RunspaceInvoke invoke = new RunspaceInvoke();

 

                ScriptBlock scriptblock = invoke.Invoke(

                  "{ return 'foo'; }")[0].BaseObject as ScriptBlock;

 

                //A second runspace.

                RunspaceInvoke mySecondInvoke = new RunspaceInvoke();

                Collection<MshObject> results = mySecondInvoke.Invoke

                  ("$script = $input | write-object; & $script",

                  new object[] { scriptblock });

                string resultStr = results[0].BaseObject.ToString();

                Console.WriteLine(resultStr);

Which results in:

 

foo

Press any key to exit...

 

 

So grabbing a scriptblock from one runspace and sending it across to another runspace works.  When doing this you should always keep in mind that these are different runspaces with different SessionState so variables, providers and cmdlets that exist in one runspace might not exist in the other.  It all depends, of course, on how you created the runspace and what you’ve run after it was created.  Anyway, getting back to the original subject, what we really wanted was an example of a C# method that takes a scriptblock.  Well… while you can’t invoke a scriptblock outside of the runspace thread, you CAN call C# methods from Monad.  And if you call a C# method from Monad, it gets invoked on the runspace thread.  So…. to try this out, the first thing we need is a method that takes a ScriptBlock.

 

        public static string MyTestMethod(ScriptBlock scriptBlock)

        {

            return scriptBlock.Invoke("foobar")[0].ToString();

        }

 

Now… all we need to do is invoke this static method and get the results.  A slight modification of our earlier program will do the trick.

 

        static void Main(string[] args)

        {

            try

            {

                RunspaceInvoke ri = new RunspaceInvoke();

 

                // Create a scriptblock and pass it to our

                // static method.

                string script =

                    "$script = { return $args; }              \r\n" +

                    "[Test.Program]::MyTestMethod($script);   \r\n";

 

                string scriptBlockResult = ri.Invoke(script)

                     [0].ToString();

                Console.WriteLine(scriptBlockResult);

            }

            finally

            {

                Console.WriteLine("Press any key to exit...");

                Console.ReadKey();

            }

        }

 

 

The script we are running is fairly simple.  First it creates a scriptblock and assigns it to a variable.  Then it calls the static method MyTestMethod on class Test.Program, passing it the scriptblock as an argument.  If you compile and run this you’ll get the following output:

 

foobar

Press any key to exit...

 

- Marcel

Posted by Monad Team | 1 Comments

Minding Path Inputs in a Cmdlet

When I was a Monad neophyte, I was asked to write a Cmdlet taking a file path as a parameter. A big mistake I made was not keeping in mind that in Monad the FileSystem provider was just one of the many providers. (This makes Monad different from many other shells where you are always in a directory.) For a path-taking Cmdlet, this means two things. The first is that the input path can be specifying a non-FileSystem provider such as the registry provider. The second is that the Cmdlet can be invoked from a non-FileSystem provider. This makes a relative input path not relative to the FileSystem provider. Besides the provider issues, a path in Monad can also be drive-qualified or home-relative. Also, a drive can be mounted at a location that is not the root of the file system.

To deal with all these conditions, the Cmdlet needs to call SessionState.Path.GetResolvedProviderPathFromMshPath and checks the provider returned . If the path does not exist, SessionState.Path.GetUnresolvedProviderPathFromMshPath is used. However, as of Beta 3, this API does not return the provider. We are looking to add a version that does return the provider in V1. As usual, we can't promise this will happen.

In future versions, we hope to provide enough functionalities so that Cmdlets that writes to a path can use InvokeProvider.Content.GetWriter(path). This enables the Cmdlet to write to any provider that implements the IContentCmdletProvider interface. Moreover, the Cmdlet does not need to worry about what provider a path specifies. Again, we can't promise this will happen.

--
Kevin Loo [MSFT]
Microsoft Command Shell Development
Microsoft Corporation
This posting is provided "AS IS" with no warranties, and confers no rights.





Posted by Monad Team | 7 Comments

Adding help for cmdlets in a MshSnapIn

Now that you know how to create an MshSnapIn, you might want to add help content for all those cmdlets that you developed and get them displayed by “get-help” cmdlet.  To add help content you must know how a Monad Cmdlet Help file looks. See any of the *-Help.xml files in Monad installation directory for reference. I wont be describing the format of the help file in this blog (I will do that on some other day. In the mean time please use one of the *-Help.xml files for your reference).

When "add-mshsnapin XYZ.TestNameSpace.MyMshSnapIn" is run on the command-line, Monad engine will look for snapin information under registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSH\1\MshSnapIns\{Your_MshSnapIn_Key here}. This key will have information like ApplicationBase, AssemblyName, etc. The monad engine loads the assembly (and if everything succeeds), identifies all the cmdlets/providers that are defined in MshSnapIn assembly. Along with all the necessary information that is required, the engine will add a default help file name which is of format <MshSnapIn assembly name>.dll-Help.xml for every cmdlet/provider that is present in the MshSnapIn assembly.

Later when help engine tries to load help file for a cmdlet, the help engine looks for the help file relative to MshSnapIn “ApplicationBase” directory. Remember this ApplicationBase is added to the registry during MshSnapIn installation phase.

You can control the name of the help file ( infact you can have a separate helpfile for each of your cmdlets/providers ) by creating a Custom MshSnapIn

So here are the steps to add help content to your MshSnapIns:

Create a help file and name it as appropriate

(a) If your snapin is a general MshSnapIn (ie., deriving from System.Management.Automation.MshSnapIn) name it <your dll name>.dll-Help.xml

(b) If your snapin is a custom MshSnapIn (ie., deriving from System.Management.Automation.CustomMshSnapIn) name it according to the information you supplied with “public override Collection<CmdletConfigurationEntry> Cmdlets” property.

When your snapin is deployed make sure the help files are deployed to the same location relative to ApplicationBase.

If you follow the above steps, then “get-help <Your custom MshSnapIn cmdlet>” should display help for your cmdlets.

Hope this helps.

---

Krishna Vutukuri [MSFT]

Monad Development
Microsoft Corporation
This posting is provided "AS IS" with no warranties, and confers no rights.

 

Posted by Monad Team | 1 Comments

Single Shell vs Custom Shell

With Monad Beta 3 release, we've introduced the concept of the "single shell". What is a single shell? "One shell to rule them all ... and in the darkness..." (Sorry I can't stop saying that quote everytime I hear "single shell"!) Well rather than having to create a separate executable/shell to host your own cmdlets/providers, you can now create mshsnapins that can be added/removed from a single shell (msh.exe). This can be done at initialization time or during a running session.

What is an mshsnapin? Well, its basically a way to package cmdlets/providers into a logical unit so that they can be added to a running session of msh.exe for execution. So a company/team could group all their cmdlets/providers together for managing related items in a single mshsnapin. See Mini-FAQ for what type of information can be included in an mshsnapin.

You can continue to create a separate custom shell (or minishell) using make-shell.exe to include all the cmdlets & providers you've written. And in fact for packaging reasons you may still want to do that. Custom shells allow you to customize the environment thru startup/built-in scripts, custom types/format files and a separate authorization manager. So if you want a tightly controlled environment, you may want to stick with the custom shell.

If, however, all you want to do is be able to run your own cmdlets/providers using Monad, then the single shell is your ticket. Simply create your mshsnapin, and run add-mshsnapin to load it.

Look at George's previous MshSnapin posts on how to create/install an mshsnapin.

Look at the below miniFAQ which I hope will answer some questions you may have when working with mshsnapins and some of the changes we've made to accommodate this feature.

Hope this helps

-Scott

Single Shell Mini-FAQ:

1) differences between custom shell & single shell

- Single shell allows you to add/remove mshsnapins dynamically or at startup. Custom shell doesn't have this capability. you CAN'T add mshsnapins to a custom shell. In fact the commands add-mshsnapin,get-mshsnapin,remove-mshsnapin, & export-console don't exist in custom shells. So you can only load mshsnapins from the single shell(msh.exe)

- Custom shell is 100% managed code where single shell now has a small native msh.exe which dynamically loads the CLR and then executes managed code at a known entry-point.

2) with my custom shell I was able to run my cmdlets without having to run any commands after startup (i.e. add-mshsnapin mysnapin)

- You can pass a config file parameter to msh.exe which will cause msh.exe to load with a set of mshsnapins. The extension of this file must be ".mcf" This config file contains information such as version of Monad to load, and any snapins to be initially loaded at startup.Just add a separate "MshSnapIn" XML node for each snapin to load at startup. OR you can add the mshsnapins to a running Monad session and run export-console to create your .mcf file to load later. That might be easier for you.

Example config file:

<?xml version="1.0" encoding="utf-8"?>
<MshConsoleFile ConsoleSchemaVersion="1.0">
  <MshVersion>1.0</MshVersion>
  <MshSnapIns>
    <MshSnapIn Name="snapin_name" />
  </MshSnapIns>
</MshConsoleFile>

3) what can I include in a MshSnapin?

- typedata files (which will typically include the types exposed by the cmdlets/providers in your snapin) - requires custom MshSnapin

- formatdata files (which will typically include how to display the types exposed by your cmdlets/providers)- requires custom MshSnapin

- cmdlets

- providers

- Types : this is implicit. Types that are used by your scripts will be added since the mshsnapins assembly will be loaded. For instance, you could then use new-object to create objects of types that are defined in your mshsnapin

4) What happens when the mshsnapin gets loaded?

- All the cmdlets from the mshsnapin get added to the running session. Providers are added and initialized. If you have typedata files (types.*.mshxml), they will be loaded. If you have formatdata files they will be loaded. You may see some errors when trying to add your mshsnapin. This doesn't mean your mshsnapin wasn't added successfully. When in doubt, run "get-mshsnapin" and see if your mshsnapin name is listed to determine if it was loaded or not. Just because there are errors, doesn't mean it wasn't loaded. Note that there are 2 types of errors when loading an mshsnapins:

   a) terminating - these errors cause the mshsnapin NOT to be loaded. An example would be unable to find the mshsnapin assembly or the mshsnapin doesn't exist(not installed in the registry)

   b) non-terminating - These are errors that do not prevent the mshsnapin from being loaded and there are many possibilites here. Let me try to cover a few

      - Can't find types/format file: If you have included a typdata file but it isn't located where its supposed to be (same goes for formatdata files)

      - Can't initialize duplicate default drive: since many providers can be loaded, there may be a case where multiple providers try to create a drive ("MYDRIVE"). First one loaded creates it, 2nd provider loaded will produce the error when the mshsnapin containing that provider is loaded. the mshsnapin & provider will still be loaded

      -  Duplicate type entry: If your typedata file has entries for types that are in other type files already loaded, 1st typedata file loaded wins and the 2nd produces the error. All other types in the file will be loaded.

5) What's a MshSnapin qualified path?

- It may be possible for a provider with the same Provider Name to exist in multiple MshSnapins. To disambiguate, you may need to provide the MshSnapin qualified name. Example:

MSH > get-provider MySnapin\MyProvider # gets the provider "MyProvider" that exists in the "MySnapin" MshSnapin.

You may also have noticed that when you do a get-childitem on theFilesystem provider you get:

MSH C:\monad> dir | more

    Directory: Microsoft.Management.Automation.Core\FileSystem::C:\monad

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          1/5/2006  10:05 AM       6817 about_Alias.help.txt

The "Microsoft.Management.Automation.Core" is the name of the MshSnapin that the FileSystem provider is defined in. There are a few "default" MshSnapins that we always load to add the providers/cmdlets that come default with Monad. You wont have to worry about this. We include this information just to be explicit and leave no doubt.

The Microsoft.Management.Automation.Core\FileSystem::C:\monad is an example of a mshsnapin qualified path. Since Custom shells don't allow MshSnapins to be added, Provider names or Paths aren't MshSnapin qualified.

6) What's a custom mshsnapin?

- Good question(not as good as #5 but still good)! You don't need to worry about this unless you have multiple assemblies that comprise your mshsnapin or you want to include custom typedata/formatdata files. The simplest form of an mshsnapin is a single assembly that contains cmdlet(s) and/or provider(s). In this case all we do is simply scan the assembly using reflection to determine what cmdlets/providers are there and load them so they can be used. A custom mshsnapin requires a type derived from CustomMshSnapin. This type has properties for specifying what cmdlets/providers/types/format entries to add when the mshsnapin is loaded. This type is also used by InstallUtil.exe since it derives from MshSnapinInstaller. This allows you to create an mshsnapin that spans multiple assemblies. Let me repeat this: IF you are creating an mshsnapin but have .NET types/cmdlets/providers that span multiple assemblies, you MUST create a custom MshSnapin. Also, if you're including typedata or formatdata files with your mshsnapins, you MUST create a custom mshsnapin. Creating one is similar to the normal MshSnapin except you have to derive from CustomMshSnapin and there are extr properties you must override:

public abstract class CustomMshSnapIn : MshSnapInInstaller
{
   public virtual Collection<CmdletConfigurationEntry> Cmdlets { get; }
   public virtual Collection<FormatConfigurationEntry> Formats { get; }
   public virtual Collection<ProviderConfigurationEntry> Providers { get; }
   public virtual Collection<TypeConfigurationEntry> Types { get; }
}

Posted by Monad Team | 2 Comments

Ensuring Script Portability with #requires

With the MshSnapIn model in Beta 3, there is no gaurantee that a certain Cmdlet or Provider, apart from those in the default MshSnapIns, is available at any point. There could be a number of reasons. The MshSnapIn may not have been installed. The MshSnapIn could be removed with the remove-mshsnapin Cmdlet or not even added yet. This will cause scripts using the unavailable commands to fail or worse exhibit unpredictable behaviors. Along with the MshSnapIn model, we introduce two flavors of #requires that a script can use to express dependencies on MshSnapIns and Monad version. The former has the following syntax:

#requires -MshSnapIn SomeMshSnapIn [-version N[.n]]  where the parts in square brackets are optional. #requires must be at the beginning of a line.

When only MshSnapIn is specified, the script will run if and only if the MshSnapIn SomeMshSnapIn is already added with the Add-MshSnapIn Cmdlet (or the RunspaceConfiguration.AddMshSnapIn API). Moreover, if the major version N is specified, the added MshSnapin's major version must match N. Furthermore, if the minor version n is also specified, the added MshSnapin's minor version must be equal to or higher than n. Failing any of the checks will cause the script not to run. A script can have multiple #requires -MshSnapIn directives. In this case, all MshSnapIn checks will need to pass for the script to run.

The latter's syntax is as follows.

#requires -version N[.n]  where the parts in square brackets are optional. #requires must be at the beginning of a line.

When only the major version N is specified, the current Monad shell's version must match N. If the minor version is also n specified, the current Monad shell's minor version must be equal to or higher than n. Failing any of the checks will cause the script not to run.

Finally, for the custom shell environment, the #requires -ShellId flavor is still available. A script can have any combinations of the three #requires flavors. #requires -version will check the Monad shell version for all combinations. As for the other two flavors, here is the behavior summary:

If a script has Monad Shell Custom Shell
#requires -MshSnapIn and no #requires -ShellId Check MshSnapIn Script won’t run
#requires -ShellId and no #requires -MshSnapIn Script won’t run Check ShellId
#requires -ShellId and #requires -MshSnapIn Check MshSnapIn Check ShellId

--
Kevin Loo [MSFT]
Monad Development
Microsoft Corporation
This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by Monad Team | 4 Comments

Mshsnapin (part 2): developing a mshsnapin.

To develop a mshsnapin, you can use following three simple steps,

  1. Create a class derived from MshSnapIn class.
  2. Build an assembly to contain the mshsnapin class created in step1 and also other cmdlet/provider classes to be included in the mshsnapin.
  3. Install the mshsnapin assembly created in step2 using installutil.exe.

Following is the sample code for a mshsnapin class. Basically, you just need to fill in information about name, vendor and description of the mshsnapin.

namespace XYZ.TestNameSpace
{
    [RunInstaller(true)]
    public class MyMshSnapIn : MshSnapIn
    {
        public MyMshSnapIn()
            : base()
        {
        }

        /// <summary>
        /// Gets name of the mshsnapin. This will be the string to be used for registering this
        /// mshsnapin.
        /// </summary>
        public override string Name
        {
            get
            {
                return "XYZ.TestNameSpace.MyMshSnapIn";
            }
        }

        /// <summary>
        /// Gets vendor of the mshsnapin.
        /// </summary>
        public override string Vendor
        {
            get
            {
                return "XYZ Corporation";
            }
        }

        /// <summary>
        /// Gets description of the mshsnapin.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This is a test mshsnapin";
            }
        }

}

For step 2, you build the mshsnapin code (from step 1), cmdlet code, and provider code (from your normal cmdlet and provider development) into one assembly (for example MyMshSnapin.dll).

For step 3, you install the snapin assembly by running following command. (installutil.exe is an standard utility from CLR).

   installutil.exe -i MyMshSnapin.dll

- George

 

 
Page view tracker