The App Compat Guy

Chris Jackson's Semantic Consonance

October, 2008

  • The App Compat Guy

    Why don’t elevated applications receive environment variables set by non-elevated calling process?

    • 3 Comments

    I had a conversation with a customer (via email) the other day, and I wanted to to into a bit of detail here explaining what is going on.

    Essentially, the customer was attempting to pass information to another application while launching it using environment variables, and it wasn’t working. Of course, it used to work, and it was confusing that it didn’t. Particularly since this failure seems to contradict the following statement directly from the SDK: “By default, a child process inherits a copy of the environment block of the parent process.”

    The launching process was not elevated, but the target process was.

    I recommended using command lines instead, but wanted to illustrate what is happening here. To see it for yourself, you can create the following two programs:

    elevation_launcher.cpp

    #include <windows.h>
    #include <shellapi.h>

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {

        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nShowCmd);

        // Configure the process to launch
        SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };
        sei.lpFile = TEXT("Elevation Target.exe");
        sei.nShow = SW_SHOWNORMAL;

        // Attempt to pass data using environment variables
        SetEnvironmentVariable(TEXT("FromParent"), TEXT("Passed using environment variable"));

        // Attempt to pass data using the command line
        TCHAR szCommandLine[] = TEXT("\"Passed using command line\"");
        sei.lpParameters = szCommandLine;

        // Launch the child app
        if (!ShellExecuteEx(&sei)) {
            DWORD dwStatus = GetLastError();
            // ... handle the error
        } else {
            // ... handle the success
        }
        return 0;
    }

    elevation_target.cpp

    #include <windows.h>

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {

        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nShowCmd);

        // Retrieve and display the parameter passed using environment variables
        PTSTR pszValue = NULL;
        DWORD dwResult = GetEnvironmentVariable(TEXT("FromParent"), pszValue, 0);
        if (dwResult != 0) {
            DWORD size = dwResult * sizeof(TCHAR);
            pszValue = (PTSTR)malloc(size);
            GetEnvironmentVariable(TEXT("FromParent"), pszValue, size);
            MessageBox(NULL, pszValue, TEXT("Environment Variable"), MB_OK | MB_ICONINFORMATION);
            free(pszValue);
        } else {
            MessageBox(NULL, TEXT("The environment variable was not found"), TEXT("Environment Variable"), MB_OK | MB_ICONERROR);
        }

        // Retrieve and display the parameter passed using the command line
        int nNumArgs;
        PWSTR *ppArgv = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
        if (nNumArgs > 1) {
            MessageBox(NULL, ppArgv[1], TEXT("Command Line"), MB_OK | MB_ICONINFORMATION);
        } else {
            MessageBox(NULL, TEXT("The command line was not found"), TEXT("Command Line"), MB_OK | MB_ICONERROR);
        }
        HeapFree(GetProcessHeap(), 0, ppArgv);

        return 0;

    }

    Now, if you manifest both files with an asInvoker reference, both pieces of data are sent to the child process – the environment variable, and the command line. However, if you manifest elevation_target as requireAdministrator, leaving elevation_launcher as asInvoker, you still get the command line, but you lose the environment variable.

    Huh?

    To understand what is going on, you have to understand that, when you elevate, the application, you aren’t actually the parent. Rather, the shell calls into the Application Information Service. This service calls consent.exe, which is what prompts for elevation. Assuming the request is approved, the service then uses the linked elevated token and calls CreateProcessAsUser using the linked token.

    image

    So, the Application Information Service is the parent of the elevated process, not the process that called ShellExecute(Ex). And the elevated process inherits that environment block.

    Of course, it confuses things somewhat that we then reparent the process so it looks like the launching process is the actual parent if you look at the process tree using a tool such as Process Explorer.

  • The App Compat Guy

    Windows Application Compatibility Engagement

    • 1 Comments

    I’m apparently going for a record, trying to see if I can spend the entire month of October jet-lagged. So far, so good (just got back from Singapore)…

    Anyway, I like free stuff as much as far more than the next guy. So, I figured I’d pass this one along – an initiative sponsored by my friend Fumie:

     

    Windows Application Compatibility Engagement (ACE)

    Concerned about application compatibility but don’t know what it takes? 

    Do your critical applications seem incompatible with Vista?

    Sign up for ACE!

    The Application Compatibility Engagement (ACE) program is a 2-3 day fully funded (FREE) on-site consultative engagement for customers who have 100+ PCs, who are considering Vista Deployment, and/or have issues with application compatibility.  One of Microsoft’s ACE partners will go to your site and conduct a full assessment and develop concrete steps toward remediating any incompatible applications.

    • Click  to the Microsoft Application Compatibility Engagement Site
    • Go immediately to Step 2 and fill out the form, OR
    • Email us with the following basic deployment information: 
      • company name
      • contact name
      • contact phone/email
      • seats being deployed
      • number of 3rd party apps
      • number of in house apps
      • the ACE Partner (see step 2) you’d like to work with (or we can pick one for you)
    • A Microsoft representative will contact you to discuss your needs.
    • We’ll organize a conference call to discuss your environment, objectives, and assessment logistics, with the ACE partner, and set an assessment date.

    Once you participate in ACE you would have an opportunity to participate in a Case Study and have it placed on microsoft.com.

  • The App Compat Guy

    The Windows SDK Breaks the New TR1 Extensions in Visual Studio 2008 SP1 (Until you Repair It, That Is)

    • 4 Comments

    One of the main uses for my blog is to share those little annoyances that I spend hours or days solving and spare you the “fun” of going through this yourself. So, even though this isn’t really about application compatibility, which has kind of become the main theme here, it will still hopefully help save somebody some time (thanks to search engines).

    I recently picked up a new laptop (Lenovo T61p, FWIW) and got everything set up according to my typical usage scenarios. I was then going about building some code, and discovered that in the transition, several of my builds had broken.

    Weird, I thought. We recently moved some MFC updates and the addition of TR1 extensions to C++ from a separate feature pack into Visual Studio 2008 SP1. I was getting all of the MFC extension bits. I was missing some of the TR1 extension bits. I indexed all of the header files, and they just plain weren’t there. The shared_ptr class, for example, lived in zero header files on my hard drive.

    What happened?

    The fix, of course, is to just re-install Visual Studio 2008 SP1. But what broke it?

    It turns out that the Windows SDK lays down files not only in C:\Program Files\Microsoft SDKs\Windows\v6.1\Include, but takes the liberty of laying down (older) files in C:\Program Files\Microsoft Visual Studio 9.0\VC\include. Yep – it up and clobbered the SP1 header files and broke my builds.

    So, if you install the Windows Vista SP1 / Windows Server 2008 SDK, you may want to re-install VS2008 SP1 afterwards…

Page 1 of 1 (3 items)