Welcome, guest | Sign In | My Account | Store | Cart

This code implements a Python solution for locking a workstation via a secure screensaver as recommended by the Microsoft KB article Q262646. It sets up a secure screensaver, then calls it. Really simple code.

Python, 79 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""
Locks the users workstation, for Windows NT and 2000

Design philosophy for this was simple, create a utility to lock
a users' workstation, without messing up their screensaver settings.
"""

import win32con
import win32api

#########################################
#Set up the two methods we'll use in this program:
#########################################

#We need a method to check what they have setup for their screensaver, so
#that we can return things to what they should be once the workstation is
#locked:
def queryHKCUValues(valuePath, valueName):
	settingsDict = {}
	#First, we need to open a handle to the relevant HIVE in the registry...
	keyHandle = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, valuePath, 0, win32con.KEY_READ)
	for value in valueName:
		(valueData, valueType) = win32api.RegQueryValueEx(keyHandle, value)
		settingsDict[value] = [valueData, valueType]
	keyHandle.Close()
	#This dictionary now holds all the info that we'll need to put things back where
	#they belong once we're done...
	return settingsDict

#This method we will use to 'tidy up' after ourselves...
def returnHKCUValues(valuePath, valueDict):
	#First, we need to open a handle to the relevant HIVE in the registry...
	keyHandle = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, valuePath, 0, win32con.KEY_WRITE)
	valueDictKeys = valueDict.keys()
	#'Unpack' the dictionary values back into the registry where they belong:
	for value in valueDictKeys:
		win32api.RegSetValueEx(keyHandle, value, 0, valueDict[value][1], valueDict[value][0])
	keyHandle.Close()


#########################################
#On to the real work now!
#########################################

#Before we begin, lets record the state of the values that we'll be working with:
tempDict = queryHKCUValues('Control Panel\Desktop', ['ScreenSaveActive', 'ScreenSaverIsSecure', 'SCRNSAVE.EXE'])

#Now we can assign our own values to these without fear of messing them up:
keyHandle = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, 'Control Panel\Desktop', 0, win32con.KEY_WRITE)
#Now that we have the handle, we can use it to setup the relevant settings in the registry.
#These ensure that every time this program is run, the screensaver is in a predictable state.
win32api.RegSetValueEx(keyHandle, 'ScreenSaveActive', 0, win32con.REG_SZ, '1')
win32api.RegSetValueEx(keyHandle, 'ScreenSaverIsSecure', 0, win32con.REG_SZ, '1')
win32api.RegSetValueEx(keyHandle, 'SCRNSAVE.EXE', 0, win32con.REG_SZ, 'logon.scr')
#Now that we've satisfied all assumptions, we can close the handle:
keyHandle.Close()

#Now that we now that a secure screensaver is guaranteed, we send the
#'Launch Screensaver' message, as recommended in Microsoft KB article
#Q262646.  Don't ask why we have to call it twice to get it to work...
win32api.SendMessage(win32con.HWND_TOPMOST, win32con.WM_SYSCOMMAND, win32con.SC_SCREENSAVE, 0)
win32api.SendMessage(win32con.HWND_TOPMOST, win32con.WM_SYSCOMMAND, win32con.SC_SCREENSAVE, 0)

#Now that the workstation is locked, we will return everything to the way
#it was beforehand:
returnHKCUValues('Control Panel\Desktop', tempDict)

#Finally, we will force the value of the screen saver grace period to zero
#so that locking happens instantaneously upon the launching of the
#screensaver.  This setting requires a reboot to take effect, so we will
#simply force it here everytime to make sure that it is always set for
#when the user reboots:
keyHandle = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows NT\CurrentVersion\Winlogon', 0, win32con.KEY_WRITE)
win32api.RegSetValueEx(keyHandle, 'ScreenSaverGracePeriod', 0, win32con.REG_SZ, '0')
keyHandle.Close()

#Have fun!
#God bless Python and the Win32 extensions library ;-)
#esrever_otua

Despite a fairly simple-seeming implementation, my experiments with this raised some fairly interesting issues, I'd love input from anyone else who sees this and tries it. However, first things first, I'll answer the question, 'why would anyone want to use this?' This code is useful for a number of reasons, first off, it works on both Windows NT 4.0 Server and Workstation, and Windows 2000 Pro and Server etc. It probably works on most Win32 platforms, but I've only tested it on those mentioned. It serves two purposes: 1) it can be used to automatically lock workstations that are set to automatically log in by adding an appropriate command line to the Run key in the registry. As any sysadmin will know, there are lots of 'NT' applications out there that must run on logged-in servers to work. This means lots of servers with the auto-logon parameters set (which is a massive security hole). This allows those servers to be set up in a secure manner by locking themselves as soon as they log in. It can also be compiled with py2exe into a nicely working executable and run whenever (put a shortcut on the desktop, it's a lot easier than CTRL+ALT+DEL-ing and waiting for the screen to refresh before locking it and walking away...) I did run in to one issue when exploring this function, and I'd love comment on this: 1) when run on NT, unless you use the SendMessage call twice, the screen saver simply appears then bombs out again

So, enjoy, and have fun coding with python... esrever_otua

3 comments

Mike Rooney 19 years, 6 months ago  # | flag

A problem, if a screen saver is set to "None"

At least in XP, the screen saver can be set to "None" from the drop-down box. Selecting this option completely removes the "SCRNSAVE.EXE" Value from CONTROL PANEL\DESKTOP, and causes an error later when trying to check/change the value.

I wrote a quick solution to this, though it uses the built-in _winreg module in python.

I will overlap the code sligthly so you can see where I put it.

#########################################
#On to the real work now!
#########################################

#Before we begin, lets record the state of the values that we'll be working with:

#If no screen saver is set, we have to create the key
import _winreg
keyValName = "SCRNSAVE.EXE"
deleteSCRNSAVEkey = False
n = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "CONTROL PANEL\\DESKTOP\\", 0, _winreg.KEY_SET_VALUE)
try:
    _winreg.QueryValueEx(n, keyValName)
except WindowsError:
    deleteSCRNSAVEkey = True
    _winreg.SetValueEx(n, keyValName, 0, _winreg.REG_SZ, "")
else:
    n.Close()


This will attempt to access the value, and if it cannot, it will create the value as an empty string. I could put the value we want in now, but that would be redundant since that is taken care of later.

Next, at the end of the code, I simply put:

if deleteSCRNSAVEkey == True: #clean up!
    _winreg.DeleteValue(n, keyValName)
    n.Close()

and it seems to work just as expected. With a screen saver set to None, it will run fine, and afterwards no SCRNSAVE.EXE value exists.

The reason I did not do n.Close() if it needed to be deleted was so we could call _winreg.DeleteValue using it later. While DeleteValue can be called with just the Key name, it will not have appropriate permissions to actually remove the Value, and will only clear the data. It needs an open handle to do so.

I am sure this could have been coded into the other functions and have been much smoother, but that was not my goal. If anyone has any improvements or comments on this, they are welcome. Is this only an XP issue?
Michael Dvorznak 18 years, 6 months ago  # | flag

Easier way. I was looking to do the same thing and came across your code. It looks kind of complicated to me. Here's what worked for me.

import os

winpath=os.environ['windir']#yes, brackets not parentheses

path to windows installation directory,

usually c:\windows or c:\winnt

os.system(winpath + '\system32\rundll32 user32.dll, LockWorkStation')

if you want, you can use the newer subprocess.Popen() or subprocess.call()

Darryl Dixon 17 years, 9 months ago  # | flag

Only works in 2000+. The 'LockWorkStation' function doesn't exist in Windows NT (when this recipe was written).

The only way is to call the screensaver :)

Created by Darryl Dixon on Thu, 10 Jan 2002 (PSF)
Python recipes (4591)
Darryl Dixon's recipes (1)

Required Modules

Other Information and Tasks