Hey guys,
In my product team we created a custom powershell-based shell in as a way to controll our cloud service.
We did this based on the ConsoleShell object and customised RunspaceConfiguration.
As a part of moving to .Net Core we started examing the posibillity of moving our shell to powershell core but as we all know RunspaceConfiguration does not exist in the core version and there is no way to pass InitialSessionState to the ConsoleShell.Start() method.
if we could pass a initialSessionState to the Start method that would be great!
Thanks,
Asaf.
@SteveL-MSFT Could you please comment the scenario and feature request? What is recommended way to create custom PowerShell console shell?
It seems that when we removed RunspaceConfiguration, we should have added back an overload to accept InitialSessionState. cc @JamesWTruher
:tada:This issue was addressed in #9802, which has now been successfully released as v7.0.0-preview.2
.:tada:
Handy links:
@asrosent @SteveL-MSFT is ConsoleShell
class included in Microsoft.PowerShell.SDK
nuget package? I seem to have hard time finding it with version 6.2.3
As noted above by the bot, the change was made available in v7.0.0-preview.2
-- I expect a new SDK package will be published when 7.0.0
gets a GA release (which if I recall correctly may be sometime in December/January?)
Don't think the PS team tend to publish preview SDKs, historically speaking. 馃檪
Thanks @vexx32 for pointing that out, the related PR adds back taking InitialSessionState
as parameter, but what I'm experiencing is I couldn't even find the ConsoleShell
class. But before the PR, the class still exists publicly, so I was thinking if I'm looking at the wrong package...
Oh, that's a good point, my apologies! Not sure... have to defer to @SteveL-MSFT on that point. 馃檪
The Microsoft.PowerShell.ConsoleShell
class has always been there. The only change was to add back an overload:
PS> [Microsoft.PowerShell.ConsoleShell]::Start
OverloadDefinitions
-------------------
static int Start(string bannerText, string helpText, string[] args)
static int Start(initialsessionstate initialSessionState, string bannerText, string helpText, string[] args)
@SteveL-MSFT that's what I thought, but I couldn't find it in PowerShell Core:
https://docs.microsoft.com/en-us/dotnet/api/?view=pscore-6.2.0&term=ConsoleShell
Is it part of the Microsoft.PowerShell.Sdk
package?
https://www.nuget.org/packages/Microsoft.PowerShell.SDK/
Not sure why it's missing from that API browser thought... weird. Pretty sure it should be in the SDK? Might be a docs issue, potentially.
This class provides an entry point which is called by minishell's main to transfer control to Msh console host implementation.
@vexx32 you referred to Windows PowerShell while I'm seeking it in PowerShell Core 馃槂
Yeah, I'm wondering if the API docs aren't entirely up to date. If the PS Core SDK includes the referenced DLL (Microsoft.PowerShell.ConsoleHost.dll) then ConsoleShell should be available.
OK, I think I may get the reason, Microsoft.PowerShell.ConsoleHost.dll
is not included in Microsoft.PowerShell.SDK
, instead it's part of another package Microsoft.PowerShell.ConsoleHost
馃槂
The nuget package contains Microsoft.PowerShell.ConsoleHost.dll
in runtimes for different platforms. @SteveL-MSFT this may sound silly, but how do I reference the class ConsoleShell
in my C# codes?
There might be a problem with the nupkg for ConsoleHost.dll. I tried a simple app and had to directly ref the assembly to get it to work, but still got this warning:
PS> dotnet publish --runtime osx-x64
Microsoft (R) Build Engine version 16.4.0-preview-19502-03+3af680463 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 1.06 sec for /Users/steve/test/consoleshell/consoleshell.csproj.
You are using a preview version of .NET Core. See: https://aka.ms/dotnet-core-preview
CSC : warning CS8012: Referenced assembly 'Microsoft.PowerShell.ConsoleHost, Version=6.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' targets a different processor. [/Users/steve/test/consoleshell/consoleshell.csproj]
consoleshell -> /Users/steve/test/consoleshell/bin/Debug/netcoreapp2.1/osx-x64/consoleshell.dll
consoleshell -> /Users/steve/test/consoleshell/bin/Debug/netcoreapp2.1/osx-x64/publish/
For now, to get unblocked, try referencing the assembly directly:
<Reference Include="Microsoft.PowerShell.ConsoleHost">
<HintPath>/Users/steve/.nuget/packages/microsoft.powershell.consolehost/6.2.3/runtimes/osx/lib/netcoreapp2.1/Microsoft.PowerShell.ConsoleHost.dll</HintPath>
</Reference>
cc @adityapatwardhan
@SteveL-MSFT using your work-around I could get assembly reference in place, however, when the shell starts, I'm seeing following error:
The shell cannot be started. A failure occurred during initialization:
Object reference not set to an instance of an object.
The issue is reproducible with Microsoft.PowerShell.SDK
version 6.2.3
targeting netcoreapp2.1
using Microsoft.PowerShell;
namespace PSCore
{
class Program
{
static void Main(string[] args)
{
ConsoleShell.Start("Hello", "", new string[0] { });
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.3" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.PowerShell.ConsoleHost">
<!-- NETCORE workaround for GitHub issue https://github.com/PowerShell/PowerShell/issues/9734 -->
<HintPath>$(NuGetPackageRoot)\microsoft.powershell.consolehost\6.2.3\runtimes\win-x64\lib\netcoreapp2.1\Microsoft.PowerShell.ConsoleHost.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
The issue seems to be non-existent with version 7.0.0-preview4
forwards. Switching to this version would require us to target .netcoreapp3.0
. Is there any work around to stay at version 6?
Unfortunately, it seems there was a bug in 6.2 that has since been fixed in 7. I don't see any workaround to stay on 6. We are planning to GA PS7 in Jan, so it's not too far off.
@SteveL-MSFT with preview-5, I updated my sample code to
using Microsoft.PowerShell;
using System.Management.Automation.Runspaces;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
InitialSessionState config = InitialSessionState.CreateDefault();
config.StartupScripts.Add("Get-ChildItem");
ConsoleShell.Start(config, "", "", args);
}
}
}
It seems like StartupScripts
are not executed?
@ritchxu StartupScripts
are executed, but the results are not collected and not displayed. The code that executes it is here:
https://github.com/PowerShell/PowerShell/blob/5ad4d2c399317e3b3cca9fbe448c3891ad6c1870/src/System.Management.Automation/engine/InitialSessionState.cs#L2893-L2903
@daxian-dbw Maybe add logging in the code or script block logging already works here?
@daxian-dbw here is my proof of concept code. I was expecting current working directory to become C:\
once the console shell is started. Did I miss anything?
Program.cs:
using Microsoft.PowerShell;
using System.Management.Automation.Runspaces;
namespace Test
{
class Program
{
static void Main(string[] args)
{
InitialSessionState initialSessionState = InitialSessionState.CreateDefault2();
initialSessionState.StartupScripts.Add(@"Set-Location C:\");
ConsoleShell.Start("Hello", "", new string[0] { });
}
}
}
Test.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.0-preview.6" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.PowerShell.ConsoleHost">
<HintPath>$(NuGetPackageRoot)\microsoft.powershell.consolehost\7.0.0-preview.6\runtimes\win\lib\netcoreapp3.1\Microsoft.PowerShell.ConsoleHost.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
@ritchxu initialSessionState.StartupScripts
is for you to run one more more script files (.ps1), not script code directly. Here is the code to process the startup scripts:
https://github.com/PowerShell/PowerShell/blob/5ad4d2c399317e3b3cca9fbe448c3891ad6c1870/src/System.Management.Automation/engine/InitialSessionState.cs#L2858-L2876
This line psToInvoke.AddCommand(new Command(startupScript, false, false));
creates a new command by specifying that it's not a script (the first false
argument).
The name of that property is unfortunately vague 馃槮 The following is an example to use this property:
Program.cs:
```c#
using System;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;
namespace testConsoleRef
{
class Program
{
static void Main(string[] args)
{
InitialSessionState config = InitialSessionState.CreateDefault();
config.ExecutionPolicy = ExecutionPolicy.RemoteSigned;
config.StartupScripts.Add(@"E:arenadotnetAppdotnet3testConsoleRefstartup.ps1");
int ret = ConsoleShell.Start(config, "Hello", "", new string[0]);
Console.WriteLine(ret);
}
}
}
testConsoleRef.csproj:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.0" />
</ItemGroup>
</Project>
Here is the results from execute this sample. The startup.ps1
has cd f:\tmp
in it, and the working directory is changed to that after dotnet run
[E:\arena\dotnetApp\dotnet3\testConsoleRef]
PS:22> cat .\startup.ps1
cd f:\tmp
[E:\arena\dotnetApp\dotnet3\testConsoleRef]
PS:23> dotnet run
Hello
[F:\tmp]
PS:1> $pshome
E:\arena\dotnetApp\dotnet3\testConsoleRef\bin\Debug\netcoreapp3.1\runtimes\win\lib\netcoreapp3.1
[F:\tmp]
PS:2> exit
0
[E:\arena\dotnetApp\dotnet3\testConsoleRef]
PS:24>
You could add a command config.Commands.Add(...)
:tada:This issue was addressed in #11545, which has now been successfully released as v7.0.0-rc.2
.:tada:
Handy links:
Most helpful comment
It seems that when we removed RunspaceConfiguration, we should have added back an overload to accept InitialSessionState. cc @JamesWTruher