WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

How To
plus
plus
plus
plus
plus
plus
plus
plus
plus
plus
plus

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Detecting a Locked Workstation


Question:

I am trying to detect if an NT+ box is locked or not. I found this article in tech support: http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/How~To/Ctrl~Alt~Del~Issues+Detect~if~a~Workstation~is~Locked.txt But it is designed to be run as the user's screen saver only, which I do not want because my goal is to run a compiled script as a service. One of the things this service will do is every night at a certain time (say 3am) the user will be logged out of their workstation. However, since there's a remote chance that someone may actually be working at 3am, I would not want to log them out. But at 3am if the script detects that the machine is locked, I can assume that it's OK to log that user out of their workstation.

Answer:

I'm not sure, try:
DeskList=IntControl(84,"",0,0,0) ; Intcontrol returns list of available desktops
and see if the return value can be used to tell is a workstation is locked or not.

User Reply:

OK, I ran IntControl(84,"",0,0,0), wrote the output to a text file, delayed a few seconds, then looped back to the start of the script. I locked the PC and checked the contents of the text file from anotehr computer.

I get the exact same output regardless of if the PC is locked or unlocked - "Default Winlogon, ".

Anything else I can try?

Answer:

Thanks for trying. But I though it had a real good chance of working.

But now you got your test script....What does WinGetActive() return.

Did you try looking on Google (and Google Groups) and see if anyone has figured it out for any language. If you can find a sample in any language, then almost always Winbatch can do it too.

Here is apparently some C code. The next step is to get it converted to WinBatch...

int getDesktopType()
{
	HDESK hDesk;
	char name[200];
	DWORD needed;
	BOOL ret;
	
	hDesk=OpenInputDesktop(0,0,0);
	if(hDesk==NULL)
	{
		return 1;		// NT is locked
	}
	else {
	
ret=GetUserObjectInformation(hDesk,UOI_NAME,name,sizeof(name),&needed);
		if(ret==TRUE)
		{
			name[needed]='\0';
		}
		else
		{
			name[0]='\0';
		}
		CloseDesktop(hDesk);
		if(stricmp(name,"Screen-saver")==0)
		{
			return 2;		// screen-saver is active
		}
		else
		{
			return 3;		// nothing special
		}
	}
}

User Reply:

OK, now we're getting somewhere.

I used my same write-to-a-test-file-and-loop code, and replaced DeskList=IntControl(84,"",0,0,0) with WinGetActive().

WinGetActive() returns Program Manager when the machine is at the desktop (or the window title of the active window), and doesn't return anything (i.e. blank) when the machine is locked.

It's not 100% reliable though, as I am getting blank entries just doing 'stuff' before locking the machine (i.e. opening and closing programs, maximizing and minimizing programs, alt-tabbing between programs,etc).

The only other idea I have to make it more reliable is if WinGetActive() returns a blank, then do a count from 1 to 10 with a 1 minute delay between each count, and compare the WinGetActive()s to make sure they are all blank. If they are all blank I can assume that the machine is locked.

Sound like a good plan, or is there a better way?

Answer:

You could maybe check for activity on the system...
#definefunction GetKeyboardState(buf)
sDLLName = StrCat(DirWindows(1), "user32.dll")
DLLCall(sDLLName, long:"GetKeyboardState", lpbinary:buf)
BinaryEodSet(buf, 256) 
return buf
#endfunction

#DefineFunction WaitForIdle(time)
BoxOpen("Check if System is Idle","Initializing")
While @True
IntControl(80,0,0,0,0);wait til no keys are pressed
statusflag = 0 

;Check mouse
currcoordinates = MouseInfo(2)
coordinates = currcoordinates

;Check keyboard
currbuf = BinaryAlloc(256)
newbuf = BinaryAlloc(256)
currkbstate = GetKeyboardState(currbuf)
kbstate = currkbstate 

count = 0
While coordinates == currcoordinates && BinaryCompare(currkbstate, 0, kbstate, 0, 256)
count = count+1
BoxText(count)
if count == time
statusflag = 1
break
endif
timedelay(1); check every second
currcoordinates = MouseInfo(2)
kbstate = GetKeyboardState(newbuf)
EndWhile

BinaryFree(currbuf)
BinaryFree(newbuf)

if statusflag == 1
return 1
endif
EndWhile
#EndFunction

;timeout = 1800 ; 30 minutes
timeout = 10 ; 10 seconds
WaitForIdle(timeout)
Pause("Workstation",StrCat("Has been Idle for ",timeout," seconds"))

User Reply:

OK, the following code seems to work just fine. Any opinions/advice is appreciated.

BTW, yes, my code is inefficient spaghetti code with terrible gotos and everything. I am not a professional programmer and it's nowhere near perfect, but my crappy code gets the job done most of the time ;0)

If anyone can help make it more efficient and/or clean up my code, feel free to...

:Check_Time
Time=TimeYmdHms ( )
Hour=StrSub(Time, 12, 2)
If Hour>="20" || Hour<="04"
goto Check_if_Locked
Else
Delay(1800); 30 minutes
Goto Check_Time
endif

:Check_if_Locked
; This function checks if PC is locked
Active_Window=WinGetActive()
if Active_Window==""
; Display(5,"","Workstation is locked")
goto Check_if_Inactive
else
Delay(1800); 30 minutes
goto Check_if_Locked
endif

