飘易博客(作者:Flymorn)
订阅《飘易博客》RSS,第一时间查看最新文章!
飘易首页 | 留言本 | 关于我 | 订阅Feed

C#创建window service服务启动另外的winform

Author:飘易 Source:飘易
Categories:C#编程 PostTime:2017-7-11 11:52:17
正 文:

C#有需求如下,一个客户端winform应用实现监听TCP端口,叫Demo.exe,重新启动操作系统时,在用户没有登陆账户之前,这个winform程序是无法运行的,要解决这个问题,通过新建一个window service服务(名称是DemoService.exe)去执行开机启动时自动打开Demo.exe。


一、创建一个Windows Service


1)创建Windows Service项目

imageimage

2)对Service重命名

将Service1重命名为你服务名称,这里我们命名为ServiceTest。

二、创建服务安装程序


1)添加安装程序

image

image

之后我们可以看到上图,自动为我们创建了ProjectInstaller.cs以及2个安装的组件。

2)修改安装服务名

右键serviceInsraller1,选择属性,将ServiceName的值改为ServiceTest。

image

3)修改安装权限

右键serviceProcessInsraller1,选择属性,将Account的值改为LocalSystem。

image

三、写入服务代码


1)打开ServiceTest代码

右键ServiceTest,选择查看代码。

2)写入Service逻辑

添加如下代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;

using System.IO;

namespace DemoService
{
    public partial class DemoService : ServiceBase
    {
        //全局变量
        string path = AppDomain.CurrentDomain.BaseDirectory;

        public DemoService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            LogWrite("Start.");//日志
            //启动目标应用程序 winform
            StartProgram(path + @"Demo.exe");            
        }

        protected override void OnStop()
        {
            LogWrite("Stop.");//日志
        }

        /// <summary>
        /// 启动所有要启动的程序 ProgramPath:完整路径
        /// </summary>
        private void StartProgram(string ProgramPath)
        {
            try
            {
                string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
                if (!IsExistProcess(fileName))
                {
                    ProcessStartInfo startInfo = new ProcessStartInfo(ProgramPath);
                    startInfo.WindowStyle = ProcessWindowStyle.Normal;
                    Process.Start(startInfo);
                    LogWrite("Winform: " + fileName + " started.");
                }
            }
            catch (Exception err)
            {
                LogWrite(err.Message);
            }
        }

        /// <summary>
        /// 检查该进程是否已启动
        /// </summary>
        /// <param name="processName"></param>
        /// <returns></returns>
        private bool IsExistProcess(string processName)
        {
            Process[] MyProcesses = Process.GetProcesses();
            foreach (Process MyProcess in MyProcesses)
            {
                if (MyProcess.ProcessName.CompareTo(processName) == 0)
                {
                    return true;
                }
            }
            return false;
        }
        
        /// <summary>
        /// 写日志
        /// </summary>
        public void LogWrite(string str)
        {
            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(path + @"DemoLog.txt", true))
            {
                sw.WriteLine(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + str);
            }
        }
        
    }
}

四、创建安装脚本

在项目中添加2个文件如下(必须是ANSI或者UTF-8无BOM格式):

1)安装脚本Install.bat

1
2
3
cd /d %~dp0

SET regpath=%cd%

%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe %regpath%\DemoService.exe

Net Start DemoService

::echo "开机自动运行"

sc config DemoService start= AUTO

::echo "允许服务与桌面交互"

sc config DemoService type= interact type= own

pause

2)卸载脚本Uninstall.bat

1
cd /d %~dp0

SET regpath=%cd%

%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u %regpath%\DemoService.exe

pause

3)安装脚本说明

请以管理员身份运行上述bat脚本,否则会提示权限不足问题。另外,以管理员身份打开bat脚本时,路径会自动变成c:/windows/system32 ,所以需要前面的两句:cd /d %~dp0 和 SET regpath=%cd%



【补充】:关于服务设置可与桌面交互选项

当我们从服务中启动winform应用程序时,会出现winform的界面无法显示的问题。因为服务是用系统账户system运行的,那么从服务里启动winform时,winform的运行账户也是system,而system账户是没有GUI用户界面的,所以我们会发现进程里有demo.EXE这个程序,但是没有界面显示。


飘易尝试了3种方式:

方式1:bat脚本里设置允许服务和桌面交互

sc config DemoService type= interact type= own

失败。


方式2:为 serviceProcessInstaller1 添加事件 serviceProcessInstaller1_Committed

//为当前Windows服务设置可与桌面交互选项,否则winform程序没有显示界面
        private void serviceProcessInstaller1_Committed(object sender, InstallEventArgs e)
        {
            try
            {
                ConnectionOptions myConOptions = new ConnectionOptions();
                myConOptions.Impersonation = ImpersonationLevel.Impersonate;
                ManagementScope mgmtScope = new System.Management.ManagementScope(@"root\CIMV2", myConOptions);
                mgmtScope.Connect();
                ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + serviceInstaller1.ServiceName + "'");
                ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
                InParam["DesktopInteract"] = true;
                ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);
            }
            catch (Exception )
            {
            }
        }

也是失败。


方式3:手动打开“设置允许服务和桌面交互”

同样失败。


总结下:以服务的形式启动winform应用程序,并设置“允许服务与桌面交互”,在win vista以前的版本中也许行得通,但是在window vista之后的系统里,已经不行了。原因为:在vista之后的windows系统中,所有的system服务都跑在用户会话0上,而你登陆的用户永远是在会话1以及之后,所以系统服务的UI不是弹不出来,是你在当前桌面看不到。想穿透这个屏障需要使用跨会话通信技术。传统点的可以使用系统api,新鲜点的可以在会话间使用wcf通讯,也有极端点办法比如利用IIS。

可以参考:解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离


我们可以转化下思路,后台服务里不放GUI相关的,也不要在服务里直接启动GUI程序,然后做个GUI应用程序去控制这个服务的启动、停止等。同时这个GUI程序设置开机启动,这样在用户登录后,这个GUI就可以启动了。


既然如此,飘易为什么还要用本文开头提到的这种方式呢?因为我的winform界面其实不是必须的,只是显示监听端口的日志而已,所以,服务里启动的winform没有界面也无所谓。但是,我调试winform的时候就方便多了,我可以不通过服务启动它,直接手工启动该winform,就可以看到调试日志了。而如果直接把监听功能也放在服务里,我每次都要先卸载该服务,重新编译,然后再重新安装服务,比较麻烦。



【参考】:

1、C#创建Windows Service(Windows 服务)基础教程:http://www.cnblogs.com/sorex/archive/2012/05/16/2502001.html/

2、C# 实现windows 系统服务(全,含代码)http://www.cnblogs.com/bq-blog/articles/windowsservice.html 

3、C# 编写Windows Service(windows服务程序) http://www.cnblogs.com/bluestorm/p/3510398.html

4、C#实现WinForm随WINDOWS服务一起启动 http://www.cnblogs.com/xiaofengfeng/archive/2011/09/28/2194130.html 


作者:飘易
来源:飘易
版权所有。转载时必须以链接形式注明作者和原始出处及本声明。
上一篇:没有了
下一篇:APP集成小米消息推送自定义铃声设置
0条评论 “C#创建window service服务启动另外的winform”
No Comment .
发表评论
名称(*必填)
邮件(选填)
网站(选填)

记住我,下次回复时不用重新输入个人信息
© 2007-2019 飘易博客 Www.Piaoyi.Org 原创文章版权由飘易所有