简体   繁体   中英

Programmatically Lock workstation from Windows Service

From my Windows Service, I'm trying to lock my Workstation using the below code:

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool LockWorkStation();

if (!LockWorkStation()){
 //Workstation was unable to lock(Write this on event log)
}

but the above code is not working.

Anybody has a solution for this?

You can do it with theWTSDisconnectSession Windows API, it will log out user in the same way LockWorkStation does.

However as the service is in a special session, you can't just disconnect WTS_CURRENT_SESSION and have to disconnect every active session on the computer.

using System;
using System.Runtime.InteropServices;

public class LockWorkstation
{
    [DllImport("wtsapi32.dll", SetLastError = true)]
    static extern bool WTSDisconnectSession(IntPtr hServer, int sessionId, bool bWait);

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

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

    [StructLayout(LayoutKind.Sequential)]
    private struct WTS_SESSION_INFO
    {
        public Int32 SessionID;

        [MarshalAs(UnmanagedType.LPStr)]
        public String pWinStationName;

        public WTS_CONNECTSTATE_CLASS State;
    }

    private enum WTS_INFO_CLASS
    {
        WTSInitialProgram,
        WTSApplicationName,
        WTSWorkingDirectory,
        WTSOEMId,
        WTSSessionId,
        WTSUserName,
        WTSWinStationName,
        WTSDomainName,
        WTSConnectState,
        WTSClientBuildNumber,
        WTSClientName,
        WTSClientDirectory,
        WTSClientProductId,
        WTSClientHardwareId,
        WTSClientAddress,
        WTSClientDisplay,
        WTSClientProtocolType
    }

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

    public static void LockWorkStation()
    {
        IntPtr ppSessionInfo = IntPtr.Zero;
        Int32 count = 0;
        Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count);
        Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
        IntPtr currentSession = ppSessionInfo;

        if (retval == 0) return;

        for (int i = 0; i < count; i++)
        {
            WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure(currentSession, typeof(WTS_SESSION_INFO));
            if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive) WTSDisconnectSession(IntPtr.Zero, si.SessionID, false);
            currentSession += dataSize;
        }
        WTSFreeMemory(ppSessionInfo);
    }
}

You can't do that from Windows service, in clearly stated in documentation that this API function only can be called from process running in interactive desktop. Windows services are not running in interactive desktop.

If you must do that you should first check if user is logged on and then spawn process under inpersonating user that you want to lock. But this seems to me as pretty hackish solution. Maybe a better solution would be launch hidden or tray application when user logs and then do the job from that application.

A Windows service does not run on the desktop, so you can't call it from the service.

From the LockWorkStation documentation, emphasis mine:

The LockWorkStation function is callable only by processes running on the interactive desktop . In addition, the user must be logged on, and the workstation cannot already be locked.

It might be a hack, but maybe you could create a tray application that can, in response to some sort of interprocess call from the service, call LockWorkStation .

If you don't like the visible nature of a tray application, consider creating a console application that spawns a thread to wait for the call, and runs windowless when the user logs in.

Another possibility is creating a Windows application that never creates a UI window. This is definitely what you want to do if you use the data copy API.

The key here, is that something has to be running in the user's interactive context.

For reference, you can look at Microsoft's MSDN topic on IPC .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM