一直感覺VC++太復雜了,但昨天看了汪蒲陽編著的因特網應用編程,其中寫到后臺服務程序的編寫,論述的非常詳細,而且邏輯清晰,看了之后感覺明白不少,故拿來與需要之人共享,并更正了原程序的一些錯誤,補充了一些材料。另外還有一種用C++編寫后臺服務程序的思路(不算.NET上服務程序開發模型),以后整理好了再發上來。
?
在2000/XP等基于NT 的操作系統中,有一個服務管理器,它管理的后臺進程被稱為 service。
服務是一種應用程序類型,它在后臺運行,與 UNIX 后臺應用程序類似。服務應用程序通常可以
在本地和通過網絡為用戶提供一些功能,例如客戶端/服務器應用程序、Web 服務器、數據庫服
務器以及其他基于服務器的應用程序。
??? 后臺服務 程序是在后臺悄悄運行的。我們通過將自己的程序登記為服務,可以使自己的程序不出現
在任務管理器中,并且隨系統啟動而最先運行,隨系統關閉而最后停止。
????
???? 服務控制管理器是一個RPC 服務器,它顯露了一組應用編程接口,程序員可以方便的編寫程序來配置
服務和控制遠程服務器中服務程序。
???? 服務程序通常編寫成控制臺類型的應用程序,總的來說,一個遵守服務控制管理程序接口要求的程序
包含下面三個函數:
1、服務程序主函數(main):調用系統函數 StartServiceCtrlDispatcher 連接程序主線程到服務控制管理程序。
2、服務入口點函數(ServiceMain):執行服務初始化任務,同時執行多個服務的服務進程有多個服務入口函數。
3、控制服務處理程序函數(Handler):在服務程序收到控制請求時由控制分發線程引用。(此處是Service_Ctrl)。
?另外在系統運行此服務之前需要安裝登記服務程序:installService 函數。刪除服務程序則需要先刪除服務安裝登記:removeService 函數。
?
服務類型:
類型說明SERVICE_FILE_SYSTEM_DRIVER=2文件系統驅動服務。SERVICE_KERNEL_DRIVER=1驅動服務。SERVICE_WIN32_OWN_PROCESS=16獨占一個進程的服務。SERVICE_WIN32_SHARE_PROCESS=32與其他服務共享一個進程的服務。
?
新建WIN32控制臺程序, 其源文件名為service.cpp 。我用的開發工具是VC++.NET。
?1.服務程序主函數
??? 服務控制管理程序啟動服務程序后,等待服務程序主函數調用系統函StartServiceCtrlDispatcher。一個SERVICE_WIN32_OWN_PROCESS 類型的服務應該立即調用 StartServiceCtrlDispatcher 函數,可以在服務啟動后讓服務入口點函數完成初始化工作。對于 SERVICE_WIN32_OWN_PROCESS 類型的服務和程序中所有服務共同的初始化工作可以在主函數中完成,但不要超過30秒。否則必須建立另外的線程完成這些共同的初始化工作,從而保證服務程序主函數能及時地調用 StartServiceCtrlDispatcher 函數。
?
?
?
?
[cpp] view plaincopy
????#include?"stdafx.h"????#include?"Windows.h"????#define?SZAPPNAME??????"serverSample"?????//服務程序名????#define?SZSERVICENAME??"serviceSample"????//標識服務的內部名?????????????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.服務入口點函數
?
服務入口點函數 service_main 首先調用系統函數 RegisterServiceCtrlHandler 注冊服務控制處理函數 service_ctrl,然后調用 ReportStatusToSCMgr 函數,它通過系統函數 SetServiceStatus 更新服務的狀態,然后調用特定的服務初始化入口函數 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.控制處理程序函數
?
函數 Service_Ctrl 是服務的控制處理程序函數,由主函數線程的控制分發程序引用。在處理控制請求碼時,應該在確定的時間間隔內更新服務狀態檢查點,避免發生服務不能響應的錯誤。
?
[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);????}???????
?
?
?
除了系統定義的五種控制碼外(還有一種是:SERVICE_CONTROL_SHUTDOWN),用戶還可自定義控制碼,其取值范圍是128-255。用戶可以通過控制面板中的服務項向特定服務程序的控制處理函數發送控制碼,程序員可以調用系統函數 ControlService 直接向服務程序的控制處理函數發送控制碼。其函數原型如下:
?
[c-sharp] view plaincopy
BOOL?ControlService(??????SC_HANDLE?hService,??????DWORD?dwControl,??????LPSERVICE_STATUS?lpServiceStatus????);?? hService :函數 OpenService or CreateService 返回的服務程序句柄。
dwControl :控制碼,不能是SERVICE_CONTROL_SHUTDOWN。
lpServiceStatus:返回最后收到的服務狀態信息。
?
4.安裝服務程序
?
??? 每個已安裝服務程序在 HKEY_LOCAL_MACHINE/SYSTE/CurrentControlSet/Services 下都有一個服務名的關鍵字,程序員可以調用系統函數 CreateService 安裝服務程序,并指定服務類型,服務名等。這個函數創建一個服務對象,并將其增加到相關的服務控制管理器數據庫中。
下面是函數原型:
[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?????);?? ?
???? 對于一個已安裝的服務程序,可以調用系統函數 OpenService 來獲取服務程序的句柄
下面是其函數原型:
[c-sharp] view plaincopy
SC_HANDLE?OpenService(??????SC_HANDLE?hSCManager,??????LPCTSTR?lpServiceName,??????DWORD?dwDesiredAccess????);?? ?
hSCManager :服務控制管理程序微服的登記數據庫的句柄。由函數 OpenSCManager function 返回 這個句柄。
lpServiceName :將要打開的以NULL 結尾的服務程序的名字,和 CreateService? 中的 lpServiceName 相對應。
dwDesiredAccess :指定服務的訪問類型。服務響應請求時,首先檢查訪問類型。
用CreateService 或OpenService 打開的服務程序句柄使用完畢后必須用CloseServiceHandle 關閉。
OpenSCManager打開的服務管理數據庫句柄也必須用它來關閉。
?
?
[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());????}??????
?
?
主函數處理了三中命令行參數:- install,- remove,- debug,分別用于安裝,刪除和調試服務程序。如果不帶參數運行,則認為是服務控制管理出現啟動該服務程序。參數不正確則給出提示信息。
?
StartServiceCtrlDispatcher 函數負責把程序主線程連接到服務控制管理程序。具體描述如下:
BOOL StartServiceCtrlDispatcher(
? const LPSERVICE_TABLE_ENTRY lpServiceTable);
lpServiceStartTable 指向 SERVICE_TABLE_ENTRY 結構類型的數組,他包含了調用進程所提供的每個服務的入口函數和字符串名。表中的最后一個元素必須為 NULL,指明入口表結束。SERVICE_TABLE_ENTRY 結構具體描述如下:
?
typedef struct _SERVICE_TABLE_ENTRY {? LPTSTR lpServiceName;? LPSERVICE_MAIN_FUNCTION lpServiceProc;
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
?
lpServiceName 是一個以 NULL 結尾的字符串,標識服務名。如果是 SERVICE_WIN32_OWN_PROCESS 類型的服務,這個字符串會被忽略。
lpServiceProc 指向服務入口點函數。
?
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/camforg2010/archive/2010/04/20/5505171.aspx
總結
以上是生活随笔為你收集整理的C++后台服务程序开发模式的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。