您的位置:首页 > 其它

服务程序需要注意的地方

2011-12-30 16:15 190 查看
如何编写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;这个也重要,如果你没有设置的话,那么服务就不会受你控制了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: