本博文由CSDN博主zuishikonghuan所作,版權歸zuishikonghuan所有,轉載請註明出處:http://blog.csdn.net/zuishikonghuan/article/details/47604179
一。服務程序基本概念
在Windows中,有一個特殊的羣體,他們天生擁有極高的權限,在一些特殊用戶,比如“SYSTEM”的用戶中工作。他們享受很高的優待。很多系統功能(更新服務,觸摸屏服務。。。)都是以服務運行的。應用程序也可以安裝控制自己的服務(使用服務管理器API,以後會講)。下面,讓我們看看如何編寫一個Win32服務。
圖:系統中安裝的服務
二。服務程序的編寫框架
1。StartServiceCtrlDispatcher函數
BOOL WINAPI StartServiceCtrlDispatcher(
_In_ const SERVICE_TABLE_ENTRY *lpServiceTable
);
函數功能:連接到服務控制管理器
參數:一個SERVICE_TABLE_ENTRY結構的指針,成員表中的最後一項必須具有NULL值,來指定表的末尾。
2。SERVICE_TABLE_ENTRY結構:
typedef struct _SERVICE_TABLE_ENTRY {
LPTSTR lpServiceName;
LPSERVICE_MAIN_FUNCTION lpServiceProc;
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
lpServiceName:要在此服務進程中運行的服務名稱,不能和其他程序的重了。我一般把這個名稱設爲註冊到系統的服務名稱一致。
如果服務安裝的是SERVICE_WIN32_OWN_PROCESS服務類型,此成員將被忽略,但不能爲 NULL,可以是空字符串("")。
lpServiceProc:指針,指向ServiceMain函數。
3。ServiceMain函數:(服務啓動回調函數)
VOID WINAPI ServiceMain(
_In_ DWORD dwArgc,
_In_ LPTSTR *lpszArgv
);
dwArgc:lpszArgv數組中字符串個數
lpszArgv:啓動參數
當服務控制管理器收到請求啓動服務時,便會啓動服務進程(如果尚未運行)。服務進程的主線程調用StartServiceCtrlDispatcher函數用到的SERVICE_TABLE_ENTRY結構數組的指針。然後服務控制管理器將啓動請求發送到服務控制調度程序爲此服務的進程。服務控制調度程序將創建一個新線程來執行服務正在啓動ServiceMain函數。
ServiceMain函數應立即調用RegisterServiceCtrlHandlerEx函數來指定一個HandlerEx函數來處理控制請求。接下來,它應該調用SetServiceStatus函數來將狀態信息發送到服務控制管理器。這些調用完成之後,該函數應完成初始化服務。
不應在服務初始化期間調用任何系統的功能。只有在它報告SERVICE_RUNNING狀態後,服務代碼纔可以調用系統函數。
4。RegisterServiceCtrlHandlerEx函數
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx(
_In_ LPCTSTR lpServiceName,
_In_ LPHANDLER_FUNCTION_EX lpHandlerProc,
_In_opt_ LPVOID lpContext
);
函數功能:註冊一個函數來處理擴展的服務控制請求。
lpServiceName:通過調用線程運行的服務的名稱。這是服務控制程序在創建服務時,CreateService函數中指定的服務名稱。(和上面說的SERVICE_TABLE_ENTRY中的lpServiceName一致即可)
lpHandlerProc:指向要註冊的處理程序函數(HandlerEx)的指針
lpContext:上下文。當多個服務共享一個進程,表示任何用戶定義的數據。此參數傳遞給處理程序函數,可以幫助識別服務。(可空)
DWORD WINAPI HandlerEx(
_In_ DWORD dwControl,
_In_ DWORD dwEventType,
_In_ LPVOID lpEventData,
_In_ LPVOID lpContext
);
SERVICE_CONTROL_NETBINDDISABLE:通知網絡服務及其綁定之一已被禁用。服務應該重讀它的綁定信息和刪除綁定。應用程序應改用插即用功能。
SERVICE_CONTROL_NETBINDENABLE:通知網絡服務已啓用已禁用的綁定。服務應該重讀它的綁定信息,並添加新的綁定。應用程序應改用插即用功能。
SERVICE_CONTROL_NETBINDREMOVE:通知網絡服務綁定的組件已被刪除。服務應該重讀它的綁定信息和取消綁定從已刪除的組件。應用程序應改用插即用功能。
SERVICE_CONTROL_PARAMCHANGE:通知服務特定於服務的啓動參數已更改。服務應該重讀其啓動參數。
SERVICE_CONTROL_PAUSE:通知它應該暫停服務。
SERVICE_CONTROL_PRESHUTDOWN:通知服務,系統將關閉。在系統關機時需要額外的時間來執行清理任務等緊迫的時間的服務可以使用此通知。Windows Server 2003 和 Windows XP: 不支持此值。
SERVICE_CONTROL_SHUTDOWN:通知系統正在關閉,所以這項服務可以執行清理任務的服務。請注意註冊 SERVICE_CONTROL_PRESHUTDOWN 通知的服務無法接收此通知,因爲他們已經停止了。如果服務接受此控制代碼,它必須停止後它執行其清理任務並返回 NO_ERROR。SCM 發送此控制代碼之後,它不會向服務發送其他控制代碼。
SERVICE_CONTROL_STOP:通知應停止服務。如果服務接受此控制代碼,它必須在收到後停止並返回 NO_ERROR。SCM 發送此控制代碼之後,它不會向服務發送其他控制代碼。
Windows XP: 如果服務返回 NO_ERROR,繼續運行,它繼續接收控制代碼。這種行爲與 Windows Server 2003 和 Windows XP sp2 開始改變了。
此參數也可以是下面的擴展的控制代碼之一。請注意,這些控制代碼不支持由處理程序函數處理:。(該服務必須註冊以接收這些通知,使用 RegisterDeviceNotification 函數。)
SERVICE_CONTROL_DEVICEEVENT:通知服務設備事件。DwEventType 和 lpEventData 參數包含附加信息。
SERVICE_CONTROL_HARDWAREPROFILECHANGE:通知已更改計算機的硬件配置文件服務。DwEventType 參數包含的其他信息。
SERVICE_CONTROL_POWEREVENT:通知系統電源事件服務。DwEventType 參數包含的其他信息。如果 dwEventType 是 PBT_POWERSETTINGCHANGE,lpEventData 參數還包含其他信息。
SERVICE_CONTROL_SESSIONCHANGE:通知服務的會話更改事件。請注意,只有在是否它是完全加載之前進行登錄嘗試只將用戶登錄的通知服務。DwEventType 和 lpEventData 參數包含附加信息。
SERVICE_CONTROL_TIMECHANGE:通知服務的系統時間已更改。LpEventData 參數包含的其他信息。不使用 dwEventType 參數。Windows Server 2008、 Windows Vista、 Windows Server 2003 和 Windows XP: 此控制代碼不受支持。
SERVICE_CONTROL_TRIGGEREVENT:通知服務註冊爲已發生事件的服務觸發事件。Windows Server 2008、 Windows Vista、 Windows Server 2003 和 Windows XP: 此控制代碼不受支持。
SERVICE_CONTROL_USERMODEREBOOT:通知服務的用戶已開始重新啓動。Windows Server 2008 R2、 Windows 7、 Windows Server 2008,Windows Vista、 Windows Server 2003 和 Windows XP: 此控制代碼不受支持。
用戶自定義的控制代碼:從 128 到 255。
DBT_DEVICEREMOVECOMPLETE
DBT_DEVICEQUERYREMOVE
DBT_DEVICEQUERYREMOVEFAILED
DBT_DEVICEREMOVEPENDING
DBT_CUSTOMEVENT
DBT_QUERYCHANGECONFIG
DBT_CONFIGCHANGECANCELED
如果 dwControl 是 SERVICE_CONTROL_POWEREVENT 和 dwEventType 是 PBT_POWERSETTINGCHANGE,這個數據是指向 POWERBROADCAST_SETTING 結構的指針。
如果 dwControl 是 SERVICE_CONTROL_SESSIONCHANGE,此參數是指向 WTSSESSION_NOTIFICATION 結構的指針。
如果 dwControl 是 SERVICE_CONTROL_TIMECHANGE,這個數據是指向 SERVICE_TIMECHANGE_INFO 結構的指針。
如果您的服務處理 SERVICE_CONTROL_HARDWAREPROFILECHANGE,返回可授予請求和錯誤代碼拒絕該請求。
如果您的服務處理 SERVICE_CONTROL_POWEREVENT,返回可授予請求和錯誤代碼拒絕該請求。
對於所有其他控制代碼你服務句柄,返回 NO_ERROR。
BOOL WINAPI SetServiceStatus(
_In_ SERVICE_STATUS_HANDLE hServiceStatus,
_In_ LPSERVICE_STATUS lpServiceStatus
);
typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
SERVICE_FILE_SYSTEM_DRIVER:服務是文件系統驅動程序。
SERVICE_KERNEL_DRIVER:服務是一個設備驅動程序。
SERVICE_WIN32_OWN_PROCESS:服務運行在它自己的進程。
SERVICE_WIN32_SHARE_PROCESS:服務與其他服務共享一個進程。
如果服務類型是 SERVICE_WIN32_OWN_PROCESS 或 SERVICE_WIN32_SHARE_PROCESS,並且該服務運行在本地系統帳戶的上下文中,也可以指定以下類型:
SERVICE_INTERACTIVE_PROCESS:服務可以與桌面交互。
dwCurrentState:服務的當前狀態。此成員可以是下列值之一。SERVICE_CONTINUE_PENDING:服務繼續處於掛起狀態。
SERVICE_PAUSE_PENDING:服務暫停被掛起。
SERVICE_PAUSED:服務已暫停。
SERVICE_RUNNING:該服務正在運行。
SERVICE_START_PENDING:服務正在啓動。
SERVICE_STOP_PENDING:服務正在停止。
SERVICE_STOPPED:服務已停止。
SERVICE_ACCEPT_NETBINDCHANGE:服務是網絡組件,它可以接受更改其綁定中的沒有被停止並重新啓動。此控制代碼允許接收 SERVICE_CONTROL_NETBINDADD、 SERVICE_CONTROL_NETBINDREMOVE、 SERVICE_CONTROL_NETBINDENABLE 和 SERVICE_CONTROL_NETBINDDISABLE 的通知服務。
SERVICE_ACCEPT_PARAMCHANGE:該服務可以重讀其啓動參數沒有被停止並重新啓動。此控制代碼允許接收 SERVICE_CONTROL_PARAMCHANGE 通知服務。
SERVICE_ACCEPT_PAUSE_CONTINUE:可以暫停和繼續服務。此控制代碼允許接收 SERVICE_CONTROL_PAUSE 和 SERVICE_CONTROL_CONTINUE 通知服務。
SERVICE_ACCEPT_PRESHUTDOWN:該服務可以執行 preshutdown 任務。此控制代碼使服務能夠接收 SERVICE_CONTROL_PRESHUTDOWN 通知。請注意,這個和 ControlServiceEx 不能發送此通知;只有系統可以發送它。Windows Server 2003 和 Windows XP: 不支持此值。
SERVICE_ACCEPT_SHUTDOWN:系統關機時,將通知服務。此控制代碼允許接收 SERVICE_CONTROL_SHUTDOWN 通知服務。請注意,這個和 ControlServiceEx 不能發送此通知;只有系統可以發送它。
SERVICE_ACCEPT_STOP:可以停止該服務。此控制代碼允許接收 SERVICE_CONTROL_STOP 通知服務。
此成員還可以包含下面的擴展的控制代碼,僅由 HandlerEx 支持。(請注意,這些控制代碼無法通過ControlServiceEx發送)。
SERVICE_ACCEPT_HARDWAREPROFILECHANGE:當計算機的硬件配置文件發生更改時,將通知服務。這使系統能夠向服務發送 SERVICE_CONTROL_HARDWAREPROFILECHANGE 通知。
SERVICE_ACCEPT_POWEREVENT:當計算機電源狀態更改通知服務。這使系統能夠向服務發送 SERVICE_CONTROL_POWEREVENT 通知。
SERVICE_ACCEPT_SESSIONCHANGE:當計算機的會話狀態發生更改時,將通知服務。這使系統能夠向服務發送 SERVICE_CONTROL_SESSIONCHANGE 通知。
SERVICE_ACCEPT_TIMECHANGE:當系統時間已更改通知服務。這使系統能夠向服務發送 SERVICE_CONTROL_TIMECHANGE 通知。Windows Server 2008、 Windows Vista、 Windows Server 2003 和 Windows XP: 此控制代碼不受支持。
SERVICE_ACCEPT_TRIGGEREVENT:服務註冊爲該事件發生時通知服務。這使系統能夠向服務發送 SERVICE_CONTROL_TRIGGEREVENT 通知。Windows Server 2008、 Windows Vista、 Windows Server 2003 和 Windows XP: 此控制代碼不受支持。
SERVICE_ACCEPT_USERMODEREBOOT:當用戶啓動重新啓動時,將通知服務。Windows Server 2008 R2、 Windows 7、 Windows Server 2008,Windows Vista、 Windows Server 2003 和 Windows XP: 此控制代碼不受支持。
#include "stdafx.h"
#include <windows.h>
static TCHAR* name = TEXT("MyService");//服務名稱
SERVICE_STATUS_HANDLE hStatus;//服務狀態句柄
SERVICE_STATUS ServiceStatus;//當前服務的狀態信息
DWORD WINAPI HandlerEx(_In_ DWORD dwControl, _In_ DWORD dwEventType, _In_ LPVOID lpEventData, _In_ LPVOID lpContext)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP://控制代碼:要求停止停止
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus, &ServiceStatus);//報告服務運行狀態
return 0;
case SERVICE_CONTROL_SHUTDOWN://控制代碼:關機
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus, &ServiceStatus);//報告服務運行狀態
return 0;
default:
break;
}
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hStatus, &ServiceStatus);//報告服務運行狀態
return 0;
}
VOID WINAPI ServiceMain(_In_ DWORD dwArgc, _In_ LPTSTR *lpszArgv)
{
hStatus = RegisterServiceCtrlHandlerEx(name, &HandlerEx, NULL);
if (hStatus == (SERVICE_STATUS_HANDLE)0)
{
//這裏是對RegisterServiceCtrlHandler失敗的處理
return;
}
RtlZeroMemory(&ServiceStatus, sizeof(SERVICE_STATUS));//結構體清空
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;//接受關機和停止控制
SetServiceStatus(hStatus, &ServiceStatus);//報告服務運行狀態
if (ServiceStatus.dwCurrentState == SERVICE_RUNNING){
//把服務要做的工作放到這裏
}
}
int _tmain(int argc, _TCHAR* argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = name;
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)&ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
StartServiceCtrlDispatcher(ServiceTable);
return 0;
}
服務程序如何關閉自己?正確的做法是
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hStatus, &ServiceStatus);
return;
如何把自己的服務安裝進系統?使用服務管理器API,我們就可以讓我們的程序安裝服務,管理服務了,以後會講。現在我們可以使用一些工具來安裝(比如srvinstw),其實這些工具也是調用的服務管理器API