




已閱讀5頁,還剩7頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
Windows服務(wù)程序2006年10月14日 星期六 13:28有那么一類應(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ù),需要熟悉動態(tài)連接庫(Dlls)、結(jié)構(gòu)異常處理、內(nèi)存映射文件、虛擬內(nèi)存、設(shè)備I/O、線程及其同步、Unicode以及其他的由WinAPI函數(shù)提供的應(yīng)用接口。當(dāng)然本文討論的只是建立一個(gè)可以安裝、運(yùn)行、啟動、停止的沒有任何其他功能的服務(wù),所以無需上述知識仍可以繼續(xù)看下去,我會在過程中將理解本文所需要的知識逐一講解。第二要知道的是一個(gè)服務(wù)決不需要用戶界面。大多數(shù)的服務(wù)將運(yùn)行在那些被鎖在某些黑暗的,冬暖夏涼的小屋子里的強(qiáng)大的服務(wù)器上面,即使有用戶界面一般也沒有人可以看到。如果服務(wù)提供任何用戶界面如消息框,那么用戶錯(cuò)過這些消息的可能性就極高了,所以服務(wù)程序通常以控制臺程序的形式被編寫,進(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ī)對某臺機(jī)器上面的服務(wù)進(jìn)行管理。比如Windows 2000里面的“控制臺”程序(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è)用戶賬號下,也就會因此獲得相應(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啟動的時(shí)候會自動運(yùn)行,伴隨著操作系統(tǒ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è)儲存著已安裝的服務(wù)和驅(qū)動程序的信息的數(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í)會以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ù),就會發(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啟動一個(gè)服務(wù)程序之后,它會等待該程序的主線程去調(diào)StartServiceCtrlDispatcher。如果那個(gè)函數(shù)在兩分鐘內(nèi)沒有被調(diào)用,SCM將會認(rèn)為這個(gè)服務(wù)有問題,并調(diào)用TerminateProcess去殺死這個(gè)進(jìn)程。這就要求你的主線程要盡可能快的調(diào)用StartServiceCtrlDispatcher。StartServiceCtrlDispatcher函數(shù)則并不立即返回,相反它會駐留在一個(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è)線程就會激活。當(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就會返回到入口點(diǎn)函數(shù),以便能夠執(zhí)行任何與進(jìn)程有關(guān)的清除工作并結(jié)束進(jìn)程。如果還有服務(wù)在運(yùn)行,哪怕只是一個(gè)服務(wù),StartServiceCtrlDispatcher也會繼續(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_MACHINESYSTEMCurrentControlSetServiceServiceNameParameters子鍵下存放自己的設(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ā)出啟動服務(wù)請求之后,如果在一定時(shí)間之內(nèi)無法完成服務(wù)的初始化,SCM會認(rèn)為服務(wù)的啟動已經(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會認(rèn)為服務(wù)已經(jīng)失敗。但在這種情況下,SCM不會終止服務(wù),不過在NT 4中將無法啟動這個(gè)服務(wù),同時(shí)會返回一個(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_STATUSDWORD 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è)通知而激活,它會先處理這個(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 MeaningSERVICE_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ù)的暫?;蛲V共僮髡趫?zhí)行的時(shí)候,必須指定你認(rèn)為這種操作所需要的時(shí)間:這是因?yàn)橐粋€(gè)服務(wù)也許不能立即改變它的狀態(tài),它可能必須等待一個(gè)網(wǎng)絡(luò)請求被完成或者數(shù)據(jù)被刷新到一個(gè)驅(qū)動器上。指定時(shí)間的方法就像我上一章說的那樣,用成員dwCheckPoint和dwWaitHint來指明它完成狀態(tài)改變所需要的時(shí)間。如果需要,可以用增加dwCheckPoint成員的值和設(shè)置dwWaitHint成員的值去指明你期待的服務(wù)到達(dá)下一步的時(shí)間的方式周期性的報(bào)告進(jìn)展情況。當(dāng)整個(gè)啟動的過程完成之后,要再一次調(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ù)所需要的最小行動集,這是為了確定機(jī)器能及時(shí)關(guān)閉。缺省時(shí)系統(tǒng)只給很少的時(shí)間去關(guān)閉所有的服務(wù),MSDN里面說大概是20秒的時(shí)間,不過那可能是Windows NT 4的設(shè)置,在我的Windows 2000 Server里這個(gè)時(shí)間是10秒,你可以手動的修改這個(gè)數(shù)值,它被記錄在HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControl子鍵里面的WaitToKillServiceTimeout,單位是毫秒。當(dāng)CtrlHandler函數(shù)收到任何用戶定義的代碼時(shí),它應(yīng)該執(zhí)行期望的用戶自定義行動。除非用戶自定義的行動要強(qiáng)制服務(wù)去暫停、繼續(xù)或停止,否則不調(diào)SetServiceStatus函數(shù)。如果用戶定義的行動強(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ū)動器上,而這些操作所需要的時(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_
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 智能交通系統(tǒng)承包開發(fā)合同范本三
- 廠房租賃與節(jié)能環(huán)保技術(shù)改造合同
- AbMole:Z-VAD-FMK讓凋亡焦亡壞死性凋亡機(jī)制研究更上一層樓
- 車輛轉(zhuǎn)讓與保險(xiǎn)權(quán)益轉(zhuǎn)移合同范本
- LC術(shù)后并發(fā)癥及護(hù)理
- 基于財(cái)產(chǎn)分配及子女監(jiān)護(hù)權(quán)的個(gè)性化夫妻離異協(xié)議
- 鄉(xiāng)墅公司活動策劃方案
- 烏魯木齊幼兒閱讀活動方案
- 冬季河道安全管理制度
- 公司宿舍公寓管理制度
- 張雙樓煤礦安全評價(jià)報(bào)告(出版稿10.14)
- 關(guān)于贛州市登革熱病例疫情的初步調(diào)查報(bào)告
- 網(wǎng)絡(luò)輿論監(jiān)督存在的問題及對策分析研究行政管理專業(yè)
- 普佑克四期臨床方案
- (蘇教版)二年級科學(xué)(下冊)第四單元課件全套
- 深圳實(shí)驗(yàn)學(xué)校小學(xué)畢業(yè)班數(shù)學(xué)試卷
- 工業(yè)產(chǎn)品質(zhì)量監(jiān)督抽查復(fù)查復(fù)檢樣品確認(rèn)書(模板)
- 掛牌上鎖及維修管理程序文件
- 上海初中地理會考知識點(diǎn)匯總(上海鄉(xiāng)土地理
- 模糊層次分析法講解
- 《合成生物學(xué)》課件.ppt
評論
0/150
提交評論