天天看點

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

本博文由CSDN部落客zuishikonghuan所作,版權歸zuishikonghuan所有,轉載請注明出處:http://blog.csdn.net/zuishikonghuan/article/details/47604179

一。服務程式基本概念

在Windows中,有一個特殊的群體,他們天生擁有極高的權限,在一些特殊使用者,比如“SYSTEM”的使用者中工作。他們享受很高的優待。很多系統功能(更新服務,觸摸屏服務。。。)都是以服務運作的。應用程式也可以安裝控制自己的服務(使用服務管理器API,以後會講)。下面,讓我們看看如何編寫一個Win32服務。

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

圖:系統中安裝的服務

二。服務程式的編寫架構

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: 已 發生 的 事件 的 類型 。(一般不需要) 如果 dwControl 是 SERVICE_CONTROL_DEVICEEVENT 、 SERVICE_CONTROL_HARDWAREPROFILECHANGE、 SERVICE_CONTROL_POWEREVENT 或 SERVICE_CONTROL_SESSIONCHANGE , 則 使用 此 參數 。 否則 , 它 是 零 。 如果 dwControl 是 SERVICE_CONTROL_DEVICEEVENT , 此 參數 可以 是 下列 值 之一:DBT_DEVICEARRIVAL

DBT_DEVICEREMOVECOMPLETE

DBT_DEVICEQUERYREMOVE

DBT_DEVICEQUERYREMOVEFAILED

DBT_DEVICEREMOVEPENDING

DBT_CUSTOMEVENT 如果 dwControl 是 SERVICE_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_STOP或SERVICE_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 。 dwServiceSpecificExitCode: dwWin32ExitCode 成員 設定 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 

繼續閱讀