web-dev-qa-db-fra.com

Comment puis-je exécuter un programme EXE à partir d'un service Windows en utilisant C #?

Comment puis-je exécuter un programme EXE à partir d'un service Windows utilisant C #?

Ceci est mon code:

System.Diagnostics.Process.Start(@"E:\PROJECT XL\INI SQLLOADER\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");

Lorsque j'exécute ce service, l'application ne démarre pas.
Qu'est ce qui ne va pas avec mon code?

38
xoops

Cela ne fonctionnera jamais, du moins pas sous Windows Vista ou ultérieur. Le problème clé est que vous essayez de l'exécuter à partir d'un service Windows plutôt que d'une application Windows standard. Le code que vous avez montré fonctionnera parfaitement dans une application Windows Forms, WPF ou Console, mais ne fonctionnera pas du tout dans un service Windows.

Les services Windows ne peuvent pas démarrer d'autres applications car elles ne s'exécutent pas dans le contexte d'un utilisateur particulier. Contrairement aux applications Windows classiques, les services sont exécutés dans une session isolée et il leur est interdit d'interagir avec un utilisateur ou le bureau. Cela ne laisse aucune place pour l'application à exécuter.

Plus d'informations sont disponibles dans les réponses aux questions suivantes:

La meilleure solution à votre problème, comme vous l'avez probablement déjà compris, consiste à créer une application Windows standard au lieu d'un service. Celles-ci sont conçues pour être exécutées par un utilisateur particulier et sont associées au bureau de cet utilisateur. De cette façon, vous pouvez exécuter des applications supplémentaires quand vous le souhaitez, en utilisant le code que vous avez déjà affiché.

Une autre solution possible, en supposant que votre application console ne nécessite aucune interface ou sortie, consiste à indiquer au processus de ne pas créer de fenêtre. Cela empêchera Windows de bloquer la création de votre processus, car il ne demandera plus qu'une fenêtre de console soit créée. Vous pouvez trouver le code correspondant dans cette réponse à une question connexe.

61
Cody Gray

j'ai essayé cet article Code Project , ça marche très bien pour moi ..__ J'ai aussi utilisé le code. L'article est excellent en explication avec capture d'écran.

J'ajoute l'explication nécessaire à ce scénario

Vous venez de démarrer votre ordinateur et vous êtes sur le point de vous connecter. Lorsque vous vous connectez, le système vous attribue un identifiant de session unique. Sous Windows Vista, le premier utilisateur à ouvrir une session sur l'ordinateur se voit attribuer un identifiant de session de 1 par le système d'exploitation. Le prochain utilisateur à se connecter se verra attribuer un identifiant de session de 2, et ainsi de suite. Vous pouvez afficher l'ID de session attribué à chaque utilisateur connecté à partir de l'onglet Utilisateurs du Gestionnaire de tâches . enter image description here

Mais votre service Windows est soumis à l'ID de session 0. Cette session est isolée des autres sessions. Cela empêche finalement le service Windows d'appeler l'application qui s'exécute sous une session utilisateur telle que 1 ou 2.

Afin d'appeler l'application à partir du service Windows, vous devez copier le contrôle de winlogon.exe qui agit en tant qu'utilisateur connecté présent, comme indiqué dans la capture d'écran ci-dessous . enter image description here

Codes importants

// obtain the process id of the winlogon process that 
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
    if ((uint)p.SessionId == dwSessionId)
    {
        winlogonPid = (uint)p.Id;
    }
}

// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
    CloseHandle(hProcess);
    return false;
}

// Security attibute structure used in DuplicateTokenEx and   CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just 
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and   therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);

// copy the access token of the winlogon process; 
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
    (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
      CloseHandle(hProcess);
      CloseHandle(hPToken);
      return false;
    }

 STARTUPINFO si = new STARTUPINFO();
 si.cb = (int)Marshal.SizeOf(si);

// interactive window station parameter; basically this indicates 
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";

// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

// create a new process in the current User's logon session
 bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token
                            null,             // file to execute
                            applicationName,  // command line
                            ref sa,           // pointer to process    SECURITY_ATTRIBUTES
                            ref sa,           // pointer to thread SECURITY_ATTRIBUTES
                            false,            // handles are not inheritable
                            dwCreationFlags,  // creation flags
                            IntPtr.Zero,      // pointer to new environment block 
                            null,             // name of current directory 
                            ref si,           // pointer to STARTUPINFO structure
                            out procInfo      // receives information about new process
                            );
7

Vous pouvez très bien exécuter un fichier .exe à partir d’un service Windows dans Windows XP. Je l'ai fait moi-même dans le passé.

Vous devez vous assurer que vous avez coché l'option "Autoriser l'interaction avec le Bureau" dans les propriétés du service Windows. Si cela n'est pas fait, il ne sera pas exécuté.

Je dois vérifier sous Windows 7 ou Vista car ces versions nécessitent des privilèges de sécurité supplémentaires, ce qui peut générer une erreur, mais je suis certain que cela peut être réalisé directement ou indirectement. Pour XP, je suis certain de l'avoir fait moi-même.

2
Depinder Bharti

Tout d'abord, nous allons créer un service Windows qui s'exécute sous le fichier Compte système. Ce service sera responsable de la génération d'un processus interactif dans la session utilisateur actuellement active. Ce Le processus nouvellement créé affiche une interface utilisateur et s'exécute avec un administrateur complet droits. Lorsque le premier utilisateur ouvre une session sur l'ordinateur, ce service sera être démarré et sera exécuté dans Session0; Cependant, le processus qui ce service spawns sera exécuté sur le bureau de la version en cours Utilisateur connecté. Nous nous référerons à ce service en tant que LoaderService.

Ensuite, le processus winlogon.exe est responsable de la gestion de la connexion utilisateur et procédures de déconnexion. Nous savons que chaque utilisateur qui se connecte au l’ordinateur aura un identifiant de session unique et un .__ correspondant. winlogon.exe associé à leur session. Maintenant, nous avons mentionné ci-dessus, LoaderService s'exécute sous le compte système. Nous avons aussi a confirmé que chaque processus winlogon.exe sur l'ordinateur s'exécute sous le compte système. Parce que le compte système est le propriétaire des deux LoaderService et les processus winlogon.exe, notre LoaderService peut copier le jeton d'accès (et l'ID de session) du processus winlogon.exe puis appelez la fonction CreateProcessAsUser de l'API Win32 pour lancer un fichier processus dans la session actuellement active de l'utilisateur connecté. Puisque l'identifiant de session situé dans le jeton d'accès du copié processus winlogon.exe est supérieur à 0, nous pouvons lancer un interactif processus en utilisant ce jeton.

Essayez celui-ci . Subvertir Vista UAC dans les architectures 32 et 64 bits

2
Elshan

Je pense que vous copiez le fichier .exe vers un emplacement différent. Cela pourrait être le problème, je suppose. Lorsque vous copiez le fichier .exe, vous ne copiez pas ses dépendances.

Donc, ce que vous pouvez faire est de mettre toutes les dll dépendantes dans GAC afin que tout exe .net puisse y accéder.

Sinon, ne copiez pas l'exe dans un nouvel emplacement. Créez simplement une variable d’environnement et appelez le fichier exe dans votre c #. Comme le chemin est défini dans les variables d’environnement, votre programme c # peut accéder au fichier .exe.

Mettre à jour:

auparavant, j’avais une sorte de même problème dans mon projet c # .net 3.5 dans lequel j’essayais d’exécuter un fichier .exe à partir de code c # .net et cet exe n’était rien, mais un autre exe de projet (où j’ai ajouté quelques dll fonctionnalité) et les méthodes DLL que j'utilisais dans mon application exe. Enfin, j'ai résolu ce problème en créant cette application en tant que projet distinct pour la même solution et j'ai ajouté cette sortie de projet à mon projet de déploiement. Selon ce scénario, j'ai répondu: «Si ce n'est pas ce qu'il veut, je suis extrêmement désolé.

0
SharpUrBrain

vous pouvez utiliser le planificateur de tâches Windows à cette fin. Il existe de nombreuses bibliothèques telles que TaskScheduler qui vous aident.

par exemple, considérons que nous voulons programmer une tâche qui sera exécutée une fois cinq secondes plus tard:

using (var ts = new TaskService())
        {

            var t = ts.Execute("notepad.exe")
                .Once()
                .Starting(DateTime.Now.AddSeconds(5))
                .AsTask("myTask");

        }

notepad.exe sera exécuté cinq secondes plus tard.

pour plus de détails et plus d'informations s'il vous plaît aller à wiki

si vous savez de quelle classe et de quelle méthode vous avez besoin dans cette Assemblée, vous pouvez l'invoquer vous-même de la manière suivante:

        Assembly assembly = Assembly.LoadFrom("yourApp.exe");
        Type[] types = Assembly.GetTypes();
        foreach (Type t in types)
        {
            if (t.Name == "YourClass")
            {
                MethodInfo method = t.GetMethod("YourMethod",
                BindingFlags.Public | BindingFlags.Instance);
                if (method != null)
                {
                    ParameterInfo[] parameters = method.GetParameters();
                    object classInstance = Activator.CreateInstance(t, null);

                    var result = method.Invoke(classInstance, parameters.Length == 0 ? null : parameters);

                    break;
                }
            }

        }
0
Rahmat Anjirabi