从Win服务启动UI程序

从windows服务启动一个带UI程序的界面,这个需求在xp中是很随意的,从Vista开始似乎没有那么随意了,因为Vista中加入了Session的概念,那么什么是Session,我想这篇文章介绍的应该比我权威的多。Session隔离介绍

明白了Session的概念后,我将通过Win32 API来实现从windows服务启动一个带UI的界面(从Session 0中启动Session *的程序),这个实现过程是我从C++代码翻译过来的。

实现的思路

  1. 找到一个除Session 0之外的活动Session
  2. 通过Session ID获取用户Token
  3. 通过Token来启动UI程序

涉及的Win32 API

  1. WTSGetActiveConsoleSessionId获取活动的Session ID
  2. WTSQueryUserToken根据Session ID获取用户Token
  3. CreateProcessAsUser使用用户Token来启动UI程序
public class ProcessAsUser
{
    public struct SECURITY_ATTRIBUTES
    {
        public uint nLength;
        public uint lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    public struct STARTUPINFO
    {
        public uint cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public uint dwX;
        public uint dwY;
        public uint dwXSize;
        public uint dwYSize;
        public uint dwXCountChars;
        public uint dwYCountChars;
        public uint dwFillAttribute;
        public uint dwFlags;
        public ushort wShowWindow;
        public ushort cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;

    }
    public struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;

    }

    [DllImport("kernel32.dll")]
    static extern uint WTSGetActiveConsoleSessionId();

    [DllImport("Wtsapi32.dll")]
    private static extern bool WTSQueryUserToken(uint SessionId, out uint hToken);

    [DllImport("Kernel32.dll")]
    private static extern uint GetLastError();

    [DllImport("kernel32.dll")]
    private static extern bool CloseHandle(IntPtr hSnapshot);

    [DllImport("advapi32.dll")]
    public extern static bool CreateProcessAsUser(IntPtr hToken,
                                            string lpApplicationName,
                                            string lpCommandLine,
                                            ref SECURITY_ATTRIBUTES lpProcessAttributes,
                                            ref SECURITY_ATTRIBUTES lpThreadAttributes,
                                            bool bInheritHandle,
                                            uint dwCreationFlags,
                                            uint lpEnvironment,
                                            string lpCurrentDirectory,
                                            ref STARTUPINFO lpStartupInfo,
                                            out PROCESS_INFORMATION lpProcessInformation);

    public static bool StartUIProcessFromService(string exePath)
    {
        //获取Session ID
        var sId=WTSGetActiveConsoleSessionId();
        if (sId == 0)
        {
            return false;
        }
        uint hToken;
        var isOk=WTSQueryUserToken(sId, out hToken);
        if (!isOk || hToken == 0)
        {
            return false;
        }
        var lpProcessAttr = new SECURITY_ATTRIBUTES();
        lpProcessAttr.nLength = (uint)Marshal.SizeOf(lpProcessAttr);

        var lpThreadAttr = new SECURITY_ATTRIBUTES();
        lpThreadAttr.nLength = (uint)Marshal.SizeOf(lpThreadAttr);

        var lpStratupInfo = new STARTUPINFO();
        lpStratupInfo.cb = (uint)Marshal.SizeOf(lpStratupInfo);
        lpStratupInfo.lpDesktop = @"winsta0\default";

        PROCESS_INFORMATION lpProcessInfo;
        isOk=CreateProcessAsUser((IntPtr)hToken,
                                    exePath,
                                    null,
                                    ref lpProcessAttr,
                                    ref lpThreadAttr,
                                    false,
                                    0,
                                    0,
                                    null,
                                    ref lpStratupInfo,
                                    out lpProcessInfo
                                );
        CloseHandle((IntPtr)hToken);
        return isOk;            
    }    
}
 

枚举活动Session ID

之前我们通过WTSGetActiveConsoleSessionId获取活动Session ID,当有多个用户登录时,Windows提供了WTSEnumerateSessions方法枚举多个Session ID。

主要涉及API

  1. WTSEnumerateSessions 检索在远程桌面会话主机 (RD 会话主机) 服务器上的会话的列表。
  2. WTSFreeMemory 释放由远程桌面服务函数分配的内存。

实现代码

[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pSessionInfo);

[DllImport("Wtsapi32.dll")]
private extern static bool WTSEnumerateSessions(IntPtr hServer, uint reserved, uint version, out IntPtr ppSessionInfo, out uint pCount);
struct WTS_SESSION_INFO
{
    public uint SessionId;
    public string pWinStationName;
    public WTS_CONNECTSTATE_CLASS State;
}

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

