PowerShellHostVersion - WTF?

PowerShellHostVersion - WTF?

  • Comments 3

Superstar Shay Levy recently wrote a blog article, Module Manifest Gotcha .  He was exploring the various options that the Module Manifest provides and saw what he thought was a problem with using the PowerShellHostVersion key.

PowerShellHostVersion key
Specifies the minimum version of the Windows PowerShell host program that works with the module.

The “gotcha” here is that there are LOTS of PowerShell Hosts:  PowerShell.exe,  Powershell_ISE.exe, PowerGUI, Sapien’s PrimalScript, PowerShellPlus and others (apologizes in advance to those I forgot to mention).   Shay created a manifest with PowerShellHostVersion = “2.0” and it worked fine with PowerShell.exe but barfed when he imported it into PowerShell_ISE.exe (because that is Version 1.0).

Say concluded:

“To be on the safe side, do not set a value for ‘PowerShellHostVersion’.”

That is almost, but not quite, right.  To understand why we added this seemingly useless feature, you need to take a look at the other keys in the manifest ( HERE ).  There are about 15 keys you can specify there and in general,  you can safely ignore most of them and everything will work fine.  Most of these keys are put there to allow people that have specific requirements and would generate weird errors or unpredictable behavior if their requirements were not met. 

Let’s take an example.  PowerShell_ISE is our new Integrated Scripting Environment that we shipped with PowerShell V2.  We are super excited with PowerShell_ISE but the fact of the matter is that it is a pretty lean tool without many features.  Each of the tools I mentioned above blow the doors of PowerShell_ISE in terms of features (you should check them all out – they are great).  We knew that there would be features that people needed that we were not delivering so we provided an extensibility mechanism, the PowerShell ISE Scripting Object Model.  People do all sorts of wonderful things with this.  For instance, James Brundage created ISE_Pack which is part of the (Free) Windows 7 Resource kit PowerShell Pack.  This delivers a bunch of cool functions that only work with PowerShell_ISE (because they script against the PowerShell ISE Scripting Object). 

So what what happens when you try to import this module into plain old PowerShell.exe where they won’t work because it doesn’t have the PowerShell scripting model:

PS> Import-Module isepack
PS>

errrrr….. hmmm….. ooooookaaaaaay….. well that’s embarrassing! 

I just loaded a module that has exactly 0% chance of working in this environment and got no error message.  Let’s see what happens if I try to do one of the functions:

PS> New-ScriptModuleFromCurrentLocation
Split-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:16 char:27
+     $location = Split-Path <<<<  $psise.CurrentFile.FullPath
    + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCo
   mmand

Split-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:17 char:31
+     $locationName = Split-Path <<<<  $location -Leaf
    + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCo
   mmand

