Tout d’abord, je tiens à remercier toutes les personnes qui travaillent pour ce site, très utile pour un développeur. C’est la première fois que je suis bloqué dans mon développement depuis 3 jours. J’ai cherché des solutions sur Internet mais je ne trouve rien qui résout ce problème.
Je développe donc un service qui doit exécuter un programme externe sur Vista / Seven / XP lorsqu’un utilisateur est connecté. Quelques caractéristiques de ce service:
Pour exécuter l’application graphique externe en tant qu’utilisateur interactif:
après cela, je lance l’application avec cette fonction:
function RunInteractive(prog_filename: Ssortingng; sessionID: Cardinal): boolean; var hToken: THandle; si: _STARTUPINFOA; pi: _PROCESS_INFORMATION; begin ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); SI.lpDesktop := nil; if WTSQueryUserToken(sessionID, hToken) then begin if CreateProcessAsUser(hToken, nil, PChar(prog_filename), nil, nil, False, 0, nil, PChar(ExtractFilePath(prog_filename)), si, pi) then result := true else result := false; end else Begin result := false; End; CloseHandle(hToken); end;
Ce code est correct dans la plupart des cas sauf un: quand je change d’utilisateur. Permettez-moi de l’expliquer avec 2 utilisateurs simples (Domain \ user1 et Domain \ user2):
Si je le fais X fois, le résultat est toujours le même, très bon … mais si je fais ceci:
Quelque chose ne va pas, mais je ne trouve pas la solution. Merci pour vos réponses …
Vous n’avez pas besoin d’énumérer les processus explorer.exe en cours d’exécution, vous pouvez utiliser WTSGetActiveConsoleSessionId()
, puis transmettre ce SessionId à WTSQueryUserToken()
. Notez que WTSQueryUserToken()
renvoie un jeton d’emprunt d’identité mais CreateProcessAsUser()
besoin d’un jeton principal, utilisez donc DuplicateTokenEx()
pour cette conversion.
Vous devez également utiliser CreateEnvironmentBlock()
pour que le processus généré ait un environnement approprié adapté au compte d’utilisateur utilisé.
Enfin, définissez le champ STARTUPINFO.lpDesktop
sur 'WinSta0\Default'
au lieu de nil
pour que l’interface utilisateur générée puisse être rendue visible correctement.
J’utilise cette approche depuis plusieurs années maintenant et je n’ai eu aucun problème avec elle. Par exemple:
function CreateEnvironmentBlock(var lpEnvironment: Pointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall; external 'userenv.dll' function DestroyEnvironmentBlock(lpEnvironment: Pointer): BOOL; stdcall; external 'userenv.dll'; function RunInteractive(prog_filename: Ssortingng): Boolean; var hUserToken, hToken: THandle; si: _STARTUPINFOA; pi: _PROCESS_INFORMATION; SessionId: DWORD; Env: Pointer; begin Result := False; ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.lpDesktop := 'WinSta0\Default'; SessionId := WTSGetActiveConsoleSessionId; if SessionId = $FFFFFFFF then Exit; if not WTSQueryUserToken(SessionID, hToken) then Exit; try if not DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hUserToken) then Exit; finally CloseHandle(hToken); end; try if not CreateEnvironmentBlock(Env, hUserToken, False) then Exit; try Result := CreateProcessAsUser(hUserToken, nil, PChar(prog_filename), nil, nil, False, CREATE_UNICODE_ENVIRONMENT, Env, PChar(ExtractFilePath(prog_filename)), si, pi); if Result then begin CloseHandle(pi.hThread); CloseHandle(pi.hProcess); end; finally DestroyEnvironmentBlock(Env); end; finally CloseHandle(hUserToken); end; end;
Probablement votre méthode pour obtenir l’ID de session en trouvant le “bon” explorer.exe ne fonctionne pas pour un changement rapide d’utilisateur.
Essayez de faire enregistrer votre application pour les notifications de modification de session avec WTSRegisterSessionNotification . Vous recevrez alors des notifications lorsque la session change, complétez avec l’ID de session en cours.
Notez les points suivants:
Pour recevoir les notifications de modification de session d’un service, utilisez la fonction HandlerEx .