百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

c#编写windows服务的应用实例

bigegpt 2024-08-08 11:52 2 浏览

昨天,我研究了一下如何用c#编写windows服务,如果有兄弟不知道怎么弄,可以去看一下,因为我昨天晚上研究了它的应用,今天准备和大家分享一下。

昨天的文章在这里:彻底搞懂如何用c#编写windows服务

c#编写windows服务,这个链接第一次用,不太知道怎么弄。


昨天安装了一个啥也没干的windows服务,我就在想,这个东西能做什么呢?最后我决定制作一个守护程序,功能就是监测另一个程序,看它有没有启动,没有的话就启动它。

制作一个简单的程序,我们就来监测它是不是启动了,程序文件名:text.exe。

切换到service的代码视图

整体代码如下,,这里我用LOG文件做了日志:

using System;
using System.IO;
using System.ServiceProcess;
using System.Windows.Forms;

namespace A_Fw
{
    public partial class TheService : ServiceBase
    {
        //定义一个timer
        public System.Timers.Timer timer = new System.Timers.Timer();
        //运行路径
        string thepath = Application.StartupPath;
        //日志文件路径
        string logfile = "\\log.txt";

        //被监控程序的进程名,用来检查它是不是已经运行
        string jinchengming = "test";
        //被监控程序的文件名
        string runfilename = "\\test.exe";
        public TheService()
        {
            InitializeComponent();

            //定义timer事件
            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            timer.Interval = 1000; //时间间隔
        }
        //timer启动后,每隔一段时间检测下被监视程序是否运行,如果没有就运行它
        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            //根据进程名得到被监测程序的进程对象
            System.Diagnostics.Process[] myproc = System.Diagnostics.Process.GetProcessesByName(jinchengming);
            int isrun = myproc.Length;

            //如果没有进程,说明这个程序没有运行
            if (isrun == 0)
            {
                try
                {
                    Log("程序未运行,准备运行...");
                    //运行被监控程序
                    System.Diagnostics.Process.Start(thepath + runfilename);
                    Log("程序已经启动...");
                }
                catch (Exception ex)
                {
                    Log("启动程序时发现错误:" + ex.ToString());
                    Application.Exit();
                }
            }
            else
            {
                Log("程序正在运行");
            }
        }
        protected override void OnStart(string[] args)
        {
            //服务启动时的事件,timer开始工作
            timer.Enabled = true;
            timer.Start();
            Log("服务启动...");
        }

        protected override void OnStop()
        {
            //服务停止时的事件,timer停止工作
            timer.Enabled = false;
            timer.Stop();
            Log("服务停止...");
        }
        void Log(string logcontent)   //记录日志
        {
            using (FileStream st = new FileStream(thepath + logfile, FileMode.Append))
            {
                using (StreamWriter wr = new StreamWriter(st))
                {
                    wr.WriteLine(#34;{DateTime.Now.ToLongTimeString()},{logcontent}");
                }
            }
        }
    }
}

出现以下情况

是因为我们写的不是windows窗体服务,添加一下system.windows.Forms.dll,

再引用一下 using System.Windows.Forms; 就可以了。

重新生成解决方案,因为我程序中都是用的应该程序根目录,这样方便调试,所以这次需要把3个文件放在同一目录下

G:\x>installUtil.exe A_Fw.exe 安装服务,然后启动服务

运行后,test.exe并未弹出

但查看日志文件没有发现问题

查看进程,发现已经有了test进程,杀死后,还会出现这个进程,说明服务程序没有问题。但需要注意的是,此时test进程为“后台进程”

这是什么原因呢?给服务加上与桌面交互选项也是不行的

查了些资料,确定原因出现在这句:

System.Diagnostics.Process.Start(thepath + runfilename);

我不知道是什么原因,用它启动text.exe就是后台进程,

解决方法如下:新建一个文件(我并不知道为啥,这个是网上抄的)

这个文件直接照抄就行了。

using System;
using System.Runtime.InteropServices;

namespace RFW
{
    public class WinAPI_Interop
    {
        public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
        /// <summary>
        /// 服务程序执行消息提示,前台MessageBox.Show
        /// </summary>
        /// <param name="message">消息内容</param>
        /// <param name="title">标题</param>
        public static void ShowServiceMessage(string message, string title)
        {
            int resp = 0;
            WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTSGetActiveConsoleSessionId(), title, title.Length, message, message.Length, 0, 0, out resp, false);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int WTSGetActiveConsoleSessionId();

