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

ijwhost.dll fails to load the .net8.0 runtime in Powershell 7.5 on Windows when using C++/Cli #112402

Open
GeraldW1 opened this issue Feb 11, 2025 · 5 comments
Labels
area-Host needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners untriaged New issue has not been triaged by the area owner

Comments

@GeraldW1
Copy link

Description

Powershell 7.5 (uses .net9.0 runtime) is unable to run a Commandlet in some cases if the CommandLet is implemented using C#, C++ and C++/Cli using .net8.0. If I execute the same commandlet with Powershell 7.4 - which uses .net8.0 runtime - all is fine.

Reproduction Steps

I have attached a Visual Studio Solution for reproducing the issue

  • Unzip the attached PowershellCommandLet.zip
  • Open Powershell 7.5
  • Open the the folder "Powershell\PowershellCmdlet\bin\Debug\net8.0-windows7.0" in Powershell 7.5. In this folder you will find the already compiled Powershell module PowershellCmdlet.dll and its two assemblies it depends on.
  • Import the module with "Import-Module -Name ".\PowershellCmdlet.dll""
  • Execute the Commandlet with "Add-TestCmdlet"
  • The Commandlet has some output. But at the end there is thrown an exception: Add-TestCmdlet: External component has thrown an exception.

Projects in the attached Visual Studio Solution

PowershellCmdlet.dll:

  • Programming language: C#
  • Targetframework: net8.0-windows7.0
  • Implements a simple Powershell Cmdlet named Add-TestCmdlet. The Cmdlet calls ManagedClass.Test in CPPClr.dll.

CPPClr.dll:

  • Programming language: C++/Cli
  • Targetframework: net8.0
  • ManagedClass.Test calls NativeClass::Test in CPPClrAndNative.dll

CPPClrAndNative.dll:

  • Programming language: C++ and C++/Cli
  • Targetframework (for the C++/Cli part): net8.0
  • NativeClass::Test calls function ClrFunction which calls Console::WriteLine which is managed. When calling ClrFunction the crash occurs!

Expected behavior

The attached Add-TestCmdlet writes some output to the console and is executing without an exception.

Actual behavior

The attached Add-TestCmdlet writes some output to the console but before it finish there is an exception:
Add-TestCmdlet: External component has thrown an exception.

Regression?

We are currently porting a big Visual Studio Solution with about 300 C#, C++ and C++/Cli projects from .net Framework 4.8 to .net8.0. This solution also implements Powershell Commandlets using our code base. Using .net Framework 4.8 the Powershell Commandlets are working as expected. Currently this issue is a real showstopper for the migration to .net8.0.

Known Workarounds

No known workaround unless you use PowerShell 7.4. But this is not an option. We cannot force our customers not to update to PowerShell 7.5 or deinstall Powershell 7.5 and install Powershell 7.4.

Configuration

.NET version:

  • Powershell 7.5 is using .net9.0. The Powershell CommandLet is using .net8.0
  • OS version: Windows 11 Enterprise, 24H2
  • Architecture: x64
  • Do you know whether it is specific to that configuration? I do not think that this issue is specific to this configuration

Other information

