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; }
}