Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConsoleApp hosting PowerShell fails when published as single file #13540

Closed
stwehrli opened this issue Aug 29, 2020 · 37 comments
Closed

ConsoleApp hosting PowerShell fails when published as single file #13540

stwehrli opened this issue Aug 29, 2020 · 37 comments
Labels
Committee-Reviewed PS-Committee has reviewed this and made a decision Needs-Triage The issue is new and needs to be triaged by a work group. Resolution-No Activity Issue has had no activity for 6 months or more WG-DevEx-SDK hosting PowerShell as a runtime, PowerShell's APIs, PowerShell Standard, or development of modules

Comments

@stwehrli
Copy link

Probably the wrong repo to report this and most likely I did something wrong. I get an exception when I try to create a runspace in a console application published as a single file. Runs fine when published normally.

Steps to reproduce

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.PowerShell.SDK" Version="7.1.0-preview.6" />
  </ItemGroup>
</Project>
using System;
using System.Management.Automation.Runspaces;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var rs = RunspaceFactory.CreateRunspace();
            Console.WriteLine("Hello World!");
        }
    }
}
dotnet publish -r win-x64 --self-contained false /p:PublishSingleFile=true

Expected behavior

Hello World

Actual behavior

Unhandled exception. System.TypeInitializationException: The type initializer for 'System.Management.Automation.PSVersionInfo' threw an exception.
 ---> System.ArgumentException: The path is empty. (Parameter 'path')
   at System.IO.Path.GetFullPath(String path)
   at System.Diagnostics.FileVersionInfo.GetVersionInfo(String fileName)
   at System.Management.Automation.PSVersionInfo..cctor()
   --- End of inner exception stack trace ---
   at System.Management.Automation.PSVersionInfo.get_PSVersion()
   at Microsoft.PowerShell.DefaultHost..ctor(CultureInfo currentCulture, CultureInfo currentUICulture)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace()
   at ConsoleApp1.Program.Main(String[] args)

Environment data

dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.100-preview.8.20417.9
 Commit:    fc62663a35

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19041
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.100-preview.8.20417.9\

Host (useful for support):
  Version: 5.0.0-preview.8.20407.11
  Commit:  bf456654f9

.NET SDKs installed:
  5.0.100-preview.8.20417.9 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.NETCore.App 5.0.0-preview.8.20407.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 5.0.0-preview.8.20411.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
@stwehrli stwehrli added the Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a label Aug 29, 2020
@iSazonov
Copy link
Collaborator

I tried to compile PowerShell to single file a month ago with the same result.
I guess the exception comes from follow line:

string assemblyPath = typeof(PSVersionInfo).Assembly.Location;

I found 29 such patterns in PowerShell code. And I don't know whether the exception is a .Net bug or we should use another pattern/workaround.

I think the scenario (single file) has a right to exist and we could support it in next milestone.

It is a question for PowerShell Committee.

/cc @SteveL-MSFT @daxian-dbw

@iSazonov iSazonov added Review - Committee The PR/Issue needs a review from the PowerShell Committee Area-Maintainers-Build specific to affecting the build labels Aug 29, 2020
@fMichaleczek
Copy link

@iSazonov same problem occurs when trying to publish a Blazor webassembly with SMA. (Not the only one).

@stwehrli
Copy link
Author

stwehrli commented Aug 31, 2020

My initial report is not entirely correct. I claimed that a normal publish works. That's not true, at least not on my box. It works fine with a simple build in release mode from Visual Studio 2019 Preview.

dotnet publish -c release

However, as soon as I publish the solution with a runtime identifier (which is required for single file publish), the binary explodes upon startup. Publishing to win-x64 does not work.

dotnet publish -c release -r win-x64

@daxian-dbw daxian-dbw removed the Review - Committee The PR/Issue needs a review from the PowerShell Committee label Aug 31, 2020
@daxian-dbw
Copy link
Member

@iSazonov We need to first understand what is the problem before having the committee involved.

@iSazonov
Copy link
Collaborator

iSazonov commented Sep 1, 2020

@daxian-dbw My question is does PowerShell team want to support single file build scenario? If yes we can start to investigate problems in implementation of the scenario.

@fMichaleczek
Copy link

API incompatibility :

API Note
Assembly.Location Returns an empty string.
Module.FullyQualifiedName Returns a string with the value of  or throws an exception.
Module.Name Returns a string with the value of .
Assembly.GetFile Throws IOException.
Assembly.GetFiles Throws IOException.
Assembly.CodeBase Throws PlatformNotSupportedException.
Assembly.EscapedCodeBase Throws PlatformNotSupportedException.
AssemblyName.CodeBase Returns null.
AssemblyName.EscapedCodeBase Returns null.

Recommendations :

