Microsoft Application Virtualization
Making Applications Compatible with Windows 7 in a Virtualized Environment
Microsoft Application Virtualization (App-V) 4.6, boasting full support for Windows 7, is right around the corner; many customers who are planning a Windows 7 deployment are including App-V among the components of their desktop transformation project. (An OS deployment is frequently combined with an overhaul of both applications and infrastructure in a “modern desktop” or “next-generation desktop” initiative.)
When IT professionals think about pairing investments in App-V and Windows 7, the following questions almost always come up as part of the conversation:
- I heard that App-V is an application-compatibility solution. Does that mean it will help make my applications compatible with Windows 7?
- Will I need to re-sequence the App-V packages I have already created for my current Windows XP desktops?
- What do I need to do to remediate incompatible applications when I use App-V as my deployment solution?
Let’s explore each of these questions.
Is Microsoft App-V an Application-Compatibility Solution?
Microsoft App-V is, first and foremost, an application management and deployment solution that can convey significant benefit to the enterprise—reducing packaging costs, increasing system stability, and supporting today’s highly mobile workforce with dynamic access to software assets. But as part of the marketing messaging, the overloaded term application compatibility grew to be misinterpreted over time: that App-V could help with compatibility problems between the application and the OS. For the most part, it can’t. (The exceptions today exist mostly for historical reasons, and are not something to depend on, so I won’t go into the details here.)
The resulting customer confusion has led to some clarification in the messaging; we now forego use of the term “application compatibility” and instead speak directly to the underlying benefits: that you reduce application-to-application conflicts (note the strategic omission of the word compatibility), and that, as a result, you significantly reduce regression testing.
The official stance of the product team on application-to-OS compatibility is this:
As noted in prior discussions, App-V is not a general purpose application-to-OS compatibility solution; however, if an application compatibility shim allows an app to work on a given Windows version natively (non-virtualized), it will in most cases, and for most shims, work with App-V when the shimmed app is virtualized. Thus, as a general rule, App-V will support app use with shims that are provided as part of Microsoft’s App Compat tools as long as the shimmed application can run natively on the targeted OS version.
So, it’s pretty clear that App-V isn’t intended to be an application-to-OS compatibility solution. (We will discuss combining shims with App-V later in this article.) Let’s look at the other impacts that application virtualization has on OS compatibility.
Is Microsoft App-V a Package-Compatibility Solution?
When we talk about application compatibility, it’s fair to separate package compatibility from runtime compatibility. In fact, our recommended process for application-compatibility testing (shown in Figure 3 of my June 2009 article on Planning Your Application Compatibility Project) separates install testing from runtime testing. Let’s start with the official product team guidance:
It is often possible to sequence on one OS and run the virtualized app on a different OS; however, this scenario is both app- and OS-dependent and is not guaranteed to work for all app/OS combinations because App-V is not a general purpose OS compatibility solution. If problems are encountered, the customer may be required to sequence on the same OS environment as the App-V client is running in order to resolve those problems.
OK, so this doesn’t sound so promising—the official stance is, basically, “it depends.” But when you’re thinking about measuring risk, compare the three primary setup technologies in use today:
Setup.exe: This runs arbitrary code, and thus the arbitrary code is subject to the same potential runtime problems that the arbitrary code of the application itself is, so it will have to be thoroughly tested.
Windows Installer: This also runs code to execute an install, but much of this code is structured, declarative code. Only the imperative code of custom actions is arbitrary, so outside of quantifiable changes in rules that affect the processing of code running against the database and the arbitrary code in custom actions, you expect better (though still not perfect) compatibility and less testing.
Microsoft App-V: This doesn’t have to run code at all. It just copies over a blob of data that is the virtual file system, and then says “OK, your virtual file system is here.” So, your primary concern isn’t whether the application will install (copying one blob of data is, after all, really easy), it’s the runtime compatibility of the application. So, you expect install testing to be shortest of all three technologies. That’s goodness!
So, while nobody is officially going to say that packages will all work, it seems the primary reason for the messaging is that many people conflate install issues and runtime issues. In general, you can look to reduce your projected costs of application installation testing significantly if you have already invested in Microsoft App-V.
How Do I Fix Runtime Compatibility Issues When my Application Is Packaged with Microsoft App-V?
Remember the tantalizing bit from the support statement? “App-V will support app use with shims that are provided as part of Microsoft’s App Compat tools….” How do you actually implement that? Are the two fundamentally compatible?
The answer is, fortunately, yes. In fact, you have a couple of different options for doing so.
A Brief Primer on Shims
For those of you who are unfamiliar with shims, a shim is one of the very few four-letter words in use by Microsoft that isn’t an acronym of some sort. It’s a metaphor based on the English language word shim, which is an engineering term used to describe a piece of wood or metal that is inserted between two objects to make them fit together better. In this particular case, the two objects are the application program and Windows, and the shim material is additional code that causes the two to behave better together, as shown in Figures 1 and 2.
Figure 1 Before the shim is applied, the application interacts directly with Windows.
Figure 2 After the shim is applied, the application interacts with Windows indirectly; the shim code is injected and can modify the request to Windows, the response from Windows, or both.
A shim works using API interception. The Windows API is implemented using a collection of DLLs. Each application built for Windows imports these DLLs, and maintains a table of the address of each of these functions in memory. Because the address of the Windows functionality is sitting in a table, it is straightforward for the shim engine to replace this address with the address of the shim DLL instead. The application is generally unaware that the request is going to a shim DLL instead of to Windows itself, and Windows is unaware that the request is coming from a source other than the application (because the shim DLL is just another DLL inside the application’s process).
For example, a very commonly used shim is a version-lie shim. To implement this shim, we intercept several APIs that are used to determine which version of Windows the application is running on. Normally, this information is passed on to Windows itself, and it answers truthfully. With the shim applied, however, these APIs are intercepted. Instead of passing on the request to Windows, a different version of Windows is returned (for example, Windows XP instead of Windows 7). If the application is programmed to run only on Windows XP, this is a way to trick the application into believing it’s running on the correct OS. (Frequently this is all that is necessary to resolve an application compatibility problem!)
There are a huge number of tricks you can play with shims. For example:
- The ForceAdminAccess shim tries to trick the application into believing that the current user is a member of the local Administrator group, even if he is not. (Many applications outright fail if you are not a local administrator, though you may be able to use other tricks, such as UAC File and Registry Virtualization, to resolve the issues that caused the check in the first place.) How it implements this check can be fairly straightforward. For example, this shim intercepts the API IsUserAnAdmin from shell32.dll. The complete source code of the shimmed function (which has wonderful performance characteristics compared to the actual API) is simply return TRUE;
- The WrpMitigation shim tricks application installers into believing they can write to files that are protected by Windows Resource Protection (WRP). If you try to write to a file that’s protected, the shim first creates a new temporary file, marks it to be deleted once the handle is closed, and then returns the handle to the temporary file as if it were the actual protected file. The application installs the crusty old version of kernel32.dll or shell32.dll (or whichever other file it picked up while it was being packaged) into a temp file, but then that temp file goes away and the matching, patched, up-to-date version of the protected file remains on the file system. So, WRP can still ensure that you don’t end up with an ancient copy of shell32.dll from Windows 95 on your computer, but the installer won’t fail with ACCESS_DENIED when you use this shim.
- The CorrectFilePaths shim can redirect files from one location to another. So, if you have an application that is trying to write to c:\myprogramdir (which isn’t automatically fixed using UAC File and Registry Virtualization), you can redirect the files that are modified at runtime to a per-user location. This allows you to run as a standard user without having to loosen access control lists (ACLs), because you know your security folks hate it when you loosen ACLs.
- There are hundreds of general-purpose shims available to resolve application compatibility issues, and it can be a significant cost saving to be able to take advantage of these fixes. For example, customers often use shims when:
- The vendor is out of business so they have no option to obtain an updated version. If you can’t afford to obtain a different application from a new vendor or build a new version yourself, this can buy you some time.
- The application isn’t important enough to invest in updated versions (which would come with support statements), but because it makes your users happy you don’t mind if they run an unsupported version.
- The application is developed internally but you don’t want to have to wait for the team to release a fully updated version. Instead, you prefer to apply a temporary fix and allow the development team to release the permanent fix in conjunction with its next scheduled release of the application. With this approach, you no longer have to halt all application development activities and devote time and resources to fixing compatibility bugs. You can apply temporary fixes and allow the team to release the fixes with the new functionality it is already in the middle of developing.
Leveraging shims as part of your application compatibility approach can lead to significant cost savings and accelerate a deployment of Windows 7 dramatically.
The Options for Managing Shims in the Enterprise
In my white paper on Managing Shims in the Enterprise from November 2007, I outline the two primary options most people choose between when deciding how to manage shims as an application-compatibility solution in their organization. To briefly recap, they are:
Single, Centrally Managed Shim Database With this option, you deploy a single, centrally managed shim database that contains entries for every application that requires a shim. You can then push out updates to the central database as you discover additional applications that require fixes. This is the approach that Microsoft uses. We shipped Windows 7 with a system shim database fixing thousands of applications, and as we find more we ship updates to the database via Windows Update. You can do the same thing, baking a shim database into your master image and updating via your systems management software (such as System Center Configuration Manager).
Per-Application Shim Databases Alternatively, you can deploy application fixes directly with the application. Some customers choose to do this when they only have one or two applications that require fixes, as it can be less expensive in a small-scale environment than setting up a process for managing a central database. A significant disadvantage of per-application shim databases is that it becomes difficult to update the shim database if there are any problems. However, App-V removes this as a drawback, as now you can update the per-application database and have the updated version streamed out to your users. (Though, as you will see, this introduces another, far more significant drawback.)
The approach that Microsoft App-V uses to deploy software broadens the choices you have for deploying application fixes across the enterprise, and you can choose whichever approach best fits the process model currently in place in your organization.
Shimming App-V Applications with a Single, Centrally Managed Shim Database
So, from a tactical standpoint, what do you have to do in order to deploy a single, centrally managed shim database and have it picked up by an application sequenced using App-V? It’s easy—just install it as you normally would! An application installed using App-V is launched in much the same way that the application normally would be, and uses the same loader mechanisms where shims are applied. They’re just launched by a surrogate process. Specifically, sfttray.exe is what does the work to launch the new process, rather than explorer. The process tree, as a result, is shown in Figure 3.
Figure 3 A Surrogate Process Tree
When the application is launched, it proceeds through the loader as any other application would. The Microsoft Application Virtualization Client Interface Layer (sftintf.dll) makes a call into CreateProcessW, which calls into the internal API CreateProcessInternalW. It is in the CreateProcessInternalW API that the shim engine is invoked and shims are wired up to the process.
OK, so that’s pretty easy. Are there any catches? Well, yes, there is one. It doesn’t handle elevation well. You can’t simply ask for elevation of an application (using a RunAsAdmin shim) or repair problems with applications that require elevation using ElevateCreateProcess, for example. Why? Because of the bubble.
For example, let’s take an application that’s trying to launch an automatic update of itself (unfortunately, an all-too-frequent task). Running natively, say it had a problem in that it used the CreateProcess API, which is unable to invoke elevation. It would then return error -1073740756 – STATUS_ELEVATION_REQUIRED. The ElevateCreateProcess shim would catch this return value and then invoke the Application Information Service to provide the elevation. But this service cannot find the application to elevate, because the service lives outside of the bubble!
So, as long as your application does not require elevation, deploying a single shim database solution is trivially easy if you already have a process worked out—you simply continue doing the same thing.
Shimming App-V Applications with Per-Application Shim Databases
The alternative for deploying shims to resolve application compatibility issues is to deploy the shims with the application. In the MSI world, you would typically include the .sdb file in the installer, and then incorporate a custom action that called sdbinst against the .sdb file it dropped. This would install the custom shim database as part of the installer. (If you subsequently need to update the shims for that application, you need to locate all of your installed clients and run a script to install an update, which complicates things somewhat.)
With App-V, you can do much the same thing. You can include the shim database (.sdb) file into the sequence itself, so the .sdb file lives inside of the bubble. First, you sequence the application that requires shims in order to function properly. Once you’ve done that, I like to drag the custom shim database into system32.
(While normally I advise people to never, ever put anything into system32 unless your paycheck is signed by Steve Ballmer and your reporting chain includes Steven Sinofsky, in this particular instance I choose to do so because it simplifies scripting—system32 will be in your path, and you’re not actually leaving anything in system32 on a production system, just in what looks like system32 to the virtual file system).
Once I’ve done this, I edit the .sprj file. I navigate to the Virtual File System tab and click on View | Virtual File System | Add. Then, I browse to the location where I dropped the .sdb file (c:\windows\system32\appshim.sdb) and click on OK. Now, this .sdb file will be present inside the bubble and it will be installed when I install the softgrid package. Since the file is part of the container, it will be carried around as part of the application (and can be updated when the application is updated).
Of course, simply having the .sdb file on the file system isn’t sufficient to apply the application fixes—they have to be installed. However, the shim database must be installed outside of the App-V bubble. If you install the shim database inside of the bubble, then the virtualized .sdb file won’t be discoverable until after the process has been created, when it’s too late for the shim engine to find it and take action on it. You need to have the shim database accessible to the loader outside of the bubble as the process is being created.
Fortunately, App-V contains a mechanism to run scripts as part of the application launch process. If you take a look at the OSD file for an application using your favorite text editor, you’ll see that it is just XML that can be edited. The <SOFTPGK> element contains an <IMPLEMENTATION> element, a <DEPENDENCY> element, a <PACKAGE> element, an <ABSTRACT> element, a <MGMT_SHORTCUTLIST> element and a <MGMT_FILEASSOCIATIONS> element. The <DEPENDENCY> element is the one you want to edit—you are going to add child <SCRIPT> elements to execute the installation of the custom shim database. Here is an example of a script that can install the custom shim database when the application is launched, and remove it again when the application exits (so it is a JIT installation that cleans up behind itself, an important goal of App-V):
<SCRIPT EVENT="LAUNCH" PROTECT="TRUE" TIMING="PRE" WAIT="TRUE" EXTERN="TRUE">
<SCRIPTBODY LANGUAGE="Batch">sdbinst /q appshims.sdb</SCRIPTBODY>
<SCRIPT EVENT="SHUTDOWN" PROTECT="TRUE" TIMING="POST" WAIT="TRUE" EXTERN="TRUE">
<SCRIPTBODY LANGUAGE="Batch">sdbinst /u appshims.sdb</SCRIPTBODY>
With this script, you will be calling the shim database installer (sdbinst.exe) from outside of the App-V bubble, but using an argument of an .sdb that lives inside of the App-V bubble and is consequently deployed as part of the application deployment sequence. Thus, it is easy to deploy and update the application fixes as if they were any other part of the application.
Installing custom shim databases is a process that requires administrator rights; is it a problem that we’re installing this at runtime? You don’t want to require admin rights for your users, and your users don’t want to have to click on UAC dialogs every time they launch their application. Fortunately, you don’t need to do that; the call to sdbinst.exe is automatically elevated for you. The process tree for installing the shims is shown in Figure 4.
Figure 4 A Shim Installation Process Tree
A command shell is created, which then makes a call to sdbinst.exe to install the custom shim database. This fails twice with STATUS_ELEVATION_REQUIRED before a call is made to the Application Information Service, which is the service that provides elevation. This service calls into consent.exe, which checks the policy to determine whether the application should be elevated. The policy (in the virtual environment) returns yes without having to prompt the user; consent.exe approves the elevation, and an elevated instance of sdbinst.exe is created that installs the custom shim database. But it’s important to note that a policy to elevate without prompting only works silently if you are able to elevate. If you are a standard user, every launch of the application will result in a prompt for administrator credentials, as will every time you close the application. Because this will likely be seen as extremely undesirable in a standard user environment, this approach only works if you have deployed local administrator users. We hope that the percentage of users this applies to is extremely small!
Microsoft Application Virtualization is a powerful application management and deployment tool. When working it in to your plans for a Windows 7 migration, you can benefit from the potential for reduced cost during installation testing (though this cost is not promised to be zero).
You can also continue to leverage most of the same processes for resolving application compatibility issues using shims. If you’re deploying a single, organization-wide shim database, the transition will be very minimal—you need only be careful to avoid elevation shims (in much the same way you should avoid elevation in general in an App-V environment). If you are deploying per-application shims, there are some elegant ways to package and deploy the shim database with the application, but you have the very significant drawback of being unable to reasonably run the application as a non-administrator user. As such, my strong recommendation (as it is with MSI-based application deployments) is to strive to deploy shims as a single, centrally managed database, which you can deploy using your existing systems management software. This enables you to look to a future where a larger and larger percentage of your users will run as non-administrators.
Chris Jackson (“The App Compat Guy”) is a principal consultant at Microsoft and the technical lead of the Windows Application Experience SWAT Team. Jackson is a widely recognized expert in the field of Windows application compatibility, creating the technical documentation, training, and service offerings used inside and outside of Microsoft based on years of real-world experience with enterprise customers and independent software vendors. Jackson can be reached via his popular blog.