Centrum Skrypciarzy - Systemy Operacyjne

Jak rozpocząć proces, a po jego ukończeniu wylogować użytkownika?

Udostępnij na: Facebook

Skrypciarze odpowiadają na Wasze pytania

Cześć Skrypciarze!

Witamy w rubryce TechNet, w której Skrypciarze z firmy Microsoft odpowiadają na częste pytania dotyczące używania skryptów w administracji systemu. Jeśli macie jakieś pytania z tej dziedziny, zachęcamy do wysłania e-maila na adres: scripter@microsoft.com. Nie możemy zagwarantować odpowiedzi na każde otrzymane pytanie, ale staramy się jak możemy.

Jak rozpocząć proces, a po jego ukończeniu wylogować użytkownika?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Muszę napisać skrypt rozpoczynający proces i czekający do jego zakończenia, a następnie wylogowujący użytkownika.

-- AG

Cześć Skrypciarze! Odpowiedź

Cześć, AG. Wiesz co, Skrypciarz piszący te słowa nie lubi się chwalić – głównie dlatego, że rzadko ma czym. Tak jest i tym razem; nie pozostaje więc nic innego, jak tylko przejść od razu do skryptu:

strComputer = "."



Set objWMIService = GetObject _

    ("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")

 

objWMIService.Create "Notepad.exe", null, null, intProcessID



Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")



Set colItems = objWMIService. _

    ExecNotificationQuery("Select * From __InstanceDeletionEvent " _ 

            & "Within 1 Where TargetInstance ISA 'Win32_Process'")

Do 

    Set objProcess = colItems.NextEvent

    If objProcess.TargetInstance.ProcessID = intProcessID Then

        Exit Do

    End If

Loop



Set objWMIService = GetObject("winmgmts:{(Shutdown)}\\" & _

        strComputer & "\root\cimv2")



Set colItems = objWMIService.ExecQuery _

    ("Select * from Win32_OperatingSystem")

 

For Each objItem in colItems

    objItem.Win32Shutdown(0)

Next

No cóż, nie da się ukryć, że skrypt jest raczej z tych długich. Nie powstrzyma nas to jednak przed jego omówieniem. Jak widać, zaczynamy od połączenia z usługą WMI komputera lokalnego. Warto tu zwrócić uwagę na dwie rzeczy. Otóż po pierwsze, łączymy się bezpośrednio z klasą Win32_Process; stąd się bierze fragment \root\cimv2\:Win32_Process poniższego polecenia:

Set objWMIService = GetObject _

    ("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")

Po drugie, zwykle omawiając skrypt WMI mówimy: „cośtam cośtam cośtam, możemy równie dobrze uruchomić go na komputerze zdalnym”. Czy tak jest i tym razem? Nie do końca. Skrypt zadziała bez problemu na komputerze zdalnym, ale proces zostanie uruchomiony w oknie ukrytym. Nie będzie w ogóle widoczny – czy to na komputerze zdalnym, czy gdziekolwiek indziej. Jeśli aplikacja nie wymaga udziału użytkownika, nie będzie z tym kłopotu, jednak w przeciwnym wypadku zdalne uruchomienie skryptu będzie bez sensu – i tak nikt nie będzie mógł użyć uruchomionej aplikacji.

Jest to spowodowane względami bezpieczeństwa i nic się nie da na to poradzić.

Po połączeniu z usługą WMI (i klasą Win32_Process), za pomocą poniższego wiersza uruchamiamy aplikację (w przykładzie używamy Notatnika):

objWMIService.Create "Notepad.exe", null, null, intProcessID

Jak widać, nie jest to szczególnie skomplikowane – wywołujemy metodę Create, podając także nazwę procesu, który chcemy utworzyć. Po tej nazwie podajemy dwa parametry o wartości zerowej (Null) – są to opcjonalne parametry, które nas nie interesują – a następnie parametr wyjściowy o nazwie intProcessID.

Parametr wyjściowy jest kluczem do działania skryptu. Jest to po prostu zmienna przekazana metodzie, która przypisuje jej wartość. W wypadku metody Create, jest to identyfikator procesu nowego wystąpienia Notatnika. Jak się zapewne domyślacie, dzięki temu będziemy mogli później sprawdzić, czy aplikacja zakończyła działanie – zniknięcie identyfikatora równa się zamknięcie aplikacji.

