c++ - secundario - sector terciario
¿Cómo puedo ejecutar un proceso secundario que requiere elevación y esperar? (3)
Win 7 / UAC me está volviendo loco.
Desde dentro de mi aplicación C ++, necesito ejecutar un ejecutable que requiera elevación en Windows 7. Quiero desconectarlo 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 de CreateProcess()
, pero falla para los ejecutables que requieren elevación.
Traté de ejecutar usando cmd.exe /c ...
través de CreateProcess
, que funciona pero aparece una ventana de terminal de cmd feo.
Estoy leyendo que ShellExecute()
permitirá la elevación, pero no parece fácil esperar a que el ejecutable finalice al usar ShellExecute()
. ¿Funcionará algo simple como system()
?
¡Cualquier otra idea es muy apreciada!
Lo que puede hacer es crear un conducto para el proceso hijo "stdin" (como si fuera a redirigir la entrada a ese proceso) y esperar a que se rompa la tubería.
Tenga en cuenta que el proceso realmente no recibirá la entrada redirigida.
Por ejemplo, si intenta desde el símbolo del sistema no elevado, haga
echo help | diskpart
verá elevación y la ventana de comandos esperará hasta que cierre la ventana por separado.
Para ejecutar con privilegios elevados, requiere que el proceso de lanzamiento tenga privilegios elevados. 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 de Archivos de Programa, etc. Intenté eso antes de tener que pasar al modelo de servicio hace años.
Entonces, para iniciar su proceso y pasar el UAC, la única forma de hacerlo es iniciar desde un proceso que ya tiene la seguridad privilegiada para heredar
Use ShellExecuteEx
, en lugar de ShellExecute
. Esta función proporcionará un identificador para el proceso creado, que puede usar para llamar a WaitForSingleObject
en ese identificador para que se bloquee hasta que el proceso finalice. Finalmente, simplemente llame a CloseHandle
en el identificador del proceso para cerrarlo.
Código de muestra (la mayor parte de la comprobación de errores se omite por 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);
}
Especificar el verbo "runas" para lpVerb
es lo que hace que UAC eleve la aplicación que está a punto de iniciarse. 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 realiza esta ruta, simplemente pasará "abierto" 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>
Finalmente, asegúrese de que cualquier elemento de su aplicación que desencadena la ejecución del proceso que requiere elevación UAC se marque en consecuencia . 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: