Hey, Scripting Guy!

Hey, Scripting Guy!

新しい TechNet コラムへようこそ。このコラムでは、よく寄せられるシステム管理スクリプトに関する質問に Scripting Guys がお答えします。システム管理スクリプトについて質問がある場合は、scripter@microsoft.com (英語のみ) までお送りください。すべての質問に回答することはできないかもしれませんが、可能な限り対応いたします。

今日の質問 : スクリプトを使用してワークステーションをロックすることはできますか。


スクリプトを使用してワークステーションをロックすることはできますか。

Q

Scripting Guy さん、よろしくお願いします。スクリプトを使用してコンピュータをシャットダウンする方法を知っています。スクリプトを使用してユーザーをログオフする方法も知っています。しかし、スクリプトを使用してワークステーションをロックする方法はあるでしょうか。

-- TO-R

A

TO-R さん、こんにちは。WMI クラス Win32_OperatingSystem の Win32Shutdown メソッドに詳しいようですね。このメソッドを使用するとコンピュータのシャットダウンやリブートはもちろん、現在のユーザーをログオフすることも可能です。たとえば、次のスクリプトでコンピュータがシャットダウンします。

Const SHUTDOWN = 1

strComputer = "."
Set objWMIService = GetObject_
    ("winmgmts:{impersonationLevel=impersonate,(Shutdown)}\\" & _
        strComputer & "\root\cimv2")

Set colOperating Systems = objWMIService.ExecQuery _
    ("Select * from Win32_OperatingSystem")
 
For Each objOperatingSystem in colOperatingSystems
    objOperatingSystem.Win32Shutdown(SHUTDOWN)
Next

Win32Shutdown メソッドでコンピュータのロックはできません。WMI にはコンピュータをロックできるようにする手段はない、と言ってもいいくらいです。実際、スクリプトを使用してコンピュータをロックするために私たちが編み出した方法は、Rundll32.exe を使用して user32.dll の LockWorkStation メソッドを呼び出すだけのスクリプトを記述するくらいでした。

On Error Resume Next

Set objShell = CreateObject("Wscript.Shell")
objShell.Run "%windir%\System32\rundll32.exe user32.dll,LockWorkStation"

このスクリプトはローカル コンピュータでは効果的に機能しますが、リモート コンピュータをロックしようとする場合はあまり便利ではありません。なぜなら、Rundll32.exe の実行時は、リモート コンピュータ名を指定できないからです。けれども、大丈夫です。このスクリプトがローカルでのみ動作するのであれば、必ずリモート コンピュータ上でローカルに実行されるようにすればよいのです。そのようなことができるのでしょうか。もちろんできます。

まず、先ほどのスクリプト (実際にコンピュータをロックするスクリプト) を Lock_workstation.vbs として保存します。話を単純化するため、ここでは C:\Scripts に保存します。次に、2 つ目のスクリプト (名前は自由に付けてください) を作成し、次のコードを 2 つ目のスクリプトに貼り付けます。

Const OverwriteExisting = TRUE

Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.CopyFile "C:\Scripts\lock_workstation.vbs", _
    "\\atl-ws-01\C$\Scripts\", OverWriteExisting

strComputer = "atl-ws-01"
Set objWMIService = GetObject _
("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")

Error = objWMIService.Create _
    ("cscript c:\scripts\lock_workstation.vbs", null, null, _
        intProcessID)

このコードは何を実行するのでしょうか。まず、1 つ目のスクリプト C:\Scripts\Lock_workstation.vbs をリモート コンピュータ atl-ws-01 の C:\Scripts フォルダにコピーします。ここでは 2 つの点に注意してください。1 つは、このスクリプトでは、リモート コンピュータの管理共有 (C$) を無効にしないことを前提にしています。もう 1 つは、フォルダ C:\Scripts がリモート コンピュータに存在すると仮定しています (もちろん、ファイルを必ず C:\Scripts にコピーしなければならない理由は何もありません。好きなフォルダにコピーできます)。

スクリプトの 1 番目の部分が実行されると、ファイル Lock_workstation.vbs がリモート コンピュータの C:\Scripts フォルダに配置されます。スクリプトの 2 番目の部分では、リモート コンピュータ上の WMI サービスに接続し、Create メソッド (これはたまたま Win32_Process クラスの一部になっています) を呼び出します。さて、Create メソッドで具体的に何ができるのでしょうか。ここではスクリプト Lock_workstation.vbs を実行するだけです。しかし、この操作のすばらしいところは、Create メソッドによってスクリプトがリモート コンピュータで実行される、ということです。スクリプトがリモート コンピュータで実行されるため、Rundll32.exe が動作し、したがってリモート コンピュータがロックされます。ぜひ自分で試してみてください。

メモ 注意点があります。このスクリプトは、リモート コンピュータにログオンしたユーザーのセキュリティ コンテキストのもとで実行されます。そのユーザーがコンピュータをロックする権限を持っていなければ、スクリプトは失敗します。

もちろん、ここで少なくとも 2 つの質問が思い付きます。まずは簡単なものから取り上げましょう。ワークステーションをロック解除するスクリプトは記述できるでしょうか。私たちの知る限りでは、できません。実際、私たちの知る限り、ワークステーションをロック解除するには、だれかがコンピュータのある場所に足を運び、Ctrl + Alt + Del キーを押してログオンするしかありません。しかしさいわいなことに、ロックされたワークステーション、つまり、まだ起動していて稼動している、ロックされたコンピュータに対して、スクリプトを実行することは引き続き可能です。

2 つ目の質問は多少厄介です。コンピュータが実際にロックされているかどうかを知るための方法はあるでしょうか。少なくとも私たちが知る限りにおいては、絶対確実という方法はありません。しかし、以下の条件さえ満たしていれば、そのようなを確認ができるスクリプトを作成できます。

1. リモート コンピュータがスクリーンセーバーを使用するように構成されていること。

2. スクリーンセーバーが実行されると、ワークステーションが必ず自動的にロックされること。

3. "タイムアウト" の期間が経過し、スクリーンセーバーが実際に実行されていること (たとえば、既定ではコンピュータが 10 分間非アクティブになる (またはロックされる) まで、スクリーンセーバーは起動しません)。

以上の条件がすべて成り立つ場合は、次のスクリプトを使用してコンピュータがロックされているかどうかを知ることができます。このスクリプトは、スクリーンセーバーの実行可能ファイル名を取得し、その実行可能ファイルが実行中かどうかをチェックします (Win32_Process クラスのインスタンスを調べます)。スクリーンセーバーが実行中の場合は、先ほどの定義に従い、コンピュータがロックされています。

On Error Resume Next
HKEY_CURRENT_USER = &H80000001

strComputer = "crowtrobot2"

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

strKeyPath = "Control Panel\Desktop"
ValueName = "ScrnSave.exe"
objWMIService.GetStringValue HKEY_CURRENT_USER, strKeyPath, ValueName, strValue
strValue = Replace(strValue, "\", "\\")

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

Set colItems = objWMIService.ExecQuery _
    ("Select * From Win32_Process Where ExecutablePath ='" & strValue & "'")

If colItems.Count > 0 Then
    Wscript.Echo "The computer is locked."
Else
    Wscript.Echo "The computer is not locked."
End If

このスクリプトは見た目ほど複雑ではありませんが、次のコード行は不自然に思えるかもしれません。

strValue = Replace(strValue, "\", "\\")

これが必要な理由は、ExecutablePath が次のようになっているためです。

C:\WINDOWS\System32\logon.scr

WMI クエリの中でパスを使用するときは、次のように円記号をすべて二重にする必要があります。

C:\\WINDOWS\\System32\\logon.scr

そのため、Replace 関数で個々の \ を \\ に置換しています。

既に述べたように、この方法は絶対確実というわけではありませんが、今のところ考えられる最善の方法です。もう少しだけ確実な動作を望むのであれば、コンピュータが実際にスクリーンセーバーを使用していること (ScreenSaverActive)、および、スクリーンセーバーが実行されるとワークステーションが必ず自動的にロックされること (ScreenSaverSecure) を、それぞれ確かめるコードを追加できます。さらに万全を期すならば、スクリーンセーバーのタイムアウト値を調べ (ScreenSaverTimeout)、その時間だけスクリプトを一時停止して、それから、スクリーンセーバーが実行されているかどうかをチェックすることもできます。

もちろん、どの方法も十分とは言えません。結局このコードでは、ロックされているコンピュータと、だれもログオンしていないコンピュータとを区別することはできず、スクリーンセーバーが実行されていることがわかるだけです。だれかがコンピュータにログオンしているかどうかを知る方法はあるのでしょうか。正直なところ、私たちにはよくわかりませんが、詳しく調べてみる価値はあるでしょう。


関連情報

Hey, Scripting Guy! - アーカイブ もチェックしてください。

ページのトップへページのトップへ