From 463342edd2d83d2dd77c7ea5e60fdfb505a2419f Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 23 Mar 2017 18:01:22 -0700 Subject: [PATCH 1/5] Add documentation about hosting PowerShell Core --- docs/host-powershell/README.md | 149 ++++++++++++++++++ .../sample-dotnet1.1/Logic/Logic.csproj | 16 ++ .../sample-dotnet1.1/Logic/UseRunspace.cs | 35 ++++ .../sample-dotnet1.1/MyApp/MyApp.csproj | 17 ++ .../sample-dotnet1.1/MyApp/Program.cs | 32 ++++ .../sample-dotnet1.1/NuGet.config | 8 + 6 files changed, 257 insertions(+) create mode 100644 docs/host-powershell/README.md create mode 100644 docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj create mode 100644 docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs create mode 100644 docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj create mode 100644 docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs create mode 100644 docs/host-powershell/sample-dotnet1.1/NuGet.config diff --git a/docs/host-powershell/README.md b/docs/host-powershell/README.md new file mode 100644 index 00000000000..d86c87181e8 --- /dev/null +++ b/docs/host-powershell/README.md @@ -0,0 +1,149 @@ +# Host PowerShell Core in .NET Core Applications + +> This documentation is based on PowerShell Core packages built against .NET Core 1.1 and prior. +> Things may change after PowerShell Core starts to build against .NET Core 2.0. + +## PowerShell Core targeting .NET Core 1.1 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` 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: + +- For `SetPowerShellAssemblyLoadContext`, +it's designed to be used by a native host whose TPA (Trusted Platform Assemblies) list doesn't 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. + +- For `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. + +### Example + +The following code is how you host Windows PowerShell in an application. +It's straight forward that you can directly run your business logic code from the application entry point `'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, where `'Main'` method resides, 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 + { + /// + /// Managed entry point shim, which starts the actual program + /// + 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 + { + /// + /// Start the actual logic + /// + 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; + } + } +} +``` + +You can find the sample application project `"MyApp"` under [sample-dotnet1.1](./sample-dotnet1.1). +To build the sample project, run the following commands: + +```powershell +dotnet restore .\MyApp\MyApp.csproj +dotnet publish .\MyApp -c release -r win10-x64 +``` + +Then you can run `MyApp.exe` from the publish folder and see the results: + +``` +PS:> .\MyApp.exe + +Evaluating 'Get-Command Write-Output' in PS Core Runspace + +Write-Output + +Evaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace + +System.Management.Automation.ActionPreference +System.Management.Automation.AliasAttribute +``` + +[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/master/src/powershell/Program.cs#L27 \ No newline at end of file diff --git a/docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj b/docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj new file mode 100644 index 00000000000..dc8816bca50 --- /dev/null +++ b/docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj @@ -0,0 +1,16 @@ + + + + netstandard1.6 + Logic + win10-x64 + $(PackageTargetFallback);dnxcore50;portable-net45+win8 + + + + + + + + + diff --git a/docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs b/docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs new file mode 100644 index 00000000000..b13a08c244a --- /dev/null +++ b/docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs @@ -0,0 +1,35 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ +using System; +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +namespace Application.Test +{ + public sealed class Logic + { + /// + /// Start the actual logic + /// + 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; + } + } +} diff --git a/docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj b/docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj new file mode 100644 index 00000000000..012db33cc60 --- /dev/null +++ b/docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp1.1 + MyApp + Exe + win10-x64 + $(PackageTargetFallback);dnxcore50;portable-net45+win8 + 1.1.1 + + + + + + + + diff --git a/docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs b/docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs new file mode 100644 index 00000000000..f0fa6b5fedf --- /dev/null +++ b/docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs @@ -0,0 +1,32 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System.Management.Automation; +using System.Reflection; + +namespace Application.Test +{ + public class Program + { + /// + /// Managed entry point shim, which starts the actual program + /// + 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 }); + } + } +} diff --git a/docs/host-powershell/sample-dotnet1.1/NuGet.config b/docs/host-powershell/sample-dotnet1.1/NuGet.config new file mode 100644 index 00000000000..58f8d2c9b6d --- /dev/null +++ b/docs/host-powershell/sample-dotnet1.1/NuGet.config @@ -0,0 +1,8 @@ + + + + + + + + From 9d191388eee3b5e9fc8674a00eaeb7d83297e56f Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 23 Mar 2017 22:30:36 -0700 Subject: [PATCH 2/5] fix spelling error --- .spelling | 2 ++ docs/host-powershell/README.md | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.spelling b/.spelling index 8ad6cc803f9..f2aa4f867b4 100644 --- a/.spelling +++ b/.spelling @@ -749,3 +749,5 @@ Coveralls.net powershell.version csmacnz Coveralls.exe + - docs/host-powershell/README.md +sample-dotnet1 diff --git a/docs/host-powershell/README.md b/docs/host-powershell/README.md index d86c87181e8..c0db6f29eb4 100644 --- a/docs/host-powershell/README.md +++ b/docs/host-powershell/README.md @@ -1,7 +1,7 @@ # Host PowerShell Core in .NET Core Applications > This documentation is based on PowerShell Core packages built against .NET Core 1.1 and prior. -> Things may change after PowerShell Core starts to build against .NET Core 2.0. +> Things may change after we move to .NET Core 2.0. ## PowerShell Core targeting .NET Core 1.1 and Prior @@ -55,7 +55,7 @@ public class Program ``` 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, where `'Main'` method resides, is loaded in the default load context, +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, From b2b929ad8ae17b368a1e3b46325f1e151a78022d Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 24 Mar 2017 11:03:50 -0700 Subject: [PATCH 3/5] Address review comments --- docs/host-powershell/README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/host-powershell/README.md b/docs/host-powershell/README.md index c0db6f29eb4..6b29325180a 100644 --- a/docs/host-powershell/README.md +++ b/docs/host-powershell/README.md @@ -8,32 +8,32 @@ ### 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` in order to do tasks like type resolution. +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: -- For `SetPowerShellAssemblyLoadContext`, -it's designed to be used by a native host whose TPA (Trusted Platform Assemblies) list doesn't include powershell assemblies, +- `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. -- For `InitializeAndCallEntryMethod`, -it's designed to be used with `dotnet.exe` where the TPA list includes powershell 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. -### Example +### Comparison - Hosting Windows PowerShell vs. Hosting PowerShell Core -The following code is how you host Windows PowerShell in an application. -It's straight forward that you can directly run your business logic code from the application entry point `'Main'` method. +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 @@ -122,6 +122,8 @@ namespace Application.Test } ``` +### .NET Core Sample Application + You can find the sample application project `"MyApp"` under [sample-dotnet1.1](./sample-dotnet1.1). To build the sample project, run the following commands: @@ -145,5 +147,6 @@ System.Management.Automation.ActionPreference System.Management.Automation.AliasAttribute ``` +[CorePsAssemblyLoadContext.cs]: https://github.com/PowerShell/PowerShell/blob/master/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/master/src/powershell/Program.cs#L27 \ No newline at end of file From 56e576bac3f9b344d62786c8b4a14044891226af Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 24 Mar 2017 11:52:31 -0700 Subject: [PATCH 4/5] Update .spelling again --- .spelling | 1 + 1 file changed, 1 insertion(+) diff --git a/.spelling b/.spelling index f2aa4f867b4..1c82b2b6aa7 100644 --- a/.spelling +++ b/.spelling @@ -751,3 +751,4 @@ csmacnz Coveralls.exe - docs/host-powershell/README.md sample-dotnet1 +CorePsAssemblyLoadContext.cs From 9bc47824a47d5cee65775445c0013281e18514cd Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 24 Mar 2017 16:10:58 -0700 Subject: [PATCH 5/5] Address new comments --- docs/cmdlet-example/README.md | 129 --------------------------------- docs/host-powershell/README.md | 28 ++++--- 2 files changed, 17 insertions(+), 140 deletions(-) delete mode 100644 docs/cmdlet-example/README.md diff --git a/docs/cmdlet-example/README.md b/docs/cmdlet-example/README.md deleted file mode 100644 index 5648e384dc7..00000000000 --- a/docs/cmdlet-example/README.md +++ /dev/null @@ -1,129 +0,0 @@ -Building a C# Cmdlet -==================== - -This example project demonstrates how to build your own C# cmdlet for PowerShell. -When built in the following manner, the resulting DLL can be imported everywhere: -Windows PowerShell with Desktop .NET (FullCLR) and PowerShell on Windows, Linux, and macOS with .NET Core (CoreCLR). - -Setup ------ - -We use the [.NET Command-Line Interface][dotnet-cli] (`dotnet`) to build the cmdlet library. -Install the `dotnet` tool and ensure `dotnet --version` is at least `1.0.0-rc2`. - -.NET CLI uses a `project.json` file for build specifications: - -```json -{ - "name": "SendGreeting", - "description": "Example C# Cmdlet project", - "version": "1.0.0-*", - - "dependencies": { - "Microsoft.PowerShell.5.ReferenceAssemblies": "1.0.0-*" - }, - - "frameworks": { - "netstandard1.3": { - "imports": [ "net40" ], - "dependencies": { - "Microsoft.NETCore": "5.0.1-*", - "Microsoft.NETCore.Portable.Compatibility": "1.0.1-*" - } - } - } -} -``` - -Note that no source files are specified. -.NET CLI automatically will build all `.cs` files in the project directory. - -Going through this step-by-step: - -- `"name": "SendGreeting"`: Name of the assembly to output (otherwise it defaults to the name of the containing folder). - -- `"version": "1.0.0-*"`: The wild-card can be replaced using the `--version-suffix` flag to `dotnet build`. - -- [Microsoft.PowerShell.5.ReferenceAssemblies][powershell]: Contains the SDK reference assemblies for PowerShell version 5. - Targets the `net40` framework. - -- [netstandard1.3][]: The target framework for .NET Core portable libraries. - This is an abstract framework that will work anywhere its dependencies work. - Specifically, the 1.3 version allows this assembly to work even on Windows PowerShell with Desktop .NET. - -- `"imports": [ "net4" ]`: Since the PowerShell reference assemblies target the older `net40` framework, - we `import` it here to tell `dotnet restore` that we know we're loading a possibly-incompatible package. - -- [Microsoft.NETCore][netcore]: Provides a set of packages that can be used when building portable - libraries on .NETCore-based platforms. - -- [Microsoft.NETCore.Portable.Compatibility][portable]: Enables compatibility - with portable libraries targeting previous .NET releases like .NET Framework 4.0. - Required to build against the PowerShell reference assemblies package. - -Other dependencies can be added as needed; -refer to the [.NET Core package gallery][myget] for package availability, name, and version information. - -Because the .NET Core packages are not yet released to NuGet.org, -you also need this `NuGet.config` file to setup the [.NET Core MyGet feed][myget]: - -```xml - - - - - - - - -``` - -[dotnet-cli]: https://github.com/dotnet/cli#new-to-net-cli -[powershell]: https://www.nuget.org/packages/Microsoft.PowerShell.5.ReferenceAssemblies -[netstandard1.3]: https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-standard-applications.md -[netcore]: https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.NETCore -[portable]: https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.NETCore.Portable.Compatibility -[myget]: https://dotnet.myget.org/gallery/dotnet-core - -Building --------- - -.NET Core is a package-based platform, so the correct dependencies first need to be resolved: - -``` -dotnet restore -``` - -This reads the `project.json` and `NuGet.config` files and uses NuGet to restore the necessary packages. -The generated `project.lock.json` lockfile contains the resolved dependency graph. - -Once packages are restored, building is simple: - -``` -dotnet build -``` - -This will produce the assembly `./bin/Debug/netstandard1.3/SendGreeting.dll`. - -This build/restore process should work anywhere .NET Core works, including Windows, Linux, and macOS. - -Deployment ----------- - -In PowerShell, check `$env:PSModulePath` and install the new cmdlet in its own -module folder, such as, on Linux, -`~/.powershell/Modules/SendGreeting/SendGreeting.dll`. - -Then import and use the module: - -```powershell -> Import-Module SendGreeting # Module names are case-sensitive on Linux -> Send-Greeting -Name World -Hello World! -``` - -You can also import by the path: - -```powershell -> Import-Module ./bin/Debug/netstandard1.3/SendGreeting.dll -``` diff --git a/docs/host-powershell/README.md b/docs/host-powershell/README.md index 6b29325180a..0b76917ca5f 100644 --- a/docs/host-powershell/README.md +++ b/docs/host-powershell/README.md @@ -16,14 +16,14 @@ So applications that want to host PowerShell Core (using PowerShell APIs) need t 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, +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. +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][]. @@ -33,7 +33,7 @@ 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. +As shown below, you can insert your business logic code directly in the `Main` method. ```CSharp // MyApp.exe @@ -54,14 +54,14 @@ public class Program } ``` -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, +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. +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: @@ -79,7 +79,7 @@ namespace Application.Test /// public static int Main(string[] args) { - // Application needs to use PowerShell AssemblyLoadContext if it needs to create powershell runspace + // 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); @@ -125,7 +125,7 @@ namespace Application.Test ### .NET Core Sample Application You can find the sample application project `"MyApp"` under [sample-dotnet1.1](./sample-dotnet1.1). -To build the sample project, run the following commands: +To build the sample project, run the following commands ([.NET Core SDK 1.0.1](https://github.com/dotnet/cli/releases/tag/v1.0.1) is required): ```powershell dotnet restore .\MyApp\MyApp.csproj @@ -147,6 +147,12 @@ System.Management.Automation.ActionPreference System.Management.Automation.AliasAttribute ``` +### Remaining Issue + +PowerShell Core builds separately for Windows and Unix, so the assemblies are different between Windows and Unix platforms. +Unfortunately, all PowerShell NuGet packages that have been published so far only contain PowerShell assemblies built specifically for Windows. +The issue [#3417](https://github.com/PowerShell/PowerShell/issues/3417) was opened to track publishing PowerShell NuGet packages for Unix platforms. + [CorePsAssemblyLoadContext.cs]: https://github.com/PowerShell/PowerShell/blob/master/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/master/src/powershell/Program.cs#L27 \ No newline at end of file