Beginner's introductory guide to writing,installing,starting,stopping NT services

本文围绕NT服务展开,介绍了Windows NT/2K启动的服务控制管理器(SCM)及NT服务加载机制。给出通用服务骨架,详细阐述服务骨架各函数,如main、ServiceMain等功能。还说明了服务的安装、编程启动和停止方法,为新手提供了NT服务开发的基础指引。

Introduction to NT Services

At system boot, Windows NT/2K starts an RPC server called as the Service Control Manager (SCM). An NT service is basically a win32 program that is loaded by the SCM. They are loaded before any user has logged into the system. Services may sometimes be manually started instead of getting started automatically at boot time. It was quite recently that I made my first attempt at writing an NT service and I found to my chagrin that very little information was available for a service-newbie. Even on Code Project I could only find wrapper classes which was not what I wanted. 

This article gives you a generic service skeleton which you can use as a starting point when you write your first service. The service does nothing basically. I found a couple of examples on the net both of which were called beeper services because that's what they did. They beeped the system speaker at regular intervals. I thought I'd do the same for my skeleton service because that seems to be the easiest way to give an indication that the service is up and running.

Service Skeleton

The main function

I have written my service as a console application and therefore the main function. But I presume there is nothing stopping you from writing your service as a GUI application with a WinMain but since I haven't tried it out yet, I won't delve too much into that. All that the main function does is to call StartServiceCtrlDispatcher to connect the main thread of our service to the SCM. We simply fill up the SERVICE_TABLE_ENTRY structure and call StartServiceCtrlDispatcher passing the SERVICE_TABLE_ENTRY structure as a parameter.

