I wrote countless scripts and a good amount of modules (and functions) in my years as Service Engineer but all of them are script modules, I never really created binary modules. The main reason is that I like to write in Powershell but I also like the fact that, not being a compiled language, it is very easy to share and modify the source code for a script module and it is immediately ready to be reloaded and used. Anyway out of curiosity and to learn a different approach to building modules, I decided to try to convert one of mine from script to binary; my first step was (of course) a quick search to find some samples and getting started articles and I found a few good ones (referenced below) but all of them use Visual Studio and the full version of the .NET Framework, while I want to use Visual Studio Code and .NET Core. So here’s what I came up with.
First off of course I need .NET Core (I am using the latest .NET Core 3 preview 8 at the moment), Visual Studio Code and the C# Extension. Next, I’m going to create a Class Library project using dotnet at the command line; note the -f parameter to indicate the framework version I want to use:
PS > dotnet new classlib -f netcoreapp3.0 -o HelloWorldCore
Couldn't find an installed template that matches the input, searching online for one that does...
Matches from template source: NuGet
-----------------------------------
Template name "Razor Class Library" (razorclasslib) from author "Microsoft" in pack Microsoft.DotNet.Web.ProjectTemplates.2.2
To use this template, run the following command and try again:
dotnet new -i Microsoft.DotNet.Web.ProjectTemplates.2.2::2.2.6
Since I am attempting this command on a fresh installation I am missing some templates but luckily dotnet suggests the command to run to fix this problem:
PS > dotnet new -i Microsoft.DotNet.Web.ProjectTemplates.2.2::2.2.6
Restore completed in 1.73 sec for C:\Users\carlo\.templateengine\dotnetcli\v3.0.100-preview8-013656\scratch\restore.csproj.
Usage: new [options]
Options:
-h, --help Displays help for this command.
-l, --list Lists templates containing the specified name. If no name is specified, lists all templates.
-n, --name The name for the output being created. If no name is specified, the name of the current directory is used.
-o, --output Location to place the generated output.
-i, --install Installs a source or a template pack.
-u, --uninstall Uninstalls a source or a template pack.
--nuget-source Specifies a NuGet source to use during install.
--type Filters templates based on available types. Predefined values are "project", "item" or "other".
--dry-run Displays a summary of what would happen if the given command line were run if it would result in a template creation.
--force Forces content to be generated even if it would change existing files.
-lang, --language Filters templates based on language and specifies the language of the template to create.
--update-check Check the currently installed template packs for updates.
--update-apply Check the currently installed template packs for update, and install the updates.
Templates Short Name Language Tags
----------------------------------------------------------------------------------------------------------------------------------
Console Application console [C#], F#, VB Common/Console
Class library classlib [C#], F#, VB Common/Library
WPF Application wpf [C#], VB Common/WPF
WPF Class library wpflib [C#], VB Common/WPF
WPF Custom Control Library wpfcustomcontrollib [C#], VB Common/WPF
WPF User Control Library wpfusercontrollib [C#], VB Common/WPF
Windows Forms (WinForms) Application winforms [C#], VB Common/WinForms
Windows Forms (WinForms) Class library winformslib [C#], VB Common/WinForms
Worker Service worker [C#] Common/Worker/Web
Unit Test Project mstest [C#], F#, VB Test/MSTest
NUnit 3 Test Project nunit [C#], F#, VB Test/NUnit
NUnit 3 Test Item nunit-test [C#], F#, VB Test/NUnit
xUnit Test Project xunit [C#], F#, VB Test/xUnit
Razor Component razorcomponent [C#] Web/ASP.NET
Razor Page page [C#] Web/ASP.NET
MVC ViewImports viewimports [C#] Web/ASP.NET
MVC ViewStart viewstart [C#] Web/ASP.NET
Blazor Server App blazorserver [C#] Web/Blazor
ASP.NET Core Empty web [C#], F# Web/Empty
ASP.NET Core Web App (Model-View-Controller) mvc [C#], F# Web/MVC
ASP.NET Core Web App webapp [C#] Web/MVC/Razor Pages
ASP.NET Core with Angular angular [C#] Web/MVC/SPA
ASP.NET Core with React.js react [C#] Web/MVC/SPA
ASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPA
Razor Class Library razorclasslib [C#] Web/Razor/Library/Razor Class Library
ASP.NET Core Web API webapi [C#], F# Web/WebAPI
ASP.NET Core gRPC Service grpc [C#] Web/gRPC
dotnet gitignore file gitignore Config
global.json file globaljson Config
NuGet Config nugetconfig Config
Dotnet local tool manifest file tool-manifest Config
Web Config webconfig Config
Solution File sln Solution
Protocol Buffer File proto Web/gRPC
Examples:
dotnet new mvc --auth Individual
dotnet new nunit-test
dotnet new --help
Now I can re-run the dotnet new command:
PS > dotnet new classlib -f netcoreapp3.0 -o HelloWorldCore
The template "Class library" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on HelloWorldCore\HelloWorldCore.csproj...
Restore completed in 70.99 ms for C:\Users\carlo\Git\HelloWorldCore\HelloWorldCore.csproj.
Restore succeeded.
PS > Get-ChildItem -Recurse
Directory: C:\Users\carlo\Git\HelloWorldCore
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 8/19/2019 4:59 PM bin
d---- 8/19/2019 4:59 PM obj
-a--- 8/19/2019 4:44 PM 91 Class1.cs
-a--- 8/19/2019 4:44 PM 144 HelloWorldCore.csproj
Directory: C:\Users\carlo\Git\HelloWorldCore\bin
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 8/19/2019 4:59 PM Debug
Directory: C:\Users\carlo\Git\HelloWorldCore\bin\Debug
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 8/19/2019 4:59 PM netcoreapp3.0
Directory: C:\Users\carlo\Git\HelloWorldCore\obj
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 8/19/2019 4:59 PM Debug
-a--- 8/19/2019 4:44 PM 149 HelloWorldCore.csproj.nuget.cache
-a--- 8/19/2019 4:44 PM 2253 HelloWorldCore.csproj.nuget.dgspec.json
-a--- 8/19/2019 4:44 PM 1166 HelloWorldCore.csproj.nuget.g.props
-a--- 8/19/2019 4:44 PM 294 HelloWorldCore.csproj.nuget.g.targets
-a--- 8/19/2019 4:44 PM 2268 project.assets.json
Directory: C:\Users\carlo\Git\HelloWorldCore\obj\Debug
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 8/19/2019 4:59 PM netcoreapp3.0
Directory: C:\Users\carlo\Git\HelloWorldCore\obj\Debug\netcoreapp3.0
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 8/19/2019 4:59 PM 1015 HelloWorldCore.AssemblyInfo.cs
-a--- 8/19/2019 4:59 PM 42 HelloWorldCore.AssemblyInfoInputs.cache
-a--- 8/19/2019 4:59 PM 188 HelloWorldCore.assets.cache
-a--- 8/19/2019 4:59 PM 42 HelloWorldCore.csproj.CoreCompileInputs.cache
-a--- 8/19/2019 4:59 PM 109810 HelloWorldCore.csprojAssemblyReference.cache
Even with .NET Core I still need to add a reference to the System.Management.Automation assembly, which is available as a nuget package. Visual Studio Code has a thriving marketplace with tons of very useful extension so I decided to check if something was available to help with this scenario and sure enough I found a number of suitable extensions. I went for NuGet Package Manager and I added a reference to System.Management.Automation version 6.2.2 to match my .NET Core version
Now I can import the System.Management.Automation namespace, decorate the class with the Cmdlet attribute, have it inherit from Cmdlet and override the ProcessRecord() method; for this sample I don’t need to be too fancy with my code so here it is:
using System.Management.Automation;
namespace HelloWorldCore
{
[Cmdlet(VerbsCommon.Get, "HelloWorld")]
public class Class1 : Cmdlet
{
private string _name;
[Parameter(Mandatory = true)]
public string Name
{
get { return _name; }
set { _name = value; }
}
protected override void ProcessRecord()
{
WriteObject("Hey " + this.Name);
}
}
}
Next step, build the project and generate the .dll I will later import in my Powershell session; to do that I could use dotnet at the command line or use a VSCode extension such as Dotnet core commands
PS > dotnet build --configuration Debug
Microsoft (R) Build Engine version 16.3.0-preview-19377-01+dd8019d9e for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 1.63 sec for C:\Users\carlo\Git\HelloWorldCore\HelloWorldCore.csproj.
You are using a preview version of .NET Core. See: https://aka.ms/dotnet-core-preview
HelloWorldCore -> C:\Users\carlo\Git\HelloWorldCore\bin\Debug\netcoreapp3.0\HelloWorldCore.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:02.76
Finally, these are a few pointers to get started building binary Powershell modules
- Tutorials for Writing Cmdlets
- Examples of Cmdlets Code
- Using C# to Create PowerShell Cmdlets: The Basics
- Writing a PowerShell module in C#
| You cannot help men permanently by doing for them what they could and should do for themselves. – Abraham Lincoln |