<

Automatic logoff at schedule

Published on
3,175 Points
75 Views
1 Endorsement
Last Modified:
Editors:
Shaun Vermaak
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.
Over time I have seen a number of questions asking how to logoff users at a specific time. I personally haven't required this but decided to develop a little Windows service that manages this via schedule and not a legacy scheduled task running shutdown /l or via AD logon hours

Introduction


For this project, I used the amazing TopShelf library to host code that essentially triggers the WTSLogoffSession API when the desired schedule occurs.


Setup


1) Download the AutomaticLogoff tool and extract its contents to a folder on a computer that you are planning to install the tool on. (I recommend extracting to C:\Program Files\AutomaticLogoff)


2) Run Configurator.exe (Configurator Editor). On the LogoffSchedules tab, specify the schedules when users should be logged off (+ or INS to add, - or DEL to delete, Enter or double-click to edit).


Please note: Configurator Editor is a generic config file editor I developed and for AutomaticLogoff tool there is no need to adjust anything on the Settings tab or the Encrypt tab. Key values are used internally and are automatically generated.


Here's an example of some valid schedules:

Every Weekday at 09:00 PM

Sundays on 21:00

Thursdays at 19:00:00



3) Run the following command to install the AutomaticLogoff service


AutomaticLogoff.exe install


The Code


using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace AutomaticLogoff
{
[StructLayout(LayoutKind.Sequential)]
internal struct WTS_SESSION_INFO
{
public int SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public string pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}

internal enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}

internal enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo
}

internal class Sessions
{
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern bool WTSLogoffSession(IntPtr hServer, int SessionId, bool bWait);

[DllImport("Wtsapi32.dll")]
static extern bool WTSQuerySessionInformation(
System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned);

[DllImport("wtsapi32.dll", SetLastError = true)]
static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);

[DllImport("wtsapi32.dll")]
static extern void WTSCloseServer(IntPtr hServer);

[DllImport("wtsapi32.dll", SetLastError = true)]
static extern Int32 WTSEnumerateSessions(IntPtr hServer, [MarshalAs(UnmanagedType.U4)] Int32 Reserved, [MarshalAs(UnmanagedType.U4)] Int32 Version, ref IntPtr ppSessionInfo, [MarshalAs(UnmanagedType.U4)] ref Int32 pCount);

[DllImport("wtsapi32.dll")]
static extern void WTSFreeMemory(IntPtr pMemory);

internal static List<int> GetSessionIDs(IntPtr server)
{
List<int> sessionIds = new List<int>();
IntPtr buffer = IntPtr.Zero;
int count = 0;
int retval = WTSEnumerateSessions(server, 0, 1, ref buffer, ref count);
int dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (int)buffer;

if (retval != 0)
{
for (int i = 0; i < count; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += dataSize;
sessionIds.Add(si.SessionID);
}
WTSFreeMemory(buffer);
}
return sessionIds;
}

internal static void LogoffAllUsers()
{
string input = string.Empty;
IntPtr server = WTSOpenServer(Environment.MachineName);

try
{
foreach (int sessionID in GetSessionIDs(server))
{
WTSLogoffSession(server, sessionID, true);
}
}
finally
{
WTSCloseServer(server);
}
}
}
}


Conclusion


You can do this installation, change the startup state etc. via Group Policy Preferences for example, here is an article on deploying a simple application https://www.experts-exchange.com/articles/29126/Deploy-single-EXE-applications-without-installers.html


When you deploy the tool via a GPO File Preference, the schedule can be centrally managed by simply deploying a new config file.


I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

 

Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts...  

 

Please do not forget to press the "Thumb's Up" button if you think this article was helpful and valuable for EE members.


It also provides me with positive feedback. Thank you!

1
  • 3
  • 3
6 Comments
LVL 63

Expert Comment

by:McKnife
Hi.

I wonder why you have decided against windows' own scheduler. We use a batch two-liner with it to do the same.
0
LVL 51

Author Comment

by:Shaun Vermaak
It works directly with the API and I have more control than a shutdown.exe.
0
LVL 63

Expert Comment

by:McKnife
Aha.

for /f "tokens=1" %%a in ('qwinsta ^| findstr Disc') do logoff %%a
for /f "tokens=3" %%a in ('qwinsta ^| findstr Active') do logoff %%a

Isn't that just as much control? I am not sure if I understand what added control I get using yours.
0
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

LVL 51

Author Comment

by:Shaun Vermaak
Still limited to logoff.exe implementation of API

I can use any of the options available exposed by the OS via API
0
LVL 63

Expert Comment

by:McKnife
Will I understand the limitation once I use yours? I have no idea, what you mean. I want to logoff all users at a given time - I can and I don't need extra tools or schedulers.
0
LVL 51

Author Comment

by:Shaun Vermaak
Many ways to skin a cat. I prefer to use methods where I can replace a config file an alter all configuration.
0

Featured Post

CompTIA Security+

Learn the essential functions of CompTIA Security+, which establishes the core knowledge required of any cybersecurity role and leads professionals into intermediate-level cybersecurity jobs.

This tutorial will walk an individual through the process of configuring their Windows Server 2012 domain controller to synchronize its time with a trusted, external resource. Use Google, Bing, or other preferred search engine to locate trusted NTP …
Attackers love to prey on accounts that have privileges. Reducing privileged accounts and protecting privileged accounts therefore is paramount. Users, groups, and service accounts need to be protected to help protect the entire Active Directory …

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month