SERVICE_TABLE_ENTRY servicetable[]=
{
	{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
	{NULL,NULL}
};

strServiceName is the name of our service. We also pass a pointer to our ServiceMain function. I used the name ServiceMain thinking that, that was mandatory, but later on I realized that you can use any name you want to use. Rather silly of me to think so, I guess. Members of the final entry in the table must have NULL values as an indication that this is the end of the table.

StartServiceCtrlDispatcher(servicetable);

Calling StartServiceCtrlDispatcher is a straightforward thing as you can see. Simply pass a pointer to a SERVICE_TABLE_ENTRY array. If StartServiceCtrlDispatcher fails it returns false immediately, otherwise it will return only after our service has terminated. Very recently I have understood that the same executable can have more than one service, but again since I didn't really try it out, I will refrain from making any bold statements. Anyway I think that one service per exe is a smart way of doing things which follows the keep-it-simple paradigm.

The ServiceMain function

The ServiceMain is the entry point function for our service. When the SCM starts our service it creates a new thread for executing our ServiceMain function. The first thing a ServiceMain does is to call RegisterServiceCtrlHandler to register a handler function. The service uses this handler function as it's control handler function which receives control codes including codes to start, stop, pause and continue the service.

RegisterServiceCtrlHandler(strServiceName,
	(LPHANDLER_FUNCTION)ServiceCtrlHandler);

Once we have registered our service control handler, we need to update the SCM with regard to our service's status. We can do this using the SetServiceStatus API call. We will need to do this several times during the course of our program and each time it involves filling up a SERVICE_STATUS structure. Therefore I have written a function called UpdateServiceStatus which will automate this for us. I discuss this function later on in this article. Basically what we do after registering our handler is to update the SCM with the SERVICE_START_PENDING status for our service, which means that our service is starting.

UpdateServiceStatus(SERVICE_START_PENDING,NO_ERROR,0,1,3000);

3000 is the dwWaitHint parameter of the SERVICE_STATUS structure which is in milliseconds. If this time has expired and the service status has not yet changed, the SCM assumes that an error has occured. Once we have updated the SCM with our status, we create an event. We do this so that we can use WaitForSingleObject on this event. We can then set the event to terminate our service somewhere else in our program.

killServiceEvent=CreateEvent(0,TRUE,FALSE,0);

After we do this we call UpdateServiceStatus again with the SERVICE_START_PENDING status, only this time we increment the dwCheckPoint parameter of the SERVICE_STATUS structure. This parameter is used to track the progress of a service during a lengthy start or stop operation. Now we start our service execution thread.

StartServiceThread();

I discuss this function later on, but in summary it simply starts a new thread using CreateThread where we put our actual functionality. Now we call UpdateServiceStatus again, passing SERVICE_RUNNING as our parameter.

UpdateServiceStatus(SERVICE_RUNNING,NO_ERROR,0,0,0);

Well, now that our service is up and running we need to call WaitForSingleObject on the event we created earlier. Because ServiceMain should not finish till our service has terminated. Again this is a very simple step as shown below.

WaitForSingleObject(killServiceEvent,INFINITE);

The UpdateServiceStatus function

As I had mentioned earlier, I wrote this function to wrap the SetServiceStatus API call. It is by no means an innovative idea. Just about every example of a service that I saw on the web used some form of a wrapper function, because during the course of a service program, we need to change the service status several times. Basically what we do in this function is to populate a SERVICE_STATUS structure. I'll mention some of the members of this structure that are important to us. I strongly suggest that you look up this structure in your copy of MSDN.

dwCurrentState :- This indicates the current state of the service. Some of the values we use are SERVICE_STOPPED, SERVICE_RUNNING and SERVICE_START_PENDING. You can look up the other allowed values on MSDN.

dwControlsAccepted :- This is used to indicate the control codes that will be handled by our service handler. For our skeleton service I have used SERVICE_ACCEPT_STOP and SERVICE_ACCEPT_SHUTDOWN. These are the only two control codes our skeleton service will handle. When our service is in the SERVICE_START_PENDING state we must set this parameter to zero.

dwCheckPoint :- I have mentioned about this parameter earlier. The service increments this value during a lengthy start or stop operation. Any program that invokes an operation on the service can use this value to track the progress of various operations. If you are wondering how a program can do that, take a look at the QueryServiceStatus API call.

dwWaitHint :- This specifies the interval in milliseconds before the service status changes again. If the service status has not changed by then, the SCM assumes that an error has occured. Use zero for this parameter when we are setting the service status to SERVICE_RUNNING.

SetServiceStatus(nServiceStatusHandle,&nServiceStatus);

The first parameter we pass is the service status handle which is returned by the RegisterServiceCtrlHandler function. I have saved this in a global variable. The second parameter is a pointer to the SERVICE_STATUS structure that we have populated.

The StartServiceThread function

Well, this function simply starts our service execution thread using the CreateThread API call. If the thread is created successfully I also set the global nServiceRunning variable to true. I have used CreateThread but you might want to use _beginthreadex if you are planning on using some of the CRT functions in your thread.

The ServiceExecutionThread function

This function is our main service execution thread. In our skeleton service I have simply put a while loop using my global nServiceRunning BOOL variable as the while's evaluation expression. Thus till nServiceRunning is made false the while loop will loop endlessly. Please keep in mind that an endless while loop will use up your CPU infinitely till your machine crawls to a pathetic frozen state. I am avoiding this using a Sleep, but you might want to use some kind of blocking calls or waiting calls in your programs.

while(nServiceRunning)
{		
	Beep(450,150);
	Sleep(4000);
}

Well, that's the service body for you. It won't get any simpler than that I guess. Of course the functionality is useless, that's why we call it a skeleton service.

The ServiceCtrlHandler function

This is our service's control handler function. All service control requests like starting a service, stopping a service etc. are handled by the control handler. The MSDN prototype for this function is as follows.

VOID WINAPI Handler(
  DWORD fdwControl   // requested control code
);

Basically we put a switch statement on the fdwControl variable and we have case blocks for each control code that we intend to handle.

switch(nControlCode)
{	
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
	nServiceCurrentStatus=SERVICE_STOP_PENDING;				
	success=UpdateServiceStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000);
	KillService();		
	return;
default:
	break;
}

As you can see our skeleton program's switch construct handles only two control codes, SERVICE_CONTROL_SHUTDOWN and SERVICE_CONTROL_STOP. If you scroll up, you'll see that when I set the service status to SERVICE_RUNNING I set the dwControlsAccepted member of the SERVICE_STATUS structure to SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN. Thus these are the only two control codes that the SCM will send to our control handler function. As you can see, for both cases, we are using the same code. All we do is to change our global service status variable to SERVICE_STOP_PENDING and then we call UpdateServiceStatus passing SERVICE_STOP_PENDING. Then we call our own KillService function (which I explain down below) and return.

The KillService function

Well, we use the KillService function to terminate our service. We first set nServiceRunning to false, so that our service execution thread exits. Then we set our blocking event so that ServiceMain will exit.

nServiceRunning=false;
SetEvent(killServiceEvent);

Once we have done that we need to inform the SCM that our service has terminated. So we call UpdateServiceStatus passing SERVICE_STOPPED as the dwCurrentState parameter.

UpdateServiceStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);

Installing our service

First we use the API call OpenSCManager to get a handle to the SCM database.

scm=OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);

We pass 0 for both lpMachineName and lpDatabaseName as we need to open the SCM database on the local machine. We pass SC_MANAGER_CREATE_SERVICE as our dwDesiredAccess so that we can use the CreateService API call to create our new service and add it to the SCM database.

