5 votes

Comment verrouiller le poste de travail à partir d'un service Windows ?

Je dois verrouiller le poste de travail à partir d'un service Windows écrit en VB.Net. J'écris l'application sur Windows 7 mais elle doit fonctionner sous Vista et XP également.

L'API User32 LockWorkStation ne fonctionne pas car elle nécessite un bureau interactif et j'obtiens une valeur de retour de 0.

J'ai essayé d'appeler %windir% \System32\rundll32.exe user32.dll,LockWorkStation à partir d'un processus et du shell, mais rien ne se passe.

Configurer le service pour qu'il interagisse avec le bureau est impossible car j'exécute le service sous le compte administrateur pour qu'il puisse faire d'autres choses qui nécessitent des droits d'administrateur - comme désactiver le réseau, et vous ne pouvez sélectionner l'option d'interaction avec le bureau que si vous l'exécutez sous le compte système local.

Ce serait la question secondaire - comment exécuter une autre application avec des droits d'administration à partir d'un service fonctionnant sous le compte système local sans gêner l'utilisateur.

Je suis en train d'écrire une application pour contrôler l'accès à l'ordinateur et à l'Internet de mes enfants (que je prévois d'ouvrir une fois terminée) et j'ai donc besoin que tout se passe aussi discrètement que possible.

J'ai une interface utilisateur qui gère les paramètres et les notifications d'état dans la barre des tâches, mais elle est facile à tuer et donc à vaincre le verrouillage. Je pourrais créer une autre application Windows Forms cachée pour gérer le verrouillage, mais cela semble être une solution plutôt inélégante.

De meilleures idées, quelqu'un ?

6voto

Adriano Pedro Points 46

Je me suis battu avec ce problème pendant plus d'une semaine et après avoir lu beaucoup d'informations sur ce sujet, j'ai finalement trouvé la solution...

Vous devez utiliser la fonction CreateProcessAsUser comme ceci :

  Private Shared Sub Executer(ByVal content As String)
    Dim objProcess As System.Diagnostics.Process

    Dim filename As String
    filename = "e:\lock.bat" 
    'create a bat file with ''rundll32.exe user32.dll,LockWorkStation'' inside

    Dim UserTokenHandle As IntPtr = IntPtr.Zero
    WindowsApi.WTSQueryUserToken(WindowsApi.WTSGetActiveConsoleSessionId, UserTokenHandle)

    Dim ProcInfo As New WindowsApi.PROCESS_INFORMATION
    Dim StartInfo As New WindowsApi.STARTUPINFOW
    StartInfo.cb = CUInt(Marshal.SizeOf(StartInfo))

    WindowsApi.CreateProcessAsUser(UserTokenHandle, filename, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, StartInfo, ProcInfo)
    If Not UserTokenHandle = IntPtr.Zero Then
        WindowsApi.CloseHandle(UserTokenHandle)
    End If

End Sub

La plupart du code provient de ici où vous pouvez, également, trouver le WindowsApi à utiliser avec cette fonction. J'essaie toujours de trouver si on peut éviter le fichier bat mais au moins c'est une solution très décente.

EDIT : Pour éviter d'utiliser un fichier *.bat externe pour exécuter le code, il suffit de modifier la classe WindowsApi et de remplacer la partie CreateProcessAsUser et l'importation de advapi32.dll par ceci :

    <DllImport("Advapi32.dll", EntryPoint:="CreateProcessAsUser", ExactSpelling:=False,      SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Public Shared Function CreateProcessAsUser( _
                       ByVal hToken As IntPtr, _
                       ByVal lpApplicationName As String, _
                       <[In](), Out(), [Optional]()> ByVal lpCommandLine As StringBuilder, _
                       ByVal lpProcessAttributes As IntPtr, _
                       ByVal lpThreadAttributes As IntPtr, _
                       <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandles As Boolean, _
                       ByVal dwCreationFlags As Integer, _
                       ByVal lpEnvironment As IntPtr, _
                       ByVal lpCurrentDirectory As String, _
                       <[In]()> ByRef lpStartupInfo As STARTUPINFOW, _
                       <Out()> ByRef lpProcessInformation As PROCESS_INFORMATION) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

Et maintenant, vous pouvez utiliser un constructeur de chaîne comme troisième argument (ligne de commande) pour la fonction CreateProcessAsUser et mettre le deuxième (nom de l'application) comme 'Rien' comme ceci :

Dim cmdline As New StringBuilder
cmdline.Append("rundll32.exe user32.dll,LockWorkStation")
WindowsApi.CreateProcessAsUser(UserTokenHandle, Nothing, cmdline, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, StartInfo, ProcInfo)

Et ça va marcher !!!!

Regards, AP

2voto

overslacked Points 3033

Ce que vous essayez de faire est activement bloqué par Microsoft. Si vous parveniez à le faire fonctionner, vous exploiteriez une faille qui sera certainement bientôt comblée.

Ce que vous pouvez faire cependant est le Friar Tuck/Robin Hood solution - avoir deux programmes en cours d'exécution qui se surveillent mutuellement. Lorsque l'un d'entre eux est tué, l'autre le détecte et le relance (ou déconnecte simplement l'utilisateur actuel en guise de punition, selon la sévérité que vous souhaitez).

1voto

Michael Burr Points 181287

Une autre solution peu élégante (mais sans les inconvénients de la signalisation de votre application d'interface utilisateur) est d'avoir un autre service qui est installé pour interagir avec le bureau dont le travail est d'écouter un signal pour verrouiller le bureau.

Je suis d'accord pour dire que ce n'est pas génial de devoir utiliser les informations d'identification du système local, mais si le service ne fait que verrouiller le bureau, l'empreinte à sécuriser est assez faible.

0voto

SoftwareGeek Points 2899

Vous pouvez essayer de lancer un économiseur d'écran à partir d'un service Windows qui verrouille automatiquement le poste de travail.

0voto

Brad Mathews Points 83

Je ne suis pas totalement satisfait de ma réponse mais la sécurité de Window ne me laisse guère d'alternative. Tout ce qui est ouvert par le service (par le biais d'un processus, d'un shell ou autre) n'aura pas accès au bureau. Je comprends les raisons derrière les limitations que Microsoft a créées mais c'est quand même frustrant !

Mon service utilise IPC pour dire à mon interface utilisateur de verrouiller l'ordinateur. Voici un lien de base sur la façon de le faire :

http://anoriginalidea.wordpress.com/2007/08/09/simple-inter-process-communication-in-vbnet/

Voir ses liens de référence pour des données supplémentaires.

Cependant, cela ne fonctionne pas encore tout à fait. Consultez également ce lien pour savoir comment procéder sans les messages Access Is Denied :

http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/ce968b5b-04fe-46d2-bb75-73e367a8b0c3

Assurez-vous que vos URI sont corrects. La propriété portName du côté serveur est la première partie du chemin IPC dans l'appel de la méthode GetObject. La deuxième partie correspond au deuxième paramètre de l'appel RegisterWellKnownServiceType du côté serveur.

Et apparemment, les propriétés du nom de port doivent être DIFFÉRENTES du côté du serveur et du côté du client.

Si vous obtenez "Failed to connect to an IPC Port : The system cannot find the file specified." sur votre client, c'est que le serveur n'a pas encore démarré et qu'il n'y a donc rien à entendre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X