1. 起因
因为需要在锁屏界面和UAC界面搞点事情,经过测试,发现锁屏和系统弹出UAC 均是切换到了Windows 的安全桌面,一般Windows在启动后(这里说的Windows指的是Vista之后的)会有一个Default和一个安全桌面。这里的Default 就是普通程序运行的桌面,在弹出UAC 或者 锁屏的时候Windows则会自动切换到 安全桌面, 不同桌面的消息等是隔离的。Windows上存在的所有桌面可以用 EnumDesktopsW 这个API获取。然后可以使用 SetThreadDesktop 将线程指定到相应桌面,或者使用 SwitchDesktop 切换到指定桌面。
但是,在普通管理员权限下使用上述API将线程或切换到安全桌面 会因为权限不足的问题导致失败,所以这时候就想到了使用服务来创建相应的进程。
2. 相关Win32API
相关API的P/Invoke 封装,相关结构体有点繁杂放在此文结尾。
[DllImport("Kernel32.dll", CharSet = CharSet.Ansi,SetLastError =true)]
public static extern void OutputDebugString(string lpOutputString); //用来Debug
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool OpenProcessToken([In]IntPtr ProcessHandle, [In]uint DesiredAccess, [Out]out IntPtr TokenHandle);
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool DuplicateTokenEx([In]IntPtr hExistingToken, uint dwDesiredAccess,IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
[In]TOKEN_TYPE TokenType, [Out]out IntPtr DuplicateTokenHandle);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength);
[DllImport("Userenv.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateEnvironmentBlock([Out]out IntPtr lpEnvironment,[In]IntPtr hToken,[In]bool bInherit);
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessAsUserW([In]IntPtr hToken,[In][MarshalAs(UnmanagedType.LPWStr)]string lpApplicationName,[In][MarshalAs(UnmanagedType.LPWStr)]string lpCommandLine, [In]IntPtr lpProcessAttributes,[In]IntPtr lpThreadAttributes,[In] bool bInheritHandles,[In]uint dwCreationFlags,[In]IntPtr lpEnvironment, [In][MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory,[Out] IntPtr lpStartupInfo,[Out] IntPtr lpProcessInformation);
3. 实现正文
首先使用VS新建一个服务项目(这个怎么建服务就省略了,就是点点点),建好项目之后定位到Service1.cs的OnStart,如下图
OnStart为服务启动时要执行的代码,首先需要先取得当前进程的Token,因为Token内包含了需要的SessionID、相关进程的权限信息等,关于Token的具体可以Windows核心编程或者MSDN。然后使用