最近的任务---老大让我把linux上的一个把键盘码解析成“行为”再给服务器发,使得键盘控制与服务器相连的摄像机的程序在window上用VC 写成window服务程序。
在这之前,小弟没有接触过window服务程序的编写,不过不要紧,有google嘛
进入正题,在google上看了很多关于window服务程序的编写,说法挺多,其实到最后都是一样的,就是那几个步骤。
这里我以自己的理解介绍个我感觉挺容易理解的版本:
首先yourfunction();函数,(这个函数就是你的工作函数,你服务程序要完成的工作)这很容易理解。
但是你的这个程序要放在ServiceMain(int argc, char* argv)函数中,ServiceMain函数是服务程序入口,就是说如果你的服务程序写好了,当你启动服务程序时,它是从这个函数进入的。在ServiceMain里你要完成的工作是为服务器注册控制处理器,就是下面这个函数:
hStatus = RegisterServiceCtrlHandler(szSvName,ControlHandler);
if (NULL == hStatus)
{
//注册失败就返回
OutputDebugString(TEXT("注册服务失败!"));
return;
} 其中hStatus是SERVICE_STATUS_HANDLE类型,就是个句柄
然后设置服务的控制权限
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP;
你需要服务可以被几个选项控制就 | 几个选项。SHUTDOWN 是关机 STOP是停止
对于相应的选择后面我们还要写对应的回应,就是ControlHandler(DWORD request)函数,它里面是个switch语句。
void WINAPI ControlHandler(DWORD request){
switch(request)
{
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus,&ServiceStatus);
case SERVICE_CONTROL_STOP:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus,&ServiceStatus);
break;
//根据前面的设置,实现控制代码,如果只设置了只接受一个消息就可以只写一个分支,如果不接受任何消息也可以不写.,
}
注册好后,就设置服务当前状态,现在我们刚刚进入服务程序,当然要设置成正在启动状态:
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
这样写完系统还是不知道服务的状态,我们需要把他发送出去,就用SetServiceStatus(hStatus,&ServiceStatus); 函数就行了
然后error = InitService(); //初始化数据
if (error)
{
// 初始化失败,终止服务
ServiceStatus.dwCurrentState = SERVICE_STOPPED; //停止
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus); //向 SCM 报告服务的状态
return; // 退出 ServiceMain
}
其实这个函数的作用就是“确认”的作用InitService里面的内容是自己写的,一般很简单这里的int InitService()
{
//获取系统目录地址,失败就返回-1
k = 100;
return 0;
} 有时候我觉得也可以不写的
然后就是把当前状态设置成正在运行
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hStatus,&ServiceStatus);
后面就是放入我们的工作函数了
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
{
if(k != 100)
return;
Sleep(3000);
yourfunction();
}
最后写个
return;
这样ServiceMain就完成了,但是这样我们的服务程序还是没有完成,我们还需要MSC知道有这个服务,我们要让我们的服务在MSC窗口中出现,所以我们还要写个main函数,注意是main函数,但是却是相当简单的,如下;
int main()
{
SERVICE_TABLE_ENTRY ServiceTable[2];
//服务分派表存放服务入口函数,一个服务程序可以有多个服务入口函数,
//这里只举例一个的,注意表容量一定比服务入口函数大一,
//因为表的最后一项一定要为NULL;
ServiceTable[0].lpServiceName = szSvName;
ServiceTable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc= NULL;
// 启动服务的控制分派机线程
if(StartServiceCtrlDispatcher(ServiceTable)==0)
CreateMyService();
return 0;
}
其中主要的就是 SERVICE_TABLE_ENTRY ServiceTable[2];数组和StartServiceCtrlDispatcher函数和CreateMyService函数
我们可以看到ServiceTable里面就是服务名和服务入口函数,当然我们这里简单,只有一个服务入口函数,当服务程序复杂时,有多个服务名和服务入口函数,到时候就有相互关联的问题了。
最后一定要有一对NULL!!
然后用StartServiceCtrlDispatcher函数就可启动服务的控制分派机线程 CreateMyService();函数的作用是使得你的服务出现在MSC上
void CreateMyService()
{
SCMger=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
CreateService(
SCMger,
szSvName,
szSvName,
SERVICE_START,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,
TEXT("E:\\devwork\\kbdresponse\\Debug\\kbdresponse.exe"), //程序自身的地址
NULL,
NULL,
NULL,
NULL,
NULL);
};
里面主要就是OpenSCManager和CreateService函数了
所以呢 我们要先main函数 ( 开启控制分派机线程)
再ServiceMain函数。 (为服务器注册控制处理器 按逻辑设置服务状态)
ServiceMain函数里放我们的工作函数
服务程序的调试问题
比如我们现在在MSC启动了服务,(VC6.0用,VC2005以上有附加到进程功能,但是VC6.0这方面不好使)到任务管理器里选择它,右键点击调试进入VC6.0环境,现在是出现汇编语句,这时你点击open打开你的主程序.cpp文件,在里面设置断点,就可以调试了,但是程序的有些地方调不了 这和你的内容有关。
还有遇到的socket在你开的线程里出现“不可用的套接字”错误,而主线程却没有错的问题,那可能是你开线程时的if判断语句写错,使得你虽然线程开成功了,却进入了if语句close掉了sock。