:Check_if_Inactive
; This function checks for inactivity
#definefunction GetKeyboardState(buf)
sDLLName = StrCat(DirWindows(1), "user32.dll")
DLLCall(sDLLName, long:"GetKeyboardState", lpbinary:buf)
BinaryEodSet(buf, 256) 
return buf
#endfunction

#DefineFunction WaitForIdle(time)
While @True
IntControl(80,0,0,0,0);wait til no keys are pressed
statusflag = 0 

;Check mouse
currcoordinates = MouseInfo(2)
coordinates = currcoordinates

;Check keyboard
currbuf = BinaryAlloc(256)
newbuf = BinaryAlloc(256)
currkbstate = GetKeyboardState(currbuf)
kbstate = currkbstate 

count = 0
While coordinates == currcoordinates && BinaryCompare(currkbstate, 0, kbstate, 0, 256)
count = count+1
BoxText(count)
if count == time
statusflag = 1
break
endif
timedelay(1); check every second
currcoordinates = MouseInfo(2)
kbstate = GetKeyboardState(newbuf)
EndWhile

BinaryFree(currbuf)
BinaryFree(newbuf)

if statusflag == 1
return 1
endif
EndWhile
#EndFunction

timeout = 1800 ; 30 minutes
WaitForIdle(timeout)
goto Logout_User

:Logout_User
IntControl(67,0,1,0,0) 

Answer:

Here is some revised code that doesn't use the Goto processing.
; This function checks for inactivity
  #DefineFunction Getkeyboardstate(buf) ; From 18 30
     sdllname = StrCat(DirWindows(1), "user32.dll")
     DllCall(sdllname, long:"GetKeyboardState", lpbinary:buf)
     BinaryEodSet(buf, 256)
     Return buf
  #EndFunction                         ; Getkeyboardstate.
  #DefineFunction Waitforidle(time)    ; From 55
     While @True
        Intcontrol(80,0,0,0,0)         ; Wait til no keys are pressed.
        statusflag = 0
;Check mouse
        currcoordinates = MouseInfo(2)
        coordinates = currcoordinates
;Check keyboard
        currbuf = BinaryAlloc(256)
        newbuf = BinaryAlloc(256)
        currkbstate = Getkeyboardstate(currbuf) ; Line 2
        kbstate = currkbstate
        count = 0
        While coordinates == currcoordinates && BinaryCompare(currkbstate, 0, kbstate, 0, 256)
           count = count+1
           BoxText(count)
           If count == time
              statusflag = 1
              Break
           EndIf                       ; Count == time.
           TimeDelay(1)                ; Check every second.
           currcoordinates = MouseInfo(2)
           kbstate = Getkeyboardstate(newbuf) ; Line 2
        EndWhile                       ; Coordinates == currcoordinates && BinaryCompare(currkbstate, 0, kbstate, 0, 256).
        BinaryFree(currbuf)
        BinaryFree(newbuf)
        If statusflag == 1
           Return 1
        EndIf                          ; Statusflag == 1.
     EndWhile                          ; @True.
  #EndFunction                         ; Waitforidle.

; Start of real code

  time=TimeYMDHMS ( )
  While @True
     hour=StrSub(time, 12, 2)
     If hour>="20" || hour<="04" Then Break
     Delay(1800)                       ; 30 minutes.
  EndWhile                             ; @True.
  While @True
; This function checks if PC is locked
     active_window=WinGetActive()
     If active_window=="" Then Break
     Delay(1800)                       ; 30 minutes.
  EndWhile                             ; @True.
  timeout = 1800                       ; 30 minutes.
  Waitforidle(timeout)                 ; Line 8
  Intcontrol(67,0,1,0,0)               ; Reboot.


Another Users Code:

Successfully tested on:

XP Pro, Sp1 (user32.dll 5.1.2600.1255)
2000 Pro, Sp4 (user32.dll 5.0.2195.6897)
NT4, Sp6a (user32.dll 4.0.1381.310)

By Steve Guettler

#definefunction IsMachineLocked()
	dill = strcat(dirwindows(1),"user32.dll")		;Set the path to the User32.dll
	lock_state = @false				;Initialize variable (Default return value = @false)
	dll_hand = dllload(dill)			;Retrieve handle to $dill
	desktop_hand = dllcall(dll_hand,long:"OpenInputDesktop",word:0,word:0,word:0) ;Retrieve handle of current desktop
	
	if desktop_hand < 1				;If $desktop_hand < 1 (NULL or 0), then machine is locked.
		lock_state = @true			;Set $lock_state to true if $desktop_hand > 0
	endif

	dllfree(dll_hand)				;Free the DLL handle
	return(lock_state)				;Return the lock state
#endfunction
Note: the success or failure of OpenInputDesktop is going to depend solely on the privileges of the user account calling the function and the effective permissions that exist on the desktop that currently has keyboard & mouse input attached to itself.

Keeping that in mind, in most the situations the method shown above will work provided that the "Winlogon" desktop has not had its security altered to allow the user's process to access it. When the workstation is locked, the "Winlogon" desktop will be the active desktop, and the user's process would normally not have permissions to access it so attempting to open it should fail, with the failure being interpreted as meaning that the workstation is locked.

Using this method in a service may be less reliable, with the reliability depending on whether or not the the service is configured to interact with the desktop.


Article ID:   W16457
File Created: 2014:07:18:09:51:38
Last Updated: 2014:07:18:09:51:38