        [DllImport("wtsapi32.dll", SetLastError = true)]
        public static extern bool WTSSendMessage(IntPtr hServer, int SessionId, String pTitle, int TitleLength, String pMessage, int MessageLength, int Style, int Timeout, out int pResponse, bool bWait);
        #region P/Invoke WTS APIs
        private enum WTS_CONNECTSTATE_CLASS
        {
            WTSActive,
            WTSConnected,
            WTSConnectQuery,
            WTSShadow,
            WTSDisconnected,
            WTSIdle,
            WTSListen,
            WTSReset,
            WTSDown,
            WTSInit
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct WTS_SESSION_INFO
        {
            public UInt32 SessionID;
            public string pWinStationName;
            public WTS_CONNECTSTATE_CLASS State;
        }

        [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool WTSEnumerateSessions(
            IntPtr hServer,
            [MarshalAs(UnmanagedType.U4)] UInt32 Reserved,
            [MarshalAs(UnmanagedType.U4)] UInt32 Version,
            ref IntPtr ppSessionInfo,
            [MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount
            );

        [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern void WTSFreeMemory(IntPtr pMemory);

        [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
        #endregion

        #region P/Invoke CreateProcessAsUser
        /// <summary> 
        /// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser. 
        /// </summary> 
        ///  

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }

        /// <summary>
        /// 以当前登录的windows用户(角色权限)运行指定程序进程
        /// </summary>
        /// <param name="hToken"></param>
        /// <param name="lpApplicationName">指定程序(全路径)</param>
        /// <param name="lpCommandLine">参数</param>
        /// <param name="lpProcessAttributes">进程属性</param>
        /// <param name="lpThreadAttributes">线程属性</param>
        /// <param name="bInheritHandles"></param>
        /// <param name="dwCreationFlags"></param>
        /// <param name="lpEnvironment"></param>
        /// <param name="lpCurrentDirectory"></param>
        /// <param name="lpStartupInfo">程序启动属性</param>
        /// <param name="lpProcessInformation">最后返回的进程信息</param>
        /// <returns>是否调用成功</returns>
        [DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
                                                      bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory,
                                                      ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("KERNEL32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool CloseHandle(IntPtr hHandle);
        #endregion

        /// <summary>
        /// 以当前登录系统的用户角色权限启动指定的进程
        /// </summary>
        /// <param name="ChildProcName">指定的进程(全路径)</param>
        public static void CreateProcess(string ChildProcName)
        {
            IntPtr ppSessionInfo = IntPtr.Zero;
            UInt32 SessionCount = 0;
            if (WTSEnumerateSessions(
                                    (IntPtr)WTS_CURRENT_SERVER_HANDLE,  // Current RD Session Host Server handle would be zero. 
                                    0,  // This reserved parameter must be zero. 
                                    1,  // The version of the enumeration request must be 1. 
                                    ref ppSessionInfo, // This would point to an array of session info. 
                                    ref SessionCount  // This would indicate the length of the above array.
                                    ))
            {
                for (int nCount = 0; nCount < SessionCount; nCount++)
                {
                    WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)), typeof(WTS_SESSION_INFO));
                    if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State)
                    {
                        IntPtr hToken = IntPtr.Zero;
                        if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken))
                        {
                            PROCESS_INFORMATION tProcessInfo;
                            STARTUPINFO tStartUpInfo = new STARTUPINFO();
                            tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
                            bool ChildProcStarted = CreateProcessAsUser(
                                                                        hToken,             // Token of the logged-on user. 
                                                                        ChildProcName,      // Name of the process to be started. 
                                                                        null,               // Any command line arguments to be passed. 
                                                                        IntPtr.Zero,        // Default Process' attributes. 
                                                                        IntPtr.Zero,        // Default Thread's attributes. 
                                                                        false,              // Does NOT inherit parent's handles. 
                                                                        0,                  // No any specific creation flag. 
                                                                        null,               // Default environment path. 
                                                                        null,               // Default current directory. 
                                                                        ref tStartUpInfo,   // Process Startup Info.  
                                                                        out tProcessInfo    // Process information to be returned. 
                                                     );
                            if (ChildProcStarted)
                            {
                                CloseHandle(tProcessInfo.hThread);
                                CloseHandle(tProcessInfo.hProcess);
                            }
                            else
                            {
                                int err = Marshal.GetLastWin32Error();
                                ShowServiceMessage("CreateProcessAsUser失败:" + err.ToString(), "CreateProcess");
                            }
                            CloseHandle(hToken);
                            break;
                        }
                    }
                }
                WTSFreeMemory(ppSessionInfo);
            }
        }
    }
}

