久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Windows服務(wù)程序

 紫殿 2012-02-26

有那么一類應(yīng)用程序,,是能夠?yàn)楦鞣N用戶(包括本地用戶和遠(yuǎn)程用戶)所用的,,

擁有用戶授權(quán)級進(jìn)行管理的能力,并且不論用戶是否物理的與正在運(yùn)行該應(yīng)用程

序的計(jì)算機(jī)相連都能正常執(zhí)行,,這就是所謂的服務(wù)了,。

(一)服務(wù)的基礎(chǔ)知識

Question 1. 什么是服務(wù)?它的特征是什么,?

  在NT/2000中,,服務(wù)是一類受到操作系統(tǒng)優(yōu)待的程序。一個(gè)服務(wù)首先是一個(gè)

Win32可執(zhí)行程序,,如果要寫一個(gè)功能完備且強(qiáng)大的服務(wù),,需要熟悉動(dòng)態(tài)連接庫

(Dlls)、結(jié)構(gòu)異常處理,、內(nèi)存映射文件,、虛擬內(nèi)存、設(shè)備I/O、線程及其同步,、

Unicode以及其他的由WinAPI函數(shù)提供的應(yīng)用接口,。當(dāng)然本文討論的只是建立一

個(gè)可以安裝、運(yùn)行,、啟動(dòng),、停止的沒有任何其他功能的服務(wù),所以無需上述知識

仍可以繼續(xù)看下去,,我會(huì)在過程中將理解本文所需要的知識逐一講解,。

  第二要知道的是一個(gè)服務(wù)決不需要用戶界面。大多數(shù)的服務(wù)將運(yùn)行在那些被

鎖在某些黑暗的,,冬暖夏涼的小屋子里的強(qiáng)大的服務(wù)器上面,,即使有用戶界面一

般也沒有人可以看到。如果服務(wù)提供任何用戶界面如消息框,,那么用戶錯(cuò)過這些

消息的可能性就極高了,,所以服務(wù)程序通常以控制臺(tái)程序的形式被編寫,進(jìn)入點(diǎn)

函數(shù)是main()而不是WinMain(),。

  也許有人有疑問:沒有用戶界面的話,,要怎樣設(shè)置、管理一個(gè)服務(wù),?怎樣開

始,、停止它?服務(wù)如何發(fā)出警告或錯(cuò)誤信息,、如何報(bào)告關(guān)于它的執(zhí)行情況的統(tǒng)計(jì)

數(shù)據(jù),?這些問題的答案就是服務(wù)能夠被遠(yuǎn)程管理,Windows NT/2000提供了大量

的管理工具,,這些工具允許通過網(wǎng)絡(luò)上的其它計(jì)算機(jī)對某臺(tái)機(jī)器上面的服務(wù)進(jìn)行

管理,。比如Windows 2000里面的“控制臺(tái)”程序(mmc.exe),用它添加“管理單

元”就可以管理本機(jī)或其他機(jī)器上的服務(wù),。

Question 2. 服務(wù)的安全性…

  想要寫一個(gè)服務(wù),,就必須熟悉Win NT/2000的安全機(jī)制,在上述操作系統(tǒng)之

中,,所有安全都是基于用戶的,。換句話說——進(jìn)程、線程,、文件,、注冊表鍵、信

號,、事件等等等等都屬于一個(gè)用戶,。當(dāng)一個(gè)進(jìn)程被產(chǎn)生的時(shí)候,,它都是執(zhí)行在一

個(gè)用戶的上下文(context),這個(gè)用戶賬號可能在本機(jī),,也可能在網(wǎng)絡(luò)中的其他

機(jī)器上,,或者是在一個(gè)特殊的賬號:System Account——即系統(tǒng)賬號的上下文

  如果一個(gè)進(jìn)程正在一個(gè)用戶賬號下執(zhí)行,那么這個(gè)進(jìn)程就同時(shí)擁有這個(gè)用戶

所能擁有的一切訪問權(quán)限,,不論是在本機(jī)還是網(wǎng)絡(luò),。系統(tǒng)賬號則是一個(gè)特殊的賬

號,它用來標(biāo)識系統(tǒng)本身,,而且運(yùn)行在這個(gè)賬號下的任何進(jìn)程都擁有系統(tǒng)上的所

有訪問權(quán)限,,但是系統(tǒng)賬號不能在域上使用,無法訪問網(wǎng)絡(luò)資源…

  服務(wù)也是Win32可執(zhí)行程序,,它也需要執(zhí)行在一個(gè)context,,通常服務(wù)都是在

系統(tǒng)賬號下運(yùn)行,但是也可以根據(jù)情況選擇讓它運(yùn)行在一個(gè)用戶賬號下,,也就會(huì)

因此獲得相應(yīng)的訪問資源的權(quán)限,。

Question 3. 服務(wù)的三個(gè)組成部分

  一個(gè)服務(wù)由三部分組成,第一部分是Service Control Manager(SCM),。每個(gè)

Windows NT/2000系統(tǒng)都有一個(gè)SCM,,SCM存在于Service.exe中,在Windows啟動(dòng)

的時(shí)候會(huì)自動(dòng)運(yùn)行,,伴隨著操作系統(tǒng)的啟動(dòng)和關(guān)閉而產(chǎn)生和終止,。這個(gè)進(jìn)程以系

統(tǒng)特權(quán)運(yùn)行,并且提供一個(gè)統(tǒng)一的,、安全的手段去控制服務(wù),。它其實(shí)是一個(gè)RPC

Server,因此我們可以遠(yuǎn)程安裝和管理服務(wù),,不過這不在本文討論的范圍之內(nèi)。

SCM包含一個(gè)儲(chǔ)存著已安裝的服務(wù)和驅(qū)動(dòng)程序的信息的數(shù)據(jù)庫,,通過SCM可以統(tǒng)一

的,、安全的管理這些信息,因此一個(gè)服務(wù)程序的安裝過程就是將自身的信息寫入

這個(gè)數(shù)據(jù)庫,。

  第二部分就是服務(wù)本身,。一個(gè)服務(wù)擁有能從SCM收到信號和命令所必需的的

特殊代碼,并且能夠在處理后將它的狀態(tài)回傳給SCM,。

  第三部分也就是最后一部分,,是一個(gè)Service Control Dispatcher(SCP)。

它是一個(gè)擁有用戶界面,,允許用戶開始,、停止,、暫停、繼續(xù),,并且控制一個(gè)或多

個(gè)安裝在計(jì)算機(jī)上服務(wù)的Win32應(yīng)用程序,。SCP的作用是與SCM通訊,Windows

2000管理工具中的“服務(wù)”就是一個(gè)典型的SCP,。

  在這三個(gè)組成部分中,,用戶最可能去寫服務(wù)本身,同時(shí)也可能不得不寫一個(gè)

與其伴隨的客戶端程序作為一個(gè)SCP去和SCM通訊,,本文只討論去設(shè)計(jì)和實(shí)現(xiàn)一個(gè)

服務(wù),,關(guān)于如何去實(shí)現(xiàn)一個(gè)SCP則在以后的其它文章中介紹。

Question 4. 怎樣開始設(shè)計(jì)服務(wù)

  還記得前面我提到服務(wù)程序的入口點(diǎn)函數(shù)一般都是main()嗎,?一個(gè)服務(wù)擁有

很重要的三個(gè)函數(shù),,第一個(gè)就是入口點(diǎn)函數(shù),其實(shí)用WinMain()作為入口點(diǎn)函數(shù)

也不是不可以,,雖然說服務(wù)不應(yīng)該有用戶界面,,但是其實(shí)存在很少的幾個(gè)例外,

這就是下面圖中的選項(xiàng)存在的原因,。

  由于要和用戶桌面進(jìn)行信息交互,,服務(wù)程序有時(shí)會(huì)以WinMain()作為入口點(diǎn)

函數(shù)。

  入口函數(shù)負(fù)責(zé)初始化整個(gè)進(jìn)程,,由這個(gè)進(jìn)程中的主線程來執(zhí)行,。這意味著它

應(yīng)用于這個(gè)可執(zhí)行文件中的所有服務(wù)。要知道,,一個(gè)可執(zhí)行文件中能夠包含多個(gè)

服務(wù)以使得執(zhí)行更加有效,。主進(jìn)程通知SCM在可執(zhí)行文件中含有幾個(gè)服務(wù),并且

給出每一個(gè)服務(wù)的ServiceMain回調(diào)(Call Back)函數(shù)的地址,。一旦在可執(zhí)行文件

內(nèi)的所有服務(wù)都已經(jīng)停止運(yùn)行,,主線程就在進(jìn)程終止前對整個(gè)進(jìn)程進(jìn)行清除。

  第二個(gè)很重要的函數(shù)就是ServiceMain,,我看過一些例子程序里面對自己的

服務(wù)的進(jìn)入點(diǎn)函數(shù)都固定命名為ServiceMain,,其實(shí)并沒有規(guī)定過一定要那樣命

名,任何的函數(shù)只要符合下列的形式都可以作為服務(wù)的進(jìn)入點(diǎn)函數(shù),。

VOID WINAPI ServiceMain(
DWORD dwArgc, // 參數(shù)個(gè)數(shù)
LPTSTR *lpszArgv // 參數(shù)串
);

  這個(gè)函數(shù)由操作系統(tǒng)調(diào)用,,并執(zhí)行能完成服務(wù)的代碼。一個(gè)專用的線程執(zhí)行

每一個(gè)服務(wù)的ServiceMain函數(shù),,注意是服務(wù)而不是服務(wù)程序,,這是因?yàn)槊總€(gè)服

務(wù)也都擁有與自己唯一對應(yīng)的ServiceMain函數(shù),關(guān)于這一點(diǎn)可以用“管理工具

”里的“服務(wù)”去察看Win2000里面自帶的服務(wù),,就會(huì)發(fā)現(xiàn)其實(shí)很多服務(wù)都是由

service.exe單獨(dú)提供的,。當(dāng)主線程調(diào)用Win32函數(shù)StartServiceCtrlDispatcher

的時(shí)候,,SCM為這個(gè)進(jìn)程中的每一個(gè)服務(wù)產(chǎn)生一個(gè)線程。這些線程中的每一個(gè)都

和它的相應(yīng)的服務(wù)的ServiceMain函數(shù)一起執(zhí)行,,這就是服務(wù)總是多線程的原因

——一個(gè)僅有一個(gè)服務(wù)的可執(zhí)行文件將有一個(gè)主線程,,其它的線程執(zhí)行服務(wù)本身

  第三個(gè)也就是最后的一個(gè)重要函數(shù)是CtrlHandler,,它必須擁有下面的原型

VOID WINAPI CtrlHandler(
DWORD fdwControl //控制命令
)

  像ServiceMain一樣,,CtrlHandler也是一個(gè)回調(diào)函數(shù),用戶必須為它的服務(wù)

程序中每一個(gè)服務(wù)寫一個(gè)單獨(dú)的CtrlHandler函數(shù),,因此如果有一個(gè)程序含有兩

個(gè)服務(wù),,那么它至少要擁有5個(gè)不同的函數(shù):作為入口點(diǎn)的main()或WinMain(),

用于第一個(gè)服務(wù)的ServiceMain函數(shù)和CtrlHandler函數(shù),,以及用于第二個(gè)服務(wù)的

ServiceMain函數(shù)和CtrlHandler函數(shù),。

  SCM調(diào)用一個(gè)服務(wù)的CtrlHandler函數(shù)去改變這個(gè)服務(wù)的狀態(tài)。例如,,當(dāng)某個(gè)

管理員用管理工具里的“服務(wù)”嘗試停止你的服務(wù)的時(shí)候,,你的服務(wù)的

CtrlHandler函數(shù)將收到一個(gè)SERVICE_CONTROL_STOP通知。CtrlHandler函數(shù)負(fù)責(zé)

執(zhí)行停止服務(wù)所需的一切代碼,。由于是進(jìn)程的主線程執(zhí)行所有的CtrlHandler函

數(shù),,因而必須盡量優(yōu)化你的CtrlHandler函數(shù)的代碼,使它運(yùn)行起來足夠快,,以

便相同進(jìn)程中的其它服務(wù)的CtrlHandler函數(shù)能在適當(dāng)?shù)臅r(shí)間內(nèi)收到屬于它們的

通知,。而且基于上述原因,你的CtrlHandler函數(shù)必須要能夠?qū)⑾胍獋鬟_(dá)的狀態(tài)

送到服務(wù)線程,,這個(gè)傳遞過程沒有固定的方法,,完全取決于你的服務(wù)的用途。

(二)對服務(wù)的深入討論之上

  上一章其實(shí)只是概括性的介紹,,下面開始才是真正的細(xì)節(jié)所在,。在進(jìn)入點(diǎn)函

數(shù)里面要完成ServiceMain的初始化,準(zhǔn)確點(diǎn)說是初始化一個(gè)

SERVICE_TABLE_ENTRY結(jié)構(gòu)數(shù)組,,這個(gè)結(jié)構(gòu)記錄了這個(gè)服務(wù)程序里面所包含的所

有服務(wù)的名稱和服務(wù)的進(jìn)入點(diǎn)函數(shù),,下面是一個(gè)SERVICE_TABLE_ENTRY的例子:

SERVICE_TABLE_ENTRY service_table_entry[] =
{
{ "MyFTPd" , FtpdMain },
{ "MyHttpd", Httpserv},
{ NULL, NULL },
};

  第一個(gè)成員代表服務(wù)的名字,第二個(gè)成員是ServiceMain回調(diào)函數(shù)的地址,,

上面的服務(wù)程序因?yàn)閾碛袃蓚€(gè)服務(wù),所以有三個(gè)SERVICE_TABLE_ENTRY元素,,前

兩個(gè)用于服務(wù),,最后的NULL指明數(shù)組的結(jié)束。

  接下來這個(gè)數(shù)組的地址被傳遞到StartServiceCtrlDispatcher函數(shù):

BOOL StartServiceCtrlDispatcher(
LPSERVICE_TABLE_ENTRY lpServiceStartTable
)

  這個(gè)Win32函數(shù)表明可執(zhí)行文件的進(jìn)程怎樣通知SCM包含在這個(gè)進(jìn)程中的服務(wù)

,。就像上一章中講的那樣,,StartServiceCtrlDispatcher為每一個(gè)傳遞到它的數(shù)

組中的非空元素產(chǎn)生一個(gè)新的線程,,每一個(gè)進(jìn)程開始執(zhí)行由數(shù)組元素中的

lpServiceStartTable指明的ServiceMain函數(shù)。

  SCM啟動(dòng)一個(gè)服務(wù)程序之后,,它會(huì)等待該程序的主線程去調(diào)

StartServiceCtrlDispatcher,。如果那個(gè)函數(shù)在兩分鐘內(nèi)沒有被調(diào)用,SCM將會(huì)

認(rèn)為這個(gè)服務(wù)有問題,,并調(diào)用TerminateProcess去殺死這個(gè)進(jìn)程,。這就要求你的

主線程要盡可能快的調(diào)用StartServiceCtrlDispatcher。

  StartServiceCtrlDispatcher函數(shù)則并不立即返回,,相反它會(huì)駐留在一個(gè)循

環(huán)內(nèi),。當(dāng)在該循環(huán)內(nèi)時(shí),StartServiceCtrlDispatcher懸掛起自己,,等待下面兩

個(gè)事件中的一個(gè)發(fā)生,。第一,如果SCM要去送一個(gè)控制通知給運(yùn)行在這個(gè)進(jìn)程內(nèi)

一個(gè)服務(wù)的時(shí)候,,這個(gè)線程就會(huì)激活,。當(dāng)控制通知到達(dá)后,線程激活并調(diào)用相應(yīng)

服務(wù)的CtrlHandler函數(shù),。CtrlHandler函數(shù)處理這個(gè)服務(wù)控制通知,,并返回到

StartServiceCtrlDispatcher。StartServiceCtrlDispatcher循環(huán)回去后再一次

懸掛自己,。

  第二,,如果服務(wù)線程中的一個(gè)服務(wù)中止,這個(gè)線程也將激活,。在這種情況下

,,該進(jìn)程將運(yùn)行在它里面的服務(wù)數(shù)減一。如果服務(wù)數(shù)為零,,

StartServiceCtrlDispatcher就會(huì)返回到入口點(diǎn)函數(shù),,以便能夠執(zhí)行任何與進(jìn)程

有關(guān)的清除工作并結(jié)束進(jìn)程。如果還有服務(wù)在運(yùn)行,,哪怕只是一個(gè)服務(wù),,

StartServiceCtrlDispatcher也會(huì)繼續(xù)循環(huán)下去,繼續(xù)等待其它的控制通知或者

剩下的服務(wù)線程中止,。

  上面的內(nèi)容是關(guān)于入口點(diǎn)函數(shù)的,,下面的內(nèi)容則是關(guān)于ServiceMain函數(shù)的

。還記得以前講過的ServiceMain函數(shù)的的原型嗎,?但實(shí)際上一個(gè)ServiceMain函

數(shù)通常忽略傳遞給它的兩個(gè)參數(shù),,因?yàn)榉?wù)一般不怎么傳遞參數(shù)。設(shè)置一個(gè)服務(wù)

最好的方法就是設(shè)置注冊表,,一般服務(wù)在
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Service/ServiceName/Parame

ters
子鍵下存放自己的設(shè)置,,這里的ServiceName是服務(wù)的名字,。事實(shí)上,可能要寫

一個(gè)客戶應(yīng)用程序去進(jìn)行服務(wù)的背景設(shè)置,,這個(gè)客戶應(yīng)用程序?qū)⑦@些信息存在注

冊表中,,以便服務(wù)讀取。當(dāng)一個(gè)外部應(yīng)用程序已經(jīng)改變了某個(gè)正在運(yùn)行中的服務(wù)

的設(shè)置數(shù)據(jù)的時(shí)候,,這個(gè)服務(wù)能夠用RegNotifyChangeKeyValue函數(shù)去接受一個(gè)

通知,,這樣就允許服務(wù)快速的重新設(shè)置自己。

  前面講到StartServiceCtrlDispatcher為每一個(gè)傳遞到它的數(shù)組中的非空元

素產(chǎn)生一個(gè)新的線程,。接下來,,一個(gè)ServiceMain要做些什么呢?MSDN里面的原

文是這樣說的:The ServiceMain function should immediately call the

RegisterServiceCtrlHandler function to specify a Handler function to

handle control requests. Next, it should call the SetServiceStatus

function to send status information to the service control manager. 為

什么呢,?因?yàn)榘l(fā)出啟動(dòng)服務(wù)請求之后,,如果在一定時(shí)間之內(nèi)無法完成服務(wù)的初始

化,SCM會(huì)認(rèn)為服務(wù)的啟動(dòng)已經(jīng)失敗了,,這個(gè)時(shí)間的長度在Win NT 4.0中是80秒

,,Win2000中不詳…

  基于上面的理由,ServiceMain要迅速完成自身工作,,首先是必不可少的兩

項(xiàng)工作,,第一項(xiàng)是調(diào)用RegisterServiceCtrlHandler函數(shù)去通知SCM它的

CtrlHandler回調(diào)函數(shù)的地址:

SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
LPCTSTR lpServiceName, //服務(wù)的名字
LPHANDLER_FUNCTION lpHandlerProc //CtrlHandler函數(shù)地址
)


  第一個(gè)參數(shù)指明你正在建立的CtrlHandler是為哪一個(gè)服務(wù)所用,第二個(gè)參

數(shù)是CtrlHandler函數(shù)的地址,。lpServiceName必須和在SERVICE_TABLE_ENTRY里

面被初始化的服務(wù)的名字相匹配,。RegisterServiceCtrlHandler返回一個(gè)

SERVICE_STATUS_HANDLE,這是一個(gè)32位的句柄,。SCM用它來唯一確定這個(gè)服務(wù),。

當(dāng)這個(gè)服務(wù)需要把它當(dāng)時(shí)的狀態(tài)報(bào)告給SCM的時(shí)候,就必須把這個(gè)句柄傳給需要

它的Win32函數(shù),。注意:這個(gè)句柄和其他大多數(shù)的句柄不同,,你無需關(guān)閉它。

  SCM要求ServiceMain函數(shù)的線程在一秒鐘內(nèi)調(diào)用

RegisterServiceCtrlHandler函數(shù),,否則SCM會(huì)認(rèn)為服務(wù)已經(jīng)失敗,。但在這種情

況下,SCM不會(huì)終止服務(wù),,不過在NT 4中將無法啟動(dòng)這個(gè)服務(wù),,同時(shí)會(huì)返回一個(gè)

不正確的錯(cuò)誤信息,這一點(diǎn)在Windows 2000中得到了修正,。

  在RegisterServiceCtrlHandler函數(shù)返回后,,ServiceMain線程要立即告訴

SCM服務(wù)正在繼續(xù)初始化。具體的方法是通過調(diào)用SetServiceStatus函數(shù)傳遞

SERVICE_STATUS數(shù)據(jù)結(jié)構(gòu),。

BOOL SetServiceStatus(
SERVICE_STATUS_HANDLE hService, //服務(wù)的句柄
SERVICE_STATUS lpServiceStatus //SERVICE_STATUS結(jié)構(gòu)的地址
)

  這個(gè)函數(shù)要求傳遞給它指明服務(wù)的句柄(剛剛通過調(diào)用

RegisterServiceCtrlHandler得到),,和一個(gè)初始化的SERVICE_STATUS結(jié)構(gòu)的地

址:

typedef struct _SERVICE_STATUS
{
DWORD dwServiceType; 
DWORD dwCurrentState; 
DWORD dwControlsAccepted; 
DWORD dwWin32ExitCode; 
DWORD dwServiceSpecificExitCode; 
DWORD dwCheckPoint; 
DWORD dwWaitHint; 
} SERVICE_STATUS, *LPSERVICE_STATUS;

  SERVICE_STATUS結(jié)構(gòu)含有七個(gè)成員,它們反映服務(wù)的現(xiàn)行狀態(tài),。所有這些成

員必須在這個(gè)結(jié)構(gòu)被傳遞到SetServiceStatus之前正確的設(shè)置,。

  成員dwServiceType指明服務(wù)可執(zhí)行文件的類型。如果你的可執(zhí)行文件中只

有一個(gè)單獨(dú)的服務(wù),,就把這個(gè)成員設(shè)置成SERVICE_WIN32_OWN_PROCESS,;如果擁

有多個(gè)服務(wù)的話,就設(shè)置成SERVICE_WIN32_SHARE_PROCESS,。除了這兩個(gè)標(biāo)志之

外,,如果你的服務(wù)需要和桌面發(fā)生交互(當(dāng)然不推薦這樣做),就要用“OR”運(yùn)算

符附加上SERVICE_INTERACTIVE_PROCESS,。這個(gè)成員的值在你的服務(wù)的生存期內(nèi)

絕對不應(yīng)該改變,。

  成員dwCurrentState是這個(gè)結(jié)構(gòu)中最重要的成員,它將告訴SCM你的服務(wù)的

現(xiàn)行狀態(tài),。為了報(bào)告服務(wù)仍在初始化,,應(yīng)該把這個(gè)成員設(shè)置成

SERVICE_START_PENDING。在以后具體講述CtrlHandler函數(shù)的時(shí)候具體解釋其它

可能的值,。

  成員dwControlsAccepted指明服務(wù)愿意接受什么樣的控制通知,。如果你允許

一個(gè)SCP去暫停/繼續(xù)服務(wù),就把它設(shè)成SERVICE_ACCEPT_PAUSE_CONTINUE,。很多

服務(wù)不支持暫?;蚶^續(xù),就必須自己決定在服務(wù)中它是否可用,。如果你允許一個(gè)

SCP去停止服務(wù),,就要設(shè)置它為SERVICE_ACCEPT_STOP。如果服務(wù)要在操作系統(tǒng)關(guān)

閉的時(shí)候得到通知,,設(shè)置它為SERVICE_ACCEPT_SHUTDOWN可以收到預(yù)期的結(jié)果,。

這些標(biāo)志可以用“OR”運(yùn)算符組合。

  成員dwWin32ExitCode和dwServiceSpecificExitCode是允許服務(wù)報(bào)告錯(cuò)誤的

關(guān)鍵,,如果希望服務(wù)去報(bào)告一個(gè)Win32錯(cuò)誤代碼(預(yù)定義在WinError.h中),,它就

設(shè)置dwWin32ExitCode為需要的代碼。一個(gè)服務(wù)也可以報(bào)告它本身特有的,、沒有

映射到一個(gè)預(yù)定義的Win32錯(cuò)誤代碼中的錯(cuò)誤,。為了這一點(diǎn),要把

dwWin32ExitCode設(shè)置為ERROR_SERVICE_SPECIFIC_ERROR,,然后還要設(shè)置成員

dwServiceSpecificExitCode為服務(wù)特有的錯(cuò)誤代碼,。當(dāng)服務(wù)運(yùn)行正常,沒有錯(cuò)

誤可以報(bào)告的時(shí)候,就設(shè)置成員dwWin32ExitCode為NO_ERROR,。

  最后的兩個(gè)成員dwCheckPoint和dwWaitHint是一個(gè)服務(wù)用來報(bào)告它當(dāng)前的事

件進(jìn)展情況的,。當(dāng)成員dwCurrentState被設(shè)置成SERVICE_START_PENDING的時(shí)候

,應(yīng)該把dwCheckPoint設(shè)成0,,dwWaitHint設(shè)成一個(gè)經(jīng)過多次嘗試后確定比較合

適的數(shù),,這樣服務(wù)才能高效運(yùn)行。一旦服務(wù)被完全初始化,,就應(yīng)該重新初始化

SERVICE_STATUS結(jié)構(gòu)的成員,,更改dwCurrentState為SERVICE_RUNNING,然后把

dwCheckPoint和dwWaitHint都改為0,。

  dwCheckPoint成員的存在對用戶是有益的,,它允許一個(gè)服務(wù)報(bào)告它處于進(jìn)程

的哪一步。每一次調(diào)用SetServiceStatus時(shí),,可以增加它到一個(gè)能指明服務(wù)已經(jīng)

執(zhí)行到哪一步的數(shù)字,,它可以幫助用戶決定多長時(shí)間報(bào)告一次服務(wù)的進(jìn)展情況。

如果決定要報(bào)告服務(wù)的初始化進(jìn)程的每一步,,就應(yīng)該設(shè)置dwWaitHint為你認(rèn)為到

達(dá)下一步所需的毫秒數(shù),,而不是服務(wù)完成它的進(jìn)程所需的毫秒數(shù)。

  在服務(wù)的所有初始化都完成之后,,服務(wù)調(diào)用SetServiceStatus指明

SERVICE_RUNNING,,在那一刻服務(wù)已經(jīng)開始運(yùn)行。通常一個(gè)服務(wù)是把自己放在一

個(gè)循環(huán)之中來運(yùn)行的,。在循環(huán)的內(nèi)部這個(gè)服務(wù)進(jìn)程懸掛自己,,等待指明它下一步

是應(yīng)該暫停、繼續(xù)或停止之類的網(wǎng)絡(luò)請求或通知,。當(dāng)一個(gè)請求到達(dá)的時(shí)候,,服務(wù)

線程激活并處理這個(gè)請求,然后再循環(huán)回去等待下一個(gè)請求/通知,。

  如果一個(gè)服務(wù)由于一個(gè)通知而激活,,它會(huì)先處理這個(gè)通知,除非這個(gè)服務(wù)得

到的是停止或關(guān)閉的通知,。如果真的是停止或關(guān)閉的通知,,服務(wù)線程將退出循環(huán)

,執(zhí)行必要的清除操作,,然后從這個(gè)線程返回,。當(dāng)ServiceMain線程返回并中止

時(shí),引起在StartServiceCtrlDispatcher內(nèi)睡眠的線程激活,,并像在前面解釋過

的那樣,,減少它運(yùn)行的服務(wù)的計(jì)數(shù)。

(三)對服務(wù)的深入討論之下 
 

  現(xiàn)在我們還剩下一個(gè)函數(shù)可以在細(xì)節(jié)上討論,那就是服務(wù)的CtrlHandler函

數(shù),。

  當(dāng)調(diào)用RegisterServiceCtrlHandler函數(shù)時(shí),,SCM得到并保存這個(gè)回調(diào)函數(shù)

的地址。一個(gè)SCP調(diào)一個(gè)告訴SCM如何去控制服務(wù)的Win32函數(shù),,現(xiàn)在已經(jīng)有10個(gè)

預(yù)定義的控制請求:

Control code  Meaning  
SERVICE_CONTROL_STOP Requests the service to stop. The hService handle

must have SERVICE_STOP access. 
SERVICE_CONTROL_PAUSE Requests the service to pause. The hService

handle must have SERVICE_PAUSE_CONTINUE access. 
SERVICE_CONTROL_CONTINUE Requests the paused service to resume. The

hService handle must have SERVICE_PAUSE_CONTINUE access. 
SERVICE_CONTROL_INTERROGATE Requests the service to update immediately

its current status information to the service control manager. The

hService handle must have SERVICE_INTERROGATE access. 
SERVICE_CONTROL_SHUTDOWN Requests the service to perform cleanup

tasks, because the system is shutting down. For more information, see

Remarks. 
SERVICE_CONTROL_PARAMCHANGE Windows 2000: Requests the service to

reread its startup parameters. The hService handle must have

SERVICE_PAUSE_CONTINUE access. 
SERVICE_CONTROL_NETBINDCHANGE Windows 2000: Requests the service to

update its network binding. The hService handle must have

SERVICE_PAUSE_CONTINUE access. 
SERVICE_CONTROL_NETBINDREMOVE Windows 2000: Notifies a network service

that a component for binding has been removed. The service should

reread its binding information and unbind from the removed component. 
SERVICE_CONTROL_NETBINDENABLE Windows 2000: Notifies a network service

that a disabled binding has been enabled. The service should reread

its binding information and add the new binding. 
SERVICE_CONTROL_NETBINDDISABLE Windows 2000: Notifies a network

service that one of its bindings has been disabled. The service should

reread its binding information and remove the binding.

  上表中標(biāo)有Windows 2000字樣的就是2000中新添加的控制代碼,。除了這些代

碼之外,服務(wù)也可以接受用戶定義的,,范圍在128-255之間的代碼。

  當(dāng)CtrlHandler函數(shù)收到一個(gè)SERVICE_CONTROL_STOP,、

SERVICE_CONTROL_PAUSE,、 SERVICE_CONTROL_CONTINUE控制代碼的時(shí)候,

SetServiceStatus必須被調(diào)用去確認(rèn)這個(gè)代碼,,并指定你認(rèn)為服務(wù)處理這個(gè)狀態(tài)

變化所需要的時(shí)間,。

  例如:你的服務(wù)收到了停止請求,首先要把SERVICE_STATUS結(jié)構(gòu)的

dwCurrentState成員設(shè)置成SERVICE_STOP_PENDING,,這樣可以使SCM確定你已經(jīng)

收到了控制代碼,。當(dāng)一個(gè)服務(wù)的暫停或停止操作正在執(zhí)行的時(shí)候,,必須指定你認(rèn)

為這種操作所需要的時(shí)間:這是因?yàn)橐粋€(gè)服務(wù)也許不能立即改變它的狀態(tài),,它可

能必須等待一個(gè)網(wǎng)絡(luò)請求被完成或者數(shù)據(jù)被刷新到一個(gè)驅(qū)動(dòng)器上。指定時(shí)間的方

法就像我上一章說的那樣,,用成員dwCheckPoint和dwWaitHint來指明它完成狀態(tài)

改變所需要的時(shí)間,。如果需要,可以用增加dwCheckPoint成員的值和設(shè)置

dwWaitHint成員的值去指明你期待的服務(wù)到達(dá)下一步的時(shí)間的方式周期性的報(bào)告

進(jìn)展情況,。

  當(dāng)整個(gè)啟動(dòng)的過程完成之后,,要再一次調(diào)用SetServiceStatus。這時(shí)就要把

SERVICE_STATUS結(jié)構(gòu)的dwCurrentState成員設(shè)置成SERVICE_STOPPED,,當(dāng)報(bào)告狀

態(tài)代碼的同時(shí),,一定要把成員dwCheckPoint和dwWaitHint設(shè)置為0,因?yàn)榉?wù)已

經(jīng)完成了它的狀態(tài)變化,。暫?;蚶^續(xù)服務(wù)的時(shí)候方法也一樣。

  當(dāng)CtrlHandler函數(shù)收到一個(gè)SERVICE_CONTROL_INTERROGATE控制代碼的時(shí)候

,,服務(wù)將簡單的將dwCurrentState成員設(shè)置成服務(wù)當(dāng)前的狀態(tài),,同時(shí),把成員

dwCheckPoint和dwWaitHint設(shè)置為0,,然后再調(diào)用SetServiceStatus就可以了,。

  在操作系統(tǒng)關(guān)閉的時(shí)候,CtrlHandler函數(shù)收到一個(gè)

SERVICE_CONTROL_SHUTDOWN控制代碼。服務(wù)根本無須回應(yīng)這個(gè)代碼,,因?yàn)橄到y(tǒng)即

將關(guān)閉,。它將執(zhí)行保存數(shù)據(jù)所需要的最小行動(dòng)集,這是為了確定機(jī)器能及時(shí)關(guān)閉

,。缺省時(shí)系統(tǒng)只給很少的時(shí)間去關(guān)閉所有的服務(wù),,MSDN里面說大概是20秒的時(shí)間

,不過那可能是Windows NT 4的設(shè)置,,在我的Windows 2000 Server里這個(gè)時(shí)間

是10秒,,你可以手動(dòng)的修改這個(gè)數(shù)值,它被記錄在

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control子鍵里面的

WaitToKillServiceTimeout,,單位是毫秒,。

  當(dāng)CtrlHandler函數(shù)收到任何用戶定義的代碼時(shí),它應(yīng)該執(zhí)行期望的用戶自

定義行動(dòng),。除非用戶自定義的行動(dòng)要強(qiáng)制服務(wù)去暫停,、繼續(xù)或停止,否則不調(diào)

SetServiceStatus函數(shù),。如果用戶定義的行動(dòng)強(qiáng)迫服務(wù)的狀態(tài)發(fā)生變化,,

SetServiceStatus將被調(diào)用去設(shè)置dwCurrentState、dwCheckPoint和dwWaitHint

,,具體控制代碼和前面說的一樣,。

  如果你的CtrlHandler函數(shù)需要很長的時(shí)間執(zhí)行操作的話,千萬要注意:假

如CtrlHandler函數(shù)在30秒內(nèi)沒有返回的話,,SCM將返回一個(gè)錯(cuò)誤,,這不是我們所

期望的。所以如果出現(xiàn)上述情況,,最好的辦法是再建立一個(gè)線程,,讓它去繼續(xù)執(zhí)

行操作,以便使得CtrlHandler函數(shù)能夠迅速的返回,。例如,,當(dāng)收到一個(gè)

SERVICE_CONTROL_STOP請求的時(shí)候,就像上面說的一樣,,服務(wù)可能正在等待一個(gè)

網(wǎng)絡(luò)請求被完成或者數(shù)據(jù)被刷新到一個(gè)驅(qū)動(dòng)器上,,而這些操作所需要的時(shí)間是你

不能估計(jì)的,那么就要建立一個(gè)新的線程等待操作完成后執(zhí)行停止命令,,

CtrlHandler函數(shù)在返回之前仍然要報(bào)告SERVICE_STOP_PENDING狀態(tài),,當(dāng)新的線

程執(zhí)行完操作之后,再由它將服務(wù)的狀態(tài)設(shè)置成SERVICE_STOPPED,。如果當(dāng)前操

作的時(shí)間可以估計(jì)的到就不要這樣做,,仍然使用前面交待的方法處理,。

  CtrlHandler函數(shù)我就先講這些,下面說說服務(wù)怎么安裝,。一個(gè)服務(wù)程序可

以使用CreateService函數(shù)將服務(wù)的信息添加到SCM的數(shù)據(jù)庫,。

SC_HANDLE CreateService( SC_HANDLE hSCManager, // handle to SCM

database LPCTSTR lpServiceName, // name of service to start LPCTSTR

lpDisplayName, // display name DWORD dwDesiredAccess, // type of

access to service DWORD dwServiceType, // type of service DWORD

dwStartType, // when to start service DWORD dwErrorControl, //

severity of service failure LPCTSTR lpBinaryPathName, // name of

binary file LPCTSTR lpLoadOrderGroup, // name of load ordering group

LPDWORD lpdwTagId, // tag identifier LPCTSTR lpDependencies, // array

of dependency names LPCTSTR lpServiceStartName, // account name

LPCTSTR lpPassword // account password );

  hSCManager是一個(gè)標(biāo)示SCM數(shù)據(jù)庫的句柄,可以簡單的通過調(diào)用

OpenSCManager得到,。

SC_HANDLE OpenSCManager( LPCTSTR lpMachineName, // computer name

LPCTSTR lpDatabaseName, // SCM database name DWORD dwDesiredAccess //

access type );

  lpMachineName是目標(biāo)機(jī)器的名字,,還記得我在第一章里說過可以在其它的

機(jī)器上面安裝服務(wù)嗎?這就是實(shí)現(xiàn)的方法,。對方機(jī)器名字必須以“//”開始,。如

果傳遞NULL或者一個(gè)空的字符串的話就默認(rèn)是本機(jī)。

  lpDatabaseName是目標(biāo)機(jī)器上面SCM數(shù)據(jù)庫的名字,,但MSDN里面說這個(gè)參數(shù)

要默認(rèn)的設(shè)置成SERVICES_ACTIVE_DATABASE,,如果傳遞NULL,就默認(rèn)的打開

SERVICES_ACTIVE_DATABASE,。所以我還沒有真的搞明白這個(gè)參數(shù)的存在意義,總

之使用的時(shí)候傳遞NULL就行了,。

  dwDesiredAccess是SCM數(shù)據(jù)庫的訪問權(quán)限,,具體值見下表:

Object access  Description  
SC_MANAGER_ALL_ACCESS Includes STANDARD_RIGHTS_REQUIRED, in addition

to all of the access types listed in this table. 
SC_MANAGER_CONNECT Enables connecting to the service control manager. 
SC_MANAGER_CREATE_SERVICE Enables calling of the CreateService

function to create a service object and add it to the database. 
SC_MANAGER_ENUMERATE_SERVICE Enables calling of the EnumServicesStatus

function to list the services that are in the database. 
SC_MANAGER_LOCK Enables calling of the LockServiceDatabase function to

acquire a lock on the database. 
SC_MANAGER_QUERY_LOCK_STATUS Enables calling of the

QueryServiceLockStatus function to retrieve the lock status

information for the database.

  想要獲得訪問權(quán)限的話,似乎沒那么復(fù)雜,。MSDN里面說所有進(jìn)程都被允許獲

得對所有SCM數(shù)據(jù)庫的SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE,

and SC_MANAGER_QUERY_LOCK_STATUS權(quán)限,,這些權(quán)限使得你可以連接SCM數(shù)據(jù)庫

,枚舉目標(biāo)機(jī)器上安裝的服務(wù)和查詢目標(biāo)數(shù)據(jù)庫是否已被鎖住,。但如果要?jiǎng)?chuàng)建服

務(wù),,首先你需要擁有目標(biāo)機(jī)器的管理員權(quán)限,一般的傳遞

SC_MANAGER_ALL_ACCESS就可以了,。這個(gè)函數(shù)返回的句柄可以被

CloseServiceHandle函數(shù)關(guān)閉,。

  lpServiceName是服務(wù)的名字,lpDisplayName是服務(wù)在“服務(wù)”管理工具里

顯示的名字,。

  dwDesiredAccess也是訪問的權(quán)限,,有一個(gè)比上面的還長的多的一個(gè)表,各

位自己查MSDN吧,。我們要安裝服務(wù),,仍然簡單的傳遞SC_MANAGER_ALL_ACCESS。

  dwServiceType是指你的服務(wù)是否和其它的進(jìn)程相關(guān)聯(lián),,一般是

SERVICE_WIN32_OWN_PROCESS,,表示不和任何進(jìn)程相關(guān)聯(lián)。如果你確認(rèn)你的服務(wù)

需要和某些進(jìn)程相關(guān)聯(lián),,就設(shè)置成SERVICE_WIN32_SHARE_PROCESS,。當(dāng)你的服務(wù)

要和桌面相關(guān)聯(lián)的時(shí)候,,需要設(shè)置成SERVICE_INTERACTIVE_PROCESS。

  dwStartType是服務(wù)的啟動(dòng)方式,。服務(wù)有三種啟動(dòng)方式,,分別是“自動(dòng)

(SERVICE_AUTO_START)”“手動(dòng)(SERVICE_DEMAND_START)”和“禁用

(SERVICE_DISABLED)”。在MSDN里還有另外的兩種方式,,不過是專為驅(qū)動(dòng)程序設(shè)

置的,。

  dwErrorControl決定服務(wù)如果在系統(tǒng)啟動(dòng)的時(shí)候啟動(dòng)失敗的話要怎么辦。

值  意義  
SERVICE_ERROR_IGNORE 啟動(dòng)程序記錄錯(cuò)誤發(fā)生,,但繼續(xù)啟動(dòng),。 
SERVICE_ERROR_NORMAL 啟動(dòng)程序記錄錯(cuò)誤發(fā)生,并彈出一個(gè)消息框,,但仍繼續(xù)

啟動(dòng) 
SERVICE_ERROR_SEVERE 啟動(dòng)程序記錄錯(cuò)誤發(fā)生,,如果是以last-known-good

configuration啟動(dòng)的話,啟動(dòng)會(huì)繼續(xù),。否則會(huì)以last-known-good

configuration重新啟動(dòng)計(jì)算機(jī),。 
SERVICE_ERROR_CRITICAL 啟動(dòng)程序記錄錯(cuò)誤發(fā)生,如果可能的話,。如果是以

last-known-good configuration啟動(dòng)的話,,啟動(dòng)會(huì)失敗。否則會(huì)以last-known

-good configuration重新啟動(dòng)計(jì)算機(jī),。好嚴(yán)重的錯(cuò)誤啊,。

  lpBinaryPathName是服務(wù)程序的路徑。MSDN里面特別提到如果服務(wù)路徑里面

有空格的話一定要將路徑用引號引起來,。例如"d://my share//myservice.exe"

就一定要指定為"/"d://my share//myservice.exe/"",。

  lpLoadOrderGroup的意義在于,如果有一組服務(wù)要按照一定的順序啟動(dòng)的話

,,這個(gè)參數(shù)用于指定一個(gè)組名用于標(biāo)志這個(gè)啟動(dòng)順序組,,不過我還沒有用過這個(gè)

參數(shù)。你的服務(wù)如果不屬于任何啟動(dòng)順序組,,只要傳遞NULL或者一個(gè)空的字符串

就行了,。

  lpdwTagId是應(yīng)用了上面的參數(shù)之后要指定的值,專用于驅(qū)動(dòng)程序,,與本文

內(nèi)容無關(guān),。傳遞NULL。

  lpDependencies標(biāo)示一個(gè)字符串?dāng)?shù)組,,用于指明一串服務(wù)的名字或者一個(gè)啟

動(dòng)順序組,。當(dāng)與一個(gè)啟動(dòng)順序組建立關(guān)聯(lián)的時(shí)候,這個(gè)參數(shù)的含義就是只有你指

定的啟動(dòng)順序組里有至少一個(gè)經(jīng)過對整個(gè)組里所有的成員已經(jīng)全部嘗試過啟動(dòng)后

,,有至少一個(gè)成員成功啟動(dòng),,你的服務(wù)才能啟動(dòng),。不需要建立依存關(guān)系的話,仍

是傳遞NULL或者一個(gè)空的字符串,。但如果你要指定啟動(dòng)順序組的話,,必須為組名

加上SC_GROUP_IDENTIFIER前綴,因?yàn)榻M名和服務(wù)名是共享一個(gè)命名空間的,。

  lpServiceStartName是服務(wù)的啟動(dòng)賬號,,如果你設(shè)置你的服務(wù)的關(guān)聯(lián)類型是

SERVICE_WIN32_OWN_PROCESS的話,你需要以DomainName/UserName的格式指定用

戶名,,如果這個(gè)賬戶在你本機(jī)的話,,用./UserName就可以指定。如果傳遞NULL的

話,,會(huì)以本地的系統(tǒng)賬戶登陸,。如果是Win NT 4.0或更早的版本的話,如果你指

定了SERVICE_WIN32_SHARE_PROCESS,,就必須傳遞./System指定服務(wù)使用本地的

系統(tǒng)賬戶,。最后,如果你指定了SERVICE_INTERACTIVE_PROCESS,,你必須使服務(wù)

運(yùn)行在本機(jī)系統(tǒng)賬戶,。

  看名字就知道了,lpPassword是賬戶的密碼,。如果指定系統(tǒng)賬戶的話,傳遞

NULL,。如果賬戶沒有密碼的話,,傳遞空字符串。

  總之服務(wù)的基本原理就是這樣子了,,到了這里這篇文章似乎可以告一段落了

,,但實(shí)際上還有很多內(nèi)容必須要討論,所以我還不能草草收筆,,敬請關(guān)注下一章

,。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多