通过Windows服务以编程方式锁定工作站

问题描述 投票:3回答:3

从Windows服务,我正在尝试使用以下代码锁定我的工作站:

[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)
}

但是上面的代码不起作用。

有人对此有解决方案吗?

c# visual-studio-2015 windows-services
3个回答
2
投票

Windows服务未在桌面上运行,因此无法从服务中调用它。

LockWorkStation文档中,强调我的:

LockWorkStation功能是仅可在交互式桌面上运行的进程调用。另外,用户必须已登录,并且工作站不能已经被锁定。

[这可能是黑客,但也许您可以创建一个托盘应用程序,该应用程序可以响应来自服务的某种进程间调用而调用LockWorkStation

如果您不喜欢任务栏应用程序的可见特性,请考虑创建一个控制台应用程序,该控制台应用程序会产生一个线程来等待调用,并在用户登录时无窗口运行。

[另一种可能性是创建一个永远不会创建UI窗口的Windows应用程序。如果使用data copy API,这绝对是您想要做的。

这里的关键是,something必须在用户的交互式上下文中运行。

供参考,您可以查看Microsoft's MSDN topic on IPC


4
投票

您可以使用WTSDisconnectSession Windows API来执行此操作,它将以LockWorkStation的相同方式注销用户。

但是,该服务处于特殊会话中,因此,您不能仅断开WTS_CURRENT_SESSION的连接,而必须断开计算机上每个活动会话的连接。

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);
    }
}

3
投票

您无法从Windows服务中执行此操作,在documentation中明确指出,只能从在交互式桌面中运行的进程中调用此API函数。 Windows服务未在交互式桌面中运行。

如果必须这样做,首先应检查用户是否已登录,然后在要锁定的模拟用户下生成进程。但是在我看来,这是一个非常棘手的解决方案。也许更好的解决方案是在用户登录时启动隐藏或任务栏应用程序,然后从该应用程序执行作业。

© www.soinside.com 2019 - 2024. All rights reserved.