• <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)計(jì)

            最新評(píng)論

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

              上一章其實(shí)只是概括性的介紹,下面開(kāi)始才是真正的細(xì)節(jié)所在。在進(jìn)入點(diǎn)函數(shù)里面要完成ServiceMain的初始化,準(zhǔn)確點(diǎn)說(shuō)是初始化一個(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é)束。

              接下來(lái)這個(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)程開(kāi)始執(zhí)行由數(shù)組元素中的lpServiceStartTable指明的ServiceMain函數(shù)。

              SCM啟動(dòng)一個(gè)服務(wù)程序之后,它會(huì)等待該程序的主線程去調(diào)StartServiceCtrlDispatcher。如果那個(gè)函數(shù)在兩分鐘內(nèi)沒(méi)有被調(diào)用,SCM將會(huì)認(rèn)為這個(gè)服務(wù)有問(wèn)題,并調(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ù)的。還記得以前講過(guò)的ServiceMain函數(shù)的的原型嗎?但實(shí)際上一個(gè)ServiceMain函數(shù)通常忽略傳遞給它的兩個(gè)參數(shù),因?yàn)榉?wù)一般不怎么傳遞參數(shù)。設(shè)置一個(gè)服務(wù)最好的方法就是設(shè)置注冊(cè)表,一般服務(wù)在
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Service\ServiceName\Parameters
            子鍵下存放自己的設(shè)置,這里的ServiceName是服務(wù)的名字。事實(shí)上,可能要寫(xiě)一個(gè)客戶應(yīng)用程序去進(jìn)行服務(wù)的背景設(shè)置,這個(gè)客戶應(yīng)用程序?qū)⑦@些信息存在注冊(cè)表中,以便服務(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è)新的線程。接下來(lái),一個(gè)ServiceMain要做些什么呢?MSDN里面的原文是這樣說(shuō)的: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ù)請(qǐng)求之后,如果在一定時(shí)間之內(nèi)無(wú)法完成服務(wù)的初始化,SCM會(huì)認(rèn)為服務(wù)的啟動(dòng)已經(jīng)失敗了,這個(gè)時(shí)間的長(zhǎ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ù)地址
            )


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

              SCM要求ServiceMain函數(shù)的線程在一秒鐘內(nèi)調(diào)用RegisterServiceCtrlHandler函數(shù),否則SCM會(huì)認(rèn)為服務(wù)已經(jīng)失敗。但在這種情況下,SCM不會(huì)終止服務(wù),不過(guò)在NT 4中將無(wú)法啟動(dòng)這個(gè)服務(wù),同時(shí)會(huì)返回一個(gè)不正確的錯(cuò)誤信息,這一點(diǎn)在Windows 2000中得到了修正。

              在RegisterServiceCtrlHandler函數(shù)返回后,ServiceMain線程要立即告訴SCM服務(wù)正在繼續(xù)初始化。具體的方法是通過(guò)調(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ù)的句柄(剛剛通過(guò)調(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)絕對(duì)不應(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)告它本身特有的、沒(méi)有映射到一個(gè)預(yù)定義的Win32錯(cuò)誤代碼中的錯(cuò)誤。為了這一點(diǎn),要把dwWin32ExitCode設(shè)置為ERROR_SERVICE_SPECIFIC_ERROR,然后還要設(shè)置成員dwServiceSpecificExitCode為服務(wù)特有的錯(cuò)誤代碼。當(dāng)服務(wù)運(yùn)行正常,沒(méi)有錯(cuò)誤可以報(bào)告的時(shí)候,就設(shè)置成員dwWin32ExitCode為NO_ERROR。

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

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

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

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


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


            99久久国产综合精品成人影院| 91精品国产9l久久久久| 99热都是精品久久久久久| 国产成人AV综合久久| 久久只这里是精品66| 性做久久久久久久久| 亚洲av日韩精品久久久久久a| AAA级久久久精品无码片| 久久国产乱子伦精品免费午夜| 久久天天婷婷五月俺也去| .精品久久久麻豆国产精品| 香港aa三级久久三级老师2021国产三级精品三级在 | 国产亚洲综合久久系列| 国产精品免费久久久久久久久 | 国产精品成人无码久久久久久| 亚洲性久久久影院| 国产精品99久久精品爆乳| 人妻系列无码专区久久五月天| 精品久久久久久久无码| 久久久久波多野结衣高潮| 久久精品国产第一区二区| 精品久久久久久无码专区| 久久精品国产清自在天天线 | 一本一道久久综合狠狠老| 国产精品美女久久久久AV福利| 久久天天躁狠狠躁夜夜avapp| 久久天天躁狠狠躁夜夜av浪潮| 久久99精品国产99久久| 国产精品久久国产精品99盘| 亚洲综合精品香蕉久久网| 色综合久久夜色精品国产| 久久综合狠狠综合久久97色| 九九热久久免费视频| 精品久久久久久无码人妻热 | 成人国内精品久久久久一区| 97久久国产露脸精品国产| 久久久久久久精品成人热色戒| 模特私拍国产精品久久| 一级女性全黄久久生活片免费| 久久精品成人免费国产片小草| 久久久久国产|