昨天用c写了一个windows服务(服务内部带一个gui窗口+系统托盘),在windows xp sp3上测试,启动服务后,系统托盘显示正常。

但在另一台windows 2003 sp2 上测试(通过远程桌面登录),晕了,服务是启动了(在进程管理器中能看到),但系统托盘看不到,也就是在桌面的右下角看不到系统托盘的图标。

到网上找原因,找到这么几篇:

http://blog.s135.com/windows_mstsc/

http://chenjava.blog.51cto.com/374566/80250

http://www.sldd.cn/web/bbsxp/ShowPost.asp?id=45110

http://hi.baidu.com/since2006bitlove/blog/item/81555e4cdc52c5f3d62afc71.html

http://topic.csdn.net/u/20090320/17/84ac2e8e-cdff-4ca8-908a-fe7e6854deb6.html

一开始,我参照:http://topic.csdn.net/u/20090320/17/84ac2e8e-cdff-4ca8-908a-fe7e6854deb6.html 在c的代码中显示窗口的前、后面加了这么一段代码:

HDESK   hdeskCurrent;
    HDESK   hdesk;
    HWINSTA hwinstaCurrent;
    HWINSTA hwinsta;

hwinstaCurrent = GetProcessWindowStation();
    if (hwinstaCurrent == NULL)
    {
        //LogEvent(_T("get window station err"));
        return;
    }

hdeskCurrent = GetThreadDesktop(GetCurrentThreadId());
    if (hdeskCurrent == NULL)
    {
        //LogEvent(_T("get window desktop err"));
        return;
    }

//打开winsta0
    hwinsta = OpenWindowStation("winsta0", FALSE,
                                WINSTA_ACCESSCLIPBOARD   |
                                WINSTA_ACCESSGLOBALATOMS |
                                WINSTA_CREATEDESKTOP     |
                                WINSTA_ENUMDESKTOPS      |
                                WINSTA_ENUMERATE         |
                                WINSTA_EXITWINDOWS       |
                                WINSTA_READATTRIBUTES    |
                                WINSTA_READSCREEN        |
                                WINSTA_WRITEATTRIBUTES);
    if (hwinsta == NULL)
    {
        //LogEvent(_T("open window station err"));

return;
    }

if (!SetProcessWindowStation(hwinsta))
    {
        //LogEvent(_T("Set window station err"));

return;
    }

//打开desktop
    hdesk = OpenDesktop("default", 0, FALSE,
                        DESKTOP_CREATEMENU |
                        DESKTOP_CREATEWINDOW |
                        DESKTOP_ENUMERATE    |
                        DESKTOP_HOOKCONTROL  |
                        DESKTOP_JOURNALPLAYBACK |
                        DESKTOP_JOURNALRECORD |
                        DESKTOP_READOBJECTS |
                        DESKTOP_SWITCHDESKTOP |
                        DESKTOP_WRITEOBJECTS);
    if (hdesk == NULL)
    {
        //LogEvent(_T("Open desktop err"));

return;
    }

SetThreadDesktop(hdesk);

//到这一步,我们获取了和用户交互(如显示窗口)的权利

//显示窗口的代码写在这里

.....................................

SetProcessWindowStation(hwinstaCurrent);
    SetThreadDesktop(hdeskCurrent);
    CloseWindowStation(hwinsta);
    CloseDesktop(hdesk);

编译后,在另一台 windows 2003 sp2 上启动服务,然后在 windows xp sp3 上,运行  mstsc /console,吊用没有。

为什么没效果呢?

接着搜索,找到一篇:

远程桌面mstsc /console(/admin) 的运用 -> http://lcq225.blog.163.com/blog/static/16978498201171252623326/

原来:如果系统是WINXP SP3,是不支持 /console这个参数的,需要使用mstsc /admin。经测试,vista 也不支持 mstsc  /console模式,看了一下帮助,是没有/console这个参数的。

windows xp升级到sp3后,命令换成mstsc /admin即可实现winXp2中MSTSC /console的功能。

我试着使用 mstsc /admin 登录,在Java 6 环境,发现系统托盘区还是没有显示图标。

但是,如果手动启动服务,系统托盘就能显示图标了。

我试着使用 mstsc /admin 登录,在Java 7 环境,发现系统托盘区可以正常显示图标。

难道不成,java 6 与 java 7在系统托盘(SystemTray)方面的底层实现有所不同???

 

我接着把上面的代码删除,再启动服务,系统托盘也会显示图标,看来上面的代码是不需要了。

当然,windows服务需要与桌面交互,还是需要设置一下服务的属性:

打开控制面板->服务,查看服务的属性->[登录]-[允许服务与桌面交互],打上钩后,系统托盘就能显示在任务栏。

至于为什么不能显示系统托盘的原因,看了这个介绍才明白