Po uruchomieniu aplikacji musimy poczekać do końca jej działania. W tym celu łączymy się ponownie z usługą WMI (tym razem wyłącznie z przestrzenią nazw root\cimv2), po czym tworzymy nową kwerendę WMI:

Set colItems = objWMIService. _

    ExecNotificationQuery("Select * From __InstanceDeletionEvent " _ 

            & "Within 1 Where TargetInstance ISA 'Win32_Process'")

Tworzymy tu tak zwaną subskrypcję zdarzeń – instruujemy WMI, by wysyłało nam powiadomienie o powstaniu nowych wystąpień klasy __InstanceDeletionEvent; jak wskazuje nazwa, wystąpienia te są tworzone w momencie usunięcia obiektu.

Nie chcemy jednak otrzymywać powiadomień o usunięciu każdego obiektu, a tylko takich (TargetInstance), które należą do klasy Win32_Process – czyli procesów.

Uwaga. Klasa __InstanceDeletionEvent? TargetInstance? Jeśli ktoś nie ma pojęcia , o czym mowa, zapraszamy do obejrzenia webcastu (j.ang.) o zdarzeniach WMI.

Po utworzeniu subskrypcji zdarzeń, zakładamy działającą bez końca pętlę Do (zwróćmy uwagę na brak kryteriów końcowych):

Do 

    Set objProcess = colItems.NextEvent

    If objProcess.TargetInstance.ProcessID = intProcessID Then

        Exit Do

    End If

Loop

Pamiętajmy jednak, że nic nie może wiecznie trwać. Nawet pętla Do. Czekamy do usunięcia procesu, co spowoduje wysłanie powiadomienia oraz przejście skryptu poza poniższy wiersz kodu (który blokuje skrypt w miejscu aż do zajścia interesującego nas zdarzenia):

Set objProcess = colItems.NextEvent

Kiedy dojdzie do takiego zdarzenia, sprawdzamy, czy wartość właściwości ProcessID obiektu TargetInstance jest równa wartości intProcessID, czyli identyfikatorowi procesu uruchomionej wcześniej aplikacji:

If objProcess.TargetInstance.ProcessID = intProcessID Then

Jeśli wartości te będą różne, będzie to znaczyło, że zakończony proces nie był tym, który nas interesuje. Wracamy więc i czekamy na zamknięcie kolejnego procesu.

Jeśli jednak identyfikatory będą takie same, będzie to znaczyło, że zamknięto ten właśnie proces, który wcześniej uruchomiliśmy za pomocą skryptu. Czas więc się wylogować.

W tym celu wywołujemy twierdzenie Exit Do, które zakończy pętlę. Następnie uruchamiamy poniższy fragment kodu:

Set objWMIService = GetObject("winmgmts:{(Shutdown)}\\" & _

        strComputer & "\root\cimv2")



Set colItems = objWMIService.ExecQuery _

    ("Select * from Win32_OperatingSystem")

 

For Each objItem in colItems

    objItem.Win32Shutdown(0)

Next

Łączymy się tu jeszcze raz z usługą WMI, uwzględniając uprawnienie Shutdown. (Uprawnienie to moglibyśmy uwzględnić wcześniej, a teraz po prostu użyć odwołania do obiektu. Ale w ten sposób jest chyba prościej.) Po połączeniu możemy użyć poniższej kwerendy, która pobierze wszystkie wystąpienia klasy Win32_OperatingSystem. (Ponieważ klasa ta może pobiera tylko i wyłącznie informacje o systemie operacyjnym, w kolekcji będzie zawsze jeden element.)

Set colItems = objWMIService.ExecQuery _

    ("Select * from Win32_OperatingSystem")

Teraz uruchamiamy pętlę For Each, która przejdzie przez wszystkie elementy kolekcji. Dla każdego z nich wywołujemy metodę Win32Shutdown, podając jako parametr wartość 0:

objItem.Win32Shutdown(0)

Parametr ten instruuje metodę Win32Shutdown, by wylogować użytkownika. Moglibyśmy też podać inne parametry powodujące ponowne uruchomienie albo wyłączenie komputera. Informacje na ten temat można znaleźć w przewodniku Microsoft Windows 2000 Scripting Guide (j.ang.).

I to już wszystko, AG.

 Do początku strony Do początku strony

Centrum Skrypciarzy - Systemy Operacyjne