将打开文件那句改成:

try
{
    Log("程序未运行,准备运行...");
    //运行被监控程序,下面这句不行
    //System.Diagnostics.Process.Start(thepath + runfilename);
            
    //用这句才可以打开应用进程
    RFW.WinAPI_Interop.CreateProcess(thepath + @runfilename);
    Log("程序已经启动...");
}

重新安装服务,运行效果如下:

明天再来研究,用其它程序来控制这个服务的安装与卸载

如果看得上的话,需要源码可以联系我。

相关推荐

【Docker 新手入门指南】第十章:Dockerfile

Dockerfile是Docker镜像构建的核心配置文件,通过预定义的指令集实现镜像的自动化构建。以下从核心概念、指令详解、最佳实践三方面展开说明,帮助你系统掌握Dockerfile的使用逻...

Windows下最简单的ESP8266_ROTS_ESP-IDF环境搭建与腾讯云SDK编译

前言其实也没啥可说的,只是我感觉ESP-IDF对新手来说很不友好,很容易踩坑,尤其是对业余DIY爱好者搭建环境非常困难,即使有官方文档,或者网上的其他文档,但是还是很容易踩坑,多研究,记住两点就行了,...

python虚拟环境迁移(python虚拟环境conda)

主机A的虚拟环境向主机B迁移。前提条件:主机A和主机B已经安装了virtualenv1.主机A操作如下虚拟环境目录:venv进入虚拟环境:sourcevenv/bin/active(1)记录虚拟环...

Python爬虫进阶教程(二):线程、协程

简介线程线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能...

基于网络安全的Docker逃逸(docker)

如何判断当前机器是否为Docker容器环境Metasploit中的checkcontainer模块、(判断是否为虚拟机,checkvm模块)搭配学习教程1.检查根目录下是否存在.dockerenv文...

Python编程语言被纳入浙江高考,小学生都开始学了

今年9月份开始的新学期,浙江省三到九年级信息技术课将同步替换新教材。其中,新初二将新增Python编程课程内容。新高一信息技术编程语言由VB替换为Python,大数据、人工智能、程序设计与算法按照教材...

CentOS 7下安装Python 3.10的完整过程

1.安装相应的编译工具yum-ygroupinstall"Developmenttools"yum-yinstallzlib-develbzip2-develope...

如何在Ubuntu 20.04上部署Odoo 14

Odoo是世界上最受欢迎的多合一商务软件。它提供了一系列业务应用程序,包括CRM,网站,电子商务,计费,会计,制造,仓库,项目管理,库存等等,所有这些都无缝集成在一起。Odoo可以通过几种不同的方式进...

Ubuntu 系统安装 PyTorch 全流程指南

当前环境:Ubuntu22.04,显卡为GeForceRTX3080Ti1、下载显卡驱动驱动网站:https://www.nvidia.com/en-us/drivers/根据自己的显卡型号和...

spark+python环境搭建(python 环境搭建)

最近项目需要用到spark大数据相关技术,周末有空spark环境搭起来...目标spark,python运行环境部署在linux服务器个人通过vscode开发通过远程python解释器执行代码准备...

centos7.9安装最新python-3.11.1(centos安装python环境)

centos7.9安装最新python-3.11.1centos7.9默认安装的是python-2.7.5版本,安全扫描时会有很多漏洞,比如:Python命令注入漏洞(CVE-2015-2010...

Linux系统下,五大步骤安装Python

一、下载Python包网上教程大多是通过官方地址进行下载Python的,但由于国内网络环境问题,会导致下载很慢,所以这里建议通过国内镜像进行下载例如:淘宝镜像http://npm.taobao.or...

centos7上安装python3(centos7安装python3.7.2一键脚本)

centos7上默认安装的是python2,要使用python3则需要自行下载源码编译安装。1.安装依赖yum-ygroupinstall"Developmenttools"...

利用本地数据通过微调方式训练 本地DeepSeek-R1 蒸馏模型

网络上相应的教程基本都基于LLaMA-Factory进行,本文章主要顺着相应的教程一步步实现大模型的微调和训练。训练环境:可自行定义,mac、linux或者window之类的均可以,本文以ma...

【法器篇】天啦噜,库崩了没备份(天啦噜是什么意思?)

背景数据库没有做备份,一天突然由于断电或其他原因导致无法启动了,且设置了innodb_force_recovery=6都无法启动,里面的数据怎么才能恢复出来?本例采用解析建表语句+表空间传输的方式进行...