Are child processes "break away" or not by default?

Copper Contributor

Hello,

I'm experiencing some difference in behavior between Windows 10 2004 and earlier versions.
Here's my scenario:
I'm creating an MSIX package for my application. I want my main executable and all of its child processes to run inside the MSIX container. According to the docs here,  this supposed to be the default behavior. However, in my testing on Windows 10 1909 I found this to be NOT the case. So I created a little executable that simply calls CreateProcess with the PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE flag. This executable is pointed by my start menu shortcut. This fixed my problem on Win10 1909. My child processes happily run inside the container.

As an aside, the MSIX documentation above and the Win32 documentation appears to be in conflict:
The win32 documentation states:

PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_ENABLE_PROCESS_TREE 0x01

The process being created will create any child processes outside of the desktop app runtime environment. This behavior is the default for processes for which no policy has been set.


So if break-away is the default then child processes will NOT run inside a container.

Back the the real problem: on Windows 10 2004 my little launcher app does not work at all. Calling CreateProcess with PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE has the exact opposite affect than what's documented. The child process "breaks away" and does not see the files inside the container. If I use "null" attribute list, then I get the behavior I want.

Can anybody confirm this confusing variety of documentation and behaviors?

Here's the code for my launcher:

#include <iostream>
#include <cassert>
#include <vector>
#include <string>
#include <regex>

#define NTDDI_VERSION NTDDI_WIN10_RS2
#include <Windows.h>
#define ASSERT assert

LPPROC_THREAD_ATTRIBUTE_LIST MakeAttributeList()
{
    LPPROC_THREAD_ATTRIBUTE_LIST attrbuteList;
    SIZE_T AttributeListSize{};
    InitializeProcThreadAttributeList(nullptr, 1, 0, &AttributeListSize);
    attrbuteList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
        GetProcessHeap(),
        0,
        AttributeListSize
    );

    DWORD error = 0;
    if (!InitializeProcThreadAttributeList(
        attrbuteList,
        1,
        0,
        &AttributeListSize))
        throw GetLastError();

    DWORD attribute = PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE;
    if (!UpdateProcThreadAttribute(
        attrbuteList,
        0,
        PROC_THREAD_ATTRIBUTE_DESKTOP_APP_POLICY,
        &attribute,
        sizeof(attribute),
        nullptr,
        nullptr))
        throw GetLastError();

    return attrbuteList;
}

int wWinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPWSTR     lpCmdLine,
    int       nShowCmd)
{
    try
    {
        const int len = 4096;
        wchar_t buf[len];
        if (len == GetModuleFileName(0, buf, len))
            throw GetLastError();

        auto fname = std::regex_replace(buf, std::wregex(L"msixlauncher\\.exe"), L"dwgviewr.exe");
        STARTUPINFOEXW startup_info = {};
        PROCESS_INFORMATION process_info = {};
        startup_info.StartupInfo.cb = sizeof(startup_info);
        startup_info.lpAttributeList = MakeAttributeList();

        if (!::CreateProcess(
            fname.c_str(),
            nullptr,
            nullptr,
            nullptr,
            false,
            EXTENDED_STARTUPINFO_PRESENT,
            nullptr,
            nullptr,
            (STARTUPINFO*)&startup_info, // startup info
            &process_info  // process info
        ))
            throw GetLastError();

        WaitForSingleObject(process_info.hProcess, INFINITE);

        ::CloseHandle(process_info.hProcess);
        ::CloseHandle(process_info.hThread);
    }
    catch (DWORD err)
    {
        return err;
    }
    return 0;
}





7 Replies

@szilvaa I believe that this is a change in the 2004 Runtime.  

 

Prior to 2004 OS, my experience was that by default child processes would run outside of the container, but now on 2004 breakaway is disabled.  My recollection is that if I controlled the process launch, the controls you show could be used to control breakaway previously, but now on 2004 those controls are ignored.

@TIMOTHY MANGAN Thanks!
Yeah, in the meantime I figured out that child process breaks away if the exe image is under AppData (i.e. LocalCache). The child process does NOT break away if the exe image is in VFS. This makes some sense. 

So, my "only" remaining problem is that PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE does not appear to work at all with 2004. The process launched with this flag does not "see" the containerized files and registry keys. I need this flag to work so that child processes whose exe image is in AppData  will remain in the container. At this point, I don't have any way to force this.

For good measure, I also tried to combine PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE | PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_OVERRIDE but it doesn't help.

The documentation for PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE is "interesting". It doesn't say anything about the "process being created". It only talks about child processes of the process being created. The documentation for PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_OVERRIDE is does talk about the process being created so the combination should definitely work. 

This appears to be a bug. Do you know how I can report these?

@szilvaa There is a chance that one of the Microsoft Engineers will respond here, but running a test and then entering it using the Feedback Hub on your device would be the way to report this.

 

I suspect that you will find that this is "by design" now.

 

An interesting thing to look at is to use Process Explorer (started with RunAsAdmin) and look at the "Jobs" tab.  The Container is where Microsoft sets the Breakaway by setting it in the Job of the first process inside the container.  This is what changed in 2004.  Any breakaway controls placed on the job appear in the lower pane.

@TIMOTHY MANGAN Thanks for your responses! The Jobs tab in process explorer is nice debugging aid! 

I have some more news:
I was able to confirm that "Windows October 2020 Update" (aka 10.0.19042.541 ) fixes the change in behavior (I think we can call it "regression" now) in Windows 10 2004 (aka 10.0.19041.508) affecting PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE. This flag works again as documented.

I guess MSIX isn't as mature as I expected it if major parts of the behavior can still regress like this.

@szilvaa Well yes, MSIX is a work in progress...

 

And I happened to notice  last night that an additional change was made to the PSF by Microsoft over the summer that looks like they are continuing to change things with breakaway.  I am unsure exactly what that change allows at present, but it would only be available on builds including the change, and we don't have any real "versions" with the PSF that we can specify to know if you are getting it.  I think the change was to allow more flexibility on configuring breakaway, at least on OS versions that allow it.

@TIMOTHY MANGAN Thanks! I don't use PSF. There are enough moving parts without it. :)