本文主要是介绍服务程序需要注意的地方,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
如何编写NT Service在MSDN->Platform SDK->DLLs, Processes, and Threads->Service中说得很清楚了,在这里我就不多说了,这里我就只说一些我个人认为的在编写service过程中要注意的地方。
0、在我们通过控制面板或NET START命令启动一个service时, Service Control Manager (SCM)从注册表里拿到service的可执行程序名,然后运行这个程序,从程序的入口main方法里得到service的service_main方法,然后就进入service_main运行。
一个service程序包含最少包含三个部分,一个是main方法,通常的工作是设置service_main方法和处理命令行参数(例如根据不同的参数执行其他的动作,象安装卸载service,手动启动停止service等等);一个是service_main方法,service程序具体要做的工作就写在这个方法里;一个是ServiceCtrlHandler方法,这个方法在service_main里设置,用来处理由SCM发给service的消息,例如service停止,暂停,系统关机等等。
1、手动启动一个service的方法是在service的main方法里调用StartService,StartService根据service的SERVICE_TABLE_ENTRY将程序转入相应的service_main。启动service时所要作的工作是写在service_main里的。通常的模式是在进入service_main后设状态为SERVICE_START_PENDING,然后进行一些初始化动作,然后执行一个线程或进程,在其中进行service的工作,然后设状态为SERVICE_RUNNING。一定要注意的是,启动service后,必须保证在两分钟内service的状态就会被设成SERVICE_RUNNING,否则会报错。
2、停止一个service的方法是在service的main方法里调用ControlService(hService,SERVICE_CONTROL_STOP,&ServStat)发送SERVICE_CONTROL_STOP消息到Service Control Manager (SCM),SCM收到这个消息后就会通知service,执行ServiceCtrlHandler里case SERVICE_CONTROL_STOP:内的工作。结束动作通常也不能太久,因为在关机时,系统会给每个service大概20秒时间清场(这个时间可以在注册表中设置)
3、安装和卸载一个service的方法是在service的main方法里调用CreateService方法和DeleteService方法,卸载方法前先要判断service是否在运行,如果在运行要先将服务停止
否则无法删除。
4、设置service状态的方法是调用SetServiceStatus,在写service的启动和停止动作是要设置其状态,分别在上面提到的service_main和ServiceCtrlHandler里。查询service状态的方法是QueryServiceStatus
,在停止服务时,
ControlService方法只是发送了一个消息后就立即返回了,因此通常要在执行完ControlService后,利用while、sleep和QueryServiceStatus
来不断查询
service
的状态,直到状态为
SERVICE_STOPPED
。
StartService与ControlService不同,并不是立即返回的,它会直到service_main内的代码执行完后才返回。
5、service_main即使返回,service的程序进程也不会退出,直到service的状态为SERVICE_STOPPED时,才会终止并退出。
6、在设置service状态时,通过指定dwWin32ExitCode和dwServiceSpecificExitCode可以设定当service在此状态下出错时的弹出式Message。不过只能设定一个错误代码。
7、使用ChangeServiceConfig2添加修改service的描述。
8、默认情况下service程序是不能与桌面交互的,即不能打开窗口。通过ChangeServiceConfig函数设定SERVICE_INTERACTIVE_PROCESS属性,或通过控制面板选中"允许服务与桌面交互",则服务程序可以打开窗口。例如在服务中使用CreateProcess创建了一个进程,只要在STARTUPINFO的wShowWindow和dwFlags设定了SW_SHOW和STARTF_USESHOWWINDOW,则进程就会在打开的一个新窗口中运行。
9、不论是否设定SERVICE_INTERACTIVE_PROCESS,在service中都可以通过MessageBox方法弹出MessageBox。这个函数的第一个参数指定为NULL,表示不指定父窗口。在第四个参数中指定MB_DEFAULT_DESKTOP_ONLY或MB_SERVICE_NOTIFICATION表示以桌面为父窗口。
10.如果要停止的一个Service上有其他正在运行的服务依赖着,这时直接停止这个服务就会出错,因此如果需要停止的服务有可能被其他服务所依赖,在停止前必须用EnumDependentServices
()
方法取得所有依赖于这个服务的服务,将这些服务依次停止后才行。具体代码示例请看
MSDN
->HOWTO->ID:Q245230。
11、启动service时可以使用启动参数,在定义service_main方法时其两个参数(DWORD argc, LPTSTR *argv),第一个即为参数个数,第二个则是一个参数数组指针。如何传入参数呢,由于service_main并不是程序入口,因此参数是通过main从命令行传至StartService方法,而调用StartService时,StartService的第二第三个参数,就是传入service_main的参数。如果service是auto-started的,每次开机时就会自动启动,没有给我们手工通过命令行传入参数的机会,这时我们只有在安装service的时候,就把参数传入。在CreateService时,将CreateService的lpBinaryPathName值设成例如SimpleService.exe arg1 arg2的样子,这样每当SCM启动service时SCM就会从main方法中获得参数并将其传入这个service的service_main中。
12.在启动一个service之后,在service的状态是”已启动(SERVICE_RUNNING)”之前,这段时间内,是无法启动另一个service的。
例子代码:
#include #include #include #include #include #include #include
#define SERVICE_NAME "TSService"#define SERVICE_DISPNAME "vPBX PathFinder TSService"#define SERVICE_DESCRIPTIONNAME "Start vPBX PathFinder service"#define SERVICE_EXENAME "\ServiceFrame.exe"#define LOG_FILENAME "\Service.log"#define START_EXENAME "\vpbxw.exe"#define STOP_EXENAME "\StopServer.exe"#define CMD_EXENAME " -svc"#define REG_ITEM "SOFTWARE\VisionNex\vPBX_Server"#define REG_KEY "home"#define FLAG_FILENAME "\PortKeeper.svc"#define START_DELAY 10000#define STOP_DELAY 2000#define COUNT_DELAY 50#define TIME_DELAY 2000
SERVICE_STATUS m_ServiceStatus;SERVICE_STATUS_HANDLE m_ServiceStatusHandle;BOOL bRunning=false;char Path[256];
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);void WINAPI ServiceCtrlHandler(DWORD Opcode);
BOOL InstallService(int flag);BOOL DeleteService();BOOL StartupService();BOOL StopService();BOOL ChangeService();
BOOL EndService();BOOL QueryReg ();DWORD GetStatus(SC_HANDLE service);void LogService(char* error);BOOL TestTs(int sleep, int count, int sec);BOOL WaitTsStartup(int sleep, int count, int sec);
int main(int argc, char* argv[]){ if(!QueryReg()) return 1; if(argc>1) { if(strcmp(argv[1],"-i")==0) { InstallService(0); } else if (strcmp(argv[1],"-id")==0){ InstallService(1); } else if(strcmp(argv[1],"-d")==0) { DeleteService(); } else if(strcmp(argv[1],"-r")==0) { StartupService(); } else if(strcmp(argv[1],"-s")==0) { StopService(); } else if(strcmp(argv[1],"-c")==0) { ChangeService(); } else if(strcmp(argv[1],"-v")==0) { printf("serivce frame version:1.0.0.5: debug=pipe(limit -0.3)"); } else { printf("Unknown Switch Usage For install use -i, for uninstall use -d, for run use -r, for stop use -s "); } } else { SERVICE_TABLE_ENTRY DispatchTable[]={{SERVICE_NAME, ServiceMain},{NULL,NULL}}; StartServiceCtrlDispatcher(DispatchTable); } return 0;}
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv){ DWORD status; DWORD specificError; LogService("Service Startup..."); m_ServiceStatus.dwServiceType = SERVICE_WIN32; m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwServiceSpecificExitCode = 0; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; m_ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler); if (m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) { LogService("Error: RegisterServiceCtrlHandler"); return; } /* //create pipe SECURITY_ATTRIBUTES sa; HANDLE hRead,hWrite; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE;/* if (!CreatePipe(&hRead,&hWrite,&sa,0)) { LogService("Error On CreatePipe()"); } //--------- hWrite = CreateFile("d:\process.log",GENERIC_WRITE, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); */
PROCESS_INFORMATION pinfo; STARTUPINFO sinfo; ZeroMemory(&sinfo, sizeof(sinfo)); sinfo.cb = sizeof(sinfo); char strDir[256]; char strDir_stop[256]; strcpy(strDir,Path); strcpy(strDir_stop,Path); strcat(strDir,START_EXENAME); strcat(strDir_stop,STOP_EXENAME); LPCTSTR lpszBinaryPathName=strDir; LPCTSTR lpszBinaryPathName_stop=strDir_stop;
LogService("Start Create StopServer process"); if(!CreateProcess(lpszBinaryPathName_stop, CMD_EXENAME, NULL,NULL,FALSE,0,NULL,Path,&sinfo,&pinfo)) { LogService("Error: CreateProcess:stop befor start"); return; } Sleep(STOP_DELAY);
ZeroMemory(&sinfo, sizeof(sinfo)); sinfo.cb = sizeof(sinfo); //Set Process output //sinfo.hStdError = hWrite; //sinfo.hStdOutput = hWrite; sinfo.wShowWindow = SW_SHOW; sinfo.dwFlags = STARTF_USESHOWWINDOW ;//| STARTF_USESTDHANDLES; LogService("Start Create vPBXW process"); if(!CreateProcess(lpszBinaryPathName, CMD_EXENAME, NULL,NULL,TRUE,0,NULL,Path,&sinfo,&pinfo)) { LogService("Error: CreateProcess:start"); m_ServiceStatus.dwCurrentState = SERVICE_STOPPED; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; m_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; m_ServiceStatus.dwServiceSpecificExitCode = 0; SetServiceStatus (m_ServiceStatusHandle, &m_ServiceStatus); return; } //CloseHandle(hWrite); LogService("Created vPBXW process"); //read pipe /* char buffer[4096] = {0}; DWORD bytesRead = 0; int i =1; while (3) { i = i -1; BOOL ret = ReadFile(hRead,buffer,4095,&bytesRead,NULL); if (ret == NULL){ LogService("Read return NULL"); break; } if(ret == 0){ LogService("Read return 0"); break; } if(bytesRead == 0){ LogService("Read size 0"); } else{ LogService("Read Success"); buffer[bytesRead]=0; LogService(buffer); } } //-----*/
if(!TestTs(START_DELAY,COUNT_DELAY,TIME_DELAY)){ CloseHandle(pinfo.hThread); CloseHandle(pinfo.hProcess); // CloseHandle(hRead); return; }
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; if (!SetServiceStatus (m_ServiceStatusHandle, &m_ServiceStatus)) { LogService("Error: SetServiceStatus:SERVICE_RUNNING"); return; }
CloseHandle(pinfo.hThread); CloseHandle(pinfo.hProcess);//CloseHandle(hRead); return; }
BOOL EndService(){ PROCESS_INFORMATION pinfo; STARTUPINFO sinfo; ZeroMemory(&sinfo, sizeof(sinfo)); sinfo.cb = sizeof(sinfo); char strDir[256]; strcpy(strDir,Path); strcat(strDir,STOP_EXENAME); LPCTSTR lpszBinaryPathName=strDir; if(!CreateProcess(lpszBinaryPathName, CMD_EXENAME, NULL,NULL,FALSE,0,NULL,Path,&sinfo,&pinfo)) { LogService("Error: CreateProcess:stop"); return false; } LogService("Service Stop..."); LogService("Service Stop OK"); CloseHandle(pinfo.hThread); CloseHandle(pinfo.hProcess); return true;}
void WINAPI ServiceCtrlHandler(DWORD Opcode){ switch(Opcode) { case SERVICE_CONTROL_STOP: m_ServiceStatus.dwWin32ExitCode = 0; m_ServiceStatus.dwCurrentState = SERVICE_STOPPED; m_ServiceStatus.dwCheckPoint = 0; m_ServiceStatus.dwWaitHint = 0; SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus); bRunning=false; EndService(); break; case SERVICE_CONTROL_SHUTDOWN: bRunning=false; EndService(); char strDir[256]; strcpy(strDir,Path); strcat(strDir,FLAG_FILENAME); remove(strDir); break; case SERVICE_CONTROL_INTERROGATE: break; } return; }
BOOL InstallService(int flag){ HANDLE schSCManager,schService;
schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) { printf("Error Installing Service "); LogService("Error: Installing Service:OpenSCManager"); return false; }
char strDir[256]; strcpy(strDir,Path); strcat(strDir,SERVICE_EXENAME); LPCTSTR lpszBinaryPathName=strDir; schService = CreateService(schSCManager, SERVICE_NAME, SERVICE_DISPNAME, SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_AUTO_START, // start type SERVICE_ERROR_NORMAL, // error control type lpszBinaryPathName, // service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // no dependencies NULL, // LocalSystem account NULL); // no password
if (schService == NULL) { printf("Error Installing Service "); LogService("Error: Installing Service:CreateService"); CloseServiceHandle(schSCManager); return false; } printf("Service Installed OK "); LogService("Service Installed OK");
if(flag == 1){ ChangeServiceConfig(schService,SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, NULL, NULL,NULL,NULL,NULL,NULL,NULL); }
char description[] = SERVICE_DESCRIPTIONNAME; SERVICE_DESCRIPTION svrDescription; svrDescription.lpDescription = description; ChangeServiceConfig2(schService,SERVICE_CONFIG_DESCRIPTION,&svrDescr
用VC++建立Service服务应用程序
为什么要使用服务应该程序呢?服务程序就像系统的一些服务一样,能够自动地启动,并执行相应的操作;而且因为服务程序的在层次上和一般的应用程序不同,其能够在系统启动时就自动地运行,而不像一般的应用程序那样一定要在登陆后才能运行,这些就是服务的一些好处了,如果你也想你的程序具有这样的功能,那么你就可以建立一个服务应用程序了。
下面就跟着我一步一步地教你怎么去创建一个服务应用程序吧。
本文主要介绍了OpenSCManager、CreateService、OpenService、ControlService、DeleteService、RegisterServiceCtrlHandler、SetServiceStatus、StartServiceCtrlDispatcher等操作服务程序的主要几个API的用法,具体的函数参数大家可以查阅MSDN。
一、建立Win32 Application应用程序(当然你也可以建立其它的应用程序,但服务一般是没有用户界面的),并命名为ServiceTest。
二、定义全局函数变量
//定义全局函数变量 void Init(); BOOL IsInstalled(); BOOL Install(); BOOL Uninstall(); void LogEvent(LPCTSTR pszFormat, ...); void WINAPI ServiceMain(); void WINAPI ServiceStrl(DWORD dwOpcode); TCHAR szServiceName[] = _T("ServiceTest"); BOOL bInstall; SERVICE_STATUS_HANDLE hServiceStatus; SERVICE_STATUS status; DWORD dwThreadID; |
三、添加Init初始化函数
这里主要是设置服务句柄和状态。
hServiceStatus = NULL; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwCurrentState = SERVICE_STOPPED; tatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; status.dwWin32ExitCode = 0; status.dwServiceSpecificExitCode = 0; status.dwCheckPoint = 0; status.dwWaitHint = 0; |
四、添加安装和删除服务函数
这里主要是用到了五个函数OpenSCManager、CreateService、OpenService、ControlService、DeleteService。OpenSCManager用于打开服务控制管理器;CreateService用于创建服务;OpenService用于打开已有的服务,返回该服务的句柄;ControlService则用于控制已打开的服务状态,这里是让服务停止后才删除;DeleteService用于删除指定服务。
BOOL Install(); { //这里列出主要的两个函数,其它的可以在代码里找。 //打开服务控制管理器 OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //创建服务 SC_HANDLE hService = ::CreateService( hSCM, szServiceName, szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL, _T(""), NULL, NULL); ::CloseServiceHandle(hService); ::CloseServiceHandle(hSCM); } BOOL Uninstall(); { //这里列出主要的两个函数,其它的可以在代码里找。 //打开服务控制管理器 OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //打开服务 OpenService(hSCM, szServiceName, SERVICE_STOP | DELETE); //停止服务 ControlService(hService, SERVICE_CONTROL_STOP, &status); //删除服务 DeleteService(hService); … } |
五、添加服务主线程函数和控制函数
这里调用RegisterServiceCtrlHandler来注册服务的控制函数,这里要设置status.dwControlsAccepted为SERVICE_ACCEPT_STOP,否则你不能控制这个服务的状态。
void WINAPI ServiceMain() { // Register the control request handler status.dwCurrentState = SERVICE_START_PENDING; status.dwControlsAccepted = SERVICE_ACCEPT_STOP;//这个要使用,否则你不能控制 //注册服务控制 hServiceStatus = RegisterServiceCtrlHandler(szServiceName, ServiceStrl); if (hServiceStatus == NULL) { LogEvent(_T("Handler not installed")); return; } SetServiceStatus(hServiceStatus, &status); status.dwWin32ExitCode = S_OK; status.dwCheckPoint = 0; status.dwWaitHint = 0; status.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(hServiceStatus, &status); //模拟服务的运行,10后自动退出。应用时将主要任务放于此即可 int i = 0; while (i < 10) { Sleep(1000); i++; } // status.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(hServiceStatus, &status); LogEvent(_T("Service stopped")); } |
六、在主线程函数里注册控制函数和程序执行主体
void WINAPI ServiceMain() { … //如上,这里主要是说明这就是程序的执行体 //模拟服务的运行,10后自动退出。应用时将主要任务放于此即可 int i = 0; while (i < 10) { Sleep(1000); i++; } … } |
七、在main函数里注册添加安装、删除、注册主函数
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { Init(); dwThreadID = ::GetCurrentThreadId(); SERVICE_TABLE_ENTRY st[] = { { szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } }; if (stricmp(lpCmdLine, "/install") == 0) { Install(); } else if (stricmp(lpCmdLine, "/uninstall") == 0) { Uninstall(); } else { if (!:tartServiceCtrlDispatcher(st)) { LogEvent(_T("Register Service Main Function Error!")); } } return 0; } |
八、总结
其实做一个服务程序并不难,主要是懂得程序的执行体放于哪里?注册程序的主函数和注册控制函数,如果这两个没有注册的话,你的程序就不知道如何去控制了。status.dwControlsAccepted = SERVICE_ACCEPT_STOP;这个也重要,如果你没有设置的话,那么服务就不会受你控制了。
这篇关于服务程序需要注意的地方的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!