Powershell: Windows Store applications incorrectly assumed to be console applications

Created on 20 Jun 2019  ·  14Comments  ·  Source: PowerShell/PowerShell

Steps to reproduce

Run a Windows program installed in ${env:USERPROFILE}\AppData\Local\Microsoft\WindowsApps, e.g. iTunes.exe or wt.exe (the new Windows Terminal) from the interactive command line.

Expected behavior

The process launches in a new window, a new prompt is displayed and you can run new commands in PowerShell.

Actual behavior

PowerShell waits until the Windows application exits before allowing new commands to be entered. PowerShell is behaving as though the program was a console application.

It makes perfect sense to wait when PowerShell can't determine if the process is a Windows or console application, so a proper should do a better job processing these reparse points to inspect the real executable.

Issue-Bug Resolution-Fixed Up-for-Grabs WG-Engine

Most helpful comment

I'm seeing this repro with PS7 RC1 and 2.

All 14 comments

This is probably a Windows issue (in the CreateProcess _AppExecutionAlias_ extension) honestly. Powershell doesn't necessarily control whether new processes attach to existing or allocate new consoles.

@DHowett-MSFT - possibly, but I was thinking this PowerShell code is perhaps missing something.

I found MicrosoftEdge.exe in my WindowsApps folder and the application runs as expected. I installed iTunes - PowerShell waits.
For both SHGetFileInfo() returns zero - "not exe".

Update: then we calculate _isRunningInBackground - in both cases it returns false
https://github.com/PowerShell/PowerShell/blob/dd7e45fe2ebb358f0833c1c5be0f8f5eb90d2554/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L522

Just a guess - but does that api work with symbolic links? If not, resolving the links might be the fix.

Both is symlinks:

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          21.06.2019    14:26                AppleInc.iTunes_nzyj5cx40ttqa
d----          30.11.2018    16:57                Microsoft.MicrosoftEdge_8wekyb3d8bbwe
la---          21.06.2019    14:26              0 AppleInc.Defaults.exe ->
la---          21.06.2019    14:26              0 AppleInc.MDCrashReportTool.exe ->
la---          21.06.2019    14:26              0 iTunes.exe ->
la---          30.11.2018    16:57              0 MicrosoftEdge.exe ->

They’re not actually symbolic links. They are “app execution alias” reparse points that store a package family name and app model ID instead of a path. Check one out with fsutil reparsepoint query <filename>.

So we need to compare reparsepoint properties for the objects to find difference in their behavior?

Doesn't repro with PS7 P5

I'm seeing this repro with PS7 RC1 and 2.

It looks like the pull request above added support to the filesystem provider for detecting these, but not to the process launcher for not waiting on them to return.

Yes, the PR above added a check to FileSystem provider only. We can use the same one for AppX application launch too.

The root cause of this problem is that CreateFileW cannot open the target file like a symbolic link. Therefore,GetBinaryTypeW cannot obtain the Subsystem information of WindowsTerminal.exe, so this problem occurs.

Solving this problem is not complicated. We can directly parse ReparsePoint, and then obtain the PE subsytem of its target, so that's it.

//https://github.com/fcharlie/bela/blob/728a4b726f303e7c861823232991de7fdea4d992/src/belawin/reparsepoint.cc#L278
std::optional<std::wstring> RealPathEx(std::wstring_view src,
                                       bela::error_code &ec) {
  FileReparser reparser;
  if (!reparser.FileDeviceLookup(src, ec)) {
    if (ec.code == ERROR_NOT_A_REPARSE_POINT) {
      ec.code = 0;
      return std::make_optional(bela::PathAbsolute(src));
    }
    return std::nullopt;
  }
  switch (reparser.buffer->ReparseTag) {
  case IO_REPARSE_TAG_APPEXECLINK:
    if (AppExecTarget target; DecodeAppLink(reparser.buffer, target)) {
      return std::make_optional(std::move(target.target));
    }
    ec = bela::make_error_code(1, L"BAD: unable decode AppLinkExec");
    return std::nullopt;
  case IO_REPARSE_TAG_SYMLINK:
    CloseHandle(reparser.FileHandle);
    reparser.FileHandle = INVALID_HANDLE_VALUE;
    if (auto target = bela::RealPath(src, ec); target) {
      return std::make_optional(std::move(*target));
    }
    return std::nullopt;
  case IO_REPARSE_TAG_GLOBAL_REPARSE:
    if (std::wstring target; DecodeSymbolicLink(reparser.buffer, target)) {
      return std::make_optional(std::move(target));
    }
    ec = bela::make_error_code(1, L"BAD: unable decode Global SymbolicLink");
    return std::nullopt;
  default:
    break;
  }
  return std::make_optional<std::wstring>(src);
}
// ntimage.h
// Subsystem Values

#define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
// end_winnt
// reserved                                  4   // Old Windows CE subsystem.
// begin_winnt
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //
#define IMAGE_SUBSYSTEM_EFI_ROM              13
#define IMAGE_SUBSYSTEM_XBOX                 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    17

FWIW, Terminal is working around this (and a few other issues) by shipping a separate stub executable, wt, that exits immediately after spawning the real one. We're not blocked by this any longer. :smile:

:tada:This issue was addressed in #13481, which has now been successfully released as v7.1.0-preview.7.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings