The App Compat Guy

Chris Jackson's Semantic Consonance

October, 2009

  • The App Compat Guy

    Carrying Custom Application Sound Events Between Sound Schemes in Windows 7

    • 2 Comments

    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!

  • The App Compat Guy

    Windows 7 Compatibility Center Live on the Web

    • 6 Comments

    Hooray – I can stop pasting this link into emails!

    The Windows 7 Compatibility Center is now live.

  • The App Compat Guy

    Using the uiAccess attribute of requestedExecutionLevel to Improve Applications Providing Remote Control of the Desktop

    • 0 Comments

    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:

    • Do nothing. That’s what our solution did. It just failed every time elevation was involved.
    • Install a service. That’s what company X did. It requires the user to know an admin password, and that’s a problem for my customers
    • Run the application elevated. That’s what company Y did. It requires the user to know an admin password (a problem with my customers), and also won’t allow you to interact with any windows running at System integrity level (so an incomplete solution)

    Here’s what I wish all 3 had done:

    • Manifest with uiAccess = true

    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?

    • Our application, rather conveniently, used an external manifest. All I had to do was open up the manifest in Notepad, and type four characters (t-r-u-e) in the uiAccess attribute. Done. Now it works great. (Of course, everyone who downloads it will download a new broken one, so they’ll have to text edit it too – clearly we want to work with the team that makes this to fix it on their end, but you aren’t stuck – you can fix it without depending on anyone else.)
    • Company x, the one that used a service (claiming of course that it was UACs fault that they had to do this)? I can’t fix it. They used an internal manifest, which has precedence over any external one I might lay down there. I could extract that manifest with mt.exe, edit it, and then re-inject it, but then I invalidate the digital signature. Remember that this is a non-optional requirement for a uiAccess app! So, there is nothing I can do. I’m trying to contact the vendor.
    • Company y, the one that elevates to admin – this one I didn’t have time to examine – they do a “just in time” install and uninstall, so I couldn’t explore the manifest, but since it’s so transitive it’d be hard for me to do much anyway that would last.

    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>

  • The App Compat Guy

    Understanding the AdditiveRunAsHighest Flag on Windows 7

    • 2 Comments

    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.

  • The App Compat Guy

    The Curious Case of the Redundant UAC Policies

    • 1 Comments

    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:

    Acct. Type Account Elevation Policy Secure Desktop Policy Secure Desktop Used?
    Administrator
    Elevate Without Prompting
    Enabled n/a
    Administrator Prompt for credentials on the secure desktop Enabled Yes
    Administrator Prompt for consent on the secure desktop Enabled Yes
    Administrator Prompt for credentials Enabled Yes
    Administrator Prompt for consent Enabled Yes
    Administrator Prompt for consent for non-Windows binaries
    Enabled Yes
    Administrator
    Elevate Without Prompting
    Disabled n/a
    Administrator Prompt for credentials on the secure desktop Disabled Yes
    Administrator Prompt for consent on the secure desktop Disabled Yes
    Administrator Prompt for credentials Disabled No
    Administrator Prompt for consent Disabled No
    Administrator Prompt for consent for non-Windows binaries
    Disabled No
    Standard User
    Automatically deny elevation requests
    Enabled n/a
    Standard User Prompt for credentials on the secure desktop Enabled Yes
    Standard User Prompt for credentials
    Enabled Yes
    Standard User
    Automatically deny elevation requests
    Disabled n/a
    Standard User Prompt for credentials on the secure desktop Disabled Yes
    Standard User Prompt for credentials
    Disabled No
Page 1 of 1 (5 items)