# C# 切换桌面,切换会话,以安全会话截屏和模拟键盘

提权之后,就可以切换桌面,切换会话,实现登录界面截图和模拟键盘了

以SharpDX截屏为例

1、切换桌面定义

C#
[Flags]
public enum ACCESS_MASK : uint
{
    DELETE = 0x00010000,
    READ_CONTROL = 0x00020000,
    WRITE_DAC = 0x00040000,
    WRITE_OWNER = 0x00080000,
    SYNCHRONIZE = 0x00100000,

    STANDARD_RIGHTS_REQUIRED = 0x000F0000,

    STANDARD_RIGHTS_READ = 0x00020000,
    STANDARD_RIGHTS_WRITE = 0x00020000,
    STANDARD_RIGHTS_EXECUTE = 0x00020000,

    STANDARD_RIGHTS_ALL = 0x001F0000,

    SPECIFIC_RIGHTS_ALL = 0x0000FFFF,

    ACCESS_SYSTEM_SECURITY = 0x01000000,

    MAXIMUM_ALLOWED = 0x02000000,

    GENERIC_READ = 0x80000000,
    GENERIC_WRITE = 0x40000000,
    GENERIC_EXECUTE = 0x20000000,
    GENERIC_ALL = 0x10000000,

    DESKTOP_READOBJECTS = 0x00000001,
    DESKTOP_CREATEWINDOW = 0x00000002,
    DESKTOP_CREATEMENU = 0x00000004,
    DESKTOP_HOOKCONTROL = 0x00000008,
    DESKTOP_JOURNALRECORD = 0x00000010,
    DESKTOP_JOURNALPLAYBACK = 0x00000020,
    DESKTOP_ENUMERATE = 0x00000040,
    DESKTOP_WRITEOBJECTS = 0x00000080,
    DESKTOP_SWITCHDESKTOP = 0x00000100,

    WINSTA_ENUMDESKTOPS = 0x00000001,
    WINSTA_READATTRIBUTES = 0x00000002,
    WINSTA_ACCESSCLIPBOARD = 0x00000004,
    WINSTA_CREATEDESKTOP = 0x00000008,
    WINSTA_WRITEATTRIBUTES = 0x00000010,
    WINSTA_ACCESSGLOBALATOMS = 0x00000020,
    WINSTA_EXITWINDOWS = 0x00000040,
    WINSTA_ENUMERATE = 0x00000100,
    WINSTA_READSCREEN = 0x00000200,

    WINSTA_ALL_ACCESS = 0x0000037F
}
[DllImport("user32.dll", SetLastError = true)]
public static extern nint OpenInputDesktop(uint dwFlags, bool fInherit, ACCESS_MASK dwDesiredAccess);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool CloseDesktop(nint hDesktop);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetThreadDesktop(nint hDesktop);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SwitchDesktop(nint hDesktop);

private static nint lastInputDesktop;
public static bool SwitchToInputDesktop()
{
    try
    {
        CloseDesktop(lastInputDesktop);

        nint inputDesktop = OpenInputDesktop();
        if (inputDesktop == nint.Zero)
        {
            return false;
        }

        bool result = SetThreadDesktop(inputDesktop);
        result &= SwitchDesktop(inputDesktop);

        lastInputDesktop = inputDesktop;
        return result;
    }
    catch (Exception ex)
    {
        return false;
    }
}

2、截屏使用

在调用 TryAcquireNextFrame 获取一帧之前,调用 SwitchToInputDesktop

C#
SwitchToInputDesktop();
SharpDX.DXGI.Resource desktopResource = null;
OutputDuplicateFrameInformation frameInfo = new OutputDuplicateFrameInformation()
Result result = mDeskDupl.TryAcquireNextFrame(30, out frameInfo, out desktopResource);

3、模拟键盘使用

模拟键盘比较特殊,定时检测

C#
public const int KEYEVENTF_EXTENDEDKEY = 0x0000;
public const int KEYEVENTF_KEYUP = 0x0002;
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

private readonly  ConcurrentQueue<Action> inputActions = new();
private void CheckQueue()
{
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    Task.Factory.StartNew((a) =>
    {
        CancellationTokenSource tks = a as CancellationTokenSource;
        while (tks.IsCancellationRequested == false)
        {
            if (inputActions.IsEmpty == false)
            {
                try
                {

                    if (inputActions.TryDequeue(out var action))
                    {
                        if (Win32Interop.SwitchToInputDesktop()==false)
                        {
                            tks.Cancel();
                            CheckQueue();
                        }
                        action();
                    }
                }
                finally
                {
                }
            }
            else
            {
                Thread.Sleep(10);
            }
        }
    }, cancellationTokenSource, TaskCreationOptions.LongRunning);
}
private void TryOnInputDesktop(Action inputAction)
{
    inputActions.Enqueue(() =>
    {
        try
        {
            inputAction();
        }
        catch (Exception)
        {
        }
    });
}

调用

C#
//先调用CheckQueue定时检测
CheckQueue();

//模拟键盘
TryOnInputDesktop(() =>
{
    keybd_event(key, (byte)User32.MapVirtualKey(key, 0),type , 0);
});