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.
The process launches in a new window, a new prompt is displayed and you can run new commands in PowerShell.
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.
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:
Most helpful comment
I'm seeing this repro with PS7 RC1 and 2.