Update examples for hosting PSCore and remove old outdated examples (#8472)

This commit is contained in:
Christoph Bergmeister [MVP] 2019-01-11 22:06:22 +00:00 committed by Dongbo Wang
parent b30edf4764
commit 1334b4cbf2
18 changed files with 26 additions and 412 deletions

View file

@ -139,6 +139,7 @@ cron
crontab
crossgen
crossgen'ing
crossplatform
csharp
csmacnz
csphysicallyinstalledmemory

View file

@ -2,162 +2,25 @@
## PowerShell Core v6.0.1 and Later
The runtime assemblies for Windows, Linux and OSX are now published in NuGet package version 6.0.1.1 and above.
The runtime assemblies for Windows, Linux and OSX are now published in NuGet package version 6.*.
Please see the [.NET Core Sample Application](#net-core-sample-application) section for an example that uses PowerShell Core `6.0.1.1` NuGet packages.
## PowerShell Core v6.0.0-beta.3 and Later
PowerShell Core is refactored in v6.0.0-beta.3 to remove the dependency on a customized `AssemblyLoadContext`.
With this change, hosting PowerShell Core in .NET Core will be the same as hosting Windows PowerShell in .NET.
Please see the [.NET Core Sample Application](#net-core-sample-application) section for an example that uses PowerShell Core `beta.3` NuGet packages.
## PowerShell Core v6.0.0-beta.2 and Prior
### Overview
Due to the lack of necessary APIs for manipulating assemblies in .NET Core 1.1 and prior,
PowerShell Core needs to control assembly loading via our customized `AssemblyLoadContext` ([CorePsAssemblyLoadContext.cs][]) in order to do tasks like type resolution.
So applications that want to host PowerShell Core (using PowerShell APIs) need to be bootstrapped from `PowerShellAssemblyLoadContextInitializer`.
`PowerShellAssemblyLoadContextInitializer` exposes 2 APIs for this purpose:
`SetPowerShellAssemblyLoadContext` and `InitializeAndCallEntryMethod`.
They are for different scenarios:
- `SetPowerShellAssemblyLoadContext` - It's designed to be used by a native host
whose Trusted Platform Assemblies (TPA) do not include PowerShell assemblies,
such as the in-box `powershell.exe` and other native CoreCLR host in Nano Server.
When using this API, instead of setting up a new load context,
`PowerShellAssemblyLoadContextInitializer` will register a handler to the [Resolving][] event of the default load context.
Then PowerShell Core will depend on the default load context to handle TPA and the `Resolving` event to handle other assemblies.
- `InitializeAndCallEntryMethod` - It's designed to be used with `dotnet.exe`
where the TPA list includes PowerShell assemblies.
When using this API, `PowerShellAssemblyLoadContextInitializer` will set up a new load context to handle all assemblies.
PowerShell Core itself also uses this API for [bootstrapping][].
This documentation only covers the `InitializeAndCallEntryMethod` API,
as it's what you need when building a .NET Core application with .NET CLI.
### Comparison - Hosting Windows PowerShell vs. Hosting PowerShell Core
The following code demonstrates how to host Windows PowerShell in an application.
As shown below, you can insert your business logic code directly in the `Main` method.
```CSharp
// MyApp.exe
using System;
using System.Management.Automation;
public class Program
{
static void Main(string[] args)
{
// My business logic code
using (PowerShell ps = PowerShell.Create())
{
var results = ps.AddScript("Get-Command Write-Output").Invoke();
Console.WriteLine(results[0].ToString());
}
}
}
```
However, when it comes to hosting PowerShell Core, there will be a layer of redirection for the PowerShell load context to take effect.
In a .NET Core application, the entry point assembly that contains the `Main` method is loaded in the default load context,
and thus all assemblies referenced by the entry point assembly, implicitly or explicitly, will also be loaded into the default load context.
In order to have the PowerShell load context to control assembly loading for the execution of an application,
the business logic code needs to be extracted out of the entry point assembly and put into a different assembly, say `Logic.dll`.
The entry point `Main` method shall do one thing only -- let the PowerShell load context load `Logic.dll` and start the execution of the business logic.
Once the execution starts this way, all further assembly loading requests will be handled by the PowerShell load context.
So the above example needs to be altered as follows in a .NET Core application:
```CSharp
// MyApp.exe
using System.Management.Automation;
using System.Reflection;
namespace Application.Test
{
public class Program
{
/// <summary>
/// Managed entry point shim, which starts the actual program
/// </summary>
public static int Main(string[] args)
{
// Application needs to use PowerShell AssemblyLoadContext if it needs to create PowerShell runspace
// PowerShell engine depends on PS ALC to provide the necessary assembly loading/searching support that is missing from .NET Core
string appBase = System.IO.Path.GetDirectoryName(typeof(Program).GetTypeInfo().Assembly.Location);
System.Console.WriteLine("\nappBase: {0}", appBase);
// Initialize the PS ALC and let it load 'Logic.dll' and start the execution
return (int)PowerShellAssemblyLoadContextInitializer.
InitializeAndCallEntryMethod(
appBase,
new AssemblyName("Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"),
"Application.Test.Logic",
"Start",
new object[] { args });
}
}
}
// Logic.dll
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace Application.Test
{
public sealed class Logic
{
/// <summary>
/// Start the actual logic
/// </summary>
public static int Start(string[] args)
{
// My business logic code
using (PowerShell ps = PowerShell.Create())
{
var results = ps.AddScript("Get-Command Write-Output").Invoke();
Console.WriteLine(results[0].ToString());
}
return 0;
}
}
}
```
[CorePsAssemblyLoadContext.cs]: https://github.com/PowerShell/PowerShell/blob/v6.0.0-beta.2/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs
[Resolving]: https://github.com/dotnet/corefx/blob/ec2a6190efa743ab600317f44d757433e44e859b/src/System.Runtime.Loader/ref/System.Runtime.Loader.cs#L35
[bootstrapping]: https://github.com/PowerShell/PowerShell/blob/v6.0.0-beta.2/src/powershell/Program.cs#L27
Please see the [.NET Core Sample Application](#net-core-sample-application) section for an example that uses PowerShell Core NuGet packages.
## .NET Core Sample Application
- [sample-dotnet1.1](./sample-dotnet1.1) - .NET Core `1.1` + PowerShell Core `alpha.17` NuGet packages.
[.NET Core SDK 1.0.1](https://github.com/dotnet/cli/releases/tag/v1.0.1) is required.
- [sample-dotnet2.0-powershell.beta.1](./sample-dotnet2.0-powershell.beta.1) - .NET Core `2.0.0` + PowerShell Core `beta.1` NuGet packages.
.NET Core SDK `2.0.0-preview1-005952` or higher is required.
- [sample-dotnet2.0-powershell.beta.3](./sample-dotnet2.0-powershell.beta.3) - .NET Core `2.0.0` + PowerShell Core `beta.3` NuGet packages.
.NET Core SDK `2.0.0-preview1-005952` or higher is required.
- [sample-dotnet2.0-powershell-crossplatform](./sample-dotnet2.0-powershell-crossplatform) - .Net Core `2.0.0` + PowerShell Core `6.0.1.1` NuGet packages.
Note: The .NET Core `2.1` runtime and .NET Core SDK `2.1` or higher is required for the examples below:
You can find the sample application project `"MyApp"` in each of the above 3 sample folders.
To build the sample project, run the following commands (make sure the required .NET Core SDK is in use):
- [sample](./sample)
You can find the sample application project `MyApp` in each of the above 2 sample folders. You can quickly test-run it using `dotnet run`.
To build the sample project properly for distribution, run the following command (make sure the required .NET Core SDK is in use):
```powershell
dotnet restore .\MyApp\MyApp.csproj
dotnet publish .\MyApp -c release -r win10-x64
dotnet publish .\MyApp --configuration release
```
For cross platform project there is no need to specify `-r win10-x64`.
The runtime for the build machine's platform will automatically be selected.
Then you can run `MyApp.exe` from the publish folder and see the results:
This builds it for the runtimes specified by the `RuntimeIdentifiers` property in the `.csproj` file.
Then you can run the `MyApp` binary from the publish folder and see the results:
```none
PS:> .\MyApp.exe
@ -171,3 +34,13 @@ Evaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS C
System.Management.Automation.ActionPreference
System.Management.Automation.AliasAttribute
```
## Special Hosting Scenario For Native Host
There is a special hosting scenario for native hosts,
where Trusted Platform Assemblies (TPA) do not include PowerShell assemblies,
such as the in-box `powershell.exe` in Nano Server and the Azure DSC host.
For such hosting scenarios, the native host needs to bootstrap by calling [`PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext`](https://docs.microsoft.com/dotnet/api/system.management.automation.powershellassemblyloadcontextinitializer.setpowershellassemblyloadcontext).
When using this API, the native host can pass in the path to the directory that contains PowerShell assemblies.
A handler will then be registered to the [`Resolving`](https://github.com/dotnet/corefx/blob/d6678e9653defe3cdfff26b2ff62135b6b22c77f/src/System.Runtime.Loader/ref/System.Runtime.Loader.cs#L38) event of the default load context to deal with the loading of assemblies from that directory.

View file

@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<AssemblyName>Logic</AssemblyName>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net45+win8</PackageTargetFallback>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.6' ">
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.0.0-alpha17" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="6.0.0-alpha17" />
<PackageReference Include="Microsoft.WSMan.Management" Version="6.0.0-alpha17"/>
</ItemGroup>
</Project>

View file

@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace Application.Test
{
public sealed class Logic
{
/// <summary>
/// Start the actual logic.
/// </summary>
public static int Start(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n");
var results = ps.AddScript("Get-Command Write-Output").Invoke();
Console.WriteLine(results[0].ToString());
ps.Commands.Clear();
Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n");
results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke();
foreach (dynamic result in results)
{
Console.WriteLine(result.ToString());
}
}
return 0;
}
}
}

View file

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyName>MyApp</AssemblyName>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net45+win8</PackageTargetFallback>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
<ProjectReference Include="..\Logic\Logic.csproj" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.0.0-alpha17" />
</ItemGroup>
</Project>

View file

@ -1,31 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Management.Automation;
using System.Reflection;
namespace Application.Test
{
public class Program
{
/// <summary>
/// Managed entry point shim, which starts the actual program.
/// </summary>
public static int Main(string[] args)
{
// Application needs to use PowerShell AssemblyLoadContext if it needs to create powershell runspace
// PowerShell engine depends on PS ALC to provide the necessary assembly loading/searching support that is missing from .NET Core
string appBase = System.IO.Path.GetDirectoryName(typeof(Program).GetTypeInfo().Assembly.Location);
System.Console.WriteLine("\nappBase: {0}", appBase);
// Initialize the PS ALC and let it load 'Logic.dll' and start the execution
return (int)PowerShellAssemblyLoadContextInitializer.
InitializeAndCallEntryMethod(
appBase,
new AssemblyName("Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"),
"Application.Test.Logic",
"Start",
new object[] { args });
}
}
}

View file

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>MyApp</AssemblyName>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.0.1.1" />
</ItemGroup>
</Project>

View file

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Management.Automation;
namespace Application.Test
{
public class Program
{
/// <summary>
/// Managed entry point shim, which starts the actual program.
/// </summary>
public static int Main(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n");
var results = ps.AddScript("Get-Command Write-Output").Invoke();
Console.WriteLine(results[0].ToString());
}
return 0;
}
}
}

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="powershell-core" value="https://powershell.myget.org/F/powershell-core/api/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Logic</AssemblyName>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.0.0-beta.1" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="6.0.0-beta.1" />
<PackageReference Include="Microsoft.WSMan.Management" Version="6.0.0-beta.1"/>
</ItemGroup>
</Project>

View file

@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace Application.Test
{
public sealed class Logic
{
/// <summary>
/// Start the actual logic.
/// </summary>
public static int Start(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n");
var results = ps.AddScript("Get-Command Write-Output").Invoke();
Console.WriteLine(results[0].ToString());
ps.Commands.Clear();
Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n");
results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke();
foreach (dynamic result in results)
{
Console.WriteLine(result.ToString());
}
}
return 0;
}
}
}

View file

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>MyApp</AssemblyName>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Logic\Logic.csproj" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.0.0-beta.1" />
</ItemGroup>
</Project>

View file

@ -1,31 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Management.Automation;
using System.Reflection;
namespace Application.Test
{
public class Program
{
/// <summary>
/// Managed entry point shim, which starts the actual program.
/// </summary>
public static int Main(string[] args)
{
// Application needs to use PowerShell AssemblyLoadContext if it needs to create powershell runspace
// PowerShell engine depends on PS ALC to provide the necessary assembly loading/searching support that is missing from .NET Core
string appBase = System.IO.Path.GetDirectoryName(typeof(Program).GetTypeInfo().Assembly.Location);
System.Console.WriteLine("\nappBase: {0}", appBase);
// Initialize the PS ALC and let it load 'Logic.dll' and start the execution
return (int)PowerShellAssemblyLoadContextInitializer.
InitializeAndCallEntryMethod(
appBase,
new AssemblyName("Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"),
"Application.Test.Logic",
"Start",
new object[] { args });
}
}
}

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="powershell-core" value="https://powershell.myget.org/F/powershell-core/api/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="powershell-core" value="https://powershell.myget.org/F/powershell-core/api/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>MyApp</AssemblyName>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.0.0-beta.3" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="6.0.0-beta.3" />
<PackageReference Include="Microsoft.WSMan.Management" Version="6.0.0-beta.3"/>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.1.1" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="6.1.1" />
<PackageReference Include="Microsoft.WSMan.Management" Version="6.1.1"/>
</ItemGroup>
</Project>