I opened an issue in Powershell/Powershell before (PowerShell/PowerShell#24979). But they advised me to open an issue here. I am thinking they are right because it is not an issue regarding Powershell but an issue regarding .net runtime and C++/Cli.

If you attach Visual Studio as debugger to the pwsh.exe and execute the Add-TestCmdlet-Commandlet you will see the following error:
The specified runtimeconfig.json [C:\Users\wg\source\repos\Powershell\PowershellCmdlet\bin\Debug\net8.0-windows7.0\CPPClrAndNative.runtimeconfig.json] does not exist

I think this is another issue regarding .net core and C++/cli. The CPPClrAndNative.runtimeconfig.json is not copied to the output folder of the referencing project.. As a workaround I added already CPPClrAndNative.runtimeconfig.json to PowershellCmdlet and set the property "Copy to Output Directory" to "Copy if newer".

Now to the actual problem:
If you execute Add-TestCmdlet-Commandlet in Powershell again the following Exception occurs:
The specified framework 'Microsoft.NETCore.App', version '8.0.11', apply_patches=1, version_compatibility_range=minor is incompatible with the previously loaded version '9.0.1'.

Where does this Exception occur?

In load_fxr_and_get_delegate in fxr_resolver.h:
Image

As you can see the rc has the value -2147450715. According to https://github.com/dotnet/runtime/blob/main/docs/design/features/host-error-codes.md it means the following:
"Host configuration is incompatible with existing host context - returned by hostfxr_initialize_for_runtime_config. The component being initialized requires framework which is not available or incompatible with the frameworks loaded by the runtime already in the process. For example trying to load a component which requires 3.0 into a process which is already running a 2.0 runtime."

Some more informations

  • If I execute the same commandlet with Powershell 7.4 which uses .net8.0 all is fine.
  • If I execute the same code in a Console application with Targetframework net8.0-windows7.0 all is fine
  • If I execute the same code in a Console application with Targetframework net9.0-windows7.0 the same exception occurs
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Feb 11, 2025
@vitek-karas
Copy link
Member

You need to enable roll forward for the component/plugin. For C# projects we have an MSBuild property, I don't know if this works for C++/CLI projects though. In any case you can modify the .runtimeconfig.json and that should work: https://learn.microsoft.com/en-us/dotnet/core/versions/selection#control-roll-forward-behavior

You need to set it to Major to allow roll forward to a higher major version - the default in Minor and that's why it fails to run component built for 8 on 9 runtime.

@GeraldW1
Copy link
Author

Thank you very much @vitek-karas !

Yes, C++/Cli has this msbuild property:

  <PropertyGroup>
	<RollForward>Major</RollForward>
  </PropertyGroup>

If I add this property to the CPPClrAndNative.vcxproj the value "rollForward": "Major" is automatically added to the runtimeOptions in CPPClrAndNative.runtimeconfig.json. (This file is not copied to the Output folder of the referencing projects - but that is another story.)

Now the attached Powershell Commandlet is working in Powershell 7.5!

So we have the following project "tree":

PowershellCmdlet(C#) --referencing/calling--> CPPClr(C++/Cli) --referencing/calling--> CPPClrAndNative(C++/Cli)

  • PowershellCmdlet does NOT need the RollForward=Major setting
  • CPPClr does NOT need the RollForward=Major setting
  • CPPClrAndNative DOES NEED the RollForward=Major setting

It is a little bit strange for me that only the project CPPClrAndNative needs this setting.

Does that mean, that PowershellCmdlet and CPPClr are using the .net8.0 runtime assemblies but CPPClrAndNative is using the .net9.0 runtime assemblies?

@vitek-karas
Copy link
Member

It is a little bit strange for me that only the project CPPClrAndNative needs this setting.

When you have managed code calling another managed code, the problem doesn't exist - since in such cases the dependency is loaded by the CLR runtime running that managed code caller - so it knows that it needs to load it into itself. This is how any managed library works. Even if the dependency is C++/CLI, if it's called into through managed code, it behaves like a managed assembly (for the runtime).

The difference is between CPPClr -> CPPClrAndNative - that call is native, so the CLR runtime is not aware of it in any way. The dependency (CPPClrAndNative) is loaded via normal OS dependency loading mechanism (LoadLibrary) - and when it starts it says "wait, I have managed code, I need to start CLR runtime" - this step is the same even if it was loaded into a completely native process without any CLR runtime in it. This is the step which has the version requirement - in order to load CLR it needs to know which version to load. In this case, there already is runtime in the process, so the version requirements are compared and if they don't match it fails.

Does that mean, that PowershellCmdlet and CPPClr are using the .net8.0 runtime assemblies but CPPClrAndNative is using the .net9.0 runtime assemblies?

No - everything running on the same CLR runtime - the hosting API prevent you from loading two runtimes into the same process (which would be rather problematic) - so in this case, everything (including Powershell itself) is running on .NET 9.

Copy link
Contributor

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

@GeraldW1
Copy link
Author

Thank you very much @vitek-karas ! I have learned a lot here!

For now we will go this way:

  • Set <RollForward>Major</RollForward> to every c++/cli project
  • Prevent using our Powershell Commandlets with Powershell 7.5/.net9.0 to avoid unforeseen problems with breaking changes from .net8.0 to .net9.0. BUT with an option to bypass this check.
  • In future when we migrate our big solution to .net9.0 or even .net10.0 we will adapt this check accordingly.

It is really not an ideal situation for us but it is no longer a show stopper.

I will close this issue next week if nobody holds me back...

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Host needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners untriaged New issue has not been triaged by the area owner
Projects
Status: No status
Development

No branches or pull requests

3 participants