Launching an app under Vista with UAP enabled

Discussion in 'Windows Vista Drivers' started by Dan Updegraff, May 2, 2006.

  1. Is it possible to launch an application from a print component under Windows
    Vista while UAP (User Account Protection) is enabled?

    We are developing a printer driver, print processor, and language monitor
    for XP and Vista. Each component launches an executable at some point (e.g.
    the driver launches an app for manual duplexing animations, the print
    processor launches and app for previewing the job, the language monitor
    launches an app for showing job status.)

    The following "execute" function is used to launch an app using the active
    user account's security level. It works perfectly under XP, even when Fast
    User Switched to a Guest account with limited permissions. However, it
    won't work under Windows Vista as long as UAP is enabled.

    FYI - Input Parameters:
    inCmdLine = the full path name of the app to launch
    inOptions.bHideWindow = false
    inOptions.bImpersonateActiveUser = true
    inOptions.bWaitForStart = true
    inOptions.bWaitForFinish = false
    inOptions.bShowWaitCursor = false

    bool execute(LPCTSTR inCmdLine, ExecuteOptions inOptions)
    {
    BOOL bSuccess = FALSE;
    STARTUPINFO startupInfo;
    PROCESS_INFORMATION processInfo;
    SECURITY_ATTRIBUTES attribs;
    SECURITY_DESCRIPTOR sd;

    // Init.
    memset(&startupInfo, 0, sizeof(startupInfo));
    memset(&processInfo, 0, sizeof(processInfo));
    memset(&attribs, 0, sizeof(attribs));
    memset(&sd, 0, sizeof(sd));

    startupInfo.cb = sizeof(startupInfo);

    if (inOptions.bHideWindow)
    {
    startupInfo.dwFlags = STARTF_USESHOWWINDOW;
    startupInfo.wShowWindow = SW_HIDE;
    }

    // Need a security descriptor with a NULL DACL to allow launching
    // applications without running into security problems.
    attribs.nLength = sizeof(SECURITY_ATTRIBUTES);
    attribs.lpSecurityDescriptor = &sd;
    attribs.bInheritHandle = FALSE;

    if (InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
    {
    // Add a NULL DACL to the security descriptor.
    if (SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE))
    {
    // Execute the supplied command line either normally, or
    // while impersonating the active user.
    if (inOptions.bImpersonateActiveUser)
    {
    // Impersonate active user, then execute the command line.
    SECURITY_IMPERSONATION_LEVEL impersonationLevel =
    SecurityImpersonation;
    HANDLE hThreadToken = 0;
    HANDLE hToken = 0;

    BOOL bTmp = ImpersonateSelf(impersonationLevel);
    bSuccess = OpenThreadToken(
    GetCurrentThread(), // ThreadHandle
    TOKEN_ALL_ACCESS, // DesiredAccess
    TRUE, // OpenAsSelf
    &hThreadToken); // TokenHandle
    bTmp = RevertToSelf();

    if (bSuccess)
    bSuccess = DuplicateTokenEx(
    hThreadToken, // hExistingToken
    MAXIMUM_ALLOWED, // dwDesiredAccess
    &attribs, // lpTokenAttributes
    impersonationLevel, //
    ImpersonationLevel
    TokenPrimary, // TokenType
    &hToken); // phNewToken

    if (hThreadToken)
    CloseHandle(hThreadToken);

    if (bSuccess)
    bSuccess = CreateProcessAsUser(
    hToken, // hToken
    0, // lpApplicationName
    (LPTSTR) inCmdLine, // lpCommandLine
    &attribs, //
    lpProcessAttributes
    &attribs, //
    lpThreadAttributes
    FALSE, // bInheritHandles
    DETACHED_PROCESS, // dwCreationFlags
    0, // lpEnvironment
    0, //
    lpCurrentDirectory
    &startupInfo, // lpStartupInfo
    &processInfo); //
    lpProcessInformation

    if (hToken)
    CloseHandle(hToken);
    }
    else
    {
    // Execute the command line without any impersonation.
    bSuccess = CreateProcess(
    0, // lpApplicationName
    (LPTSTR) inCmdLine, // lpCommandLine
    &attribs, //
    lpProcessAttributes
    &attribs, //
    lpThreadAttributes
    FALSE, // bInheritHandles
    DETACHED_PROCESS, // dwCreationFlags
    0, // lpEnvironment
    0, //
    lpCurrentDirectory
    &startupInfo, // lpStartupInfo
    &processInfo); //
    lpProcessInformation
    }

    // If necessary, wait for the process to start/finish.
    if (inOptions.bWaitForStart || inOptions.bWaitForFinish)
    {
    HCURSOR hWaitCursor = ::LoadCursor(NULL,
    MAKEINTRESOURCE(IDC_WAIT));
    HCURSOR hOldCursor = 0;

    if (inOptions.bShowWaitCursor)
    hOldCursor = SetCursor(hWaitCursor);

    // Wait for the process to start running.
    DWORD result = WaitForInputIdle(processInfo.hProcess,
    30000);
    if (result == WAIT_FAILED)
    Sleep(100); // If WaitForInputIdle() fails, this sleep
    gives the app a chance to launch.
    else if (result == 0 && inOptions.bWaitForFinish)
    {
    // Wait for the process to terminate.
    DWORD exitCode = 0;
    MSG msg;

    while (GetExitCodeThread(processInfo.hThread, &exitCode)
    && exitCode == STILL_ACTIVE)
    {
    if (inOptions.bShowWaitCursor)
    SetCursor(hWaitCursor); // Restores the wait
    cursor.
    Sleep(500);

    // Pump Window's messages while we wait.
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    }
    }

    if (inOptions.bShowWaitCursor)
    SetCursor(hOldCursor);
    }
    }
    }

    // Clean up.
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);
    return (bSuccess ? true : false);
    } // execute

    Regards,
    Dan
     
    Dan Updegraff, May 2, 2006
    #1
    1. Advertisements

  2. The proper way would be to create a manifest for your application
    where you describe the elevation requirements, and, then,
    let ShellExecuteEx to inspect the manifest, evaluate the
    elevation requirements, fire-up the Consent-UI prompt,
    and, eventually, run the application.
    While you could re-label the token to upgrade it the proper
    integrity or desktop-integrity level from a service with the proper
    privileges,
    that code already exisit for your convenience in the system.

    This is kind of Out-Of-Topic for driver development, though
     
    Ivan Brugiolo [MSFT], May 2, 2006
    #2
    1. Advertisements

  3. Hi Ivan,

    Thanks for the quick response. If I understand correctly, the Consent-UI
    prompts the user for permission before running th e app. That would be
    rather unpleasant for our users. For every print job they submit with the
    preview feature turned on, or every manual duplexed job, they must accept
    the launch of the preview application (or manual duplex animation) before
    anything would happen.

    Is there another option? We really need a way to launch an app without user
    consent. The only backup plan on the table is to launch all our
    applications at system start and leave them running (hidden in the
    background until they are needed.) I'm sure most users wouldn't appreciate
    that since it eats up their system resources.

    If this is more of a general problem launching applications (not specific to
    launching from a print component,) can you recommend a more appropriate
    newsgroup?

    Thanks,
    Dan
     
    Dan Updegraff, May 2, 2006
    #3
  4. background until they are needed.) I'm sure most users wouldn't appreciate
    Very, very many hardware (ATI) and laptop (Acer) makers to this. The apps are
    tiny and sleep most of the time, so, they will not eat much.
     
    Maxim S. Shatskih, May 2, 2006
    #4
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.