I had a question come up in response to another post:

I also have another side effect from this change.  I have a process that tracks processes via JobObjects.  With Vista I had to add a Manifest file to all my processes so PCA would not track them and I could continue to track with my custom tool.  It appears I need to modify the manifests again with a compatibility tag for Windows7.  The problem is I will have to do this for every new OS, or find an alternative to jobObjects.

So, I figured I’d dive in to PCA. Because, believe it or not, it’s even possible for an application compatibility technology to have an application compatibility impact, as this customer discovered! (I’ve said it before – this stuff is really hard.)

You see, the underlying problem is that application compatibility technologies in Windows have to follow the same rules as any other application running on Windows. It’s kind of like The Matrix. We have Agents on the system, trying to keep things working. They have to obey the rules of the system, though they can stretch some of the rules and have a certain mastery of them that many applications wouldn’t have (access to the source and the developers). It’s precisely because we are bound by the same rules that we can land ourselves in trouble.

The Program Compatibility Assistant (PCA) is one such system, and in this example the rules it’s following are the rules of Job objects. Let’s work our way through this system and find out how it hurt our friend and what options he has.

We’ll be making most of our way through the system using Process Explorer to discover what’s going on, so if you have that handy, you can follow along. (I have it as an auto-start program, personally.)

First, we want to elevate Process Explorer if it isn’t running elevated already so we can see some details of system processes.

If you do a handle search for PCA_ it will take you to an instance of svchost.exe, and if you mouse over the exe you’ll see that it is hosting the Program Compatibility Assistance Service [PcaSvc]. In the list of handles for that service, you’ll see a number of handles to Job objects, such as:

\BaseNamedObjects\PCA_{EE416CB2-DD81-4EE5-A3FD-B36EA4503301}

PCA does its monitoring through jobs, and you can see them here. What’s more, you can see them in the process list below. By default, Process Explorer will highlight processes which are enclosed in jobs with a brown color. Double click on one, and you’ll be able to see the job information:

image

OK, so PCA is looking after processes by adding them to a job object. What for? Well, I haven’t reversed this completely, but my (educated) guess would be so that it could leverage JOB_OBJECT_MSG_EXIT_PROCESS to perform its work after the process exits. So far, so good.

But wait … there’s one catch. From the SDK:

“A process can be associated only with a single job.”

Oh, and then there is this:

“After you associate a process with a job, the association cannot be broken.”

Ah. Well, that kind of puts some sand in the ointment. Anything launched using ShellExecute(Ex) that doesn’t meet the PCA exclusion criteria is going to be assigned to a PCA job, and that means you can’t assign them to another job. Effectively, we just took back part of the platform from you. (Caveat – if you called ShellExecute with SEE_MASK_FLAG_NO_UI, then you would not have PCA applied.)

OK, so what can our friend who wants to use job objects do now?

Well, one option is to manifest everything that they want to track with a Windows 7 manifest. (Yes, there is a bitter irony that, in order to support an application compatibility technology designed to keep things unchanged working on current versions of Windows you have to change some things.) Of course, that also means that, if we changed nothing about the implementation, you’d have to do that again with every new version of Windows if you want to use job objects – not cool. So, what else can you do?

If you can control the creation of processes, then you can wiggle right out of PCA.

In addition to the ShellExecute escape hatch mentioned above, CreateProcess lets you specifically opt out of inheriting a job, assuming that the job allows you to (which the PCA job does). Let’s see.

First, I can create an app that serves as my launcher. It will get the PCA job applied to it, as a non-Windows 7-manifested application. But I’m not worried about it – I’m worried about the child processes that my job is going to be watching over. So, let’s create a job object:

HANDLE myJob;
myJob = CreateJobObject(NULL, TEXT("JobsAndPca"));

I’m going to elide the CreateProcess setup and cleanup work, and instead just dive right in to creating a process and assigning it to a job. Without PCA monitoring, here’s what I’d have:

if (CreateProcess(szPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) {
    if (AssignProcessToJobObject(myJob, pi.hProcess)) {
        // Success! (I don’t get here)
    } else {
        // Failure – couldn’t add to job (I probably get here)
    }
} else {
    // Failure – couldn’t create the process (I probably don’t get here)
}

So, I’ve just reproduced the failure – I can’t get in there with a job. However, if I create my process differently, I can:

if (CreateProcess(szPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi)) {
    if (AssignProcessToJobObject(myJob, pi.hProcess)) {
        // Success! (I probably get here)
    } else {
        // Failure – couldn’t add to job (I probably don’t get here)
    }
} else {
    // Failure – couldn’t create process (I probably don’t get here)
}

So, our final answer is:

If you are the creator of the process you want to monitor, then you can escape the PCA job. Otherwise, you’re going to have to re-manifest for Windows 7 compatibility.

We’re already thinking ahead to future versions of Windows, and having just told you to manifest your application for Windows 7, as the app compat team, we don’t want everything to automatically risk breaking again in future versions of Windows because our app compat stuff gets in the way, nor do we want to have to make you change your manifest every time (because, well, that kind of defeats the purpose of app compat if none of your apps are compatible without modification). How do we go about always doing right by everybody? Well, that’s a hard problem. And one that you deserve a great answer for. If the current rules require us to get in your way, then we may very well just have to change the rules…