創(chuàng)建Windows服務程序.doc_第1頁
創(chuàng)建Windows服務程序.doc_第2頁
創(chuàng)建Windows服務程序.doc_第3頁
創(chuàng)建Windows服務程序.doc_第4頁
創(chuàng)建Windows服務程序.doc_第5頁
已閱讀5頁,還剩7頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

Windows服務程序2006年10月14日 星期六 13:28有那么一類應用程序,是能夠為各種用戶(包括本地用戶和遠程用戶)所用的,擁有用戶授權級進行管理的能力,并且不論用戶是否物理的與正在運行該應用程序的計算機相連都能正常執(zhí)行,這就是所謂的服務了。(一)服務的基礎知識Question 1. 什么是服務?它的特征是什么?在NT/2000中,服務是一類受到操作系統(tǒng)優(yōu)待的程序。一個服務首先是一個Win32可執(zhí)行程序,如果要寫一個功能完備且強大的服務,需要熟悉動態(tài)連接庫(Dlls)、結構異常處理、內存映射文件、虛擬內存、設備I/O、線程及其同步、Unicode以及其他的由WinAPI函數(shù)提供的應用接口。當然本文討論的只是建立一個可以安裝、運行、啟動、停止的沒有任何其他功能的服務,所以無需上述知識仍可以繼續(xù)看下去,我會在過程中將理解本文所需要的知識逐一講解。第二要知道的是一個服務決不需要用戶界面。大多數(shù)的服務將運行在那些被鎖在某些黑暗的,冬暖夏涼的小屋子里的強大的服務器上面,即使有用戶界面一般也沒有人可以看到。如果服務提供任何用戶界面如消息框,那么用戶錯過這些消息的可能性就極高了,所以服務程序通常以控制臺程序的形式被編寫,進入點函數(shù)是main()而不是WinMain()。也許有人有疑問:沒有用戶界面的話,要怎樣設置、管理一個服務?怎樣開始、停止它?服務如何發(fā)出警告或錯誤信息、如何報告關于它的執(zhí)行情況的統(tǒng)計數(shù)據(jù)?這些問題的答案就是服務能夠被遠程管理,Windows NT/2000提供了大量的管理工具,這些工具允許通過網(wǎng)絡上的其它計算機對某臺機器上面的服務進行管理。比如Windows 2000里面的“控制臺”程序(mmc.exe),用它添加“管理單元”就可以管理本機或其他機器上的服務。Question 2. 服務的安全性想要寫一個服務,就必須熟悉Win NT/2000的安全機制,在上述操作系統(tǒng)之中,所有安全都是基于用戶的。換句話說進程、線程、文件、注冊表鍵、信號、事件等等等等都屬于一個用戶。當一個進程被產生的時候,它都是執(zhí)行在一個用戶的上下文(context),這個用戶帳號可能在本機,也可能在網(wǎng)絡中的其他機器上,或者是在一個特殊的賬號:System Account即系統(tǒng)帳號的上下文如果一個進程正在一個用戶帳號下執(zhí)行,那么這個進程就同時擁有這個用戶所能擁有的一切訪問權限,不論是在本機還是網(wǎng)絡。系統(tǒng)帳號則是一個特殊的賬號,它用來標識系統(tǒng)本身,而且運行在這個帳號下的任何進程都擁有系統(tǒng)上的所有訪問權限,但是系統(tǒng)帳號不能在域上使用,無法訪問網(wǎng)絡資源服務也是Win32可執(zhí)行程序,它也需要執(zhí)行在一個context,通常服務都是在系統(tǒng)賬號下運行,但是也可以根據(jù)情況選擇讓它運行在一個用戶賬號下,也就會因此獲得相應的訪問資源的權限。Question 3. 服務的三個組成部分一個服務由三部分組成,第一部分是Service Control Manager(SCM)。每個Windows NT/2000系統(tǒng)都有一個SCM,SCM存在于Service.exe中,在Windows啟動的時候會自動運行,伴隨著操作系統(tǒng)的啟動和關閉而產生和終止。這個進程以系統(tǒng)特權運行,并且提供一個統(tǒng)一的、安全的手段去控制服務。它其實是一個RPC Server,因此我們可以遠程安裝和管理服務,不過這不在本文討論的范圍之內。SCM包含一個儲存著已安裝的服務和驅動程序的信息的數(shù)據(jù)庫,通過SCM可以統(tǒng)一的、安全的管理這些信息,因此一個服務程序的安裝過程就是將自身的信息寫入這個數(shù)據(jù)庫。第二部分就是服務本身。一個服務擁有能從SCM收到信號和命令所必需的的特殊代碼,并且能夠在處理后將它的狀態(tài)回傳給SCM。第三部分也就是最后一部分,是一個Service Control Dispatcher(SCP)。它是一個擁有用戶界面,允許用戶開始、停止、暫停、繼續(xù),并且控制一個或多個安裝在計算機上服務的Win32應用程序。SCP的作用是與SCM通訊,Windows 2000管理工具中的“服務”就是一個典型的SCP。在這三個組成部分中,用戶最可能去寫服務本身,同時也可能不得不寫一個與其伴隨的客戶端程序作為一個SCP去和SCM通訊,本文只討論去設計和實現(xiàn)一個服務,關于如何去實現(xiàn)一個SCP則在以后的其它文章中介紹。Question 4. 怎樣開始設計服務還記得前面我提到服務程序的入口點函數(shù)一般都是main()嗎?一個服務擁有很重要的三個函數(shù),第一個就是入口點函數(shù),其實用WinMain()作為入口點函數(shù)也不是不可以,雖然說服務不應該有用戶界面,但是其實存在很少的幾個例外,這就是下面圖中的選項存在的原因。由于要和用戶桌面進行信息交互,服務程序有時會以WinMain()作為入口點函數(shù)。入口函數(shù)負責初始化整個進程,由這個進程中的主線程來執(zhí)行。這意味著它應用于這個可執(zhí)行文件中的所有服務。要知道,一個可執(zhí)行文件中能夠包含多個服務以使得執(zhí)行更加有效。主進程通知SCM在可執(zhí)行文件中含有幾個服務,并且給出每一個服務的ServiceMain回調(Call Back)函數(shù)的地址。一旦在可執(zhí)行文件內的所有服務都已經停止運行,主線程就在進程終止前對整個進程進行清除。第二個很重要的函數(shù)就是ServiceMain,我看過一些例子程序里面對自己的服務的進入點函數(shù)都固定命名為ServiceMain,其實并沒有規(guī)定過一定要那樣命名,任何的函數(shù)只要符合下列的形式都可以作為服務的進入點函數(shù)。VOID WINAPI ServiceMain(DWORD dwArgc, / 參數(shù)個數(shù)LPTSTR *lpszArgv / 參數(shù)串);這個函數(shù)由操作系統(tǒng)調用,并執(zhí)行能完成服務的代碼。一個專用的線程執(zhí)行每一個服務的ServiceMain函數(shù),注意是服務而不是服務程序,這是因為每個服務也都擁有與自己唯一對應的ServiceMain函數(shù),關于這一點可以用“管理工具”里的“服務”去察看Win2000里面自帶的服務,就會發(fā)現(xiàn)其實很多服務都是由service.exe單獨提供的。當主線程調用Win32函數(shù)StartServiceCtrlDispatcher的時候,SCM為這個進程中的每一個服務產生一個線程。這些線程中的每一個都和它的相應的服務的ServiceMain函數(shù)一起執(zhí)行,這就是服務總是多線程的原因一個僅有一個服務的可執(zhí)行文件將有一個主線程,其它的線程執(zhí)行服務本身。第三個也就是最后的一個重要函數(shù)是CtrlHandler,它必須擁有下面的原型:VOID WINAPI CtrlHandler(DWORD fdwControl /控制命令)像ServiceMain一樣,CtrlHandler也是一個回調函數(shù),用戶必須為它的服務程序中每一個服務寫一個單獨的CtrlHandler函數(shù),因此如果有一個程序含有兩個服務,那么它至少要擁有5個不同的函數(shù):作為入口點的main()或WinMain(),用于第一個服務的ServiceMain函數(shù)和CtrlHandler函數(shù),以及用于第二個服務的ServiceMain函數(shù)和CtrlHandler函數(shù)。SCM調用一個服務的CtrlHandler函數(shù)去改變這個服務的狀態(tài)。例如,當某個管理員用管理工具里的“服務”嘗試停止你的服務的時候,你的服務的CtrlHandler函數(shù)將收到一個SERVICE_CONTROL_STOP通知。CtrlHandler函數(shù)負責執(zhí)行停止服務所需的一切代碼。由于是進程的主線程執(zhí)行所有的CtrlHandler函數(shù),因而必須盡量優(yōu)化你的CtrlHandler函數(shù)的代碼,使它運行起來足夠快,以便相同進程中的其它服務的CtrlHandler函數(shù)能在適當?shù)臅r間內收到屬于它們的通知。而且基于上述原因,你的CtrlHandler函數(shù)必須要能夠將想要傳達的狀態(tài)送到服務線程,這個傳遞過程沒有固定的方法,完全取決于你的服務的用途。(二)對服務的深入討論之上上一章其實只是概括性的介紹,下面開始才是真正的細節(jié)所在。在進入點函數(shù)里面要完成ServiceMain的初始化,準確點說是初始化一個SERVICE_TABLE_ENTRY結構數(shù)組,這個結構記錄了這個服務程序里面所包含的所有服務的名稱和服務的進入點函數(shù),下面是一個SERVICE_TABLE_ENTRY的例子:SERVICE_TABLE_ENTRY service_table_entry = MyFTPd , FtpdMain , MyHttpd, Httpserv, NULL, NULL ,;第一個成員代表服務的名字,第二個成員是ServiceMain回調函數(shù)的地址,上面的服務程序因為擁有兩個服務,所以有三個SERVICE_TABLE_ENTRY元素,前兩個用于服務,最后的NULL指明數(shù)組的結束。接下來這個數(shù)組的地址被傳遞到StartServiceCtrlDispatcher函數(shù):BOOL StartServiceCtrlDispatcher(LPSERVICE_TABLE_ENTRY lpServiceStartTable)這個Win32函數(shù)表明可執(zhí)行文件的進程怎樣通知SCM包含在這個進程中的服務。就像上一章中講的那樣,StartServiceCtrlDispatcher為每一個傳遞到它的數(shù)組中的非空元素產生一個新的線程,每一個進程開始執(zhí)行由數(shù)組元素中的lpServiceStartTable指明的ServiceMain函數(shù)。SCM啟動一個服務程序之后,它會等待該程序的主線程去調StartServiceCtrlDispatcher。如果那個函數(shù)在兩分鐘內沒有被調用,SCM將會認為這個服務有問題,并調用TerminateProcess去殺死這個進程。這就要求你的主線程要盡可能快的調用StartServiceCtrlDispatcher。StartServiceCtrlDispatcher函數(shù)則并不立即返回,相反它會駐留在一個循環(huán)內。當在該循環(huán)內時,StartServiceCtrlDispatcher懸掛起自己,等待下面兩個事件中的一個發(fā)生。第一,如果SCM要去送一個控制通知給運行在這個進程內一個服務的時候,這個線程就會激活。當控制通知到達后,線程激活并調用相應服務的CtrlHandler函數(shù)。CtrlHandler函數(shù)處理這個服務控制通知,并返回到StartServiceCtrlDispatcher。StartServiceCtrlDispatcher循環(huán)回去后再一次懸掛自己。第二,如果服務線程中的一個服務中止,這個線程也將激活。在這種情況下,該進程將運行在它里面的服務數(shù)減一。如果服務數(shù)為零,StartServiceCtrlDispatcher就會返回到入口點函數(shù),以便能夠執(zhí)行任何與進程有關的清除工作并結束進程。如果還有服務在運行,哪怕只是一個服務,StartServiceCtrlDispatcher也會繼續(xù)循環(huán)下去,繼續(xù)等待其它的控制通知或者剩下的服務線程中止。上面的內容是關于入口點函數(shù)的,下面的內容則是關于ServiceMain函數(shù)的。還記得以前講過的ServiceMain函數(shù)的的原型嗎?但實際上一個ServiceMain函數(shù)通常忽略傳遞給它的兩個參數(shù),因為服務一般不怎么傳遞參數(shù)。設置一個服務最好的方法就是設置注冊表,一般服務在HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServiceServiceNameParameters子鍵下存放自己的設置,這里的ServiceName是服務的名字。事實上,可能要寫一個客戶應用程序去進行服務的背景設置,這個客戶應用程序將這些信息存在注冊表中,以便服務讀取。當一個外部應用程序已經改變了某個正在運行中的服務的設置數(shù)據(jù)的時候,這個服務能夠用RegNotifyChangeKeyValue函數(shù)去接受一個通知,這樣就允許服務快速的重新設置自己。前面講到StartServiceCtrlDispatcher為每一個傳遞到它的數(shù)組中的非空元素產生一個新的線程。接下來,一個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. 為什么呢?因為發(fā)出啟動服務請求之后,如果在一定時間之內無法完成服務的初始化,SCM會認為服務的啟動已經失敗了,這個時間的長度在Win NT 4.0中是80秒,Win2000中不詳基于上面的理由,ServiceMain要迅速完成自身工作,首先是必不可少的兩項工作,第一項是調用RegisterServiceCtrlHandler函數(shù)去通知SCM它的CtrlHandler回調函數(shù)的地址:SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(LPCTSTR lpServiceName, /服務的名字LPHANDLER_FUNCTION lpHandlerProc /CtrlHandler函數(shù)地址) 第一個參數(shù)指明你正在建立的CtrlHandler是為哪一個服務所用,第二個參數(shù)是CtrlHandler函數(shù)的地址。lpServiceName必須和在SERVICE_TABLE_ENTRY里面被初始化的服務的名字相匹配。RegisterServiceCtrlHandler返回一個SERVICE_STATUS_HANDLE,這是一個32位的句柄。SCM用它來唯一確定這個服務。當這個服務需要把它當時的狀態(tài)報告給SCM的時候,就必須把這個句柄傳給需要它的Win32函數(shù)。注意:這個句柄和其他大多數(shù)的句柄不同,你無需關閉它。SCM要求ServiceMain函數(shù)的線程在一秒鐘內調用RegisterServiceCtrlHandler函數(shù),否則SCM會認為服務已經失敗。但在這種情況下,SCM不會終止服務,不過在NT 4中將無法啟動這個服務,同時會返回一個不正確的錯誤信息,這一點在Windows 2000中得到了修正。在RegisterServiceCtrlHandler函數(shù)返回后,ServiceMain線程要立即告訴SCM服務正在繼續(xù)初始化。具體的方法是通過調用SetServiceStatus函數(shù)傳遞SERVICE_STATUS數(shù)據(jù)結構。BOOL SetServiceStatus(SERVICE_STATUS_HANDLE hService, /服務的句柄SERVICE_STATUS lpServiceStatus /SERVICE_STATUS結構的地址) 這個函數(shù)要求傳遞給它指明服務的句柄(剛剛通過調用RegisterServiceCtrlHandler得到),和一個初始化的SERVICE_STATUS結構的地址:typedef struct _SERVICE_STATUSDWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; SERVICE_STATUS, *LPSERVICE_STATUS;SERVICE_STATUS結構含有七個成員,它們反映服務的現(xiàn)行狀態(tài)。所有這些成員必須在這個結構被傳遞到SetServiceStatus之前正確的設置。成員dwServiceType指明服務可執(zhí)行文件的類型。如果你的可執(zhí)行文件中只有一個單獨的服務,就把這個成員設置成SERVICE_WIN32_OWN_PROCESS;如果擁有多個服務的話,就設置成SERVICE_WIN32_SHARE_PROCESS。除了這兩個標志之外,如果你的服務需要和桌面發(fā)生交互(當然不推薦這樣做),就要用“OR”運算符附加上SERVICE_INTERACTIVE_PROCESS。這個成員的值在你的服務的生存期內絕對不應該改變。成員dwCurrentState是這個結構中最重要的成員,它將告訴SCM你的服務的現(xiàn)行狀態(tài)。為了報告服務仍在初始化,應該把這個成員設置成SERVICE_START_PENDING。在以后具體講述CtrlHandler函數(shù)的時候具體解釋其它可能的值。成員dwControlsAccepted指明服務愿意接受什么樣的控制通知。如果你允許一個SCP去暫停/繼續(xù)服務,就把它設成SERVICE_ACCEPT_PAUSE_CONTINUE。很多服務不支持暫?;蚶^續(xù),就必須自己決定在服務中它是否可用。如果你允許一個SCP去停止服務,就要設置它為SERVICE_ACCEPT_STOP。如果服務要在操作系統(tǒng)關閉的時候得到通知,設置它為SERVICE_ACCEPT_SHUTDOWN可以收到預期的結果。這些標志可以用“OR”運算符組合。成員dwWin32ExitCode和dwServiceSpecificExitCode是允許服務報告錯誤的關鍵,如果希望服務去報告一個Win32錯誤代碼(預定義在WinError.h中),它就設置dwWin32ExitCode為需要的代碼。一個服務也可以報告它本身特有的、沒有映射到一個預定義的Win32錯誤代碼中的錯誤。為了這一點,要把dwWin32ExitCode設置為ERROR_SERVICE_SPECIFIC_ERROR,然后還要設置成員dwServiceSpecificExitCode為服務特有的錯誤代碼。當服務運行正常,沒有錯誤可以報告的時候,就設置成員dwWin32ExitCode為NO_ERROR。最后的兩個成員dwCheckPoint和dwWaitHint是一個服務用來報告它當前的事件進展情況的。當成員dwCurrentState被設置成SERVICE_START_PENDING的時候,應該把dwCheckPoint設成0,dwWaitHint設成一個經過多次嘗試后確定比較合適的數(shù),這樣服務才能高效運行。一旦服務被完全初始化,就應該重新初始化SERVICE_STATUS結構的成員,更改dwCurrentState為SERVICE_RUNNING,然后把dwCheckPoint和dwWaitHint都改為0。dwCheckPoint成員的存在對用戶是有益的,它允許一個服務報告它處于進程的哪一步。每一次調用SetServiceStatus時,可以增加它到一個能指明服務已經執(zhí)行到哪一步的數(shù)字,它可以幫助用戶決定多長時間報告一次服務的進展情況。如果決定要報告服務的初始化進程的每一步,就應該設置dwWaitHint為你認為到達下一步所需的毫秒數(shù),而不是服務完成它的進程所需的毫秒數(shù)。在服務的所有初始化都完成之后,服務調用SetServiceStatus指明SERVICE_RUNNING,在那一刻服務已經開始運行。通常一個服務是把自己放在一個循環(huán)之中來運行的。在循環(huán)的內部這個服務進程懸掛自己,等待指明它下一步是應該暫停、繼續(xù)或停止之類的網(wǎng)絡請求或通知。當一個請求到達的時候,服務線程激活并處理這個請求,然后再循環(huán)回去等待下一個請求/通知。如果一個服務由于一個通知而激活,它會先處理這個通知,除非這個服務得到的是停止或關閉的通知。如果真的是停止或關閉的通知,服務線程將退出循環(huán),執(zhí)行必要的清除操作,然后從這個線程返回。當ServiceMain線程返回并中止時,引起在StartServiceCtrlDispatcher內睡眠的線程激活,并像在前面解釋過的那樣,減少它運行的服務的計數(shù)。(三)對服務的深入討論之下 現(xiàn)在我們還剩下一個函數(shù)可以在細節(jié)上討論,那就是服務的CtrlHandler函數(shù)。當調用RegisterServiceCtrlHandler函數(shù)時,SCM得到并保存這個回調函數(shù)的地址。一個SCP調一個告訴SCM如何去控制服務的Win32函數(shù),現(xiàn)在已經有10個預定義的控制請求: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. 上表中標有Windows 2000字樣的就是2000中新添加的控制代碼。除了這些代碼之外,服務也可以接受用戶定義的,范圍在128-255之間的代碼。當CtrlHandler函數(shù)收到一個SERVICE_CONTROL_STOP、SERVICE_CONTROL_PAUSE、 SERVICE_CONTROL_CONTINUE控制代碼的時候,SetServiceStatus必須被調用去確認這個代碼,并指定你認為服務處理這個狀態(tài)變化所需要的時間。例如:你的服務收到了停止請求,首先要把SERVICE_STATUS結構的dwCurrentState成員設置成SERVICE_STOP_PENDING,這樣可以使SCM確定你已經收到了控制代碼。當一個服務的暫停或停止操作正在執(zhí)行的時候,必須指定你認為這種操作所需要的時間:這是因為一個服務也許不能立即改變它的狀態(tài),它可能必須等待一個網(wǎng)絡請求被完成或者數(shù)據(jù)被刷新到一個驅動器上。指定時間的方法就像我上一章說的那樣,用成員dwCheckPoint和dwWaitHint來指明它完成狀態(tài)改變所需要的時間。如果需要,可以用增加dwCheckPoint成員的值和設置dwWaitHint成員的值去指明你期待的服務到達下一步的時間的方式周期性的報告進展情況。當整個啟動的過程完成之后,要再一次調用SetServiceStatus。這時就要把SERVICE_STATUS結構的dwCurrentState成員設置成SERVICE_STOPPED,當報告狀態(tài)代碼的同時,一定要把成員dwCheckPoint和dwWaitHint設置為0,因為服務已經完成了它的狀態(tài)變化。暫?;蚶^續(xù)服務的時候方法也一樣。當CtrlHandler函數(shù)收到一個SERVICE_CONTROL_INTERROGATE控制代碼的時候,服務將簡單的將dwCurrentState成員設置成服務當前的狀態(tài),同時,把成員dwCheckPoint和dwWaitHint設置為0,然后再調用SetServiceStatus就可以了。在操作系統(tǒng)關閉的時候,CtrlHandler函數(shù)收到一個SERVICE_CONTROL_SHUTDOWN控制代碼。服務根本無須回應這個代碼,因為系統(tǒng)即將關閉。它將執(zhí)行保存數(shù)據(jù)所需要的最小行動集,這是為了確定機器能及時關閉。缺省時系統(tǒng)只給很少的時間去關閉所有的服務,MSDN里面說大概是20秒的時間,不過那可能是Windows NT 4的設置,在我的Windows 2000 Server里這個時間是10秒,你可以手動的修改這個數(shù)值,它被記錄在HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControl子鍵里面的WaitToKillServiceTimeout,單位是毫秒。當CtrlHandler函數(shù)收到任何用戶定義的代碼時,它應該執(zhí)行期望的用戶自定義行動。除非用戶自定義的行動要強制服務去暫停、繼續(xù)或停止,否則不調SetServiceStatus函數(shù)。如果用戶定義的行動強迫服務的狀態(tài)發(fā)生變化,SetServiceStatus將被調用去設置dwCurrentState、dwCheckPoint和dwWaitHint,具體控制代碼和前面說的一樣。如果你的CtrlHandler函數(shù)需要很長的時間執(zhí)行操作的話,千萬要注意:假如CtrlHandler函數(shù)在30秒內沒有返回的話,SCM將返回一個錯誤,這不是我們所期望的。所以如果出現(xiàn)上述情況,最好的辦法是再建立一個線程,讓它去繼續(xù)執(zhí)行操作,以便使得CtrlHandler函數(shù)能夠迅速的返回。例如,當收到一個SERVICE_CONTROL_STOP請求的時候,就像上面說的一樣,服務可能正在等待一個網(wǎng)絡請求被完成或者數(shù)據(jù)被刷新到一個驅動器上,而這些操作所需要的時間是你不能估計的,那么就要建立一個新的線程等待操作完成后執(zhí)行停止命令,CtrlHandler函數(shù)在返回之前仍然要報告SERVICE_STOP_PENDING狀態(tài),當新的線程執(zhí)行完操作之后,再由它將服務的狀態(tài)設置成SERVICE_STOPPED。如果當前操作的時間可以估計的到就不要這樣做,仍然使用前面交待的方法處理。CtrlHandler函數(shù)我就先講這些,下面說說服務怎么安裝。一個服務程序可以使用CreateService函數(shù)將服務的信息添加到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是一個標示SCM數(shù)據(jù)庫的句柄,可以簡單的通過調用OpenSCManager得到。SC_HANDLE OpenSCManager( LPCTSTR lpMachineName, / computer name LPCTSTR lpDatabaseName, / SCM database name DWORD dwDesiredAccess / access type );lpMachineName是目標機器的名字,還記得我在第一章里說過可以在其它的機器上面安裝服務嗎?這就是實現(xiàn)的方法。對方機器名字必須以“”開始。如果傳遞NULL或者一個空的字符串的話就默認是本機。lpDatabaseName是目標機器上面SCM數(shù)據(jù)庫的名字,但MSDN里面說這個參數(shù)要默認的設置成SERVICES_ACTIVE_DATABASE,如果傳遞NULL,就默認的打開SERVICES_ACTIVE_DATABASE。所以我還沒有真的搞明白這個參數(shù)的存在意義,總之使用的時候傳遞NULL就行了。dwDesiredAccess是SCM數(shù)據(jù)庫的訪問權限,具體值見下表: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. 想要獲得訪問權限的話,似乎沒那么復雜。MSDN里面說所有進程都被允許獲得對所有SCM數(shù)據(jù)庫的SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE, and SC_MANAGER_QUERY_LOCK_STATUS權限,這些權限使得你可以連接SCM數(shù)據(jù)庫,枚舉目標機器上安裝的服務和查詢目標數(shù)據(jù)庫是否已被鎖住。但如果要創(chuàng)建服務,首先你需要擁有目標機器的管理員權限,一般的傳遞SC_MANAGER_ALL_ACCESS就可以了。這個函數(shù)返回的句柄可以被CloseServiceHandle函數(shù)關閉。lpServiceName是服務的名字,lpDisplayName是服務在“服務”管理工具里顯示的名字。dwDesiredAccess也是訪問的權限,有一個比上面的還長的多的一個表,各位自己查MSDN吧。我們要安裝服務,仍然簡單的傳遞SC_MANAGER_ALL_ACCESS。dwServiceType是指你的服務是否和其它的進程相關聯(lián),一般是SERVICE_WIN32_

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論