CreateService(scm,"NishService",
	"Buster's first NT service",
	SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
	SERVICE_ERROR_NORMAL,
	"D://nish//FirstService//Debug//FirstService.exe",
	0,0,0,0,0);

You must look up CreateService on MSDN. I have used SERVICE_ALL_ACCESS as my dwDesiredAccess parameter. This allows me full rights and I can do as I please. I have used SERVICE_DEMAND_START as the dwStartType parameter. This means the service won't start automatically at system boot time. It will need to be manually started either via the control-panel's Component Services applet or programmatically using StartService. Later on in this article I show you how to programmatically start and stop our service. You need to specify the full path of the service executable. The last 5 parameters can be ignored for now. To be frank, as soon as I found that they can all be NULL, I didn't bother to interpret their purpose. But I suggest that you go ahead and figure out how you can put them to proper use.

Starting our service programmatically

The first thing we need to do is to use OpenSCManager to obtain a handle to the SCM. Now we use OpenService to obtain a handle to our skeleton service.

NishService=OpenService(scm,"NishService",SERVICE_ALL_ACCESS);

If OpenService successfully returns,( which we can figure out by checking whether it has returned NULL, in which case the call has failed), we can proceed by calling StartService using the handle returned by OpenService.

StartService(NishService,0,NULL);

Since we are not passing any parameters to ServiceMain, I am passing 0 and NULL as the 2nd and 3rd parameters. If StartService succeeds, the return value is nonzero and we can assume that our service has been successfully started. This will become obvious soon, when the PC speaker starts beeping once in 4 seconds and you might get rude stares from your co-employees. In which case you might want to stop the service.

Stopping our service programmatically

The first two steps for stopping our service are same as for starting our service. We call OpenSCManager  to get a handle to the SCM and then call OpenService to get a handle to our service. Now we can use the ControlService API call to send a control code to our service handler.

ControlService(NishService,SERVICE_CONTROL_STOP,&m_SERVICE_STATUS);

m_SERVICE_STATUS is a SERVICE_STATUS structure which will receive status information about our service. As you can see I have passed SERVICE_CONTROL_STOP as the control code. And we know how we have handled this control code in our service handler. Now it all begins to fit into a pattern, huh? By now the 4-second beeping will have stopped and the rude stares will slowly fade away.

Thank You [and remember that I am a service-newbie too]

