web-dev-qa-db-fra.com

Comment puis-je exécuter un processus enfant qui nécessite une élévation et attendre?

Win 7/UAC me rend fou.

À partir de mon application C++, je dois exécuter un fichier exécutable nécessitant une élévation de Windows 7. Je souhaite activer cette fonctionnalité et attendre qu'elle se termine avant de poursuivre. Quelle est la façon la plus simple de faire cela?

Je fais normalement ce genre de chose via CreateProcess(), mais cela échoue pour les exécutables nécessitant une élévation.

J'ai essayé d'exécuter en utilisant cmd.exe /c ... à CreateProcess, qui fonctionne mais ouvre une fenêtre de terminal cmd moche.

Je lis que ShellExecute() autorisera l’élévation, mais il ne semble pas facile d’attendre la fin du fichier exe lors de l’utilisation de ShellExecute(). Est-ce que quelque chose de simple comme system() fonctionnera?

Toutes les autres idées sont grandement appréciées!

27
KenG

UtilisezShellExecuteEx, plutôt que ShellExecute. Cette fonction fournira un identifiant pour le processus créé, que vous pourrez utiliser pour appelerWaitForSingleObjectsur cet identifiant à bloquer jusqu'à la fin de ce processus. Enfin, appelez simplementCloseHandlesur le descripteur de processus pour le fermer.

Exemple de code (la plupart des erreurs sont omises pour des raisons de clarté et de concision):

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);
}

La spécification du verbe "runas" pour lpVerb est la raison pour laquelle le contrôle de compte d'utilisateur élève l'application sur le point d'être lancée. Cela équivaut à définir le niveau d'autorisation dans le manifeste de l'application sur "requireAdministrator". Il faudra une élévation de compte d'utilisateur pour l'administrateur et un utilisateur limité.

Mais il convient de noter que, sauf si cela est absolument nécessaire, vous devriez préférer la méthode "standard" consistant à ajouter un manifeste à l'application à lancer, spécifiant le niveau d'exécution requis. Si vous allez dans cette voie, vous passerez simplement "ouvert" en tant que lpVerb. Un exemple de manifeste est présenté ci-dessous:

<?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>

Enfin, assurez-vous que tout élément de votre application qui déclenche l'exécution du processus nécessitant une élévation UAC est marqué en conséquence . C'est à vous de modéliser cela dans l'interface utilisateur; Windows ne le gère pas pour vous. Ceci est fait en affichant l'icône de bouclier sur le point d'entrée; par exemple:

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

40
Cody Gray

Ce que vous pouvez faire est de créer un canal pour le processus enfant "stdin" (comme si vous redirigiez l'entrée vers ce processus) et d'attendre que le canal se casse.

Notez que le processus ne recevra pas vraiment l'entrée redirigée.

Par exemple. si vous essayez d'invite de commande sans élévation à faire

echo help | diskpart

vous verrez l'altitude et la fenêtre de commande attendra de fermer la fenêtre séparée.

0
John

Pour s'exécuter avec des privilèges élevés, le processus qui l'engage a des privilèges élevés. Cela nécessite généralement un service sécurisé ou quelque chose qui fonctionne déjà pour lancer votre processus. Ceci est un changement commençant par Vista. Il s'agit de disposer de l'autorité (via vos jetons ACL) pour pouvoir lancer un processus et le lancer avec le niveau approprié de privilèges hérités du processus de lancement. Microsoft s'efforce de faire en sorte que les utilisateurs créent un processus élevé qui gère tous les besoins en fonctionnalités avancées et laisse le reste dans l'espace utilisateur le moins privilégié. Cela fait de temps en temps depuis que Vista était Alpha. C'est pénible, mais Microsoft préférerait que vous agissiez pour des raisons de sécurité.

En passant, l'appel ShellExec ne fonctionnera pas si vous lancez votre application à partir d'un emplacement sécurisé tel que le répertoire Program Files, etc. Essayez de le faire avant de devoir passer au modèle de service il y a des années.

Donc, pour lancer votre processus et contourner le contrôle de compte d'utilisateur, la seule façon de le faire est de démarrer à partir d'un processus qui dispose déjà de la sécurité nécessaire pour hériter.

0
Greg