Powershell: Appx - Import-Module: Operation is not supported on this platform. (0x80131539)

Created on 9 Jul 2020  路  30Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

Use Powershell 7.1 preview 4 or 5

Import-Module Appx

Expected behavior

Successfully load the module

Actual behavior

ipmo Appx
Import-Module: Operation is not supported on this platform. (0x80131539)

Environment data

Name                           Value
----                           -----
PSVersion                      7.1.0-preview.5
PSEdition                      Core
GitCommitId                    7.1.0-preview.5
OS                             Microsoft Windows 10.0.19041
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

The module can be imported properly in Powershell 7.0.2 or, in Preview 4/5 using -UseWindowsPowerShell.
I was able to load the module till 7.1 Preview 3

Is this an expected breaking change? Asking in case I need to update my scripts to use -UseWindowsPowerShell

Issue-Bug Resolution-External WG-Engine

Most helpful comment

But as @SteveL-MSFT noted here (and @iSazonov below that), if the module author can put em back, than so can PS.

That works because he's referencing the newly defined types from the package. The AppX module directly references WinRT types. This exception is at type load because of how the AssemblyRef is encoded.

My question is whether we need both of those DLLs to get this WinMD support, or if we could ship something lighter that picks up the remaining right stuff off the Windows install.

I'd love to be wrong but I'm pretty sure there's nothing PowerShell can ship to make this work, other than a new version of the AppX module.

All 30 comments

@SteveL-MSFT @joeyaiello this sounds like it could be a consequence of the missing WinRT libraries (https://github.com/PowerShell/PowerShell/issues/13042). Will the Appx module maintainers will want to package the WinRT libs with their module? Even if they do, it will mean anyone using newer versions of PS on older Windows versions will be unable to use the AppX module, since that module is shipped with Windows.

@carlocardella can you provide the output from Get-Error after the failed import as well? Thanks! 馃槉

Here you go:

[92mException             : [0m
    [92mType       : [0mSystem.PlatformNotSupportedException
    [92mTargetSite : [0m
        [92mName          : [0mGetExportedTypes
        [92mDeclaringType : [0mSystem.Reflection.RuntimeAssembly, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
        [92mMemberType    : [0mMethod
        [92mModule        : [0mSystem.Private.CoreLib.dll
    [92mStackTrace : [0m
   at System.Reflection.RuntimeAssembly.GetExportedTypes()
   at System.Reflection.Assembly.get_ExportedTypes()
   at System.Management.Automation.Runspaces.PSSnapInHelpers.GetAssemblyTypes(Assembly assembly, String name)
   at System.Management.Automation.Runspaces.PSSnapInHelpers.AnalyzeModuleAssemblyWithReflection(Assembly assembly, String name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, String helpFile, Dictionary`2& cmdlets, Dictionary`2& aliases, Dictionary`2& providers)
   at System.Management.Automation.Runspaces.PSSnapInHelpers.AnalyzePSSnapInAssembly(Assembly assembly, String name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, Dictionary`2& cmdlets, Dictionary`2& aliases, Dictionary`2& providers, String& helpFile)
   at System.Management.Automation.Runspaces.InitialSessionState.ImportCmdletsFromAssembly(Assembly assembly, PSModuleInfo module)
   at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadBinaryModule(PSModuleInfo parentModule, Boolean trySnapInName, String moduleName, String fileName, Assembly assemblyToLoad, String moduleBase, SessionState ss, ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, String prefix, Boolean loadTypes, Boolean loadFormats, Boolean& found, String shortModuleName, Boolean disableFormatUpdates)
   at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadModuleNamedInManifest(PSModuleInfo parentModule, ModuleSpecification moduleSpecification, String moduleBase, Boolean searchModulePath, String prefix, SessionState ss, ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, Boolean loadTypesFiles, Boolean loadFormatFiles, Object privateData, Boolean& found, String shortModuleName, Nullable`1 manifestLanguageMode)
   at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadModuleManifest(String moduleManifestPath, ExternalScriptInfo manifestScriptInfo, Hashtable data, Hashtable localizedData, ManifestProcessingFlags manifestProcessingFlags, Version minimumVersion, Version maximumVersion, Version requiredVersion, Nullable`1 requiredModuleGuid, ImportModuleOptions& options, Boolean& containedErrors)
   at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadModule(PSModuleInfo parentModule, String fileName, String moduleBase, String prefix, SessionState ss, Object privateData, ImportModuleOptions& options, ManifestProcessingFlags manifestProcessingFlags, Boolean& found, Boolean& moduleFileFound)
   at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadUsingExtensions(PSModuleInfo parentModule, String moduleName, String fileBaseName, String extension, String moduleBase, String prefix, SessionState ss, ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, Boolean& found, Boolean& moduleFileFound)
   at Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadUsingModulePath(PSModuleInfo parentModule, Boolean found, IEnumerable`1 modulePath, String name, SessionState ss, ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, PSModuleInfo& module)
   at Microsoft.PowerShell.Commands.ImportModuleCommand.ImportModule_LocallyViaName(ImportModuleOptions importModuleOptions, String name)
   at Microsoft.PowerShell.Commands.ImportModuleCommand.ImportModule_LocallyViaName_WithTelemetry(ImportModuleOptions importModuleOptions, String name)
   at Microsoft.PowerShell.Commands.ImportModuleCommand.ProcessRecord()
   at System.Management.Automation.CommandProcessor.ProcessRecord()
    [92mMessage    : [0mOperation is not supported on this platform. (0x80131539)
    [92mSource     : [0mSystem.Private.CoreLib
    [92mHResult    : [0m-2146233031
[92mCategoryInfo          : [0mNotSpecified: (:) [Import-Module], PlatformNotSupportedException
[92mFullyQualifiedErrorId : [0mSystem.PlatformNotSupportedException,Microsoft.PowerShell.Commands.ImportModuleCommand
[92mInvocationInfo        : [0m
    [92mMyCommand        : [0mImport-Module
    [92mScriptLineNumber : [0m1
    [92mOffsetInLine     : [0m1
    [92mHistoryId        : [0m33
    [92mLine             : [0mipmo Appx
    [92mPositionMessage  : [0mAt line:1 char:1
                       + ipmo Appx
                       + ~~~~~~~~~
    [92mInvocationName   : [0mipmo
    [92mCommandOrigin    : [0mInternal
[92mScriptStackTrace      : [0mat <ScriptBlock>, <No file>: line 1

Hmmmm, this I don't understand, as the compatible libraries should be available on the box. But it's WinRT, not NetFx, so we're probably not looking in the right place for them with our GAC logic. Or maybe the included WinRT libraries in 3.1 were forwarders all along?

@adityapatwardhan can you and I talk about this one offline this week?

@joeyaiello PowerShell is likely still loading everything as it does in 7.0, but the CoreCLR cannot consume Windows Metadata files (WinMDs) anymore.

Quote from https://github.com/dotnet/docs/issues/18875

Old behavior

Consumption of WinMDs by CoreCLR was possible. This permitted activation and consumption of WinRT types.

New behavior

Direct consumption of WinMDs is no longer possible by CoreCLR.

Examples of failures that may now occur:
...
Activation of a WinRT class will result in the following:

Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform. (0x80131539)

@joeyaiello It's throwing in Assembly::FindModuleByTypeRef when resolving an assembly reference with WindowsRuntime in it's assembly flags. Loading the WinRT package won't change that unfortunately. All binary modules that previously utilized WinRT will need to be recompiled to target the new package explicitly. Don't think there's anything PowerShell can do about it.

Edit: Oops GitHub didn't update, @daxian-dbw beat me to it 馃檪

But as @SteveL-MSFT noted here (and @iSazonov below that), if the module author can put em back, than so can PS.

My question is whether we need both of those DLLs to get this WinMD support, or if we could ship something lighter that picks up the remaining right stuff off the Windows install.

Also, I'd like to spend a little time understanding .NET's decision a little better. We may be reinventing the wheel here, or they may be a larger reason they pulled support in .NET 5 that we're missing.

I'll try to do a little digging this week

But as @SteveL-MSFT noted here (and @iSazonov below that), if the module author can put em back, than so can PS.

That works because he's referencing the newly defined types from the package. The AppX module directly references WinRT types. This exception is at type load because of how the AssemblyRef is encoded.

My question is whether we need both of those DLLs to get this WinMD support, or if we could ship something lighter that picks up the remaining right stuff off the Windows install.

I'd love to be wrong but I'm pretty sure there's nothing PowerShell can ship to make this work, other than a new version of the AppX module.

The exception is too early in the code path for anything to be injected atm. I don't know if this would work, but it might be worth talking to the dotnet team to see if the exception can be moved to after custom assembly resolve handlers are raised. Maybe if that was done the handler could point the resolution request to the new managed lib. At a glance it looks like the signatures should match.

I'm guessing that would be flimsy at absolute best but maybe that sparks an idea for them.

Nah that's a bust. To test I manually removed the assembly ref flag for WindowsRuntime on the assembly in the GAC (ty dnSpy) and added an assembly resolve handler to point the ref to the new managed lib. It actually works for the most part, but eventually I get a JIT exception because the new managed lib prefixes it's set accessor methods with set_ instead of put_.

Exception             :
    Type       : System.MissingMethodException
    Message    : Method not found: 'Void Windows.Foundation.IAsyncOperationWithProgress`2.put_Completed(Windows.Foundation.AsyncOperationWithProgressCompletedHandler`2<!0,!1>)'.
    TargetSite :
        Name          : RunAppxOperation
        DeclaringType : Microsoft.Windows.Appx.PackageManager.Commands.AppxPackageManager
        MemberType    : Method
        Module        : Microsoft.Windows.Appx.PackageManager.Commands.dll
    StackTrace :
   at Microsoft.Windows.Appx.PackageManager.Commands.AppxPackageManager.RunAppxOperation()
   at Microsoft.Windows.Appx.PackageManager.Commands.AppxPackageManager.SetVolumeState(AppxVolume volume, Boolean setOnline)
   at Microsoft.Windows.Appx.PackageManager.Commands.MountAppxVolumeCommand.ProcessRecord()
   at System.Management.Automation.CommandProcessor.ProcessRecord()
    Source     : Microsoft.Windows.Appx.PackageManager.Commands
    HResult    : -2146233069
CategoryInfo          : NotSpecified: (:) [Mount-AppxVolume], MissingMethodException
FullyQualifiedErrorId : System.MissingMethodException,Microsoft.Windows.Appx.PackageManager.Commands.MountAppxVolumeCommand
InvocationInfo        :
    MyCommand        : Mount-AppxVolume
    ScriptLineNumber : 1
    OffsetInLine     : 22
    HistoryId        : 34
    Line             : Get-AppPackageVolume|Mount-AppxVolume
    PositionMessage  : At line:1 char:22
                       + Get-AppPackageVolume|Mount-AppxVolume
                       +                      ~~~~~~~~~~~~~~~~
    InvocationName   : Mount-AppxVolume
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

I manually changed the method ref to set_ in the strings heap and now everything appears to be working fine. Maybe the team working on the managed lib could add fake put_ accessors that point to the set_ accessors for compat (hidden with EditorBrowsableState.Never).

It seems .Net team explicitly said that WinRT application should be recompiled in the case. So we need new version of the module.

@iSazonov Yeah that's definitely their current stance. It was pretty easy to coerce it into working though. I think it would be moderately easy for the dotnet team to enable an application to re-add support for these assemblies. They would be able to keep actual support for winrt completely out of dotnet/runtime, but an application like PowerShell could hook it back up potentially seamlessly.

Anyway that's mainly just to give @joeyaiello some ideas if he was still intending to discuss it with them. If not then yeah just ship a new version I suppose.

I have exactly the same problem. 馃槶

I manually changed the method ref to set_ in the strings heap and now everything appears to be working fine. Maybe the team working on the managed lib could add fake put_ accessors that point to the set_ accessors for compat (hidden with EditorBrowsableState.Never).

can you place a download link of your package so can verify it too?

@salyounis It was just for testing purposes, I would heavily advise against using an assembly whose metadata has been modified manually.

We are still investigating this, but we are open to servicing this if this isn't fixed by 7.1 release.

Per .NET team, if we retarget the Windows release to net50-windows, that would pull in the WinRT assemblies and should work. However, that new TFM isn't available until .NET 5 Preview 8.

@SteveL-MSFT are we 100% sure they mean it'll work without recompiling? It fails at the runtime level, unless they plan on lighting up the assembly resolution logic again, this module will still need to be recompiled.

@SeeminglyScience won't know until we try, the implication is that retargeting to the new TFM will pull in the necessary assemblies to make it work. My suspicion is that a recompilation of Appx module is required, but that will then break running in Windows PowerShell. The other option is to not mark it as Core compatible to have it go through the Windows PowerShell path.

Even now, you can just import-module appx -usewindowspowershell to get it working.

@SeeminglyScience won't know until we try, the implication is that retargeting to the new TFM will pull in the necessary assemblies to make it work.

This may already be understood so I apologize if I'm just reiterating facts y'all already know, but the point where this exception is thrown won't be affected by any additional assemblies. It's hard coded to throw at type ref resolution well before any other code runs. It's more or less the first stop in the code path.

Really I'm just trying to make sure y'all aren't banking on that working, if that's already understood then 馃憤

My suspicion is that a recompilation of Appx module is required, but that will then break running in Windows PowerShell. The other option is to not mark it as Core compatible to have it go through the Windows PowerShell path.

I did outline another potential path that would allow host processes like PowerShell to map these older assembly references to the new library.

  1. Defer the exception in Assembly::FindModuleByTypeRef until after assembly resolve events are raised. If a registered handler finds the assembly then don't throw, if it does then throw the same exception
  2. In the new WinRT package add put_ methods that map to the new set_ accessor methods for properties

If those are done then compat can be kept for these modules without compromising their goal of getting the code out of dotnet/runtime.

I just want to make sure all the options are being considered. If y'all don't want to pursue that then 馃憤

@SeeminglyScience spoke with .NET team and based on your idea, they agreed that to maintain compatibility, they would expose both set_ and put_ accessors for WinRT https://github.com/microsoft/CsWinRT/pull/402. Thanks!

@SteveL-MSFT That's awesome! Thanks for talking to them.

Has there been any discussion with the runtime team regarding moving the assembly resolution exception? This is sort of an all or nothing scenario where if that part doesn't happen there won't be a need for the put_ methods.

Just to spell it all out in case any of this isn't clear, let say we have a TypeRef encoded like this:

.assembly extern windowsruntime Windows
{
    .ver 255:255:255:255
}

[Windows]Windows.Foundation.AsyncStatus

That sets the content type for the assembly as WindowsRuntime.

Assembly resolution at a high level looks like this:

  1. Check if the content type is WindowsRuntime and throw NotSupportedException
  2. Run any AssemblyResolve event handlers
  3. Default resolve/load behavior

So ideally the PowerShell engine would be able to register an AssemblyResolve handler that looks something like this (psuedo code):

Assembly Resolve(object sender, ResolveEventArgs args)
{
    if (args.Name.StartsWith("Windows") && args.Name.Contains("WindowsRuntime"))
    {
        return CsWinRTAssembly;
    }

    return null;
}

But because assembly resolution never makes it past the first step, that handler will never fire. If the NotSupportedException could be moved until after assembly resolution doesn't find anything then compatibility could be kept.

Without that, these modules will still need to be recompiled and at that point they'll just use the set_ accessors anyway.

Ok, after extensive discussions with .NET team and a change in their Windows.SDK assembly that handles WinRT calls to support both put_ and set_ for compatibility, the fix still requires Appx module to be retargeted for netstandard20. Working with the Appx team to do this. Since there is no change in PowerShell that can be done, closing this.

I'd love to be wrong but I'm pretty sure there's nothing PowerShell can ship to make this work, other than a new version of the AppX module.

In the short term, can we get the compatibility deny list updated? SInce this module can not work natively with PowerShell 7.1, let's just not load it (by default).

In the short term, can we get the compatibility deny list updated? SInce this module can not work natively with PowerShell 7.1, let's just not load it (by default).

/cc @anmenaga

What is the impact of

WARNING: Module Appx is loaded in Windows PowerShell using WinPSCompatSession remoting session; 
please note that all input and output of commands from this module will be deserialized objects. 

while working on a live system?

If a module is not supported in PowerShell the PowerShell creates a remote session to Windows PowerShell and run the module there. Limitation - you will receive reserialized objects in local session.

So Get-AppxPackage will give accurate results and Add-AppxPackage will affect the live system?
If this is true, what is the difference between working with de/serialized objects and live data?

@juvtib When an object is serialized it loses methods, some properties may be typed differently or contain slightly different values, some may lose nested objects, and they may no longer work as arguments to strongly typed parameters.

Whether it effects your specific workflows is something you'll need to test to find out.

Another side effect of the compatibility solution is that, at present, Display XML created by modules is not imported into the Parent PowerShell 7 session. So the output from commands such as Get-WindowsFeature produces sub-optimal output. You can, of course, always manually import the XML to get around this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

garegin16 picture garegin16  路  3Comments

MaximoTrinidad picture MaximoTrinidad  路  3Comments

concentrateddon picture concentrateddon  路  3Comments

andschwa picture andschwa  路  3Comments

aragula12 picture aragula12  路  3Comments