Action Recommendation
To access files next to the executable use AppContext.BaseDirectory.
To find the file name of the executable use the first element of Environment.GetCommandLineArgs().
To avoid shipping loose files entirely consider using embedded resources.

Other considerations

  • Single-file doesn't bundle native libraries by default
  • There is an option to set a flag, IncludeNativeLibrariesForSelfExtract, to include native libraries in the single file bundle, but these files will be extracted to a temporary directory in the client machine when the single file application is run
  • Managed C++ components aren't well suited for single-file deployment and we recommend that you write applications in C# or another non-managed C++ language to be single-file compatible.

Source : Single file deployment and API incompatibility

@fMichaleczek
Copy link

Actual behaviour :

Unhandled exception. System.TypeInitializationException: The type initializer for 'System.Management.Automation.PSVersionInfo' threw an exception.
 ---> System.ArgumentException: The path is empty. (Parameter 'path')
   at System.IO.Path.GetFullPath(String path)
   at System.Diagnostics.FileVersionInfo.GetVersionInfo(String fileName)
   at System.Management.Automation.PSVersionInfo..cctor()
   --- End of inner exception stack trace ---
   at System.Management.Automation.PSVersionInfo.get_PSVersion()
   at Microsoft.PowerShell.DefaultHost..ctor(CultureInfo currentCulture, CultureInfo currentUICulture)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace()
   at ConsoleApp1.Program.Main(String[] args)

string assemblyPath = typeof(PSVersionInfo).Assembly.Location;
string productVersion = FileVersionInfo.GetVersionInfo(assemblyPath).ProductVersion;

should be replaced by a safer way to get the product info (from the custom attributes of the assembly)

string productVersion = typeof(PSVersionInfo).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion.Trim();

@ptupitsyn
Copy link

Any update on this issue? Seems to be a trivial bug to fix.

Same here, both Windows and Linux. Powershell does not work from single-file apps. Tried the latest preview NuGet as well (7.2.0-preview.1).