我们的软件在Windows NT/2000/XP/Vista 系统中安装了一个系统服务,这个服务负责以 SYSTEM 权限启动我们的主程序。我们的主程序启动后会在系统托盘添加一个图标,点击此图标可以弹出控制菜单,通过这个菜单也可以激活配置程序首选项的对话框。在 Windows NT/2000/XP 下我们的程序都可以正常工作。哦不,当 XP 具备了快速用户切换功能的时候我们的问题已经出现了。XP 启动后我们以用户 A 登录,我们的图标出现在系统托盘,一切工作都正常,可当我们使用快速用户切换,切换到用户B后(用户A此时也是已登录状态,并没有注销),虽然用户B已经 是本地控制台会话(Session 属性为 Console)但我们的图标已经无法出现了,自然菜单和对话框更无从谈起了。我们的程序是和本机控制台桌面相关的,这种情况无疑是个缺陷。再来看一下在 Vista 平台是怎么样吧,系统启动后以用户A登录,我们的图标更本就没有出现,查看进程管理器中的进程列表发现我们的程序已经启动了,当我们从远端检查我们的服 务,发现已经正常工作,尝试远程登录我们的服务,Vista 会在本机控制台弹出一个消息框,提示有交互式服务消息,是否查看这个消息,点击立刻查看发现切换到另外一个桌面去了。
于是开始分析这种情况发生的原因。在 Windows NT/2000 中系统服务进程和本机控制台交互式登录的用户都运行于Session0 中,默认用户桌面运行于 WinSta0 窗口站,所以我们的程序由服务程序启动时依然是和本机用户处于同一个Session中,即使在某些情况下出现不能弹出对话框或者无法添加系统托盘图标的情 况也只需要修改一下进程桌面到 WinSta0\Default 就可以了(可以参考 MSDN 中 OpenInputDesktop, SetThreadDesktop 等API的说明)。
XP为我们带来了快速用户切换,也让我们所采用的软件架构问题浮现出来。当我们快速切 换到用户B的时候,用户A仍然在会话中(Session0),而用户B则处于新启动的会话中(Session1或者其他),此时服务程序和本机控制台程序 就不在处于同一会话了,OpenInputDesktop,SetThreadDesktop 等API的工作范围仅限于本Session,用户A没有退出,Session0也依然存在但是已经是 Disconnected 状态,当进程所处的Session是 Disconnected 状态的时候调用 OpenInputDesktop 会返回错误“无效的API”。进程及线程所属的Session 是由他们的Token 结构中的 TokenSessionId 决定的(参见MSDN中SetTokenInformation 和 TOKEN_INFORMATION_CLASS的说明),我尝试以微软提供的相关API修改运行中的进程和线程的TokenSessionId 信息从而达到修改桌面环境的目的,到目前还没有成功过(或许可以尝试参考RootKit 技术,不过即使修改成功到底能不能实现我们的需求也不确定)。我们的进程无法跨越Session的界限,自然无法与当前活动的另外一个Session中的 桌面交互了, 。
Vista中又是如何的一番景象呢?处于安全方面及其他因素的考虑,Vista以及将 所有的服务程序置于Session0中,而为本机第一个交互登录的用户创建了Session1,快速切换到用户B后则是 Session2,无论是本机登录的用户,快速切换后的用户,还是远程桌面登录的用户再也没有谁和服务进程处于同一个Session中了,我们的程序还运 行在Session0中,自然我们的托盘图标是没有用户能看到了。事实上这个图标还是可以出现的。Session0因为不是一个交互式会话所以没有象其他 用户环境初始化的时候一样启动Explorer程序,但是我们开始可以手工启动他,在Session0中启动 Explorer 后任务栏出现后我们还是看到了我们的图标(具体启动Explorer的方法我们不在此文中讨论),菜单、对话框也可以使用。
既然我们的程序必须运行在Session0而我们又没有办法把我们的图标、对话框一下 子就抛到隔壁Session的用户桌面上去,只能想其他的办法了。微软也不提倡我们这种服务程序直接提供GUI与用户直接交互的方式,而他们建议使用 C/S架构,Client/Server之间用Socket/Pipe/RPC等方式通讯,这样我们只要把Client整个进程放到用户Session去 和用户交互,然后将配置信息等内容通过上述途径传递给Server,服务端在作出相应的响应即可。
把GUI分离出来并不是那么困难,然后在以前直接调用的地方加上一个通过Pipe通讯的接口,这样GUI(Client)的运行就可以灵活的掌握了。
最初我想把用户界面程序放到 Startup(启动)中随用户登录自动启动。这样当用户A和B都登录后将有两个用户界面程序在运行,而我们的服务只是和当前活动的控制台登录用户交互,所以这样并不符合需求。
接下来我们需要看看如何判定当前的活动Session是哪个,然后如何在这个活动Session中启动我们的用户界面程序了。

2012-01-21

Windows 服务程序、窗口界面、桌面交互、与远程桌面的更多相关文章

  1. win10创建Ubuntu16.04子系统,安装常用软件以及图形界面(包括win10远程桌面连接Ubuntu)

    一.开启win10子系统 [ Windows Subsystem for Linux(WSL)] 二.基本配置 三.安装常用的软件 安装配置zsh 使用 bash 客户端软件 cmder(其实是win ...

  2. Windows Server 2012 R2超级虚拟化之七 远程桌面服务的增强

    Windows Server 2012 R2超级虚拟化之七  远程桌面服务的增强 在Windows Server 2012提供的远程桌面服务角色,使用户能够连接到虚拟桌面. RemoteApp程序.基 ...

  3. Win7系统怎么开启远程桌面?Win7远程桌面怎么用(转)

    远程桌面服务开启之后,可以方便的远程管理服务器或计算机.为生活和工作带来不少便利呢,很多小伙伴还不知道怎么开启win7远程桌面吧(下面咗嚛以内网远程桌面为例)   工具/原料 Win7 Win7远程桌 ...

  4. 浅谈delphi创建Windows服务程序与窗体实现交互

    我想实现的功能是创建一个服务程序,然后在服务Start时动态创建一个窗体Form,然后把Form缩小时变成TrayIcon放在Windows托盘上. 我在服务程序的OnStart事件中写到 Start ...

  5. 如何在Windows Server 2008 上添加RD (远程桌面)会话主机配置的远程桌面授权服务器

    在Windows Server系列的现存活跃产品中都默认的会开放两个随机附送的远程控制的授权,而一些特殊条件下我们需要启用多个远程终端连接,在购买了相应的授权之后,我们如何将配置好的服务器添加到远程桌 ...

  6. Windows Server 2008 R2 实现多用户连接远程桌面

    前提 1. 确认自己的计算机开启了远程连接 2. 在远程桌面会话主机配置中将"限制每个用户只能进行一个会话"的勾去掉. 实现方法 1. 需要在角色里面安装远程桌面服务: 2. 在用 ...

  7. win10 桌面设置为远程桌面

    查看方法: 1.点击桌面“计算机”,右键,点击属性. 2.在计算机属性系统窗口中点击“远程设置”. 3.在“系统属性”对话框中远程协助勾选“允许远程协助连接这台计算机”. 4.在“远程协助”点击“高级 ...

  8. mac远程桌面连接windows 8.1 update,提示: 远程桌面连接无法验证您希望连接的计算机的身份

    在网上找到解决方案: SolutionEnable RDP security layer in Group Policy on the machine: Verify that the firewal ...

  9. CentOS6.8-minimal安装gnome桌面 安装NVC远程桌面连接

    https://blog.csdn.net/nimasike/article/details/72844403

  10. remote desktop connect btw Mac, Windows, Linux(Ubuntu) Mac,Windows,Linux之间的远程桌面连接

    目录 I. 预备 II. Mac连接Windows III. Windows连接Mac IV. Windows连接Ubuntu V. Mac连接Ubuntu VI. Ubuntu连接Mac VII, ...

随机推荐

  1. CSS 分类 (Classification) 实例

    CSS 分类 (Classification) 实例CSS 分类属性 (Classification)CSS 分类属性允许你控制如何显示元素,设置图像显示于另一元素中的何处,相对于其正常位置来定位元素 ...

  2. ZooKeeper 典型的应用场景——及编程实现

    如何使用 Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储 ...

  3. swift 各种学习

    swift使用cocoapods引用oc第三方库 1. 创建桥接文件 2. 在主工程的 build Settings 搜索 bridge   设置 Objective-C Bridging Headi ...

  4. easyUI中textbox或number的数值大小校验

    例:textbox里面,要求做两个textbox名字为(A,B),其中两个的数字大小范围是-10~10之间,之后其中A的值必须大于B所填的数字,如果输入错误,则提示出弹出框,并清空数据. <!D ...

  5. 发布阶段 github和360移动助手及总结

    经过一系列的冲刺和加工 最激动人心的无非在发布平台上公布上自己辛苦奋斗了一个周期的产品,这个时候的我们就像Iphone 6发布会上得CEO,为自己的产品完美画上了研发的句号. 接下来的日子就是准备ve ...

  6. 第三个sprint冲刺第三阶段

    公测版:

  7. 【Deep Hash】NINH

    [CVPR 2015] Simultaneous Feature Learning and Hash Coding with Deep Neural Networks [paper] Hanjiang ...

  8. 软件工程(GZSD2015)学生博客列表

    2015年贵州师范大学软件工程课程学生博客列表 陈小丽 郑倩 唐洁 周娟 李利思 肖俊 罗文豪 周静 徐明艳 毛涛 邓洪虹 岳庆 李盼 安坤 何亚 涂江凤 张义平 杨明颢 杨家堂 胡贵玲 寿克霞 吴明 ...

  9. Spring源码阅读学习一

    昨天抽时间阅读Spring源码,先从spring 4.x的core包开始吧,除了core和util里,首当其冲的就是asm和cglib. 要实现两个类实例之间的字段的复制功能: 多年之前用C#,因为阅 ...

  10. ESXi去掉 SSH已经启用的警告信息

    1. 在vCenter管理的机器里面 总是有几台服务器 提示 SSH启动连接 并且有黄色的警告信息 有时内存或者CPU报警的信息就看不到了.. 所以想着解决他,经过百度发现解决办法为: 选中host主 ...