Today, I just might ruffle a few feathers by doing so, but I figured I’d go through and demonstrate the process of reversing some incompletely documented stuff in Windows in order to fix a problem that was causing me some personal pain.
Specifically, I’m going to talk about sound schemes in Windows 7. With Windows Vista, we had only 1 sound scheme (unless you bought Ultimate, in which case the additional sound schemes were provided as Ultimate Extras). With Windows 7, you get multiple sound schemes.
And for a while, I was terrified of using any of them other than the one I had originally selected.
You see, every time I would change the sound scheme, I would lose sounds in a number of my other programs. Pretty much anything that wasn’t Windows. I like my computer making little beeps and boops all the time (seriously – I still add in the Office 97 sounds, because I like the audio feedback I get when deleting, moving copying – it feels bizarre not to have it after more than a decade of always hearing it).
When we released a whole bunch of new themes for Windows 7 on the day we reached General Availability, I had reached the last straw. The themes all changed my sound scheme, and I don’t want to lose the sound from my other programs thank you very much. So, I set out to figure out how to fix that.
Here’s a blog post that described how to set up the sounds:
A Peek Behind the Beep
Put sounds in .current, eh? Well, time to go spelunking. For the sounds that do switch around, I notice more in the registry than just .Current. I notice that each event not only has a .current subkey, it also has a subkey with the name of each of the sound schemes. So, it seems likely that everyone who adds a new set of application sounds would only affect the current sound scheme, and as soon as you changed it, those settings would go away!
A quick check with process monitor would confirm that:
8:08:59.2736148 PM rundll32.exe 4796 RegOpenKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta SUCCESS Desired Access: Maximum Allowed, Granted Access: All Access 8:08:59.2736558 PM rundll32.exe 4796 RegQueryValue HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta\(Default) BUFFER OVERFLOW Length: 144 8:08:59.2737043 PM rundll32.exe 4796 RegQueryValue HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta\(Default) SUCCESS Type: REG_SZ, Length: 172, Data: C:\Program Files (x86)\Microsoft Office Communicator\Media\COMMUNICATOR_appinvite.wav 8:08:59.2741839 PM rundll32.exe 4796 RegCloseKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta SUCCESS 8:09:00.4227580 PM rundll32.exe 4796 RegOpenKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite SUCCESS Desired Access: Maximum Allowed, Granted Access: All Access 8:09:00.4227946 PM rundll32.exe 4796 RegQueryKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite SUCCESS Query: HandleTags, HandleTags: 0x0 8:09:00.4228254 PM rundll32.exe 4796 RegCreateKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta SUCCESS Desired Access: Set Value 8:09:00.4228632 PM rundll32.exe 4796 RegSetValue HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta\(Default) SUCCESS Type: REG_SZ, Length: 172, Data: C:\Program Files (x86)\Microsoft Office Communicator\Media\COMMUNICATOR_appinvite.wav 8:09:00.4229075 PM rundll32.exe 4796 RegCloseKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\Delta SUCCESS 8:09:00.4229383 PM rundll32.exe 4796 RegQueryKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite SUCCESS Query: HandleTags, HandleTags: 0x0 8:09:00.4229707 PM rundll32.exe 4796 RegCreateKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\.current SUCCESS Desired Access: Set Value 8:09:00.4230081 PM rundll32.exe 4796 RegSetValue HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\.Current\(Default) SUCCESS Type: REG_SZ, Length: 172, Data: C:\Program Files (x86)\Microsoft Office Communicator\Media\COMMUNICATOR_appinvite.wav 8:09:00.4230401 PM rundll32.exe 4796 RegCloseKey HKCU\AppEvents\Schemes\Apps\Communicator\COMMUNICATOR_appinvite\.Current SUCCESS
Yes, indeed – when you change the sound scheme, you copy the values over from the subkey with the new scheme name into the .current key. And none of the program sounds from external programs contained subkeys for each (or any) of the schemes!
So, I’m thinking I’ve reversed the secret to carrying my program sounds over from scheme to scheme – could it be that simple? Only one way to find out – populate them! But, if there were so few that I could do so easily by hand, then I wouldn’t be reversing this stuff in the first place. So, it looks like it’s time for a PowerShell Script! Here’s what I wrote:
foreach ($app in Get-ChildItem -Path "HKCU:\AppEvents\Schemes\Apps") { if (($app.PSChildName -ne ".Default") -and ($app.PSChildName -ne "Explorer") -and ($app.PSChildName -ne "sapisvr")) { foreach ($events in Get-ChildItem -Path $app.PSPath) { $event = $events.PSPath foreach ($soundSchemes in Get-ChildItem -Path "HKCU:\AppEvents\Schemes\Names") { $soundScheme = $soundSchemes.PSChildName if (($soundScheme -ne ".Default") -and ($soundScheme -ne ".None")) { $defaultPath = $event + "\.current" $newPath = $event + "\" + $soundScheme if (Test-Path $newPath) { Remove-Item -Path $newPath } Copy-Item -Path $defaultPath -Destination $newPath } } } } }
I ran this script, and tried changing my sound scheme. Sure enough, it worked! I was able to swap out my sound schemes, and the other applications which extend the sound scheme mechanism in Windows still generate the sounds which makes their use so much more satisfying! (Or, in the case of Communicator, it’s downright critical to the way I use the application.) Hooray!
Now, this is kind of a klugy workaround, which depends on my reversing and guessing correctly on a couple of things. What could we do differently? Well, ideally the application developer would have enumerated the schemes, which is something they wouldn’t have had to do with Windows Vista (except in the case of Ultimate Extras), and added their sound events to each of the schemes. But, it’s a little bit late for that now – these apps already exist. The sound schemes already exit, and there is no API we can intercept – the apps are just writing to the registry. So, if we wanted to do something about this within Windows, we’d have to do something proactive to mitigate the issue with other programs in response to a sound scheme change event. That’s getting a little bit harder to argue for in a service pack, but you never know. It’s worth a try at least.
For now, you can use my workaround, and carry your sounds around with you from scheme to scheme. Happy dinging!
Hooray – I can stop pasting this link into emails!
The Windows 7 Compatibility Center is now live.
I’ve run into this exact same problem 3 times now in one week, so I figure that probably doesn’t bode well and I should attempt to do something about it.
With 3 different pieces of software (one of them ours), the remote control functionality is imperfectly implemented. Let’s see if this sounds familiar to anyone. You are the helpdesk. You attempt to connect to a user’s desktop. You have to elevate an application. When you do, you (the helpdesk who actually has the password) doesn’t see the UAC dialog – instead, the end user (who does not have the password) does. Even if you decide to give the user the password (it happens), you then can’t control or even see the elevated application.
Kind of makes it hard to be a helpdesk when that happens.
Here are the 3 solutions that I have seen to this problem:
Here’s what I wish all 3 had done:
Now, most people don’t really understand what this is for, and the UAC manifest is typically just a copy/paste affair. But it pays for the remote desktop developer to pay attention to it. For any regular piece of software, you generally want to stay away from it – it’s dangerous, and sidesteps a significant security feature (UIPI). But if you are remoting the desktop, it’s precisely what you want – you need to be able to see everything!
It’s dangerous enough, in fact, that we won’t allow you to set it without digitally signing your application. By default, you also have to have it installed in a secure location (such as Program Files). You can set a group policy to not require a secure location, but there is no option to not require a signature.
However, once set up, it’s really powerful. You’ll be able to remote every possible kind of window – any integrity level at all. No more blank, unresponsive screens. Everything comes across, regardless of integrity level.
You’ll also be able to leverage the group policy that lets you prompt NOT on the secure desktop if you are a UIAccess application – that way you don’t have to lose the defense in depth of using the secure desktop for normal elevation, but you also avoid writing code to remote the secure desktop when your remote desktop application is running.
All in all, you are just full of win.
Now, it’s my job to fix up apps that are written suboptimally, so you may be wondering how I did getting these working?
Anyone writing desktop remoting applications, please consider using this. And feel free to contact me if you have questions. I would be delighted to help you.
For the record, here is the corrected manifest for the one I was able to fix:
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Microsoft.FixedUpApp.SupportConsole" type="win32" /> <description>Fixed Up App</description> <dependency> <dependentAssembly> <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*' /> </dependentAssembly> </dependency> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="true"/> </requestedPrivileges> </security> </trustInfo> </assembly>
This post corrects an error from a previous post.
Back when I was explaining the changes in the operating system layers for Windows 7, I incorrectly described the AdditiveRunAsHighest. Since RunAsHighest is already confusing enough, and AdditiveRunAsHighest is even more confusing (even I got it wrong), I want to make sure I actually get it right (and apologize for not having done so before).
AdditiveRunAsHighest requests that the application receives the RunAsHighest flag if and only if nobody else has requested a higher level of elevation.
That means we will overrule a manifest if that manifest is asInvoker, but we will not overrule a manifest if that manifest is requireAdministrator. It also means we will overrule a layer if that layer requests RunAsInvoker, but we will not overrule a layer if that layer requests RunAsAdministrator.
In short, it means that this flag will only be used to increase your level of elevation (to highestAvailable) and will never be used to decrease it (from requireAdministrator).
The reason why this flag exists? Setups. If we think you are a setup, then the setup detection logic applies the VistaSetup layer, which has (as you might imagine) RunAsAdministrator. We don’t want setups to fail for every standard user out there – they still need to prompt. So, this layer will not cause standard users to stop seeing prompts for setups (or manifested apps for that matter). If we had used the existing RunAsHighest flag, then it would have broken setup detection for standard users – and we love our standard users.
One of the unfortunate consequences of actually having to ship your software at some point is that you have to make some compromises along the way. The decisions you make can vary based on the time you are called upon to make them. As frustrating as that is for somebody who is trying to understand the system by trying to reverse how we make decisions, it remains a fact of life.
Case in point: for anybody who has explored the updates to the User Account Control (UAC) policies on Windows 7, you may have noticed that there are multiple policies which appear to govern the exact same thing.
In fact, they do govern the exact same thing.
User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode has the following options:
Elevate Without Prompting Prompt for credentials on the secure desktop Prompt for consent on the secure desktop Prompt for credentials Prompt for consent Prompt for consent for non-Windows binaries (default)
User Account Control: Behavior of the elevation prompt for standard users has the following options:
Automatically deny elevation requests Prompt for credentials on the secure desktop Prompt for credentials (default)
At the same time, you’ll find a policy that configures the secure desktop:
User Account Control: Switch to the secure desktop when prompting for elevation (enabled by default)
What’s going on here – aren’t these directly overlapping?
Well, first let’s help you sort out how to use the policies, next we’ll explain why there are two ways to configure the same thing, and finally we’ll chart out the outcome to give you the easy answers.
A configuration request to use the secure desktop always wins. Whether you configure the admin policy, the standard user policy, or the secure desktop policy, any vote for the secure desktop will cause Windows to use the secure desktop.
So, what happened?
While we were re-doing UAC to make it “less prompty” for Windows 7, we were changing a number of things. We added the ability to exclude Windows binaries. We added a slider instead of an on-off switch. While you are mucking around anyway, you may think, “what if I wanted to have different secure desktop behavior for my standard users than I do for my administrators?” You can’t with only one policy. And it doesn’t take that much additional effort to add a couple of additional options. But you’ll note that there is no secure desktop option for Prompt for consent for non-Windows binaries.
We’d left the secure desktop policy there for application compatibility reasons, but we didn’t use it.
So, we’re happily moving along, when we eventually noticed that there were some accessibility issues in some scenarios when not using the secure desktop, so we needed to have an option to make the “less prompty, but on the secure desktop” setting which, as you can see, doesn’t exist.
Now, if you think about it as if it were your own software, how long would it take you to fix it? It’d take you no time at all to add a new option to the dropdown. You can probably come up with a very easy way to implement the change in consent.exe for reading that policy as well.
But Windows is a complex place. That’s not all you’d have to do. What about the customer experience improvement program? You have a finite, already defined amount of room to feed back all of your configuration and data here. If you change your compression algorithm to incorporate this new option, then you impact all kinds of teams. Could you do it? Sure. But it was at a point in the process when you stop making “blue sky” designs that are the best implementation, and instead fall in the category of “do the minimal change necessary to achieve the required goals”. Architectural changes late in the process aren’t going to win if there are other alternatives. The fastest way was to start using the existing secure desktop policy again. So we did. Better that than to delay shipping.
So, here’s what your secure desktop behavior will be, depending on configuration: