一直感覺VC++太復(fù)雜了,但昨天看了汪蒲陽編著的因特網(wǎng)應(yīng)用編程,其中寫到后臺(tái)服務(wù)程序的編寫,論述的非常詳細(xì),而且邏輯清晰,看了之后感覺明白不少,故拿來與需要之人共享,并更正了原程序的一些錯(cuò)誤,補(bǔ)充了一些材料。另外還有一種用C++編寫后臺(tái)服務(wù)程序的思路(不算.NET上服務(wù)程序開發(fā)模型),以后整理好了再發(fā)上來。
?
在2000/XP等基于NT 的操作系統(tǒng)中,有一個(gè)服務(wù)管理器,它管理的后臺(tái)進(jìn)程被稱為 service。
服務(wù)是一種應(yīng)用程序類型,它在后臺(tái)運(yùn)行,與 UNIX 后臺(tái)應(yīng)用程序類似。服務(wù)應(yīng)用程序通常可以
在本地和通過網(wǎng)絡(luò)為用戶提供一些功能,例如客戶端/服務(wù)器應(yīng)用程序、Web 服務(wù)器、數(shù)據(jù)庫服
務(wù)器以及其他基于服務(wù)器的應(yīng)用程序。
??? 后臺(tái)服務(wù) 程序是在后臺(tái)悄悄運(yùn)行的。我們通過將自己的程序登記為服務(wù),可以使自己的程序不出現(xiàn)
在任務(wù)管理器中,并且隨系統(tǒng)啟動(dòng)而最先運(yùn)行,隨系統(tǒng)關(guān)閉而最后停止。
????
???? 服務(wù)控制管理器是一個(gè)RPC 服務(wù)器,它顯露了一組應(yīng)用編程接口,程序員可以方便的編寫程序來配置
服務(wù)和控制遠(yuǎn)程服務(wù)器中服務(wù)程序。
???? 服務(wù)程序通常編寫成控制臺(tái)類型的應(yīng)用程序,總的來說,一個(gè)遵守服務(wù)控制管理程序接口要求的程序
包含下面三個(gè)函數(shù):
1、服務(wù)程序主函數(shù)(main):調(diào)用系統(tǒng)函數(shù) StartServiceCtrlDispatcher 連接程序主線程到服務(wù)控制管理程序。
2、服務(wù)入口點(diǎn)函數(shù)(ServiceMain):執(zhí)行服務(wù)初始化任務(wù),同時(shí)執(zhí)行多個(gè)服務(wù)的服務(wù)進(jìn)程有多個(gè)服務(wù)入口函數(shù)。
3、控制服務(wù)處理程序函數(shù)(Handler):在服務(wù)程序收到控制請(qǐng)求時(shí)由控制分發(fā)線程引用。(此處是Service_Ctrl)。
?另外在系統(tǒng)運(yùn)行此服務(wù)之前需要安裝登記服務(wù)程序:installService 函數(shù)。刪除服務(wù)程序則需要先刪除服務(wù)安裝登記:removeService 函數(shù)。
?
服務(wù)類型:
類型說明SERVICE_FILE_SYSTEM_DRIVER=2文件系統(tǒng)驅(qū)動(dòng)服務(wù)。SERVICE_KERNEL_DRIVER=1驅(qū)動(dòng)服務(wù)。SERVICE_WIN32_OWN_PROCESS=16獨(dú)占一個(gè)進(jìn)程的服務(wù)。SERVICE_WIN32_SHARE_PROCESS=32與其他服務(wù)共享一個(gè)進(jìn)程的服務(wù)。
?
新建WIN32控制臺(tái)程序, 其源文件名為service.cpp 。我用的開發(fā)工具是VC++.NET。
?1.服務(wù)程序主函數(shù)
??? 服務(wù)控制管理程序啟動(dòng)服務(wù)程序后,等待服務(wù)程序主函數(shù)調(diào)用系統(tǒng)函StartServiceCtrlDispatcher。一個(gè)SERVICE_WIN32_OWN_PROCESS 類型的服務(wù)應(yīng)該立即調(diào)用 StartServiceCtrlDispatcher 函數(shù),可以在服務(wù)啟動(dòng)后讓服務(wù)入口點(diǎn)函數(shù)完成初始化工作。對(duì)于 SERVICE_WIN32_OWN_PROCESS 類型的服務(wù)和程序中所有服務(wù)共同的初始化工作可以在主函數(shù)中完成,但不要超過30秒。否則必須建立另外的線程完成這些共同的初始化工作,從而保證服務(wù)程序主函數(shù)能及時(shí)地調(diào)用 StartServiceCtrlDispatcher 函數(shù)。
?
?
?
?
[cpp] view plaincopy
????#include?"stdafx.h"????#include?"Windows.h"????#define?SZAPPNAME??????"serverSample"?????//服務(wù)程序名????#define?SZSERVICENAME??"serviceSample"????//標(biāo)識(shí)服務(wù)的內(nèi)部名?????????????bool???????????????????bDebugServer=false;????SERVICE_STATUS??????????????ssStatus;????SERVICE_STATUS_HANDLE??sshStatusHandle;????DWORD???????????????????????dwErr=0;????TCHAR???????????????????????szErr[256];?????????????void??WINAPI??Service_Main(DWORD?dwArgc,?LPTSTR?*lpszArgv);????void??WINAPI??Service_Ctrl(DWORD?dwCtrlCode);????void?installService();????void?removeService();????void?debugService(int?argc,char**?argv);????bool?ReportStatusToSCMgr(DWORD?dwCurrentState,DWORD?dwWin32ExitCode,DWORD?dwWaitHint);????void?AddToMessageLog(LPTSTR?lpszMsg);?????????int?_tmain(int?argc,?_TCHAR*?argv[])????{?????????????SERVICE_TABLE_ENTRY?dispatchTable[]=?????????{?????????????{TEXT(SZSERVICENAME),(LPSERVICE_MAIN_FUNCTION)Service_Main},?????????????{?NULL,NULL}?????????};?????????if((argc>1)&&((*argv[1]=='-')||(argv[1]=="/")))?????????{?????????????if(_stricmp("install",argv[1]+1)==0)?????????????{??????????????????installService();?????????????}?????????????else?if(_stricmp("remove",argv[1]+1)==0)?????????????{??????????????????removeService();?????????????}?????????????else?if(_stricmp("debug",argv[1]+1)==0)?????????????{??????????????????bDebugServer=true;??????????????????debugService(argc,argv);?????????????}?????????????else?????????????{?????????????????????????????????????????????????printf("%s?-?install?to?install?the?service?/n",SZAPPNAME);??????????????????printf("%s?-?remove?to?remove?the?service?/n",SZAPPNAME);??????????????????printf("%s?-?debug?to?debug?the?service?/n",SZAPPNAME);??????????????????printf("/n?StartServiceCtrlDispatcher?being?called./n");??????????????????printf("This?may?take?several?seconds.Please?wait./n");??????????????????if(!StartServiceCtrlDispatcher(dispatchTable))???????????????????????AddToMessageLog(TEXT("StartServiceCtrlDispatcher?failed."));??????????????????else???????????????????????AddToMessageLog(TEXT("StartServiceCtrlDispatcher?OK."));?????????????}?????????????exit(0);?????????}?????????return?0;????}???
?
?2.服務(wù)入口點(diǎn)函數(shù)
?
服務(wù)入口點(diǎn)函數(shù) service_main 首先調(diào)用系統(tǒng)函數(shù) RegisterServiceCtrlHandler 注冊(cè)服務(wù)控制處理函數(shù) service_ctrl,然后調(diào)用 ReportStatusToSCMgr 函數(shù),它通過系統(tǒng)函數(shù) SetServiceStatus 更新服務(wù)的狀態(tài),然后調(diào)用特定的服務(wù)初始化入口函數(shù) ServiceStart 完成具體的初始化工作。
?
[c-sharp] view plaincopy
????void?ServiceStart(DWORD?dwArgc,LPTSTR*?lpszArgv);???void??WINAPI??Service_Main(DWORD?dwArgc,?LPTSTR?*lpszArgv)????{??????????????????sshStatusHandle=RegisterServiceCtrlHandler(TEXT(SZSERVICENAME),Service_Ctrl);??????????????????if(!sshStatusHandle)?????????{?????????????goto?cleanup;?????????????return;?????????}??????????????????ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;?????????ssStatus.dwServiceSpecificExitCode=0;??????????????????if(!ReportStatusToSCMgr(?????????????SERVICE_START_PENDING,?????????????NO_ERROR,?????????????????????????3000))????????????????????????????????goto?cleanup;?????????????????ServiceStart(dwArgc,lpszArgv);?????????return;????cleanup:??????????????????if(sshStatusHandle)?????????????(void)ReportStatusToSCMgr(SERVICE_STOPPED,dwErr,0);????}????
?
?
?
3.控制處理程序函數(shù)
?
函數(shù) Service_Ctrl 是服務(wù)的控制處理程序函數(shù),由主函數(shù)線程的控制分發(fā)程序引用。在處理控制請(qǐng)求碼時(shí),應(yīng)該在確定的時(shí)間間隔內(nèi)更新服務(wù)狀態(tài)檢查點(diǎn),避免發(fā)生服務(wù)不能響應(yīng)的錯(cuò)誤。
?
[c-sharp] view plaincopy
????void?WINAPI?Service_Ctrl(DWORD?dwCtrlCode)????{??????????????????switch(dwCtrlCode)?????????{??????????????????????case?SERVICE_CONTROL_STOP:?????????????ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);?????????????ServiceStop();??????????????????return;??????????????????????case?SERVICE_CONTROL_PAUSE:?????????????ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);?????????????ServicePause();?????????????????ssStatus.dwCurrentState=SERVICE_PAUSED;?????????????return;??????????????????????case?SERVICE_CONTROL_CONTINUE:?????????????ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,500);?????????????ServiceContinue();??????????????ssStatus.dwCurrentState=SERVICE_RUNNING;?????????????return;??????????????????????case?SERVICE_CONTROL_INTERROGATE:?????????????break;??????????????????????default:?????????????break;?????????}?????????ReportStatusToSCMgr(ssStatus.dwCurrentState,NO_ERROR,0);????}???????
?
?
?
除了系統(tǒng)定義的五種控制碼外(還有一種是:SERVICE_CONTROL_SHUTDOWN),用戶還可自定義控制碼,其取值范圍是128-255。用戶可以通過控制面板中的服務(wù)項(xiàng)向特定服務(wù)程序的控制處理函數(shù)發(fā)送控制碼,程序員可以調(diào)用系統(tǒng)函數(shù) ControlService 直接向服務(wù)程序的控制處理函數(shù)發(fā)送控制碼。其函數(shù)原型如下:
?
[c-sharp] view plaincopy
BOOL?ControlService(??????SC_HANDLE?hService,??????DWORD?dwControl,??????LPSERVICE_STATUS?lpServiceStatus????);?? hService :函數(shù) OpenService or CreateService 返回的服務(wù)程序句柄。
dwControl :控制碼,不能是SERVICE_CONTROL_SHUTDOWN。
lpServiceStatus:返回最后收到的服務(wù)狀態(tài)信息。
?
4.安裝服務(wù)程序
?
??? 每個(gè)已安裝服務(wù)程序在 HKEY_LOCAL_MACHINE/SYSTE/CurrentControlSet/Services 下都有一個(gè)服務(wù)名的關(guān)鍵字,程序員可以調(diào)用系統(tǒng)函數(shù) CreateService 安裝服務(wù)程序,并指定服務(wù)類型,服務(wù)名等。這個(gè)函數(shù)創(chuàng)建一個(gè)服務(wù)對(duì)象,并將其增加到相關(guān)的服務(wù)控制管理器數(shù)據(jù)庫中。
下面是函數(shù)原型:
[cpp] view plaincopy
?????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?????);?? ?
???? 對(duì)于一個(gè)已安裝的服務(wù)程序,可以調(diào)用系統(tǒng)函數(shù) OpenService 來獲取服務(wù)程序的句柄
下面是其函數(shù)原型:
[c-sharp] view plaincopy
SC_HANDLE?OpenService(??????SC_HANDLE?hSCManager,??????LPCTSTR?lpServiceName,??????DWORD?dwDesiredAccess????);?? ?
hSCManager :服務(wù)控制管理程序微服的登記數(shù)據(jù)庫的句柄。由函數(shù) OpenSCManager function 返回 這個(gè)句柄。
lpServiceName :將要打開的以NULL 結(jié)尾的服務(wù)程序的名字,和 CreateService? 中的 lpServiceName 相對(duì)應(yīng)。
dwDesiredAccess :指定服務(wù)的訪問類型。服務(wù)響應(yīng)請(qǐng)求時(shí),首先檢查訪問類型。
用CreateService 或OpenService 打開的服務(wù)程序句柄使用完畢后必須用CloseServiceHandle 關(guān)閉。
OpenSCManager打開的服務(wù)管理數(shù)據(jù)庫句柄也必須用它來關(guān)閉。
?
?
[cpp] view plaincopy
????void?installService()????{?????????SC_HANDLE?schService;?????????SC_HANDLE?schSCManager;?????????TCHAR?szPath[512];??????????????????if(GetModuleFileName(NULL,szPath,512)==0)?????????{?????????????_tprintf(TEXT("Unable?to?install?%s?-?%s?/n"),??????????????????TEXT(SZAPPNAME),?????????????GetLastError());?????????????return;?????????}??????????????????schSCManager=OpenSCManager(????????????????????????????????NULL,????????????????????????????????????NULL,????????????????????????????????????SC_MANAGER_ALL_ACCESS??????????????????????????????????);????if(schSCManager)?????????{??????????????????????????schService=CreateService(??????????????????schSCManager,??????????????????????????????????????TEXT(SZSERVICENAME),???????????????????????????????TEXT(SZAPPNAME),?????????????????????????SERVICE_ALL_ACCESS,????????????????????????????????SERVICE_WIN32_OWN_PROCESS,?????????????????????????SERVICE_DEMAND_START,??????????????????????????????SERVICE_ERROR_NORMAL,??????????????????????????????szPath,????????????????????????????????????????????????NULL,??????????????????????????????????????????????????NULL,??????????????????????????????????????????????????NULL,????????????????????????????????NULL,??????????????????????????????????????????????????NULL);?????????????if(schService)?????????????{??????????????????_tprintf(TEXT("%s?installed.?/n"),TEXT(SZAPPNAME));??????????????????CloseServiceHandle(schService);?????????????}?????????????else?????????????{??????????????????_tprintf(TEXT("CreateService?failed?-?%s?/n"),GetLastError());?????????????}?????????????CloseServiceHandle(schSCManager);?????????}?????????else?????????????_tprintf(TEXT("OpenSCManager?failed?-?%s?/n"),GetLastError());????}??????
?
?
主函數(shù)處理了三中命令行參數(shù):- install,- remove,- debug,分別用于安裝,刪除和調(diào)試服務(wù)程序。如果不帶參數(shù)運(yùn)行,則認(rèn)為是服務(wù)控制管理出現(xiàn)啟動(dòng)該服務(wù)程序。參數(shù)不正確則給出提示信息。
?
StartServiceCtrlDispatcher 函數(shù)負(fù)責(zé)把程序主線程連接到服務(wù)控制管理程序。具體描述如下:
BOOL StartServiceCtrlDispatcher(
? const LPSERVICE_TABLE_ENTRY lpServiceTable);
lpServiceStartTable 指向 SERVICE_TABLE_ENTRY 結(jié)構(gòu)類型的數(shù)組,他包含了調(diào)用進(jìn)程所提供的每個(gè)服務(wù)的入口函數(shù)和字符串名。表中的最后一個(gè)元素必須為 NULL,指明入口表結(jié)束。SERVICE_TABLE_ENTRY 結(jié)構(gòu)具體描述如下:
?
typedef struct _SERVICE_TABLE_ENTRY {? LPTSTR lpServiceName;? LPSERVICE_MAIN_FUNCTION lpServiceProc;
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
?
lpServiceName 是一個(gè)以 NULL 結(jié)尾的字符串,標(biāo)識(shí)服務(wù)名。如果是 SERVICE_WIN32_OWN_PROCESS 類型的服務(wù),這個(gè)字符串會(huì)被忽略。
lpServiceProc 指向服務(wù)入口點(diǎn)函數(shù)。
?
本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/camforg2010/archive/2010/04/20/5505171.aspx
總結(jié)
以上是生活随笔為你收集整理的C++后台服务程序开发模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。