private static uint EnumerateActiveSession()
{
    uint dwSessionID = 0xFFFFFFFF;
    uint dwCount = 0;
    IntPtr intPtr = IntPtr.Zero;
    try
    {
        IntPtr hServer = IntPtr.Zero;
        if (WTSEnumerateSessions(hServer, 0, 1, out intPtr, out dwCount))
        {
            var tmp = intPtr;
            for (var i = 0; i < dwCount; ++i)
            {
                var pSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(tmp, typeof(WTS_SESSION_INFO));

                if (WTS_CONNECTSTATE_CLASS.WTSActive == pSessionInfo.State)
                {
                    dwSessionID = pSessionInfo.SessionId;
                    break;
                }
                if (WTS_CONNECTSTATE_CLASS.WTSConnected == pSessionInfo.State)
                {
                    dwSessionID = pSessionInfo.SessionId;
                }
                tmp += Marshal.SizeOf(typeof(WTS_SESSION_INFO));
            }
            WTSFreeMemory(intPtr);
        }
        var eCode = GetLastError();
    }
    catch (Exception ex)
    {
        var eCode = GetLastError();
    }
    return dwSessionID;
}

 

 

修复Win2012 R2英特尔RST服务未在运行

启动管理员权限下的CMD,输入一下指令即可

sc create IAStorDataMgrSvc binPath= "C:\Program Files (x86)\Intel\Intel(R) Rapid Storage Technology\IAStorDataMgrSvc.exe" start= Auto depend= Winmgmt DisplayName= "Intel(R) Rapid Storage Technology"

sc description IAStorDataMgrSvc "提供存储事件通知并管理存储驱动器和用户空间应用程序间的通信。"

Windows Phone 8 加密服务仅适用于企业用户

Windows Phone支持BitLocker的完全存储加密,但是大多数Windows Phone 8用户似乎并没有注意到这一点。事实证明,只有通过Exchange ActiveSync方法,才能激活BitLocker,这将触发Windows Phone8内部存储容量的加密。

在Windows Phone8的SD卡是不加密的,因为它只能存储媒体文件,如音乐和视频。
当然作为企业用户,这是没有问题的。但作为普通用户,如果你认为你的手机默认情况下已经加密,我要告诉你的是,你不能完全相信这种加密。

微软正式启动Windows Azure移动服务

好消息!微软今天为开发者启动了Windows Azure移动服务。
该服务能使开发者非常轻松地将移动客户端与云端对接。现在开发者可以将应用的数据库、认证用户以及推送消息等在几分钟内放入云端。
此外,Windows 8应用开发者现在就可体验,而Windows Phone、iPhone以及Android的用户也将在近期尝到这份甜头。
如果有兴趣,现在就可以尝试一下,简直太棒了!
具体介绍和使用方法链接:http://www.windowsazure.com/en-us/develop/mobile/

WoW新复活卷轴服务将上线

复活卷轴全面升级,此时不邀请朋友加入《魔兽世界》更待何时,但动作要快,这项邀请是期间限定的!全新的复活卷轴系统将于不久后上线。届时,你只需成功招募一位朋友回到艾泽拉斯世界,就能获得令人心动的奖励。

为了使你和你的朋友能够更好的一起享受《大地的裂变》的最新游戏内容,接受你召唤的朋友将可以免费享受到以下超赞的福利:

—瞬间提升一个角色至80级——叮!

—一次免费的角色转服机会——可以将角色转移至你所在的服务器和阵营与你一同游戏

—1800分钟免费游戏时间——立即生效(7天内有效)

只要你的朋友接受了你的召唤,充值了4000分钟游戏时间,并将这些游戏时间消耗完毕,你就可以解锁以下之一的酷炫坐骑作为奖励:

想要收到复活卷轴的邀请,你的朋友必须是有效的《魔兽世界》付费用户,而你朋友的账号也必须在2012年2月15日0点(北京时间)之后再也没有登录过游戏。

请密切关注《魔兽世界》官网以了解更多复活卷轴的相关信息。

你可以在游戏中开启好友列表(预设快捷键为「O」),并点击复活卷轴按钮发送邀请。

昨天下午著名WoW主题站MMO-Champion就已爆料

该项活动全球同步推出,不过具体要求网易方面和暴雪其余地区稍有不同。

其实大家也看出来了,这次的幽灵狮鹫/双足飞龙,正是之前国服巫妖王之怒初开时DK的专属座骑,国服版“骸骨狮鹫”。由于事起仓促,暴雪临时用了现成的幽灵狮鹫/双足飞龙模型来代替DK原本那完全不符合空气动力学的骨头架子,虽然国服玩家没什么意见,但是大概后来暴雪自己觉得亏了……这么拉风的座骑就这么使了,这不科学啊,于是……现在国服DK是什么座骑,大家都知道了。

如果再追溯得久远一点,这两个幽灵座骑在60年代的东瘟疫之地就出现过,在地区pvp任务,占领哨塔达成后,优势阵营的玩家即可使用哨塔飞行管理员提供的幽灵座骑进行区域飞行。

随着这两个座骑推出,美版论坛再次引发论战。有的玩家认为新坐骑很不错,有的玩家则认为分配方式不公平……公平不公平且再议,现在的问题是…赶紧寻觅几个afk的基友,好吃好喝招待着,再请他们回来吧……