Skip to content

Conversation

@daxian-dbw
Copy link
Member

Fix #2291
Add documentation about how to host PowerShell Core in .NET Core application.

@daviwil
Copy link
Contributor

daviwil commented Mar 24, 2017

This is really cool!

Quick question: does this tie the application to a specific version of PowerShell Core? Specifically, if I build against Microsoft.PowerShell.SDK of a specific version, is there any way I could get my application to load assemblies from a newer version of PowerShell Core if I have its filesystem path?

I'm asking because I'm re-evaluating whether I could make PowerShell Editor Services a standalone process again, not a module running inside of powershell.exe.

@daxian-dbw
Copy link
Member Author

daxian-dbw commented Mar 24, 2017

@daviwil When you build your application this way, a specific version (depending on the package version you use) of PowerShell Core assemblies will be published to your publish folder. But your application is not necessarily tied to them. You can just replace them with a newer version of PS Core assemblies, and your application should continue to work as long as the new PS Core assemblies are targeting the same set of .NET Core packages.

There is one problem, though -- we haven't published any powershell core packages for Linux/OSX yet.
PowerShell Core builds separately for Windows and Unix, so the assemblies are different between Windows and Unix platforms. All PowerShell Core packages we published so far contain windows assemblies only ☹️.

There is a workaround. Build your application using the existing powershell core NuGet packages, but replace the powershell assemblies with those from a Linux/OSX package. That hopefully would work.

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clarify why "netstandard1.6", not "netcoreapp1.1" as in docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Becuase Logic.dll is an assembly Library while MyApp.exe is an executable. If you inspect the dependencies of Microsoft.NetCore.App, you will see that it depends on NetStandard.Library and also include the runtime and host related packages.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

### 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a link to the file here?

`SetPowerShellAssemblyLoadContext` and `InitializeAndCallEntryMethod`.
They are for different scenarios:

- For `SetPowerShellAssemblyLoadContext`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see the topic set apart from the rest of the sentence to make it a little more obvious that it is the topic of the bullet point. Something like:

  • SetPowerShellAssemblyLoadContext - It is designed to ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TPA should be in parenthesis and follow --> Trusted Platform Assemblies (TPA)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Links to additional background information would be helpful as well. Did the .Net team write up any documentation on consuming these two APIs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We own those 2 APIs. They are exposed from PowerShellAssemblyLoadContextInitializer.


### Example

The following code is how you host Windows PowerShell in an application.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested rewording:
This example demonstrates how to host Windows PowerShell in an application. Business logic should be inserted within the Main method.

Another thought: What about linking to the MSDN documentation for this? It is a supported scenario for Windows PowerShell.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't intend to introduce how to host Windows PowerShell in full .NET application.
The code about hosting Windows PowerShell is used as a comparison to show what you need to do in .NET Core for the same purpose.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I reworded it as you suggested.

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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested rewording:
The following example demonstrates hosting PowerShell Core in a .Net Core Application:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. The code about hosting windows powershell is used as a comparison to show how this familiar code should be altered when it comes to .NET Core 1.1.

This documentation only covers the `InitializeAndCallEntryMethod` API,
as it's what you need when building a .NET Core application with .NET CLI.

### Example
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be three sub-headings here:

  1. Hosting Windows PowerShell
  2. Hosting PowerShell in .Net Core
  3. Example .Net Core program

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@daviwil
Copy link
Contributor

daviwil commented Mar 24, 2017

@daxian-dbw thanks for the explanation! Seems like it would be more complex go have my own process hosting PowerShell so I think I'll stick with my current model of being hosted inside of powershell.exe :)

@daxian-dbw
Copy link
Member Author

@mirichmo I addressed your comments. Could you please take another look?

Copy link
Contributor

@joeyaiello joeyaiello left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few small changes, otherwise looks really useful to me.

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properly case "PowerShell"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properly case "PowerShell"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

### 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason to use both backticks and single quotes. Backticks will do here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}
```

However, when it comes to hosting PowerShell Core, there will be a layer of redirection for the powershell load context to take effect.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"PowerShell" casing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

```

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No single quotes needed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PowerShell casing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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'`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No single quotes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No single quotes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

```powershell
dotnet restore .\MyApp\MyApp.csproj
dotnet publish .\MyApp -c release -r win10-x64
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this document assumed that the user has installed dotnet-cli? Might it be worth putting a pointer to environment instructions (preferably not owned by us) at the top of the document?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is this the same on Linux with a different RID? Might that be worth calling out?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, as discussed above, all powershell NuGet packages that have been published so far only contain powershell assemblies built specifically for Windows. We need to publish NuGet packages that contain both Windows and Unix assemblies, but dotnet pack doesn't seem to support that.

#3417 is opened to track this.

@daxian-dbw
Copy link
Member Author

@joeyaiello @mirichmo All comments have been addressed. Can you please take another look?

Copy link
Contributor

@joeyaiello joeyaiello left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@daxian-dbw daxian-dbw dismissed mirichmo’s stale review March 25, 2017 00:29

New commits have been pushed to address the comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants