[Win32] 服務程序開發(1)基本概念和服務程序的框架

本博文由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:上下文。多個服務共享一個進程,表示任何用戶定義數據參數傳遞處理程序函數可以幫助識別服務。(可空)

如果函數成功返回服務狀態句柄如果函數失敗返回零。

5。HandlerEx回調函數
DWORD WINAPI HandlerEx(
  _In_ DWORD  dwControl,
  _In_ DWORD  dwEventType,
  _In_ LPVOID lpEventData,
  _In_ LPVOID lpContext
);
RegisterServiceCtrlHandlerEx函數應用程序定義回調函數服務程序可以使用作爲控制處理程序功能特定服務
dwControl:控制代碼
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。
dwEventType發生事件類型。(一般不需要)如果dwControlSERVICE_CONTROL_DEVICEEVENTSERVICE_CONTROL_HARDWAREPROFILECHANGE、SERVICE_CONTROL_POWEREVENTSERVICE_CONTROL_SESSIONCHANGE使用參數否則
如果dwControlSERVICE_CONTROL_DEVICEEVENT參數可以下列之一:DBT_DEVICEARRIVAL
DBT_DEVICEREMOVECOMPLETE
DBT_DEVICEQUERYREMOVE
DBT_DEVICEQUERYREMOVEFAILED
DBT_DEVICEREMOVEPENDING
DBT_CUSTOMEVENT
如果dwControlSERVICE_CONTROL_HARDWAREPROFILECHANGE參數可以下列之一:
DBT_CONFIGCHANGED
DBT_QUERYCHANGECONFIG
DBT_CONFIGCHANGECANCELED
如果 dwControl 是 SERVICE_CONTROL_POWEREVENT,此參數可以指定 WM_POWERBROADCAST 消息的 wParam 參數中的值之一。如果 dwControl 是 SERVICE_CONTROL_SESSIONCHANGE,此參數可以指定 WM_WTSSESSION_CHANGE 消息的 wParam 參數中的值之一。
lpEventData其他設備信息(一般不需要)
如果 dwControl 是 SERVICE_CONTROL_DEVICEEVENT,此數據對應的 lParam 參數的應用程序接收作爲 WM_DEVICECHANGE 消息的一部分。
如果 dwControl 是 SERVICE_CONTROL_POWEREVENT 和 dwEventType 是 PBT_POWERSETTINGCHANGE,這個數據是指向 POWERBROADCAST_SETTING 結構的指針。
如果 dwControl 是 SERVICE_CONTROL_SESSIONCHANGE,此參數是指向 WTSSESSION_NOTIFICATION 結構的指針。
如果 dwControl 是 SERVICE_CONTROL_TIMECHANGE,這個數據是指向 SERVICE_TIMECHANGE_INFO 結構的指針。
lpContext用戶RegisterServiceCtrlHandlerEx傳遞數據。多個服務共享一個進程lpContext參數可以幫助標識服務
返回值:
如果服務處理控制返回ERROR_CALL_NOT_IMPLEMENTED然而服務返回SERVICE_CONTROL_INTERROGATE即使服務處理
如果服務處理SERVICE_CONTROL_STOPSERVICE_CONTROL_SHUTDOWN時返回NO_ERROR
如果您的服務處理 SERVICE_CONTROL_DEVICEEVENT,返回可授予請求和錯誤代碼拒絕該請求。
如果您的服務處理 SERVICE_CONTROL_HARDWAREPROFILECHANGE,返回可授予請求和錯誤代碼拒絕該請求。
如果您的服務處理 SERVICE_CONTROL_POWEREVENT,返回可授予請求和錯誤代碼拒絕該請求。
對於所有其他控制代碼你服務句柄,返回 NO_ERROR。
6。SetServiceStatus
BOOL WINAPI SetServiceStatus(
  _In_ SERVICE_STATUS_HANDLE hServiceStatus,
  _In_ LPSERVICE_STATUS      lpServiceStatus
);
hServiceStatus當前服務狀態信息結構句柄句柄RegisterServiceCtrlHandlerEx函數返回
lpServiceStatus指向SERVICE_STATUS結構指針,此結構包含調用服務最新狀態信息
如果函數成功返回爲零如果函數失敗返回
7。SERVICE_STATUS結構
typedef struct _SERVICE_STATUS {
  DWORD dwServiceType;
  DWORD dwCurrentState;
  DWORD dwControlsAccepted;
  DWORD dwWin32ExitCode;
  DWORD dwServiceSpecificExitCode;
  DWORD dwCheckPoint;
  DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
dwServiceType服務類型
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:服務已停止。
dwControlsAccepted
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: 此控制代碼不受支持。
dwWin32ExitCode錯誤代碼服務使用報告啓動停止時發生錯誤若要返回服務特定錯誤代碼服務必須設置ERROR_SERVICE_SPECIFIC_ERROR指示dwServiceSpecificExitCode成員包含錯誤代碼服務運行正常終止設置NO_ERROR
dwServiceSpecificExitCodedwWin32ExitCode成員設置ERROR_SERVICE_SPECIFIC_ERROR時,此參數表示服務返回發生錯誤服務正在啓動停止服務特定錯誤代碼。否則忽略
dwCheckPoint:置0即可
dwWaitHint掛起啓動所需估計時間停止暫停繼續操作毫秒爲單位
完整源碼實例(服務程序框架):
#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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章