• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大龍的博客

            常用鏈接

            統(tǒng)計

            最新評論

            Windows服務(wù)編寫原理及探討(二)

              上一章其實(shí)只是概括性的介紹,下面開始才是真正的細(xì)節(jié)所在。在進(jìn)入點(diǎn)函數(shù)里面要完成ServiceMain的初始化,準(zhǔn)確點(diǎn)說是初始化一個SERVICE_TABLE_ENTRY結(jié)構(gòu)數(shù)組,這個結(jié)構(gòu)記錄了這個服務(wù)程序里面所包含的所有服務(wù)的名稱和服務(wù)的進(jìn)入點(diǎn)函數(shù),下面是一個SERVICE_TABLE_ENTRY的例子:

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

              第一個成員代表服務(wù)的名字,第二個成員是ServiceMain回調(diào)函數(shù)的地址,上面的服務(wù)程序因?yàn)閾碛袃蓚€服務(wù),所以有三個SERVICE_TABLE_ENTRY元素,前兩個用于服務(wù),最后的NULL指明數(shù)組的結(jié)束。

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

            BOOL StartServiceCtrlDispatcher(
            LPSERVICE_TABLE_ENTRY lpServiceStartTable
            )

              這個Win32函數(shù)表明可執(zhí)行文件的進(jìn)程怎樣通知SCM包含在這個進(jìn)程中的服務(wù)。就像上一章中講的那樣,StartServiceCtrlDispatcher為每一個傳遞到它的數(shù)組中的非空元素產(chǎn)生一個新的線程,每一個進(jìn)程開始執(zhí)行由數(shù)組元素中的lpServiceStartTable指明的ServiceMain函數(shù)。

              SCM啟動一個服務(wù)程序之后,它會等待該程序的主線程去調(diào)StartServiceCtrlDispatcher。如果那個函數(shù)在兩分鐘內(nèi)沒有被調(diào)用,SCM將會認(rèn)為這個服務(wù)有問題,并調(diào)用TerminateProcess去殺死這個進(jìn)程。這就要求你的主線程要盡可能快的調(diào)用StartServiceCtrlDispatcher。

              StartServiceCtrlDispatcher函數(shù)則并不立即返回,相反它會駐留在一個循環(huán)內(nèi)。當(dāng)在該循環(huán)內(nèi)時,StartServiceCtrlDispatcher懸掛起自己,等待下面兩個事件中的一個發(fā)生。第一,如果SCM要去送一個控制通知給運(yùn)行在這個進(jìn)程內(nèi)一個服務(wù)的時候,這個線程就會激活。當(dāng)控制通知到達(dá)后,線程激活并調(diào)用相應(yīng)服務(wù)的CtrlHandler函數(shù)。CtrlHandler函數(shù)處理這個服務(wù)控制通知,并返回到StartServiceCtrlDispatcher。StartServiceCtrlDispatcher循環(huán)回去后再一次懸掛自己。

              第二,如果服務(wù)線程中的一個服務(wù)中止,這個線程也將激活。在這種情況下,該進(jìn)程將運(yùn)行在它里面的服務(wù)數(shù)減一。如果服務(wù)數(shù)為零,StartServiceCtrlDispatcher就會返回到入口點(diǎn)函數(shù),以便能夠執(zhí)行任何與進(jìn)程有關(guān)的清除工作并結(jié)束進(jìn)程。如果還有服務(wù)在運(yùn)行,哪怕只是一個服務(wù),StartServiceCtrlDispatcher也會繼續(xù)循環(huán)下去,繼續(xù)等待其它的控制通知或者剩下的服務(wù)線程中止。

              上面的內(nèi)容是關(guān)于入口點(diǎn)函數(shù)的,下面的內(nèi)容則是關(guān)于ServiceMain函數(shù)的。還記得以前講過的ServiceMain函數(shù)的的原型嗎?但實(shí)際上一個ServiceMain函數(shù)通常忽略傳遞給它的兩個參數(shù),因?yàn)榉?wù)一般不怎么傳遞參數(shù)。設(shè)置一個服務(wù)最好的方法就是設(shè)置注冊表,一般服務(wù)在
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Service\ServiceName\Parameters
            子鍵下存放自己的設(shè)置,這里的ServiceName是服務(wù)的名字。事實(shí)上,可能要寫一個客戶應(yīng)用程序去進(jìn)行服務(wù)的背景設(shè)置,這個客戶應(yīng)用程序?qū)⑦@些信息存在注冊表中,以便服務(wù)讀取。當(dāng)一個外部應(yīng)用程序已經(jīng)改變了某個正在運(yùn)行中的服務(wù)的設(shè)置數(shù)據(jù)的時候,這個服務(wù)能夠用RegNotifyChangeKeyValue函數(shù)去接受一個通知,這樣就允許服務(wù)快速的重新設(shè)置自己。

              前面講到StartServiceCtrlDispatcher為每一個傳遞到它的數(shù)組中的非空元素產(chǎn)生一個新的線程。接下來,一個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ù)請求之后,如果在一定時間之內(nèi)無法完成服務(wù)的初始化,SCM會認(rèn)為服務(wù)的啟動已經(jīng)失敗了,這個時間的長度在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ù)地址
            )


              第一個參數(shù)指明你正在建立的CtrlHandler是為哪一個服務(wù)所用,第二個參數(shù)是CtrlHandler函數(shù)的地址。lpServiceName必須和在SERVICE_TABLE_ENTRY里面被初始化的服務(wù)的名字相匹配。RegisterServiceCtrlHandler返回一個SERVICE_STATUS_HANDLE,這是一個32位的句柄。SCM用它來唯一確定這個服務(wù)。當(dāng)這個服務(wù)需要把它當(dāng)時的狀態(tài)報告給SCM的時候,就必須把這個句柄傳給需要它的Win32函數(shù)。注意:這個句柄和其他大多數(shù)的句柄不同,你無需關(guān)閉它。

              SCM要求ServiceMain函數(shù)的線程在一秒鐘內(nèi)調(diào)用RegisterServiceCtrlHandler函數(shù),否則SCM會認(rèn)為服務(wù)已經(jīng)失敗。但在這種情況下,SCM不會終止服務(wù),不過在NT 4中將無法啟動這個服務(wù),同時會返回一個不正確的錯誤信息,這一點(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)的地址
            )

              這個函數(shù)要求傳遞給它指明服務(wù)的句柄(剛剛通過調(diào)用RegisterServiceCtrlHandler得到),和一個初始化的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)含有七個成員,它們反映服務(wù)的現(xiàn)行狀態(tài)。所有這些成員必須在這個結(jié)構(gòu)被傳遞到SetServiceStatus之前正確的設(shè)置。

              成員dwServiceType指明服務(wù)可執(zhí)行文件的類型。如果你的可執(zhí)行文件中只有一個單獨(dú)的服務(wù),就把這個成員設(shè)置成SERVICE_WIN32_OWN_PROCESS;如果擁有多個服務(wù)的話,就設(shè)置成SERVICE_WIN32_SHARE_PROCESS。除了這兩個標(biāo)志之外,如果你的服務(wù)需要和桌面發(fā)生交互(當(dāng)然不推薦這樣做),就要用“OR”運(yùn)算符附加上SERVICE_INTERACTIVE_PROCESS。這個成員的值在你的服務(wù)的生存期內(nèi)絕對不應(yīng)該改變。

              成員dwCurrentState是這個結(jié)構(gòu)中最重要的成員,它將告訴SCM你的服務(wù)的現(xiàn)行狀態(tài)。為了報告服務(wù)仍在初始化,應(yīng)該把這個成員設(shè)置成SERVICE_START_PENDING。在以后具體講述CtrlHandler函數(shù)的時候具體解釋其它可能的值。

              成員dwControlsAccepted指明服務(wù)愿意接受什么樣的控制通知。如果你允許一個SCP去暫停/繼續(xù)服務(wù),就把它設(shè)成SERVICE_ACCEPT_PAUSE_CONTINUE。很多服務(wù)不支持暫?;蚶^續(xù),就必須自己決定在服務(wù)中它是否可用。如果你允許一個SCP去停止服務(wù),就要設(shè)置它為SERVICE_ACCEPT_STOP。如果服務(wù)要在操作系統(tǒng)關(guān)閉的時候得到通知,設(shè)置它為SERVICE_ACCEPT_SHUTDOWN可以收到預(yù)期的結(jié)果。這些標(biāo)志可以用“OR”運(yùn)算符組合。

              成員dwWin32ExitCode和dwServiceSpecificExitCode是允許服務(wù)報告錯誤的關(guān)鍵,如果希望服務(wù)去報告一個Win32錯誤代碼(預(yù)定義在WinError.h中),它就設(shè)置dwWin32ExitCode為需要的代碼。一個服務(wù)也可以報告它本身特有的、沒有映射到一個預(yù)定義的Win32錯誤代碼中的錯誤。為了這一點(diǎn),要把dwWin32ExitCode設(shè)置為ERROR_SERVICE_SPECIFIC_ERROR,然后還要設(shè)置成員dwServiceSpecificExitCode為服務(wù)特有的錯誤代碼。當(dāng)服務(wù)運(yùn)行正常,沒有錯誤可以報告的時候,就設(shè)置成員dwWin32ExitCode為NO_ERROR。

              最后的兩個成員dwCheckPoint和dwWaitHint是一個服務(wù)用來報告它當(dāng)前的事件進(jìn)展情況的。當(dāng)成員dwCurrentState被設(shè)置成SERVICE_START_PENDING的時候,應(yīng)該把dwCheckPoint設(shè)成0,dwWaitHint設(shè)成一個經(jīng)過多次嘗試后確定比較合適的數(shù),這樣服務(wù)才能高效運(yùn)行。一旦服務(wù)被完全初始化,就應(yīng)該重新初始化SERVICE_STATUS結(jié)構(gòu)的成員,更改dwCurrentState為SERVICE_RUNNING,然后把dwCheckPoint和dwWaitHint都改為0。

              dwCheckPoint成員的存在對用戶是有益的,它允許一個服務(wù)報告它處于進(jìn)程的哪一步。每一次調(diào)用SetServiceStatus時,可以增加它到一個能指明服務(wù)已經(jīng)執(zhí)行到哪一步的數(shù)字,它可以幫助用戶決定多長時間報告一次服務(wù)的進(jìn)展情況。如果決定要報告服務(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)行。通常一個服務(wù)是把自己放在一個循環(huán)之中來運(yùn)行的。在循環(huán)的內(nèi)部這個服務(wù)進(jìn)程懸掛自己,等待指明它下一步是應(yīng)該暫停、繼續(xù)或停止之類的網(wǎng)絡(luò)請求或通知。當(dāng)一個請求到達(dá)的時候,服務(wù)線程激活并處理這個請求,然后再循環(huán)回去等待下一個請求/通知。

              如果一個服務(wù)由于一個通知而激活,它會先處理這個通知,除非這個服務(wù)得到的是停止或關(guān)閉的通知。如果真的是停止或關(guān)閉的通知,服務(wù)線程將退出循環(huán),執(zhí)行必要的清除操作,然后從這個線程返回。當(dāng)ServiceMain線程返回并中止時,引起在StartServiceCtrlDispatcher內(nèi)睡眠的線程激活,并像在前面解釋過的那樣,減少它運(yùn)行的服務(wù)的計數(shù)。

            posted on 2007-12-22 18:03 大龍 閱讀(393) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            国产精品福利一区二区久久| 久久久久亚洲Av无码专| 91久久精品国产成人久久| 草草久久久无码国产专区| 九九热久久免费视频| 亚洲国产成人精品女人久久久 | 欧美精品乱码99久久蜜桃| 久久人人爽人人爽人人片AV东京热| 久久精品国产2020| 91精品国产综合久久香蕉| 青青草国产97免久久费观看| 久久久久久午夜成人影院| 久久久久久亚洲精品不卡| 亚洲狠狠婷婷综合久久久久| 亚洲国产精品久久66| 青青草原精品99久久精品66| 久久久久国产精品嫩草影院 | 国产A三级久久精品| 久久久国产精品网站| 思思久久99热只有频精品66| 青青草原1769久久免费播放| 精品多毛少妇人妻AV免费久久| 日本一区精品久久久久影院| 精品久久久久久国产| 久久se精品一区精品二区国产| 久久久久久国产精品美女| 久久国产精品免费一区| 国产精品天天影视久久综合网| 久久国产欧美日韩精品免费| 久久精品国产亚洲Aⅴ香蕉| 精品免费tv久久久久久久| 久久国产精品77777| 人妻精品久久久久中文字幕一冢本| 中文精品久久久久人妻| 亚洲国产精品综合久久网络| 国产精品99久久久久久猫咪| 亚洲国产成人久久综合碰碰动漫3d| 国产精品免费看久久久| 精品无码久久久久国产| 成人国内精品久久久久一区| 久久亚洲AV成人出白浆无码国产|