下载代码方式:https://pan.quark.cn/s/604a73f2a5f9 流量分类机制(IEEE 802.1Qbv)将以太网数据传输划分为多个不同类别,每个类别均被分配特定时段以获取网络访问权,借此构建了类别专属的保护“路径”。依托IEEE 802.1Qcc的优化SRP与性能提升,用户网络接口(UNI)得到扩充,从而支持了远程集中化的网络设置。 ### IEEE 802.1Qbv TSN:流量调度技术详解 #### 一、IEEE 802.1Qbv TSN概述 在当前迅速演进的科技领域中,特别是工业自动化、汽车电子以及高性能计算等领域对实时通信的需求持续上升,时间敏感型网络(Time-Sensitive Networking, TSN)技术随之出现。其中,IEEE 802.1Qbv规范是TSN体系中的一个关键构成,主要聚焦于以太网中时间敏感数据流量的管理与调度。 #### 二、IEEE 802.1Qbv标准背景 IEEE 802.1Qbv由IEEE LAN/MAN标准委员会制定,作为IEEE 802.1Q-2014规范的一个延伸,目的是为支持定时传输的数据单元提供更高效、更精准的服务。该规范通过引入时间敏感的流量调度机制,使网络能更好地适应工业控制等环境下的实时性要求。 #### 三、核心概念阐释 **1. 流量调度(Scheduled Traffic)** - **定义**:IEEE 802.1Qbv的核心功能之一是流量调度,它允许依据预定的时间计划来传输不同类型的网络数据。 - **作用**:通过设定优先级和分配时间间隙,保障关键任务数据单元能在规定时限内完成传输,从而增强整个网络的可靠性与确定性。 **2. 类别特定的保护“路径”** - **...
打开链接下载源码: https://pan.quark.cn/s/3e18267cc8f4 ### 倍福PLC从入门到精通 #### 一、系统概述 倍福PLC(Programmable Logic Controller)是一种具有高性能的工业自动化控制设备,其采用了PC架构并融合了实时操作系统TwinCAT,非常适用于复杂多变的工业控制环境。本书着重阐述了倍福PLC的基础理论、安装设置流程以及具体的应用技巧。 **核心知识点:** 1. **原理说明**:倍福PLC基于PC的架构设计,意味着它能够借助PC的强大计算能力和丰富的接口资源来执行复杂的控制任务。同时,通过整合TwinCAT实时操作系统,能够实现高精度的时间同步和低延迟的数据处理性能。 2. **选型建议**:选择合适的倍福控制器至关重要,例如CX系列、CPxxxx系列或Cxxxx系列等,它们各自具有独特的优势,适用于不同的应用场景。选型时需要考虑的因素包括处理速度、I/O接口数量、内存容量等。 3. **安装设置**:详细说明了在Windows操作系统环境下如何安装和配置TwinCAT 2.0软件,涵盖了系统环境的准备、软件安装步骤以及必要的系统设定等。 4. **接线方法**:提供了清晰的接线图示和步骤说明,指导用户正确地将控制器与外部设备连接。 #### 二、编程入门 这一章节主要面向初次接触倍福PLC的用户,通过简单的实例程序来讲解编程的基本流程和技术要点。 **核心知识点:** 1. **编程环境熟悉**:了解TwinCAT 2.0的编程环境,包括开发工具的使用方法和程序结构等。 2. **基础编程技能**:学习如何编写控制逻辑,掌握基本的编程指令如条件语句、循环结构等。 3. **程序调试方法*...
内容概要:本文系统性地介绍了物理信息神经网络(PINNs)在结构力学领域中的应用,重点围绕铁木辛柯梁(Timoshenko Beam)方程的求解展开研究。通过结合PyTorch深度学习框架,构建PINNs模型,将偏微分方程所描述的物理规律作为先验知识嵌入神经网络训练过程,实现对复杂力学系统的高效数值模拟。文章详细阐述了Timoshenko梁理论的控制方程与边界条件,深入解析了如何设计复合损失函数以同时满足微分方程残差、初始条件与边界约束,并完整呈现了从网络架构搭建、数据采样、训练优化到结果可视化的全流程Python代码实现,充分验证了PINNs在固体力学正问题求解中的高精度与无需传统网格划分的独特优势。; 适合人群:具备一定深度学习与连续介质力学基础知识,熟悉PyTorch框架,从事科学计算、工程仿真或交叉学科研究的研发人员与研究生。; 使用场景及目标:① 探索基于深度学习的无网格方法求解复杂偏微分方程的新范式;② 学习如何将物理守恒定律与机器学习模型深度融合;③ 掌握PINNs在梁、板、壳等结构动力学问题中的建模思路与编程实现技巧; 阅读建议:建议读者结合所提供的Python代码逐模块精读,重点关注物理约束的数学形式化表达与损失函数的权重平衡策略,理解梯度计算与自动微分在物理一致性保障中的作用,并尝试迁移该方法至其他类型的微分方程求解任务中进行拓展研究。
代码下载链接: https://pan.quark.cn/s/41fd9961b764 HTML与CSS构成了网页设计的核心基础,资源"html+css网站模板网页设计源码-html个人网页设计模板.zip"提供了一套完备的个人网页设计模板,其中包含了大量运用HTML和CSS编写的源代码。该模板既适合初学者也适合经验丰富的开发者使用,能够辅助他们迅速启动一个新的网页开发项目,或者作为掌握HTML和CSS布局技巧的实例参考。 HTML(HyperText Markup Language)作为网页内容的结构化语言,用于设定页面的元素及其组织方式。在提供的模板中,HTML文档可能包含了诸如头部信息、导航栏、主体内容区块、页脚等常规网页组件。开发者可通过审视和编辑这些标记,来理解不同组件的组织与展示方式。 CSS(Cascading Style Sheets)则专注于网页的视觉表现与布局安排,它支持将设计要素如色彩、字体、尺寸及布局安排进行分离处理,从而确保页面呈现统一风格并便于后续维护。在模板内,CSS文档可能包含了针对HTML组件的样式设定,例如背景色彩、间距、边框、字体形态等。通过研究模板中的CSS内容,可以学习到如何运用选择器来精确指定HTML元素,并进行定制化设计。 此压缩文件内的源代码文件可能遵循以下结构:以HTML文件作为主导的结构性文档,并链接一个或多个CSS文件以达成视觉呈现效果。开发者可打开HTML文件,检视其<head>部分,定位<link>标签,该标签通常用于引入外部CSS文档。同时,HTML文档内部或许还嵌入了内联样式,这些样式被<style>标签所包裹,直接应用于元素之上。 对于有意向学习网页设计的人员而言,此模板提供了实践平台。用户可通过调...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值