I am using PowerShell 5.1 on a Windows 7 box and am trying to reference a type in a .NET assembly from inside a PowerShell class, and am getting an "Unable to find type" error.
My "main" program is FooProg.ps1, where I am creating a new instance of the FooMod class
#Requires -Version 5.1
using module .\FooMod\FooMod.psm1
[FooMod]::new() | out-null
The FooMod class is in a PowerShell module (FooMod.psm1) in the FooMod sub-folder
#Requires -Version 5.1
using assembly .\Foo.dll
using namespace Foo
class FooMod {
FooMod() {
Write-Host "Start - Assembly access"
[Bar] $bar = [Bar]::new()
Write-Host $bar.Name
Write-Host $bar.Greeting()
Write-Host "Finish - Assembly access"
}
}
My Foo.dll is built from the following Bar.cs, and is in the FooMod sub-folder along with the Module file (FooMod.psm1) and the Module Manifest file (FooMod.psd1)
namespace Foo {
public class Bar {
public string Name;
public Bar(string name) => this.Name = name;
public Bar() : this("Bar") {
}
public string Greeting() {
return System.String.Format("Hello! My name is {0}", Name);
}
}
}
I also have a Module Manifest in the FooMod sub-folder called FooMod.psd1 which contains these relevant entries
RootModule = 'FooMod.psm1'
RequiredAssemblies = 'Foo.dll'
Is there anything else I am missing? Using the assembly outside the class works fine, instantiating a PowerShell class (without referencing an assembly / external type) works fine - but combining PowerShell classes and assembly types is a no-go!
Figured it out! It turns out that in the main script, FooProg.ps1, the using statement needs to refer to the Module Manifest and NOT the Module Script.
So instead of using
#Requires -Version 5.1
using module .\FooMod\FooMod.psm1
[FooMod]::new() | out-null
I changed it to
#Requires -Version 5.1
using module .\FooMod\FooManifest.psd1
[FooModule]::new() | out-null
As a result, I also renamed my Module Manifest file from .\FooMod\FooMod.psd1 to .\FooMod\FooManifest.psd1
Also, in the Module Manifest file I changed the entry RootModule = 'FooMod.psm1' to RootModule = 'FooModule.psm1'. This is not a required change, but aids in understanding the structure of the different files.
This obviously led to renaming the module script from FooMod.psm1 to FooModule.psm1 and also to changing the class name within the module script from FooMod to FooModule. Again not a required change, but it is consistent with naming classes the same as the filename in which they are contained.
The whole exercise leads me to believe that the Module Manifest file which is named in the main script needs to be processed first so that the assemblies named in RequiredAssemblies are loaded before the module named in the RootModule is parsed. This makes the types in the external .NET assembly to be available when the module is parsed and prevents the "Unable to find type" error.
Now armed with the new FooModule.psm1 file
#Requires -Version 5.1
using assembly .\Foo.dll
using namespace Foo
class FooModule {
FooModule() {
Write-Host "Start - Assembly access"
Write-Host
[Bar] $bar1 = [Bar]::new("Romeo")
Write-Host $bar1.Name
Write-Host $bar1.Greeting()
Write-Host
[Bar] $bar2 = [Bar]::new("Juliet")
Write-Host $bar2.Name
Write-Host $bar2.Greeting()
Write-Host
Write-Host "Finish - Assembly access"
}
}
the output I get is as follows
Start - Assembly access
Romeo
Hello! My name is Romeo
Juliet
Hello! My name is Juliet
Finish - Assembly access
The key is to use a Module Manifest, with the RequiredAssemblies key properly set and to reference the Module Manifest rather than the Module Script itself in your main script.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With