热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

C++Builder在WIN2000环境下编制Service

C++Builder在WIN2000环境下编制Service--Linux通用技术-Linux编程与内核信息,下面是详情阅读。
Windows 2000与Windows 9x有一个非常重要的区别,即Windows 2000提供了很多功能强大的Service(服务)。这些Service可以随着WIN2000的启动而自启动,也可以让用户通过控制面板启动,还可以被Win32应用程序起停。甚至在没有用户登录系统的情况下,这些Service也能执行。许多FTP、WWW服务器和数据库就是以Service的形式存在于WIN2000上,从而实现了无人值守。就连最新版的"黑客"程序Back Orifice 2000也是以Service形式在WIN2000上藏身的。由于Service的编程较复杂,许多开发者想开发自己的Service但往往都望而却步。鉴于此,下面我们就从头到尾来构造一个全新的Service,读者只要在程序中注明的地方加上自己的代码,那么就可以轻松拥有一个自己的Service。在编写Service之前,先介绍一下几个重要的函数:

1.

SC_HANDLE OpenSCManager( LPCTSTR lpMachineName,
LPCTSTR lpDatabaseName, DWORD dwDesiredAccess)
OpenSCManager 函数打开指定计算机上的service control manager database。
其中参数lpMachineName指定计算机名,若为空则指定为本机。LpDatabaseName为指
定要打开的service control manager database名, 默认为空。dwDesiredAccess指定
操作的权限, 可以为下面取值之一:
SC_MANAGER_ALL_ACCESS //所有权限
SC_MANAGER_CONNECT //允许连接到service control manager database
SC_MANAGER_CREATE_SERVICE //允许创建服务对象并把它加入database
SC_MANAGER_ENUMERATE_SERVICE //允许枚举database 中的Service
SC_MANAGER_LOCK //允许锁住database
SC_MANAGER_QUERY_LOCK_STATUS //允许查询database的封锁信息

函数执行成功则返回一个指向service control manager database的句柄,失败则返回NULL。注意:WIN2000通过一个名为service control manager database的数据库来管理所有的Service,因此对Service的任何操作都应打开此数据库。

2.

SC_HANDLE CreateService(SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
LPCTSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCTSTR lpBinaryPathName,
LPCTSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCTSTR lpDependencies,
LPCTSTR lpServiceStartName,
LPCTSTR lpPassword)

CreatService函数产生一个新的SERVICE。其中参数 hSCManager为指向service control manager database 的句柄,由OpenSCManager返回。LpServiceName为SERVICE的名字,lpDisplayName为Service显示用名, dwDesiredAccess是访问权限,本程序中用SERVICE_ALL_ACCESS。wServiceType,指明SERVICE类型,本程序中用SERVICE_WIN32_OWN_PROCESS| SERVICE_INTERACTIVE_PROCESS。dwStartType为Service启动方式,本程序采用自启动,即 dwStartType等于SERVICE_AUTO_START。 dwErrorControl说明当Service在启动中出错时采取什么动作,本程序采用SERVICE_ERROR_IGNORE即忽约错误,读者可以改为其他的。LpBinaryPathName指明Service本体程序的路径名。剩下的五个参数一般可设为NULL。如函数调用成功则返回这个新 Service的句柄,失败则返回NULL。与此函数对应的是DeleteService( hService),它删除指定的Service。

3.

SC_HANDLE OpenService(SC_HANDLE hSCManager,LPCTSTR lpServiceName, DWORD dwDesiredAccess )

OpenService函数打开指定的Service。其中参数 hSCManager为指向service control manager database 的句柄,由OpenSCManager返回。LpServiceName为Service的名字,dwDesiredAccess是访问权限,其可选值比较多,读者可以参看SDK Help. 函数调用成功则返回打开的Service句柄,失败则返回NULL。

4.

BOOL StartService( SC_HANDLE hService, DWORD dwNumServiceArgs,LPCTSTR *lpServiceArgVectors )

StartService函数启动指定的Service。其中参数hService 为指向Service的句柄,由OpenService返回。dwNumServiceAr为启动服务所需的参数的个数。lpszServiceArgs 为 启 动 服务所需的参数。函数执行成功则返回True, 失败则返回False。

5.

BOOL ControlService(SC_HANDLE hService DWORD dwControl,LPSERVICE_STATUS lpServiceStatus )

Service程序没有专门的停止函数,而是用ControlService函数来控制Service的暂停、继续、停止等操

作。参数dwControl指定发出的控制命令,可以为以下几个值:

SERVICE_CONTROL_STOP //停止Service
SERVICE_CONTROL_PAUSE //暂停Service
SERVICE_CONTROL_CONTINUE //继续Service
SERVICE_CONTROL_INTERROGATE //查询Service的状态
SERVICE_CONTROL_SHUTDOWN //让ControlService调用失效

参数lpServiceStatus是一个指向SERVICE_STATUS的指针。SERVICE_STATUS是一个比较重要的结构,它包含了Service的各种信息,如当前状态、可接受何种控制命令等等。

6.

BOOL QueryServiceStatus( SC_HANDLE hService,LPSERVICE_STATUS lpServiceStatus )

QueryServiceStatus函数比较简单,它查询并返回当前Service的状态。

编制一个Service一般需要两个程序,一个是Service本体,一个是用于对 Service进行控制的控制程序。通常Service本体是一个console程序,而控制程序则是一个普通的Win32应用程序(当然,用户不用控制程序而通过控制面板也可对Service进行启、停,但不能进行添加、删除操作。)

首先,我们来编写Service本体。对于Service本体来说,它一般又由以下三部分组成:main()、ServiceMain()、Handler(),下面是main()的源代码:(注:由于篇幅的关系,大部分程序都没进行错误处理,读者可以自己添上)

int main(int argc, char **argv)
{
SERVICE_TABLE_ENTRY ste[2];
//一个Service进程可以有多个线程,这是每个    
//线程的入口表
ste[0].lpServiceName="W.Z.SERVICE"; //线程名字
ste[0].lpServiceProc=ServiceMain;
//线程入口地址
ste[1].lpServiceName=NULL;
//最后一个必须为NULL
ste[1].lpServiceProc=NULL;
StartServiceCtrlDispatcher(ste);
return 0;
}

main()是Service的主线程。当servie control manager开始一个Service进程时,它总是等待这个Service去调用StartServiceCtrlDispatcher()函数。 main( )作为这个进程的主线程应该在程序开始后尽快调用StartServiceCtrlDispatcher()。 StartServiceCtrlDispatcher()在被调用后并不立即返回,它把本Service的主线程连接到service control manager,从而让service control manager通过这个连接发送开始、停止等控制命令给主线程。主线程在这时就扮演了一个命令的转发器的角色,它或者调用Handle( )去处理停止、继续等控制要求,或者产生一个新线程去执行ServiceMain。StartServiceCtrlDispatcher()在整个 Service结束时才返回。

ServiceMain()是Service真正的入口点,必须在main()中进行了正确的定义。ServiceMain( )的两个参数是由StartService()传递过来的。下面是ServiceMain()的源代码:

void WINAPI ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv)
{
ssh=RegisterServiceCtrlHandler
("W.Z.SERVICE",Handler);
ss.dwServiceType=SERVICE_WIN32_OWN
_PROCESS|SERVICE_INTERACTIVE_PROCESS;
ss.dwCurrentState=SERVICE_START_PENDING;
//如用户程序的代码比较多
(执行时间超过1秒),这儿要设成SERVICE_
START_PENDING,待用户程序完成后再设为SERVICE_RUNNING。
ss.dwCOntrolsAccepted=SERVICE_ACCEPT_
STOP;//表明Service目前能接受的命令是停止命令。
ss.dwWin32ExitCode=NO_ERROR;
ss.dwCheckPoint=0;
ss.dwWaitHint=0;
SetServiceStatus(ssh, &ss);
//必须随时更新数据库中Service的状态。

Mycode(); //这儿可放入用户自己的代码

ss.dwServiceType=SERVICE_WIN32_OWN_
PROCESS|SERVICE_INTERACTIVE_PROCESS;
ss.dwCurrentState=SERVICE_RUNNING;
ss.dwCOntrolsAccepted=SERVICE_ACCEPT_STOP;
ss.dwWin32ExitCode=NO_ERROR;
ss.dwCheckPoint=0;
ss.dwWaitHint=0;
SetServiceStatus(ssh,&ss);

Mycode();// 这儿也可放入用户自己的代码
}

在ServiceMain()中应该立即调用

RegisterServiceCtrlHandler()注册一个Handler去处理控制程序或控制面板对Service的控制要求。

Handler()被转发器调用去处理要求,

下面是Handler()的源代码:

void WINAPI Handler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_STOP: //停止Service
Mycode();//这儿可放入用户自己的相关代码
ss.dwWin32ExitCode = 0;
ss.dwCurrentState =SERVICE_STOPPED;
//把Service的当前状态置为STOP
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
SetServiceStatus (ssh,&ss);
/必须随时更新数据库中Service的状态
break;
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus (ssh,&ss);
/必须随时更新数据库中Service的状态
break;
}
}

好了,Service本体程序已基本完成,我们接着来看一下Service的控制程序:

控制程序是一个标准的window程序,上面主要有四个按纽:Create Service、Delete Service、start、stop,分别用来产生、删除、开始和停止Service。下面是它们的部分源代码:

1. 产生Service
void __fastcall TForm1::CreateBtnClick
(TObject *Sender)
{
scm=OpenSCManager(NULL,NULL,
SC_MANAGER_CREATE_SERVICE);
if (scm!=NULL){
svc=CreateService(scm,
"W.Z.SERVICE","W.Z.SERVICE",//Service名字
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS
|SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START,
//以自动方式开始
SERVICE_ERROR_IGNORE,
"C:\\Ntservice.exe", //Service本体程序路径,
必须与具体位置相符
NULL,NULL,NULL,NULL,NULL);
if (svc!=NULL)
CloseServiceHandle(svc);
CloseServiceHandle(scm);
}
}

2. 删除Service
void __fastcall TForm1::DeleteBtnClick
(TObject *Sender)
{
scm=OpenSCManager(NULL,NULL,
SC_MANAGER_CONNECT);
if (scm!=NULL){
svc=OpenService(scm,"W.Z.SERVICE",
SERVICE_ALL_ACCESS);
if (svc!=NULL){
QueryServiceStatus(svc,&ServiceStatus);
if (ServiceStatus.dwCurrentState==
SERVICE_RUNNING)//删除前,先停止此Service.
ControlService(svc,
SERVICE_CONTROL_STOP,&ServiceStatus);
DeleteService(svc);
CloseServiceHandle(svc);
//删除Service后,最好再调用CloseServiceHandle
}
//以便立即从数据库中移走此条目。
CloseServiceHandle(scm);
}
}

3. 开始Service
void __fastcall TForm1::StartBtnClick(TObject *Sender)
{
scm=OpenSCManager(NULL,NULL,SC_MANAGER_CONNECT);
if (scm!=NULL){
svc=OpenService(scm,"W.Z.SERVICE",SERVICE_START);
if (svc!=NULL){
StartService(svc,0,NULL);//开始Service
CloseServiceHandle(svc);
}
CloseServiceHandle(scm);
}
}

4.停止Service
void __fastcall TForm1::StopBtnClick
(TObject *Sender)
{
scm=OpenSCManager(NULL,NULL,
SC_MANAGER_ALL_ACCESS);
if (scm!=NULL){
svc=OpenService(scm,"W.Z.SERVICE",
SERVICE_STOP|SERVICE_QUERY_STATUS);
if (svc!=NULL){
QueryServiceStatus(svc,&ServiceStatus);
if (ServiceStatus.dwCurrentState==
SERVICE_RUNNING)
ControlService(svc,
SERVICE_CONTROL_STOP,&ServiceStatus);
CloseServiceHandle(svc);
}
CloseServiceHandle(scm);
}
}

本程序在C++Builder 5.0和Windows 2000下编译通过。
推荐阅读
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • Java学习笔记之使用反射+泛型构建通用DAO
    本文介绍了使用反射和泛型构建通用DAO的方法,通过减少代码冗余度来提高开发效率。通过示例说明了如何使用反射和泛型来实现对不同表的相同操作,从而避免重复编写相似的代码。该方法可以在Java学习中起到较大的帮助作用。 ... [详细]
  • 原理:dismiss再弹出,把dialog设为全局对象。if(dialog!null&&dialog.isShowing()&&!(Activity.)isFinishing()) ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 本文详细介绍了在Centos7上部署安装zabbix5.0的步骤和注意事项,包括准备工作、获取所需的yum源、关闭防火墙和SELINUX等。提供了一步一步的操作指南,帮助读者顺利完成安装过程。 ... [详细]
  •     这里使用自己编译的hadoop-2.7.0版本部署在windows上,记得几年前,部署hadoop需要借助于cygwin,还需要开启ssh服务,最近发现,原来不需要借助cy ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 本文详细介绍了解决全栈跨域问题的方法及步骤,包括添加权限、设置Access-Control-Allow-Origin、白名单等。通过这些操作,可以实现在不同服务器上的数据访问,并解决后台报错问题。同时,还提供了解决second页面访问数据的方法。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 如何在文本中运行Java程序
    本文介绍了在文本中运行Java程序的步骤,包括创建文本文档、修改后缀、打开DOS命令窗口、编译和运行程序。通过这些步骤,可以在文本中成功运行Java程序并输出结果。 ... [详细]
author-avatar
OH-MQNZ_259
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有