This blog post is continuation of series of posts on understanding classes in PowerShell. Part 06 of series can be read at here. If you have defined PowerShell class inside a module, its not easy to import class in the current session. By default, it does not load classes. Let’s understand the details of it.
Import-Module
does not import Classes
Import-Module
has been a standard way of importing functions defined in a PowerShell module. It also comes with -verbose parameter so that you can see what has been imported. For understanding the case with classes, let’s define a simple PowerShell module HelloModule.psm1 as below code:
Class HelloWorld{ HelloWorld(){ Write-Host "New Object initiated of HelloWorld Class" } [String] sayHello(){ return "Hello World!" } } function sayHello([String]$user){ Write-Output "Hello! $user" }
Let’s import this module into script console. We’ll use the -verbose parameter to see what has been imported.
PS C:\Users\mohitgoyal\desktop> import-module .\HelloModule.psm1 -PassThru -Verbose VERBOSE: Loading module from path 'C:\Users\mohitgoyal\desktop\HelloModule.psm1'. VERBOSE: Exporting function 'sayHello'. VERBOSE: Importing function 'sayHello'. ModuleType Version Name ExportedCommands ---------- ------- ---- ---------------- Script 0.0 HelloModule sayHello
We don’t see any classes imported. In fact, if we try to find the PowerShell class, it will throw an error like this:
PS C:\Users\mohitgoyal\desktop> [HelloWorld] Unable to find type [HelloWorld]. At line:1 char:1 + [HelloWorld] + ~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (HelloWorld:TypeName) [], RuntimeException + FullyQualifiedErrorId : TypeNotFound
#Requires
Does Not Import Classes
To understand this, let’s define a data file for our module with some attributes like below:
@{ ModuleVersion = "1.0.0.0" Author = "Mohit Goyal" Copyright = "mohitgoyal.co" PowerShellVersion = "5.0" DotNetFrameworkVersion = "4.0" FunctionsToExport = "sayHello" }
We’ll save it as HelloModule.psd1. Now, let’s try to import this module using #Requires:
#Requires -Module @{ModuleName="HelloWorld.psm1";RequiredVersion="1.0.0.0"}
Again, when checking for our class, we’ll get same error:
PS C:\Users\mohitgoyal\desktop> [HelloWorld] Unable to find type [HelloWorld]. At line:1 char:1 + [HelloWorld] + ~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (HelloWorld:TypeName) [], RuntimeException + FullyQualifiedErrorId : TypeNotFound
Import Classes using Using Module
Statement
The using
statement was introduced in the PowerShell v5. It has two uses. It imports assemblies (using namespace) and it imports modules (using module), including their classes. The assembly-importing feature is a convenience; the module-importing feature is a requirement, because there’s no other way to import classes from script modules. The Using statement has a module parameter that takes a module name string or a ModuleSpecification object.
We can import our class using below syntax:
PS C:\Users\mohitgoyal\desktop> using module C:\Users\mohitgoyal\desktop\HelloModule.psm1
And when checking for the class object:
PS C:\Users\mohitgoyal\desktop> [HelloWorld] IsPublic IsSerial Name BaseType -------- -------- ---- -------- True False HelloWorld System.Object
After this, we can simply create a new variable from the class like explained in previous posts:
PS C:\Users\chrisgreen\desktop> $h1 = [HelloWorld]::new() New Object initiated of HelloWorld Class PS C:\Users\chrisgreen\desktop> $h1.sayHello() Hello World!
You can also create a version-specific Using module statement with a ModuleSpecification object.
Note that if you had multiple modules you wanted to access the class definitions in, you would simply have multiple lines of “using module”, each line referencing the module you wanted.
using module 'C:\Users\mohitgoyal\desktop\mymodule.psm1' using module 'C:\Users\mohitgoyal\desktop\anothermodule.psm1'
This should cover how to use PowerShell modules with PowerShell Classes.
This is exactly what I have been looking for! Very useful intro on classes in PowerShell.
LikeLike
What I hate about this is: you HAVE to place the using statement at the very first lines of a script. This is ok unless you want to have code to get the path right for example $currentdirectory\file.psm1 -I can’t run the code to set the $currentdirectory var
LikeLike
Use instead a script block, invoke it using ‘.’ instead of ‘&’, that will keep the types loaded in the main script
If you do that with multiple scriptblocks, it will only keep the types loaded from the latest scriptblock; make sure you load the types from all the modules within the same scriptblock
$MyFolder = read-host ‘Where is your module?’
. ([ScriptBlock]::Create(@”
using module ‘$MyFolder\mymodule.psm1’
using module ‘$MyFolder\anothermodule.psm1’
“@))
[HelloWorld]
LikeLike