類型 | 說明 |
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 函數。 主函數處理了三中命令行參數:- 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 指向服務入口點函數。 //服務程序主函數。
#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
{ //若是未能和上面的如何參數匹配,則多是服務控制管理程序來啓動該程序。當即調用
//StartServiceCtrlDispatcher 函數。
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 完成具體的初始化工做。 //服務入口點函數
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;
}
//初始化 SERVICE_STATUS 結構中的成員
ssStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
ssStatus.dwServiceSpecificExitCode=0;
//更新服務狀態
if(!ReportStatusToSCMgr(
SERVICE_START_PENDING,//服務狀態,The service is starting.
NO_ERROR, //退出碼
3000)) //等待時間
goto cleanup; //更新服務狀態失敗則轉向 cleanup
ServiceStart(dwArgc,lpszArgv);
return;
cleanup:
//把服務狀態更新爲 SERVICE_STOPPED,並退出。
if(sshStatusHandle)
(void)ReportStatusToSCMgr(SERVICE_STOPPED,dwErr,0);
} 序函數 函數 Service_Ctrl 是服務的控制處理程序函數,由主函數線程的控制分發程序引用。在處理控制請求碼時,應該在肯定的時間間隔內更新服務狀態檢查點,避免發生服務不能響應的錯誤。 //控制處理程序函數
void WINAPI Service_Ctrl(DWORD dwCtrlCode)
{
//處理控制請求碼
switch(dwCtrlCode)
{
//先更新服務狀態爲 SERVICDE_STOP_PENDING,再中止服務。
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 直接向服務程序的控制處理函數發送控制碼。其函數原型以下: 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 安裝服務程序,並指定服務類型,服務名等。這個函數建立一個服務對象,並將其增長到相關的服務控制管理器數據庫中。 下面是函數原型: SC_HANDLE CreateService( SC_HANDLE hSCManager, //服務控制管理程序維護的登記數據庫的句柄,由系統函數OpenSCManager 返回 LPCTSTR lpServiceName, //以NULL 結尾的服務名,用於建立登記數據庫中的關鍵字 LPCTSTR lpDisplayName, //以NULL 結尾的服務名,用於用戶界面標識服務 DWORD dwDesiredAccess, //指定服務返回類型 DWORD dwServiceType, //指定服務類型 DWORD dwStartType, //指定什麼時候啓動服務 DWORD dwErrorControl, //指定服務啓動失敗的嚴重程度 LPCTSTR lpBinaryPathName, //指定服務程序二進制文件的路徑 LPCTSTR lpLoadOrderGroup, //指定順序裝入的服務組名 LPDWORD lpdwTagId, //忽略,NULL LPCTSTR lpDependencies, //指定啓動該服務前必須先啓動的服務或服務組 LPCTSTR lpServiceStartName, //以NULL 結尾的字符串,指定服務賬號。如是NULL,則表示使用LocalSystem 賬號 LPCTSTR lpPassword //以NULL 結尾的字符串,指定對應的口令。爲NULL表示無口令。但使用LocalSystem時填NULL ); 對於一個已安裝的服務程序,能夠調用系統函數 OpenService 來獲取服務程序的句柄 下面是其函數原型: SC_HANDLE OpenService( SC_HANDLE hSCManager, LPCTSTR lpServiceName, DWORD dwDesiredAccess ); hSCManager :服務控制管理程序微服的登記數據庫的句柄。由函數 OpenSCManager function 返回 這個句柄。 lpServiceName :將要打開的以NULL 結尾的服務程序的名字,和 CreateService 中的 lpServiceName 相對應。 dwDesiredAccess :指定服務的訪問類型。服務響應請求時,首先檢查訪問類型。 用CreateService 或OpenService 打開的服務程序句柄使用完畢後必須用CloseServiceHandle 關閉。 OpenSCManager打開的服務管理數據庫句柄也必須用它來關閉。 //安裝服務程序
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());//@1獲取調用函數返回的最後錯誤碼
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, //沒有tag標識符
NULL, //啓動服務所依賴的服務或服務組,這裏僅僅是一個空字符串
NULL, //LocalSystem 賬號
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());
}程序員