The end of the year is approaching, and I normally like to do a bit of a review. This year, there was one statistic that really stood out for me. I have a car that is a blast to drive, and I love to drive in general. So, I’m not sure exactly how this happened:
Gah! That’s with only 4 international trips (2 of those being quick jaunts to Canada)!
Here’s an interesting story that I hope every developer internalizes: people don’t read what you have to say.
Now, it used to be that people didn’t read the manual until they had no choice. Then it got to the point that people didn’t read the manual ever, so people stopped shipping manuals. But now a lot of people aren’t even reading dialog boxes. Perhaps because there are so many of them, or perhaps because they are so seldom helpful. But, from a practical standpoint, here is what the average app compat dialog box says:
Case in point: I got married a few months ago, and as part of the process I found myself sitting and talking to a florist. My job came up as part of the conversation, so she went out on a limb to ask if I could help her solve a problem. What problem?
Adobe Photoshop was causing a UAC Prompt and she did not know how to make it stop.
Asking her which version of Photoshop she uses, it was the latest (at the time), and I happened to know that it most certainly did not require elevation, so I asked if I could have a look.
Here is what I saw (well, not exactly – the icon is probably a hint that I cheated to make this screenshot, as is the path in the location field, but you get the idea):
So, here is a dialog that people agonized over. “We can help with app compat if we just tell the user what’s happening. I mean, we’re helping, that’s good isn’t it?”
Let’s count the things the user didn’t read:
The title of the dialog box. This isn’t UAC at all. But she’d heard so many TV commercials talking about how UAC annoys you all the time, that she assumed that something prompting all the time must also be UAC.
The name of the app. Got the vendor right, but she wasn’t differentiating between Photoshop and Reader. I see this one a lot – the biggest one being people thinking that Office is part of Windows.
The recommendation to go and fix the problem. We have a big button that offers to help you get it going. Didn’t see it, even though it would have brought her to the website to download the latest (free!) version of the reader with fixes the issues she might have as well as getting rid of the prompt.
The checkbox to make the message go away. Remember, the problem wasn’t that the application wasn’t working right, it was that she hated seeing the prompting all the time. We have a checkbox right there which offers to make it go away – she never read it.
In fact, the only button she read was the one that said “Run program.” The “make it work” button.
It turns out you can’t fix application compatibility issues with words and dialog boxes. Because people don’t read words. This experience taught me a lot about what we ought to do for app compat, and increasing the number of prompts to try to be helpful isn’t the way to do it.
I’m still catching up with requests to talk about stuff – here’s one that came in back in June (and just came again today from somebody else):
“MoveIniToRegistry clearly requires parameters, but there's no documentation defining what these are. (Presumably this fix will apply an IniFileMapping?)”
I haven’t doc’d this shim yet, so here’s the quick and dirty docs on it.
First of all, it doesn’t apply an IniFileMapping. That’d be kind of cool, but that’s something you’d want to do to an installer ideally, since you only need to do it once.
Instead, it intercepts calls to CreateFileA, OpenFile, WriteFile, and CloseHandle. You feed the shim with the name of the files you’re going for, and it ensures that writes happen to both the original file AND to the registry location.
That’s pretty important. The return value from the API is *not* changed by applying this shim. Rather, you still write to the original location, but you also write to a registry location.
For an example, let’s look at Barbie Sticker Designer. Now, this is a program I consider business critical, and I spend the majority of my working days using it. I find it to be a lot less stressful to use than Outlook. How is this shim configured? If we look at it using Compatibility Administrator, you can see the command line used:
%windir%\System.ini * SCRNSAVE.EXE HKEY_CURRENT_USER “Control Panel\Desktop” SCRNSAVE.EXE REG_SZ
The generic argument list is:
IniFile [IniSection] IniKeyName RegBaseKey RegKeyPath RegValue RegValueType
Hopefully that makes the purpose of this shim more clear. It’s still going to write to system.ini (and that writing will drive the return value of the API you are calling), but it’s also going to write the screensaver information to the registry, which is where the system actually looks these days. So, it’s not fixing a permissions issue, it’s fixing an implementation detail (we moved where screensaver configuration is). If we’ve moved something else from an INI to a registry key and don’t already have a fix for it, then you could use this to fix it up as well.
If you were hoping for an easy way to apply an IniFileMapping, well, sorry about that. That’s the problem with these shims – they sometimes have these incredibly tempting sounding names, yet in the end what they actually solve isn’t always what you expected them to solve. I’m trying to get things documented in the order of what’s most useful to you, but keep calling things out to me because I may not always know, and we’ll find out!
Here is a topic I have been saying “I’ll get to it” for a while now…
We’ve talked a lot about UAC here, and I have really stressed the point that standard users shouldn’t be able to affect other users or the machine itself, and if you want to violate that rule then you need to do so explicitly.
The one area that I’ve received some questions on is what to do about shared user data. You should be using c:\programdata (not hard coded, of course!) to put your shared user data into, and then explicitly setting the ACL. You’ll need elevated permissions to set that ACL, so you should be doing so at install time.
Now, here’s the part that makes people nuts (and rightly so!) – we then never bother to tell you how you can set that at install time! At best, we’ll give you some hints. Want to know something interesting? You’d probably be surprised at how many people don’t know how to do this themselves, but nonetheless will happily tell you that it’s what you ought to be doing.
I think that’s kind of rude, so I figured I’d actually spend some time poking around so that when I tell you to do it, I could then answer the follow-up question of, “OK then, how?”.
Of course, installers could be anything, and I don’t know all of the tools (not by a long shot). I’ve never been a packager. I had to pick something, though, so I picked what I thought was best – an MSI. If you’re writing arbitrary code (or a custom action) you can just use the Windows APIs directly to set up the security descriptor. But you actually get OK (note I didn’t say “great”, or even “good”) support from the Windows Installer framework.
But how should I build the MSI? I prefer WIX. One comment talks about using the Visual Studio Setup and Deployment Project. I recommend you do not pass go and do not collect $200 until you install WIX instead. It’s not quite as simple, but it actually exposes the power of the platform instead of simplifying it by not letting you actually use the whole thing.
So, here’s the XML I wrote for WIX to create a folder (which I have to do explicitly since I made an empty one) and set the ACL to allow the Everyone group full control of this folder:
<?xml version="1.0" encoding="UTF-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Product Id="1cf0f45f-3a04-4878-becc-6f6b4331bfb6" Name="InstallerDirectoryPermissions" Language="1033" Version="1.0.0.0" Manufacturer="InstallerDirectoryPermissions" UpgradeCode="f9a6c7b0-6ed9-4b46-9db1-653eeb568236"> <Package InstallerVersion="200" Compressed="yes" /> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="CommonAppDataFolder"> <Directory Id="MySharedFolderId" Name="MySharedFolder"> <Component Id="SharedFolderComponent" Guid="84A264EF-2BC5-41e3-8124-2CA10C2805DB"> <CreateFolder Directory="MySharedFolderId"> <Permission User="Everyone" GenericAll="yes" /> </CreateFolder> </Component> </Directory> </Directory> </Directory> <Feature Id="FolderPermissions" Title="InstallerDirectoryPermissions" Level="1"> <ComponentRef Id="SharedFolderComponent" /> </Feature> </Product> </Wix>
If you compile this to create an MSI, and then edit it with Orca, you’ll see the entries in the Directory, CreateFolder, and LockPermissions tables that make all of this magic happen.
Now, remember how I said that the support was just OK? Well, have a look at what we put into the Permissions entry (which ends up in the LockPermissions table) – it’s just plain English. Well, you’re the one responsible for localizing this. From the docs:
“User - The column that identifies the localized name of the user for which permissions are to be set.”
Why did I choose the Everyone group? Because it’s special cased: “The common user names ‘Everyone’ and ‘Administrators’ may be entered in English and are mapped to well-known SIDs.” (Please note: I don’t speak any other languages, so I don’t have any localized versions of Windows installed – feel free to correct me if you do and I have misinterpreted this!)
But if you just wanted to target users, or domain users, or some other group, and you support multiple languages, you’ll want to do that work inside of a custom action (“A custom action is required to enter the localized name of any other user or group.”). Unless, of course, you already have that value in a property, such as the LogonUser property.
Hopefully this helps you sort out how to do it, instead of us just telling you to “go look it up.” Because you probably have enough to do already.
There’s a big effort going on for Windows 7 to make High DPI more usable by the masses. (You can read an article on the engineering aspect of that here.) You can even switch DPI with nothing more than a simple logoff, rather than the reboot required for Windows Vista. (It’s still not instantaneous, but it is progress.)
So, since I have a 1920 x 1200 screen on one of my laptops, I figured I could give it a go – even at high DPI, I’d be able to fit enough onto my screen in order to get some meaningful work done. So, why not?
My experience so far: text looks awesome. It’s just a pleasure to look at the screen now – everything is so much more readable. (I’m going to find it hard to go back, if I ever do.) I figured I’d share a few tricks.
First, we added a new means of doing DPI scaling. There is the old way, which is to adjust the system metrics and trust the application to handle things correctly, and the new way, which is to let the application run rendering to a 96 dpi off-screen bitmap, and have the desktop compositor resize things to make them bigger (leaving them fuzzy in the process, much like you get the fuzzies when not running at your LCD monitor's native resolution). Oh DWM, is there no end to the gifts you can give to us?
Most people don’t know about these two methods, because we auto-chose them for you until you’re in advanced mode. If you are only going to 125%, then we use the old method – higher scaling switches over to the new (DWM) method. In fact, the basic screen won’t even give you an option that would enable DWM scaling:
You can see the option when you get into the advanced dialog, accessible by clicking on Custom DPI…
What I did was switch up to 125%, but then disable Windows XP style DPI scaling (in other words, enabled DWM scaling – I have absolutely no idea why we use the language we do here, it seems completely confusing).
Two things happen as a result. First, Sidebar, which is incredibly misbehaved at high DPI, seems to work better with DWM scaling (although I have one gadget that loses a tiny bit of some text). Second, it defaults all applications to rendering at 96 DPI and then zooms them up, reducing the number of rendering bugs, but giving me the fuzzies.
And I hate the fuzzies.
An application that wants to just get the scale and opt out of DWM scaling need only manifest itself or call SetProcessDPIAware. I’ve discovered that almost nothing does this. (Office being the notable exception – they are OUTSTANDING at High DPI. Oh, and Windows Live Messenger, which marks itself as high DPI aware despite being laden with High DPI bugs (try using emoticons). I thought perhaps I had marked it somewhere myself by accident, but sure enough:
C:\Users\cjacks>sigcheck -m "c:\Program Files\Windows Live\Messenger\msnmsgr.exe"
sigcheck v1.54 - sigcheck Copyright (C) 2004-2008 Mark Russinovich Sysinternals - www.sysinternals.com
c:\program files\windows live\messenger\msnmsgr.exe: Verified: Signed Signing date: 1:02 AM 9/9/2008 Publisher: Microsoft Corporation Description: Windows Live Messenger Product: Windows Live Messenger Version: 14.0.5027.0908 File version: 14.0.5027.0908 Manifest: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" processorArchitecture="x86" name="Microsoft.MessengerService.Messenger" type="win32" /> <description>MSN Messenger Service</description> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b" /> </dependentAssembly> </dependency> <dependency> <dependentAssembly> <assemblyIdentity version="2.0.0.0" processorArchitecture="x86" name="UCCAPI" type="win32" /> </dependentAssembly> </dependency> <dependency> <dependentAssembly> <assemblyIdentity version="1.0.0.0" processorArchitecture="x86" name="VVPltFrm" type="win32" /> </dependentAssembly> </dependency>
<!-- resolves a crashing bug with the Philips webcam driver --> <file name="vphc700.dll" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo>
<asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/Wind owsSettings"> <dpiAware>true</dpiAware> </asmv3:windowsSettings> </asmv3:application> </assembly>
Yikes. Let’s hope they fix those bugs (I’m using a beta right now).
The big thing I do is to set everything that works right to render at high DPI without being scaled, because as I said I hate the fuzzies. You can do that in one of two ways. First (what I mostly do) you can set up a local custom shim database using Compatibility Administrator and simply apply the HighDPIAware shim or layer to every application that you want to render crisply. If it then misbehaves, you can remove it.
Of course, we knew that most mortals aren’t interested in creating shim databases all the time, so we also allow you to do this from the compatibility tab:
You can simply check the “Disable display scaling on high DPI settings” to apply the HighDPIAware layer to the process, and you’ll be on your way.
For people writing software, I’d sure love it if High DPI entered the test matrix. And, on our end, we should be providing more guidance. Together, we can get there, and we’ll have insanely dense displays with beautiful text. But, if I never lived it, I could never feel the pain, or enjoy the pleasures of the experience.
The same question came up two times in 26 minutes (on the same discussion list, no less), so I figured I’d answer it once here as that seems a reasonable indicator that others may have the same question.
What is the deal with shimming on Windows Vista 64-bit?
Well, it turns out it’s a bit of a mixed story, so let’s go through it.
First, Compatibility Administrator itself runs just fine on x64. It’s a 32-bit app, so it’s running on WOW64, but that’s OK. You can run the tool to create custom shim databases to run on either x86 or x64.
Now, let’s talk about the shim engine. It’s installed on x64, and it supports shimming both 32-bit and 64-bit binaries. So far, so good.
But once we start talking about the custom shim databases you can create today using Compatibility Administrator, the story is not complete today.
You see, the custom shim databases you create (whether you create them on x86 or x64) will only shim 32-bit applications. So, while the platform supports shimming 64-bit applications, the tools don’t give you the ability to do so.
So, if you have a native 64-bit application that isn’t working on Windows Vista, then you are unable to use shims today in order to mitigate the problems you discover.
I haven’t come across any problems with native code doing this, but I have come across problems with managed code. There just isn’t much native code that is compiled for 64-bit (and what is available tends to be fairly recent). Managed code, however, defaults to compiling for “any CPU” – which means that the JIT compiler will compile native 64-bit code for managed code when it’s running on 64-bit. So, you can fix it on x86, but not on x64, even though the same app runs on both.
Well, that’s not very cool. That just might discourage you from moving to x64 if you have a broken managed code application, eh?
Well, there are two solutions for that. Obviously you could recompile the application and change the compiler flags to target 32-bit only (but if you’re doing that, you may as well fix the bugs!). If you can’t recompile, however, you’re not stuck. Just use corflags.exe to set the 32BIT flag, and you’ll be on your way.
By the way, if you really want to get under the covers, you can. The underlying cause is that the XML we generate doesn’t have the (optional) OS_PLATFORM attribute. Try it yourself – if you look in Process Monitor, you’ll see that compatadmin creates an XML file in appdata\local\temp. Of course, it deletes this file when it’s done, so you’ll want to catch it before we’re done with it – a quick glance at the stack tells you that you’ve got msxml3.dll in the stack when you’re creating this, so if you bm on msxml3!* and start stepping until the call to CloseFile, you can go into the temp directory and pull out the XML to see for yourself.