2011-02-04 10 views
24

Win 7/UAC me está volviendo loco.¿Cómo puedo ejecutar un proceso secundario que requiere elevación y esperar?

Desde dentro de mi aplicación C++, necesito ejecutar un archivo ejecutable que requiera elevación en Windows 7. Quiero apagar esto y esperar a que termine antes de continuar. ¿Cuál es la forma más fácil de hacer esto?

Normalmente hago este tipo de cosas a través del CreateProcess(), pero falla para los ejecutables que requieren elevación.

He intentado ejecutar usando cmd.exe /c ... a través de , que funciona pero aparece una ventana de terminal de cmd feo.

Estoy leyendo que ShellExecute() permitirá la elevación, pero no parece ser fácil esperar a que el exe termine al usar ShellExecute(). ¿Funcionará algo simple como system()?

¡Cualquier otra idea es muy apreciada!

Respuesta

39

Use ShellExecuteEx, en lugar de ShellExecute. Esta función proporcionará un identificador para el proceso creado, que puede usar para llamar al WaitForSingleObject en ese identificador para bloquear hasta que el proceso finalice. Finalmente, solo llame al CloseHandle en el identificador del proceso para cerrarlo.

Código de la muestra (la mayor parte de la comprobación de errores se omite para mayor claridad y brevedad):

SHELLEXECUTEINFO shExInfo = {0}; 
shExInfo.cbSize = sizeof(shExInfo); 
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; 
shExInfo.hwnd = 0; 
shExInfo.lpVerb = _T("runas");    // Operation to perform 
shExInfo.lpFile = _T("C:\\MyApp.exe");  // Application to start  
shExInfo.lpParameters = "";     // Additional parameters 
shExInfo.lpDirectory = 0; 
shExInfo.nShow = SW_SHOW; 
shExInfo.hInstApp = 0; 

if (ShellExecuteEx(&shExInfo)) 
{ 
    WaitForSingleObject(shExInfo.hProcess, INFINITE); 
    CloseHandle(shExInfo.hProcess); 
} 

Especificación del verbo "runas" para el lpVerb es lo que causa el UAC para elevar la aplicación que está a punto de ser lanzado. Esto es equivalente a establecer el nivel de permisos en el manifiesto de la aplicación para "requireAdministrator". Se requerirá elevación de UAC tanto para un administrador como para un usuario limitado.

Pero vale la pena señalar que a menos que sea absolutamente necesario, debe preferir la forma "estándar" de agregar un manifiesto a la aplicación que desea iniciar que especifique su nivel de ejecución requerido. Si sigue esta ruta, simplemente pasará "abrir" como lpVerb. Un manifiesto de muestra se muestra a continuación:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
     <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.v2"> 
       <security> 
         <requestedPrivileges> 
           <requestedExecutionLevel 
             level="requireAdministrator" 
             uiAccess="false"/> 
         </requestedPrivileges> 
       </security> 
     </trustInfo> 
</assembly> 

Por último, asegúrese de que cualquier elemento que en su aplicación provoca la ejecución del proceso que requiere elevación de UAC es marked accordingly. Es su trabajo modelar esto en la interfaz de usuario; Windows no lo maneja por ti.Esto se hace mostrando el icono de escudo en el punto de entrada; por ejemplo:

                UAC shield displayed on a button                                                                       UAC shield displayed on a menu item

+0

Esto se ve bien y es muy completo, gracias! Mi hijo exe ya está marcado como "requireAdministrator" en su manifiesto; lo siento, debería haber sido claro al respecto. – KenG

+1

@KenG: De nada; Me alegra que esto funcione para ti. Originalmente asumí que tenías el manifiesto y solo intentabas ejecutar y esperar, pero luego me di cuenta de que tu pregunta en realidad no decía eso, así que volví y agregué algunos detalles más. De esta manera, sospecho que mi respuesta también será útil para otros en el futuro. –

+1

Si la aplicación generada tiene un manifiesto de UAC asociado, entonces puede usar 'CreateProcess()', no necesita usar 'ShellExecutEx()'. 'CreateProcess()' realiza el aviso y la elevación de UAC necesarios si el manifiesto de la aplicación dice que lo haga. –

0

Lo que puede hacer es crear una tubería para el proceso hijo "stdin" (como si fuera a redirigir la entrada a ese proceso) y esperar a que la tubería se rompa.

Tenga en cuenta que el proceso realmente no recibirá la entrada redirigida.

E.g. si trata de aviso de comando unelevated hacer

echo help | diskpart 

verá elevación y la ventana de comandos va a esperar hasta que cierre la ventana separada.

0

Para ejecutar con elevada privilegiada, requiere que el proceso de lanzamiento de este ha elevado privilegiada. Esto generalmente requiere un servicio seguro o algo que ya se está ejecutando para iniciar su proceso. Este es un cambio que comienza con Vista. Tiene que ver con tener la autoridad (a través de tokens ACL) para poder iniciar un proceso y ejecutarlo con el nivel apropiado de privilegios heredado del proceso de lanzamiento. Microsoft ha estado presionando mucho para que las personas creen un proceso elevado que maneje todas las necesidades de funcionalidad elevada y deje el resto en el espacio de usuario con menos privilegios. He estado haciendo esto de vez en cuando ya que Vista era Alpha. Es un dolor, pero la forma en que Microsoft preferiría que hicieras cosas por razones de seguridad.

Por cierto, la llamada ShellExec no funcionará si está iniciando su aplicación desde una ubicación segura como el directorio Archivos de programa, etc. Intentó eso antes de tener que pasar al modelo de servicio hace años.

Así que para poner en marcha su proceso y por pasar UAC, la única manera de hacerlo es poner en marcha de un proceso que ya se tiene la seguridad privilegio de heredar

+0

'ShellExecute/Ex()' con el verbo "runas" especificado puede iniciar procesos elevados desde dentro de la carpeta Archivos de programa si la aplicación que llama no está elevada. Lo hago en uno de mis proyectos, funciona bien. –

Cuestiones relacionadas