First posted to MSDN on Nov, 22 2011
I wanted to document a bit of detail about managing the Program Compatibility Assistant, a bit of technology we added for Windows Vista and have been enhancing since that time – I figured it was probably time that we came out with an update for Windows 7.
What motivated this was a series of conversations where a number of people were recommending that the well-managed enterprise should turn the feature off in its entirety. This is a recommendation that I do not generally concur with (though there certainly may be very specialized scenarios where this is a good recommendation). Rather, I prefer to tune it when there is a problem. For this reason, I also wanted to call attention to the fact that there is a completely separate area of group policy (for reasons I am completely unable to explain) which provides individual knobs for many of the PCA scenarios implemented in Windows 7. If you are having problems with a particular scenario, then it’s better to turn off that one specific scenario than to throw out the baby with the bathwater. With that in mind, below as I describe the various scenarios of group policy I will also include that scenario’s group policy switch when it has one, along with reasons why you might want to disable detection for that scenario.
But, before we begin, a brief word about PCA. The idea here is to try to reach the long tail of the Windows application ecosystem. Keep in mind, though we have a fantastic testing team and significant manual and automated tests, we’re still testing software on the order of 10
3
. Beta testing helps more, but beta testers aren’t that much more happy with application failure than any other user. So, we are looking to automate some of the work done by the app compat team.
What are we looking for today, and how can you configure it?
Installation Failure
In this scenario, we look to see if you are detected as an installer, using UAC installer detection. If so, we compare the Uninstall registry key both before and after the installer is run. If nothing new appears, we guess that you might have failed, and display a PCA prompt. If you accept the fix, we apply the WINXPSP2 layer.
Detect application install failures
disables this check, though it’s usually fairly benign. We used to have a lot of trouble with this one, as we still ran it even if you had a UAC manifest indicating asInvoker (a fairly good indication you are not an installer) and tried to install a VISTARTM layer, but based on your feedback I successfully campaigned to get this removed in a February 2010 Windows Update. It doesn’t really affect standard users, who aren’t able to install software anyway.
Updater Requires Elevation
In this scenario, we just track the ETW event for STATUS_ELEVATION_REQUIRED (0xC000042C). We’re pretty much always right on this one, as the fix is always to apply the ELEVATECREATEPROCESS shim, so we just go ahead and do it. (I have no idea why we still bother to show you a dialog box. Apparently somebody didn’t read
the memo
.)
Detect applications unable to launch installers under UAC
disables this check, though I can’t think of a reason why you’d want to. Yes, it results in a UAC prompt, but your alternative is an error – at least the prompt tells you what’s going on.
Deprecated COM Object
This scenario just implements a check in ole32 against a hard-code list of deprecated DLLs. When we find one, we give the user the chance to check for a solution online.
Detect application failures caused by deprecated COM objects
disables this check, and I’m kind of on the fence for this one with standard users. Yes, it points them to a solution that enables them to potentially find an installer they can’t use, but at least your help-desk call comes with a solution, and not just an non-debugged failure. I would consider leaving this one on, I just haven’t heard any reports of it being problematic and it has the potential to solve some issues that normally requires nerds spending time with depends.exe.
Deprecated DLL
This scenario implements a simple check (in ntdll) for deprecated DLLs, again going against a hard-coded list. Once again, we’re fairly accurate here, and the solution is to give you a web link which might assist you in debugging.
Detect application failures caused by deprecated Windows DLLs or COM objects
disables this check, and again I’d consider leaving this one on unless you encounter a specific problem with it. Again, I’ve not heard of such a scenario, but that doesn’t mean it doesn’t exist.
Undetected Installer
This scenario looks for applications which are
not
detected as setups by UAC setup detection, but which try to create a new folder in Program Files (which we detect using UAC virtualization events – so UAC virtualization needs to be enabled to satisfy this scenario). Back in Windows Vista, we also required you to try to drop a dll or an exe into the folder you create, but that is no longer the case in Windows 7. If we detect this, we give the user the option to apply both VISTASETUP and RUNASADMIN to the application.
Detect application installers that need to be run as administrator
disables this check, which you may want to do for standard users. If the user picks the wrong option as a standard user, they make themselves unable to launch the application again, which would be rather suboptimal for false positives. Since there’s no real benefit here, and potential downside, it’s the one scenario I recommend disabling most frequently.
Legacy Control Panel Applets
This scenario doesn’t even really have a heuristic. Basically, if you are a control panel applet, and you don’t have a manifest, we ask every single time, “hey, how did that go?” If you say that it went poorly, we apply RUNASADMIN.
This scenario doesn’t have a group policy controlling it. Back in the Vista days I would see it from time to time, but I haven’t seen one in several years now. What I used to do when I found them in an organization was to add an entry into the shim database which makes the correct choice for the user.
Uninstall Failure
This scenario is pretty much the opposite of install failures, though it has the more reliable launch point (the add/remove control panel execution rather than a setup heuristic) not detecting changes to the Uninstall registry key. If you do, we apply the VISTARTM layer (if you have a UAC manifest) or the WINXPSP2 layer (if you don’t).
This scenario also doesn’t have a group policy controlling it, but in my experience is benign and safe to leave enabled.
Unsigned Drivers on x64
Since we don’t allow you to load unsigned drivers on 64-bit Windows, but some folks try to sneak them in anyway by manually flipping the registry keys, we monitor changes to the registry key and disable them if they are added here.
Notify blocked drivers
disables this check, but unless you are a driver developer there isn’t much sense in disabling this.
Legacy Installer Shortcuts
This is actually a fairly complex scenario. If you trigger the Installer Failure scenario, and the user chose to put you into compat mode, then we mark the shortcuts that installer created. Then, when you launch one of those shortcuts, after you exit, we asked if the application worked for you, and give you a chance to launch the Program Compatibility Troubleshooter. The reason here is that, if the installer needed VISTARTM or WINXPSP2 to install, then the application itself might also.
This scenario doesn’t have a group policy, but disabling the Installer Failure scenario would have the consequence of disabling this check also.
Unhandled Exception in User Callback
This is our nerdiest scenario. We made a chance in the windowing subsystem that changed how exceptions were handled within callback functions. When an exception is triggered in user mode, older versions of Windows would hit a challenge going over the user mode –> kernel mode –> user mode boundary. A native 64-bit application simply wasn’t able to propagate that exception back if it wasn’t handled before making that transition. So, if your thread creates a window, and that window throws an exception, you can’t handle that in the code that created the window and receive the exception. So, we had a choice: we could either crash the application when the exception was thrown, or just swallow it and hope for the best. For app compat reasons, we swallow it and hope for the best.
We changed this for Windows 7, as just swallowing exceptions is really not a terribly good idea from a debugging or application quality perspective, and allowed exceptions to come through for 64-bit processes on x64. So, if an application were throwing an exception before but had to transit kernel mode along the way, we were just swallowing it before. Now, we pass it back up the stack.
I’m sure you can see where this is going: applications which should have been crashing on earlier versions of the OS, according to their own code, were being handed a get-out-of-jail free card, and not crashing because they happened to transit through kernel mode. Now, they no longer have this card, and if they don’t handle the exception, the application will crash.
This scenario detects that an exception transited a user callback (ntdll!KiUserCallbackDispatcherContinue) and if this exception leads to the application crashing because it wasn’t handled, we revert to the earlier behavior by applying DISABLEUSERCALLBACKEXCEPTION.
This scenario doesn’t have a group policy, but we are very accurate and this is precisely the sort of thing that PCA ought to be fixing for you.
The Future
Are you seeing a trend here? The earlier PCA scenarios, which existed back in Vista days, are broad brush heuristics, primarily focused on the big hurdle of UAC and running as standard user. What we added for Windows 7 was much more targeted, higher accuracy, and the sort of thing you’d really kind of like going in the background fixing your stuff from bugs in corners of your applications you haven’t found yet. As more of these come in the future, you want to make sure you are opting in, which is why I always guide people towards leaving the feature enabled, and at most disabling troublesome scenarios if they indeed become troublesome.