For this project, I used the amazing TopShelf library to host code that essentially triggers the WTSLogoffSession API when the desired schedule occurs.
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
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);
}
}
}
}
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!
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (6)
Author
Commented:Commented:
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.
Author
Commented:I can use any of the options available exposed by the OS via API
Commented:
Author
Commented:View More