System.TypeInitializationException: The type initializer for 'System.Management.Automation.PSVersionInfo' threw an exception.
 ---> System.ArgumentException: The path is empty. (Parameter 'path')
   at System.IO.Path.GetFullPath(String path)
   at System.Diagnostics.FileVersionInfo.GetVersionInfo(String fileName)
   at System.Management.Automation.PSVersionInfo..cctor()
   --- End of inner exception stack trace ---
   at System.Management.Automation.PSVersionInfo.get_PSVersion()
   at Microsoft.PowerShell.DefaultHost..ctor(CultureInfo currentCulture, CultureInfo currentUICulture)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace()
   at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync)
   at System.Management.Automation.PowerShell.CoreInvokeHelper[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.CoreInvoke[TOutput](IEnumerable input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.Invoke[T](IEnumerable input, IList`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.Invoke[T]()
   at CodeWeTrust.Core.Scripting.PowerShellService.ExecInternal[T](String script, Action`2 logAction, Boolean ignoreStderr, ValueTuple`2[] parameters)
   at CodeWeTrust.Core.Scripting.PowerShellService.Exec[T](String script, Action`2 logAction, Boolean ignoreStderr, ValueTuple`2[] parameters)
   at CodeWeTrust.Core.Cloc.ClocService.ExecuteCloc(String repoPath)
   at CodeWeTrust.Core.Cloc.ClocJob.RunAsync(PipelineContext context)
   at CodeWeTrust.Core.Pipeline.PipelineRunner.RunJobAsync(IPipelineJob job, PipelineContext context, IPipelineProgress progress, IReadOnlyCollection`1 jobsToRun)

@fMichaleczek
Copy link

@ptupitsyn no update at this moment. I am working to submit a PR for Wasm. I'm not sure if it will cover everything need for single file app. Do you want to make a contribution ?

@iSazonov
Copy link
Collaborator

iSazonov commented Dec 1, 2020

Any update on this issue? Seems to be a trivial bug to fix.

You could search by Regex "Assembly.*.Location" to find all such patterns we would have to fix.

@ptupitsyn
Copy link

Do you want to make a contribution

@fMichaleczek had a closer look at the code - looks like I underestimated the fix, there are multiple places that need updating, including native libraries handling. Not considering a contribution right now, sorry :(

@iSazonov
Copy link
Collaborator

iSazonov commented Dec 1, 2020

We could consider using new .Net feature - Source Generators. Today they are still not ready for production but .Net team active works on them at .Net 6.0 milestone.

@fMichaleczek
Copy link

fMichaleczek commented Dec 1, 2020

@ptupitsyn With single file on Windows, you will have 1+4 files (coreclr.dll, clrjit.dll, clrcompression.dll, mscordaccore.dll). I try to build a true single file (a sample console app) with extract option but it was not working on my test.
https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/design.md#user-experience

GitHub
This repo is used for reviewing new .NET designs. Contribute to dotnet/designs development by creating an account on GitHub.

@iSazonov iSazonov added the WG-Engine core PowerShell engine, interpreter, and runtime label Jan 18, 2021
@ALIENQuake
Copy link

I'm very interested in having this feature.

@fMichaleczek Hi,

The 'true single file' feature (without fallback to IncludeNativeLibrariesForSelfExtract) depends on .NET Core 'SuperHost' feature. For Net Core version 5.0 this feature works only for executables that target Linux. But fear not, 'SuperHost' is coming to Windows and macOS and it's tracked by Net 6.0 here: dotnet/runtime#43071.

@NN---
Copy link

NN--- commented Mar 15, 2021

@stwehrli You should use win7-x64 instead of win-x64, otherwise it doesn't have Microsoft.Management.Infrastructure.

dotnet publish -r win7-x64 --self-contained false /p:PublishSingleFile=true

It doesn't solve the issue with .NET 5.0

@goradata
Copy link

Hi,

It seems that we have a workaround for this issue :) .net team has provided flag which uses the same extraction method as .net 3.1. If you publish the single executable containing Powershell host on Windows, It looks like Powershell (7.1.3) works ok with this flag set to true. Could somebody verify that this is indeed a usable solution until .net 6.

@fMichaleczek
Copy link

fMichaleczek commented Mar 27, 2021

Hi,

It seems that we have a workaround for this issue :) .net team has provided flag which uses the same extraction method as .net 3.1. If you publish the single executable containing Powershell host on Windows, It looks like Powershell (7.1.3) works ok with this flag set to true. Could somebody verify that this is indeed a usable solution until .net 6.

Please provide reproductible instructions for help people to try and test.

What I had try on net 5.0 was wait until net6.0 for advanced scenario on all hosts. So we are near to try testing for 7.2 branch but keep in mind, dotnet team has long list of scenarios and PowerShell is the more complicated, so don't try to wait a solution from them at the first iteration.

The PowerShell Team has more importants priorities and for community, I would say we have to wait dotnet improvements, @SteveL-MSFT is a gateway with the dotnet Team but the door is big and it's only a small subset of changes about hosting after net5.0.

If someone want to help, a simple repo with all dotnet versions with all OS version with a specific set for test new dotnet feature should be a welcome point to continue this thread.

I'm waiting to find time to read dotnet specifications about net6.0 hosting, everything is connected because most of the changes are driven by a mix of AspNet, Mono and Windows UI Teams, and I have to say this is the part who was never finished about Monad Manifesto.

We can do it, but we need cooperation between us because Cloud is the Microsoft's first priority, not this kind of enhancement at short time.

@BobbyCannon
Copy link

I think I may have duplicated this here. #15382

@daxian-dbw daxian-dbw added Needs-Triage The issue is new and needs to be triaged by a work group. and removed Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Area-Maintainers-Build specific to affecting the build labels May 13, 2021
@TravisEz13 TravisEz13 removed the Issue-Enhancement the issue is more of a feature request than a bug label May 28, 2021
@ALIENQuake
Copy link

When it comes to 'PS-based' single executable tools, the advantages of the "single file" approach are that the executable bounds NET version and PSVersion so the produced tool will work reliably regardless of changes in NET/PowerShell.

There are at least 3 tier levels of supporting single file scenarios:

tier 1 - The produced executable won't crash
tier 2 - Make Start-Job command being able to use produced executable: #11968
tier 3 - Make build-in modules to be merged inside produced executable, I don't know if it's even possible

Even achieving tier 1 would be wonderful.

@daxian-dbw Any chance to review this by Committee and/or discuss this on the upcoming Jan 20th, 2022 Community Call?

@daxian-dbw daxian-dbw added the Review - Committee The PR/Issue needs a review from the PowerShell Committee label Jan 13, 2022
@daxian-dbw
Copy link
Member

@ALIENQuake Feel free to add your question about this to PowerShell/PowerShell-RFC#310.
Also add the label for committee to review. The question for review is called out here: #13540 (comment).

@iSazonov
Copy link
Collaborator

The feature requires some code refactoring. This work is blocked, as always, by a lack of reviews and feedback. Ex. #15603.

@SteveL-MSFT
Copy link
Member

One of the problems I've hit when also trying to compile PS7 to single exe is debugging didn't work (at least using VSCode).

@SteveL-MSFT SteveL-MSFT added Committee-Reviewed PS-Committee has reviewed this and made a decision and removed Review - Committee The PR/Issue needs a review from the PowerShell Committee labels Jan 19, 2022
@SteveL-MSFT
Copy link
Member

@PowerShell/powershell-committee reviewed this, we agreed that this is an issue that should be fixed to support community single exe projects that host PowerShell. Whether we ship a single exe package is a separate decision (note that Windows, as noted above, is not a SINGLE exe at this time anyways). However, as this is not a priority for the team, we would encourage the community to contribute a PR, but we would have team members spend time reviewing such a PR.

@iSazonov
Copy link
Collaborator

Ok, I marked #15603 as Contribute to #13540 - welcome to review!

@ALIENQuake
Copy link

ALIENQuake commented Jan 20, 2022

@SteveL-MSFT
That's wonderful! Thank you!

One of the problems I've hit when also trying to compile PS7 to single exe is debugging didn't work (at least using VSCode).

Debugging single-file apps require an extra step:

On Windows and Mac, Visual Studio and VS Code can be used to debug crash dumps. Attaching to a running self-contained single-file executable requires an extra file: mscordbi.{dll,so}.
...
if one were to publish a self-contained single-file executable using the dotnet CLI for Windows using the parameters -r win-x64, the executable would be placed in bin/Debug/net6.0/win-x64/publish. A copy of mscordbi.dll would be present in bin/Debug/net6.0/win-x64.

https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file#attaching-a-debugger

Learn what single file application is and why you should consider using this application deployment model.
Future improvements are tracked by https://github.com/dotnet/runtime/issues/45382

@scottpaist
Copy link

Any update on this one? Is there a workaround?

@BartoszRojek
Copy link

@scottpaist Hi, If you can use your own compiled PowerShell dll files instead of the nuget SDK then you can apply this patch:
BartoszRojek@e957b17

@SCLD-JMcCoard
Copy link

SCLD-JMcCoard commented Jun 7, 2022

My team was able to move past this by upgrading from net5.0 to net6.0 and upgrading our System.Management.Automation from 7.1.3 to 7.2.4.

We have a multi-project solution. The proj with the PS features is a library. The console exe is a separate project referenced that library.

The error message we were getting with single exe and self contained with runtime win10-x64 and net5.0.:

The type initializer for 'System.Management.Automation.PSVersionInfo' threw an exception. --    at System.Management.Automation.PSVersionInfo.get_PSVersion()
   at System.Management.Automation.RemoteRunspace..ctor(TypeTable typeTable, RunspaceConnectionInfo connectionInfo, PSHost host, PSPrimitiveDictionary applicationArguments, String name, Int32 id)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(RunspaceConnectionInfo connectionInfo, PSHost host, TypeTable typeTable, PSPrimitiveDictionary applicationArguments, String name)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(RunspaceConnectionInfo connectionInfo, PSHost host, TypeTable typeTable)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(PSHost host, RunspaceConnectionInfo connectionInfo)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(RunspaceConnectionInfo connectionInfo)

After upgrading we saw the following error message (in our prod machines):

The type initializer for 'System.Management.Automation.ExperimentalFeature' threw an exception.

Adding code to set the TrustedHosts got us past the above error oddly enough:

Set-Item WSMan:\localhost\client\trustedhosts "{targetIP}" -Concatenate -Force

@JBalanza
Copy link

Is there already an official solution to this issue?

Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

1 similar comment
Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

@microsoft-github-policy-service microsoft-github-policy-service bot added the Resolution-No Activity Issue has had no activity for 6 months or more label Nov 16, 2023
Copy link
Contributor

This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

@RobinGFT
Copy link

RobinGFT commented May 14, 2024

Push!
On year later this issue is still present with .NET 8 and PowerShell.SDK 7.4.2.
As soon, as I want to build as single file, the appication doesn't run anymore and throws following error at the first execution of a powershell script:

ERROR: 2024-05-14T12:33:51.8318820Z - Value cannot be null. (Parameter 'path1')
   at System.ArgumentNullException.Throw(String paramName)
   at System.IO.Path.Combine(String path1, String path2)
   at System.Management.Automation.PSSnapInReader.ReadEnginePSSnapIns()
   at System.Management.Automation.Runspaces.InitialSessionState.CreateDefault()
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(PSHost host)
   at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace()
   at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync)
   at System.Management.Automation.PowerShell.CoreInvokeHelper[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.CoreInvoke[TOutput](IEnumerable input, PSDataCollection`1 output, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.Invoke(IEnumerable input, PSInvocationSettings settings)
   at System.Management.Automation.PowerShell.Invoke()

I fiddled together a custom installer, with a lot of powershell scripts and a lot of dependencies. In normal mode the project is building such a mess on files, that it is hard to find the executable.
Will Microsoft provide a fix?
With .NET and PowerShell sharing Microsoft as their mother, I didn't expect to run into such a trouble...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committee-Reviewed PS-Committee has reviewed this and made a decision Needs-Triage The issue is new and needs to be triaged by a work group. Resolution-No Activity Issue has had no activity for 6 months or more WG-DevEx-SDK hosting PowerShell as a runtime, PowerShell's APIs, PowerShell Standard, or development of modules
Projects
None yet
Development

No branches or pull requests