Join-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:22 char:28
+     $modulePath = Join-Path <<<<  $location "$locationName.psm1"
    + CategoryInfo          : InvalidData: (:) [Join-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCom
   mand

Test-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:23 char:18
+     if (Test-Path <<<<  -ErrorAction SilentlyContinue $modulePath) {
    + CategoryInfo          : InvalidData: (:) [Test-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.TestPathCom
   mand

Exception calling "WriteAllText" with "2" argument(s): "Empty path name is not legal."
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:26 char:28
+     [IO.File]::WriteAllText <<<< ($modulePath, $text)
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

You cannot call a method on a null-valued expression.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:27 char:42
+     $psise.CurrentPowerShellTab.Files.Add <<<< ($modulePath)
    + CategoryInfo          : InvalidOperation: (Add:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

PS>

Ok – now I want to puke. 

What James has done is to highlight the “Formality Range” aspect of PowerShell.  When you are writing code for yourself, it can be as informal as you like.  PowerShell doesn’t get in you way with lots of extra steps or hurdles – you can GO GO GO.  But  … when you want to share it with the world, PowerShell provides incremental features to be more formal and thus more friendly to a wide range of usage scenarios. 

What James should have done is to go into his manifest and change:

    # Name of the Windows PowerShell host required by this module
    PowerShellHostName = ''

to

    # Name of the Windows PowerShell host required by this module
    PowerShellHostName = 'Windows PowerShell ISE Host'

Once you do that and try to import the module for Powershell.exe (the console host), you get this error:

PS> Import-Module isepack
Import-Module : The name of the current PowerShell host is: 'ConsoleHost'. The module 'C:\Users\jsnover\Documents\Windo
wsPowerShell\Modules\isepack\isepack.psd1' requires the following PowerShell host: 'Windows PowerShell ISE Host'.
At line:1 char:5
+ ipmo <<<<  isepack -Force
    + CategoryInfo          : ResourceUnavailable: (C:\Users\jsnove...ck\isepack.psd1:String) [Import-Module], Invalid
   OperationException
    + FullyQualifiedErrorId : Modules_InvalidPowerShellHostName,Microsoft.PowerShell.Commands.ImportModuleCommand

Now that is an error message that even I can understand!

So where are we going with all of this? 

As I mentioned the first version of Powershell_ISE is pretty lean but we might go add more features in a future version.  Let’s imagine that James releases a new version of ISEPACK with a ton of new features that use the new PowerShell_ISE features.  Since he added PowerShellHostName = 'Windows PowerShell ISE Host', we know that it will only load in the PowerShell_ISE but what happens if someone tries to load it into V1 of Powershell_ISE?  We are going to be back to the same horrible experience of it loading OK only to fail in some random manner when I try to use one of it’s features. 

But wait wait! 
Wouldn’t it be great if James could specify that his new Module ONLY worked with V3 of PowerShell_ISE? 
Wouldn’t it be great if there was a way to add something like:

    # Name of the Windows PowerShell host required by this module
    PowerShellHostName = 'Windows PowerShell ISE Host'

    # Minimum version of the Windows PowerShell host required by this module
    PowerShellHostVersion = '3.0'

Then if you loaded it into V3, it would work and if you tried to  load it into V1, it would give you a nice error message. 

PS> Import-Module ISEPACK
Import-Module : The current PowerShell host is: 'Windows PowerShell ISE Host' (version 2.0). The module 'C:\Users\jsnover\Documents\WindowsPo
werShell\Modules\ISEPACK\ISEPACK.psd1' requires a minimum PowerShell host version of '3.0' to execute.
At line:1 char:14
+ Import-Module <<<<  ISEPACK -force
    + CategoryInfo          : ResourceUnavailable: (C:\Users\jsnove...CK\ISEPACK.psd1:String) [Import-Module], InvalidOperationException
    + FullyQualifiedErrorId : Modules_InsufficientPowerShellHostVersion,Microsoft.PowerShell.Commands.ImportModuleCommand

And now you know why we added something silly like PowerShellVersionHost to manifests. 
It’s almost like we are thinking this stuff out.  :-)

Shay- thanks for bringing the topic up.  It gave me an opportunity to explain what is going on and it clearly demonstrates that we need to beef up our documentation.  We delivered SO MANY new functions in V2, that we have a lot of documentation to write.

Experiment!  Enjoy!  Engage!

Jeffrey Snover [MSFT]
Distinguished Engineer
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Leave a Comment
  • Please add 1 and 7 and type the answer here:
  • Post
  • So, say you had some module you'd written to work under PowerShell.exe 2.0 AND PowerShell_ISE.exe 1.0, but not older versions of PowerShell.exe nor  PowerGUI.  Is there a way to specify multiple PowerShellHostNames?

  • Use case:

    *) My module manifest claims PowerShellHostName = 'MyHost', PowerShellHostVersion = '4.0' because I know exactly that version 3.x of 'MyHost' is not enough. New version 4.0 is available for free 24/7. That is why I do not care of compatibility with MyHost 3.x and do require MyHost 4.0.

    *) At the same time my module works just fine in other hosts. In MyHost it just uses its known features to make things easier, better, and etc.

    What happens if I try to import my module in other host? Error:

    Import-Module : The name of the current PowerShell host is: 'ConsoleHost'. The module '...' requires the following PowerShell host: 'MyHost'.

    That is not what I want; my module works in ConsoleHost, too. So, what do I do? -- I do not use PowerShellHostName at all. And PowerShellHostVersion as well, of course.

    It looks like PowerShellHostName and PowerShellHostVersion are designed only for very host-specific modules that almost for sure do not work in any other host.

  • I should add that this is not the end of the world. I just have to perform all the checks on my own in my module and show informative errors for a user.

    But the important point is that my module and all its stuff is loaded by the PowerShell engine even in MyHost 3.x, that is in the environment for sure not suitable for loading. Safe initialisation of the module may be not even possible or may require too much programming effort.

    It would be nice if a module can specify in its manifest:

    A) One or more known supported hosts and optionally their versions;

    B) One or more known not supported hosts;

    C) Explicitly: other hosts (not specified in A and B) are supported or not supported.

    It’s more complex than just two scalar properties Host and Version, but it would cover more cases when the engine should not even try to load a module.

Page 1 of 1 (3 items)