• <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>
            隨筆 - 14, 文章 - 0, 評(píng)論 - 3, 引用 - 0
            數(shù)據(jù)加載中……

            [轉(zhuǎn)貼]Windows服務(wù)編寫(xiě)綜述

            1        服務(wù)介紹
            幾乎所有的操作系統(tǒng)在啟動(dòng)的時(shí)候都會(huì)啟動(dòng)一些不需要與用戶(hù)交互的進(jìn)程,這些進(jìn)程在Windows中就被稱(chēng)作服務(wù)。它通常用于實(shí)現(xiàn)客戶(hù)/服務(wù)器模式中的服務(wù)器方,如我們常見(jiàn)的Web服務(wù)IIS,當(dāng)操作系統(tǒng)在啟動(dòng)后它就自動(dòng)被運(yùn)行,不管是否有人登陸到系統(tǒng)只要系統(tǒng)開(kāi)啟它就能得到運(yùn)行。
            服務(wù)程序、服務(wù)控制程序(SCP,service control program)和服務(wù)控制管理器(SCM,service control manager)組成了Windows服務(wù)。我們可以通過(guò)服務(wù)控制程序操縱服務(wù)控制管理器來(lái)配置、啟動(dòng)、暫停、停止服務(wù)程序。其中服務(wù)程序和服務(wù)控制程序可以由我們自己來(lái)編寫(xiě)擴(kuò)展,而服務(wù)控制管理器(\windows\system32\servics.exe)則是操作系統(tǒng)內(nèi)置的一個(gè)部件。首先我們來(lái)了解一下SCM的工作情況,然后我們介紹服務(wù)程序的編寫(xiě)和服務(wù)控制時(shí)所涉及API的使用。
            2        服務(wù)控制管理器
            SCM本身也是一個(gè)服務(wù)程序(\windows\system32\servics.exe),作為windows的后臺(tái)服務(wù)運(yùn)行的。Winlogon在系統(tǒng)引導(dǎo)的早期會(huì)將SCM啟動(dòng)起來(lái)。SCM的服務(wù)入口函數(shù)首先創(chuàng)建一個(gè)初始化為無(wú)信號(hào)的同步事件對(duì)象(SvcCtrlEvent_A3752DX);接下來(lái),它開(kāi)始建立一個(gè)內(nèi)部服務(wù)數(shù)據(jù)庫(kù),這個(gè)數(shù)據(jù)庫(kù)要按事先規(guī)定好的一個(gè)順序列出所有服務(wù)組,并記錄與服務(wù)相關(guān)的詳細(xì)信息;當(dāng)這個(gè)數(shù)據(jù)庫(kù)建立完成時(shí)SCM就開(kāi)始按順序啟動(dòng)那些啟動(dòng)方式為自動(dòng)的服務(wù),如果有服務(wù)要?jiǎng)有杏谥付ㄓ脩?hù)賬戶(hù)中時(shí)還要調(diào)用LSASS,如果服務(wù)啟動(dòng)失敗則會(huì)被放入一個(gè)名為ScFailedDrivers的列表中。當(dāng)這些工作都完成后,SCM將同步事件對(duì)象SvcCtrlEvent_A3752DX置為有信號(hào)狀態(tài);并做好系統(tǒng)停機(jī)的準(zhǔn)備。
            當(dāng)系統(tǒng)要關(guān)機(jī)時(shí)會(huì)向Windows子系統(tǒng)進(jìn)程Csrss發(fā)送一個(gè)消息,以便調(diào)用Csrss的停機(jī)例程。Csrss會(huì)對(duì)所有活動(dòng)的進(jìn)程循環(huán)通知系統(tǒng)正在停機(jī)。對(duì)于除SCM以外的每一個(gè)系統(tǒng)進(jìn)程如果沒(méi)有返回退出的響應(yīng)Csrss 都會(huì)等待由HKEY_USER\.DEFAULT\Control Panel\Desktop\WaitToKillAppTimeout指定的毫秒數(shù)(我的系統(tǒng)中是20000,也就是20秒),然后知通下一個(gè)進(jìn)程結(jié)束。當(dāng)遇到SCM時(shí)也會(huì)通知SCM進(jìn)程系統(tǒng)正在停機(jī),但不同的是會(huì)使用一個(gè)專(zhuān)用的超時(shí)間隔值(SCM在系統(tǒng)初始化時(shí)要向Csrss登記,于是Csrss就將SCM的進(jìn)程ID保存了下來(lái)Csrss也就是通過(guò)個(gè)ID來(lái)識(shí)別SCM的)。這個(gè)專(zhuān)用的超時(shí)間隔位于HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\WaitToKillServiceTimeout中,有趣的是默認(rèn)值也是20秒。這在段時(shí)間里SCM要通知所有在初始化時(shí)(服務(wù)程序自身初始化)請(qǐng)求SCM通知自己系統(tǒng)停機(jī)的服務(wù),SCM有一個(gè)停機(jī)處理器負(fù)責(zé)這項(xiàng)工作。通知下達(dá)后SCM就等待服務(wù)退出,服務(wù)在接收到停機(jī)消息后會(huì)返回給SCM一個(gè)等待時(shí)間,SCM跟蹤所有服務(wù)返回等待時(shí)間找出其中的最大值,當(dāng)這個(gè)最大值達(dá)到后,如果有一個(gè)服務(wù)或多個(gè)服務(wù)又告訴SCM它們正在處理停機(jī)工作,那么SCM還會(huì)循環(huán)等待下去,但Csrss也再等待SCM當(dāng)Csrss等待超時(shí)后,會(huì)繼續(xù)后面的停機(jī)工作最終完成停機(jī)。
            這就要求服務(wù)程序要在Csrss等待SCM的這段時(shí)內(nèi)完成自己的停機(jī)處理工作,否則服務(wù)就沒(méi)機(jī)會(huì)在系統(tǒng)停機(jī)前完成自己的關(guān)閉工作了。
            3        服務(wù)程序
            Windows服務(wù)程序其實(shí)并不神秘,它只是遵循特定規(guī)則編寫(xiě)的一個(gè)程序。只要遵循這個(gè)特定的規(guī)則與服務(wù)控制管理器正確的交互,就可實(shí)現(xiàn)我們的服務(wù)程序。而我們只要能實(shí)現(xiàn)一個(gè)簡(jiǎn)單的服務(wù)程序,設(shè)計(jì)一個(gè)能處理復(fù)雜業(yè)務(wù)的服務(wù)也并非難事,因?yàn)閺慕Y(jié)構(gòu)上看兩者并沒(méi)有太大的區(qū)別。只要遵循與SCM交互的規(guī)則,設(shè)計(jì)服務(wù)程序與設(shè)計(jì)普通的應(yīng)用程序幾乎沒(méi)什么區(qū)別。
            3.1    程序結(jié)構(gòu)概要
                服務(wù)程序的與普通應(yīng)用程序一樣也需要一個(gè)主函數(shù)(main())作為程序的入口,與之不同的是作為一個(gè)服務(wù)程序它需要在主函數(shù)(main())中立即調(diào)用StartServiceCtrlDispatcher來(lái)注冊(cè)一個(gè)服務(wù)的入口函數(shù)
            (ServerMain(DWORD argc,LPTSTR *argv),當(dāng)然這個(gè)名字可自由命名)。StartServiceCtrlDispatcher函數(shù)的原型是:
             BOOL StartServiceCtrlDispatcher(
             LPSERVICE_TABLE_ENTRY
            lpServiceStartTable 
            );
            它的參數(shù)是一個(gè)指向SERVICE_TABLE_ENTRY的指針;SERVICE_TABLE_ENTRY結(jié)構(gòu)有兩個(gè)域;第一個(gè)域存儲(chǔ)服務(wù)的內(nèi)部名稱(chēng),第二個(gè)域是服務(wù)入口函數(shù)的指針。這個(gè)函數(shù)完成后,SCM就要可以服務(wù)啟動(dòng)的時(shí)候調(diào)用服務(wù)的入口函數(shù)。
                例如:管理員在服務(wù)管理器啟動(dòng)一個(gè)服務(wù),SCM就會(huì)在一個(gè)單獨(dú)的線(xiàn)程中調(diào)用服務(wù)注冊(cè)的入口函數(shù)。這時(shí)我們?cè)诜?wù)的這個(gè)入口函數(shù)中必須調(diào)用RegisterServiceCtrlHandler完成Handler函數(shù)的注冊(cè),這個(gè)函數(shù)用來(lái)接收和處理SCM的控制消息。下面列出Hander要處理的控制消息和RegisterServiceCtrlHandler的函數(shù)原型:
            VOID WINAPI Handler(
             DWORD fdwControl   // 請(qǐng)求控制消息代碼
            );
            控制消息宏定義
            說(shuō)明
            SERVICE_CONTROL_STOP要服務(wù)停止
            SERVICE_CONTROL_PAUSE要服務(wù)暫停
            SERVICE_CONTROL_CONTINUE要服務(wù)繼續(xù)
            SERVICE_CONTROL_INTERROGATE要服務(wù)馬上報(bào)告它的狀態(tài)
            SERVICE_CONTROL_SHUTDOWN告訴服務(wù)即將關(guān)機(jī)
            SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
             LPCTSTR lpServiceName,             // 服務(wù)的內(nèi)部名稱(chēng)
             LPHANDLER_FUNCTION lpHandlerProc   // Handler函數(shù)的地址
            );
                RegisterServiceCtrlHandler調(diào)用完成后我們就可以開(kāi)始我們的業(yè)務(wù)處理的初始化工作。初始化完成后向SCM報(bào)告服務(wù)開(kāi)始運(yùn)行(SERVICE_RUNNING)的消息。如果
            ServerMain(DWORD argc,LPTSTR *argv)函數(shù)退出服務(wù)也就停止了。下面讓我總結(jié)一下實(shí)現(xiàn)服務(wù)程序的步驟:
            (1)在main()調(diào)用StartServiceCtrlDispatcher來(lái)注冊(cè)一個(gè)服務(wù)的入口函數(shù);
            (2)在ServerMain(DWORD argc,LPTSTR *argv)中調(diào)用RegisterServiceCtrlHandler注冊(cè)Handler函數(shù)。
            (3)完成業(yè)務(wù)處理程序的初始化工作,如果初始化時(shí)間較長(zhǎng)要實(shí)時(shí)向SCM報(bào)告當(dāng)前正在啟動(dòng)
            (4)初始化完畢,報(bào)告服務(wù)正在運(yùn)行;開(kāi)始業(yè)務(wù)處理工作。
            3.2     程序?qū)嵗治?/span>
            (1)main()函數(shù)
            int main(int argc, char* argv[])
            {
                 SERVICE_TABLE_ENTRY serviceTable[]=
                 {
                     {
                          SERVICE_NAME,
                          (LPSERVICE_MAIN_FUNCTION)ServiceMain
                     }
                     {
                          NULL,NULL
                     }
                 };
                 BOOL success;
                 success = StartServiceCtrlDispatcher(serviceTable);
                 if (!success)
                 {
                     ErrorHandler("In StartServiceCtrlDispatcher",GetLastError());
                 }
                 return 0;
            }
            StartServiceCtrlDispatcher的參數(shù)必須是一個(gè)以NULL結(jié)尾的數(shù)組指針,我們可以在一個(gè)程序文件中注冊(cè)多個(gè)服務(wù)實(shí)例,只在把所要注冊(cè)的服務(wù)名和服務(wù)入口函數(shù)地址寫(xiě)到數(shù)組中即可,在我們調(diào)用CreateService創(chuàng)建服務(wù)時(shí)要把dwServiceType 參數(shù)設(shè)為共享進(jìn)程(SERVICE_WIN32_SHARE_PROCESS);不過(guò)當(dāng)要?jiǎng)?chuàng)建獨(dú)立進(jìn)程的服務(wù)時(shí)(dwServiceType 參數(shù)為SERVICE_WIN32_OWN_PROCESS時(shí))在這里就只能注冊(cè)一個(gè)服務(wù)實(shí)例。
            (2)服務(wù)入口函數(shù)ServerMain()
            VOID ServiceMain(DWORD argc,LPTSTR *argv)
            {
                 BOOL success;
                 StatusHandler=
                     RegisterServiceCtrlHandler(SERVICE_NAME,(LPHANDLER_FUNCTION)Handler);
                 if (!serviceStatusHandler)
                 {
                     return;
                 }
                 success = ReportStatus (SERVICE_START_PENDING,
                                             NO_ERROR,0,1,5000);
                 if (!success)
                 {
                     return; 
                 }
                 endEvent = CreateEvent(0,TRUE,FALSE,0);
                
                 if (!endEvent)
                 {
                     return;
                 }
                 success = ReportStatus (SERVICE_START_PENDING,
                                             NO_ERROR,0,2,5000);
                 if (!success)
                 {
                     return;
                 }
                //////init parameter start
                 RecvParam(argc,argv);
                 //////init parameter end
                success = ReportStatus (SERVICE_START_PENDING,
                                             NO_ERROR,0,3,5000);
                 if (!success)
                 {
                     return;
                 }
                 success = InitService();
                
                 if (!success)
                 {
                     return;
                 }
                 success = ReportStatus (SERVICE_RUNNING,NO_ERROR,0,0,0);
                 if (!success)
                 {
                     return;
                 }
                 WaitForSingleObject(endEvent,INFINITE);
            }
                   RegisterServiceCtrlHandler完成Handler函數(shù)的注冊(cè)( Handler函數(shù)的具體實(shí)現(xiàn)我們?cè)诘谌」?jié)中介紹),它的第一個(gè)參數(shù)是調(diào)用CreateService創(chuàng)建服務(wù)時(shí)lpServiceName指向的名服務(wù)名稱(chēng),每二個(gè)參數(shù)是Handler函數(shù)的地址;函數(shù)名可以自由命名ReportStatus是向SCM報(bào)告服務(wù)當(dāng)前狀態(tài)的一個(gè)自定義函數(shù)。它內(nèi)部調(diào)用SetServiceStatus向SCM報(bào)告服務(wù)的當(dāng)狀態(tài),此函數(shù)有兩個(gè)參數(shù)第一個(gè)就是RegisterServiceCtrlHandler完成時(shí)返回的SERVICE_STATUS_HANDLE,第二個(gè)參數(shù)是一個(gè)SERVICE_STATUS變量的指針,它指示了服務(wù)當(dāng)前的狀態(tài)信息;當(dāng)注冊(cè)完Handler函數(shù)后向SCM報(bào)告一下自己當(dāng)前的狀態(tài)(正在啟動(dòng))。接著創(chuàng)建endEvent事件對(duì)像,它是當(dāng)我們收到SCM的退出控制代碼時(shí)通知服務(wù)主函數(shù)退出的,大家可看ServiceMain的最后一句。下面又是向SCM報(bào)告自己正在啟動(dòng),當(dāng)初始化所花費(fèi)的時(shí)間非常短時(shí)這樣做并不是必須的,但如果很長(zhǎng)就必須這樣做。RecvParam(argc,argv)使用了ServiceMain函數(shù)的兩個(gè)參數(shù),大家可以看出ServiceMainmain有著一樣的形參;說(shuō)明ServiceMainmain一樣可以接收配置參數(shù),稍后我們會(huì)在服務(wù)控制程序的編寫(xiě)中給大家介紹如何給服務(wù)配置參數(shù)。InitService()完成我們的業(yè)務(wù)初始化工作并開(kāi)始業(yè)務(wù)處理。最后報(bào)告服務(wù)啟動(dòng)完成,等待endEvent事件退出服務(wù)。下面我們?cè)賮?lái)看一下SCM控制消息的處理。
            (3)SCM控制消息處理(Handler函數(shù))
            VOID Handler(DWORD controlCode)
            {
                 DWORD currentState = 0;
                 BOOL success;
                 switch(controlCode)
                 {
                 case SERVICE_CONTROL_STOP:
                     success=ReportStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,5000);
                     CloseTask();
                     success=ReportStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);
                     return;
                 case SERVICE_CONTROL_PAUSE:
                     if (runningService&&!pauseService)
                     {
                          success=ReportStatus(SERVICE_PAUSE_PENDING,NO_ERROR,0,1,1000);
                          pauseService=TRUE;
                          ServicePause();
                          currentState=SERVICE_PAUSED;
                     }
                     break;
                 case SERVICE_CONTROL_CONTINUE:
                     if (runningService&&pauseService)
                     {
                          success = ReportStatus(SERVICE_CONTINUE_PENDING,
                                                      NO_ERROR,0,1,1000);
                          pauseService = FALSE;
                          ServiceContinue();
                          currentState=SERVICE_RUNNING;
                     }
                     break;
                 case SERVICE_CONTROL_INTERROGATE://檢索更新?tīng)顟B(tài)的時(shí)
                     break;
                 case SERVICE_CONTROL_SHUTDOWN://告訴服務(wù)即將關(guān)機(jī)
                     success=ReportStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,5000);
                     CloseTask();
                     return;
                 default:
                     break;
                 }
                 ReportStatus(currentState,NO_ERROR,0,0,0);
            }
            Handler只有一個(gè)參數(shù)就是SCM傳來(lái)的控制消息代碼;這里處理的了停止,暫停,繼續(xù),更新,關(guān)機(jī)五個(gè)控制消息。但并不是這五個(gè)消息SCM都會(huì)向服務(wù)發(fā)送,要在向服務(wù)報(bào)告狀時(shí)向SCM報(bào)告自己可以響應(yīng)的控制消息,只要設(shè)置SERVICE_STATUS結(jié)構(gòu)中的dwControlsAccepted域即可,它對(duì)應(yīng)的值有:SERVICE_ACCEPT_STOP,SERVICE_ACCEPT_PAUSE_CONTINUE,SERVICE_ACCEPT_SHUTDOWN,當(dāng)要設(shè)置多個(gè)時(shí)只要把宏相或(|)傳給dwControlsAccepted域即可。在響應(yīng)SCM控制消息時(shí)也要注意及時(shí)報(bào)告服務(wù)當(dāng)前的狀態(tài)信息,否則SCM會(huì)認(rèn)為服務(wù)響應(yīng)超時(shí)出錯(cuò)了。
            (4)服務(wù)的安裝與卸載
                服務(wù)程序編寫(xiě)完成并編譯通過(guò)后,還要安裝注冊(cè)到操作系統(tǒng)中,這樣它才會(huì)出現(xiàn)在管理工具->服務(wù),那個(gè)管理器里面。API給我們提供了一個(gè)函數(shù)來(lái)實(shí)現(xiàn)我們注冊(cè)服務(wù)的功能; SC_HANDLE CreateService(
             SC_HANDLE hSCManager// 服務(wù)控制管理器的句柄 
              LPCTSTR lpServiceName, // 指向服務(wù)的內(nèi)部名稱(chēng)
             LPCTSTR lpServiceName,, // 指向服務(wù)的顯示名稱(chēng)
             DWORD dwDesiredAccess, // 服務(wù)的訪(fǎng)問(wèn)類(lèi)型
             DWORD dwServiceType,   // 服務(wù)的類(lèi)型
             DWORD dwStartType,     // 服務(wù)的啟動(dòng)方式(自動(dòng),手動(dòng),禁用)
             DWORD dwErrorControl// 錯(cuò)誤控制方式
             LPCTSTR lpBinaryPathName// 服務(wù)程序的路徑
             LPCTSTR lpLoadOrderGroup// 服務(wù)組的名稱(chēng) 
              LPDWORD lpdwTagId,     // 服務(wù)的標(biāo)簽號(hào)
             LPCTSTR lpDependencies// 服務(wù)依賴(lài)的服務(wù)或組名
             LPCTSTR lpServiceStartName, // 服務(wù)的啟動(dòng)帳戶(hù)
             LPCTSTR lpPassword       // 服務(wù)啟動(dòng)帳戶(hù)的密碼
            );
            hSCManager這是函數(shù)的第一個(gè)參數(shù)-SCM的句柄。它要調(diào)用OpenSCManager來(lái)獲得,稍后我們會(huì)講它怎么調(diào)用方法。
            lpServiceNamelpServiceName分別的服務(wù)的名稱(chēng)和服務(wù)的顯示名稱(chēng),服務(wù)是顯示名稱(chēng)就服務(wù)管理器中看到的那個(gè)服務(wù)名。則是服務(wù)在SCM中注冊(cè)的名稱(chēng),比如調(diào)用OpenService打開(kāi)服務(wù)時(shí)就會(huì)用到它。
            dwDesiredAccess標(biāo)出服務(wù)同意請(qǐng)求的訪(fǎng)問(wèn),可以是下面任意任值:
            SERVICE_ALL_ACCESS
            SERVICE_CHANGE_CONFIG
            SERVICE_ENUMERATE_DEPENDENTS
            SERVICE_INTERROGATE
            SERVICE_PAUSE_CONTINUE
            SERVICE_QUERY_CONFIG
            SERVICE_QUERY_STATUS
            SERVICE_START
            SERVICE_STOP
            SERVICE_USER_DEFINED_CONTROL
            我們可以指定一個(gè)或多個(gè),如果有多個(gè)的話(huà)要用或符號(hào)(|)聯(lián)結(jié)起來(lái)。
            dwServiceType注冊(cè)服務(wù)的類(lèi)型,它必須是下面的值:SERVICE_WIN32_OWN_PROCESS
            SERVICE_WIN32_SHARE_PROCESS
            SERVICE_KERNEL_DRIVER
            SERVICE_FILE_SYSTEM_DRIVER如果指定的是SERVICE_WIN32_OWN_PROCESS類(lèi)型的服務(wù)還可以加上SERVICE_WIN32_OWN_PROCESS(允許用戶(hù)桌面交互),我們這里介紹的服務(wù)只能注冊(cè)為SERVICE_WIN32_OWN_PROCESS或SERVICE_WIN32_SHARE_PROCESS;另兩種類(lèi)型是驅(qū)動(dòng)級(jí)的服務(wù)用的,有興趣大家可查看相關(guān)資料。
            dwStartType服務(wù)的啟動(dòng)類(lèi)型SERVICE_BOOT_START、SERVICE_SYSTEM_START、SERVICE_AUTO_START、SERVICE_DEMAND_START、SERVICE_DISABLED。分別為前兩種類(lèi)型僅對(duì)驅(qū)動(dòng)程序用效,所在我們這里所說(shuō)的這類(lèi)服務(wù)能后三種(自動(dòng),手動(dòng),禁用)。
            dwErrorControl服務(wù)的錯(cuò)誤控制標(biāo)記
            SERVICE_ERROR_IGNORE:忽略所有錯(cuò)誤
            SERVICE_ERROR_NORMAL:正常報(bào)告服務(wù)返回的錯(cuò)誤
            SERVICE_ERROR_SEVERE:當(dāng)服務(wù)返回錯(cuò)誤出現(xiàn)時(shí),如果最后已知好控制集(最后已知好控制集:是系統(tǒng)最后一次成功引導(dǎo)時(shí)使用的服務(wù)注冊(cè)表配置)尚未使用,則重新引導(dǎo)進(jìn)入最后已知好控制集,否則重新引導(dǎo)。
            SERVICE_ERROR_CRITICAL:當(dāng)服務(wù)返回錯(cuò)誤出現(xiàn)時(shí),如果最后已知好控制集尚未使用,則重新引導(dǎo)進(jìn)入最后已知好控制集,否則藍(lán)屏崩潰。
            lpBinaryPathName服務(wù)程序的文件路徑
            lpLoadOrderGroup服務(wù)所屬的組
            lpdwTagId在組中的唯一標(biāo)識(shí)
            lpDependencies服務(wù)所依賴(lài)的其它組和服務(wù)
            lpServiceStartName和lpPassword服務(wù)由哪個(gè)用戶(hù)啟動(dòng),也即服務(wù)運(yùn)行在哪個(gè)用戶(hù)權(quán)限下,分別指定用戶(hù)名和密碼.
                   下再說(shuō)兩重要的函數(shù):OpenSCManagerCloseServiceHandle給出它們的原型
            SC_HANDLE OpenSCManager(
             LPCTSTR lpMachineName// 機(jī)器名,打開(kāi)本機(jī)的SCM時(shí)可為NULL
             LPCTSTR lpDatabaseName// 指向SCM數(shù)據(jù)庫(kù)的名字可為NULL
             DWORD dwDesiredAccess   // 訪(fǎng)問(wèn)權(quán)限類(lèi)型如:SC_MANAGER_ALL_ACCESS
            );
            BOOL CloseServiceHandle(
             SC_HANDLE hSCObject   // 服務(wù)控制句柄 
            );
                   這果列出一段注冊(cè)服務(wù)的代碼供大家參考:
            SC_HANDLE newService,scm;
            BOOL success = FALSE;
            SERVICE_STATUS status;
            scm = OpenSCManager(NULL,NULL,
            SC_MANAGER_ENUMERATE_SERVICE|SC_MANAGER_CREATE_SERVICE);
                 if (!scm){
                     OUT_DEBUG("OpenSCManager ERROR!");
                     return false;
                 }
            newService = CreateService(scm,pszServiceName,pszDisplayName,
                     SERVICE_ALL_ACCESS|SERVICE_STOP,SERVICE_WIN32_OWN_PROCESS,
                     SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,pszServicePath,
                     0,0,0,0,0);
                 if (!newService){
                     OUT_DEBUG("CreateService ERROR!");
                     CloseServiceHandle(scm);
                     return false;
                 }
            CloseServiceHandle(newService);
            CloseServiceHandle(scm);
            return true;
                   刪除服務(wù)時(shí)調(diào)用DeleteService;它只有一個(gè)參數(shù)(服務(wù)句柄)。我們可分四步完成1)、打開(kāi)SCM句柄。2)、打開(kāi)要?jiǎng)h除的服務(wù)。3)、檢查當(dāng)前服務(wù)的狀態(tài)確保服務(wù)已經(jīng)停止。4)、刪除服務(wù)并關(guān)閉所有打開(kāi)的句柄。下面是一段刪除服務(wù)的程序。
            SC_HANDLE Service,scm;
                 SERVICE_STATUS status;
                 BOOL success;
                 if (pszServiceName==NULL)
                 {
                     return false;
                 }
                 scm = OpenSCManager(NULL,NULL,SC_MANAGER_ENUMERATE_SERVICE);
                 if (!scm){
                     cout<<"OpenSCManager ERROR:"<<GetLastError()<<endl;
                     CloseServiceHandle(scm);
                     return false;
                 }
                 Service = OpenService(scm,pszServiceName,SERVICE_ALL_ACCESS|DELETE);
                 if (!Service){
                     cout<<"OpenService ERROR:"<<GetLastError()<<endl;
                     CloseServiceHandle(Service);
                     CloseServiceHandle(scm);
                     return false;
                 }
                 success = QueryServiceStatus(Service,&status);
                 if (!success){
                     cout<<"QueryServiceStatus ERROR:"<<GetLastError()<<endl;
                     CloseServiceHandle(Service);
                     CloseServiceHandle(scm);
                     return false;
                 }
                 if (status.dwCurrentState!=SERVICE_STOPPED)
                 {
                     success = ControlService(Service,SERVICE_CONTROL_STOP,&status);
                     if (!success){
                          cout<<"ControlService ERROR:"<<GetLastError()<<endl;
                          CloseServiceHandle(Service);
                          CloseServiceHandle(scm);
                          return false;
                     }
                 }
                 success = DeleteService(Service);
                 if (!success){
                     cout<<"DeleteService ERROR:"<<GetLastError()<<endl;
                     CloseServiceHandle(Service);
                     CloseServiceHandle(scm);
                     return false;
                 }
                 CloseServiceHandle(Service);
                 CloseServiceHandle(scm);
                 return true;
            4        服務(wù)控制程序
            4.1    服務(wù)控制程序概要
            在上面我們了解服務(wù)程序的編寫(xiě),現(xiàn)在我們來(lái)看一下控制服務(wù)時(shí)會(huì)用到的幾個(gè)常用API。服務(wù)控制程序的編寫(xiě)與標(biāo)準(zhǔn)的Windows應(yīng)用程序無(wú)異,它要用到服務(wù)管理函數(shù),如:OpenSCManagerOpenServiceQueryServiceConfigStartServiceQueryServiceStatusControlService等;它都在系統(tǒng)的advapi32.dll中實(shí)現(xiàn)。在使用SCM的函數(shù)時(shí),SCP必須要首先調(diào)用OpenSCManager函數(shù),打開(kāi)一個(gè)通向SCM的通道。調(diào)用這個(gè)函數(shù)的時(shí)候,SCP還必須指定它想要執(zhí)行的動(dòng)作類(lèi)型;也就是我們上一節(jié)所提到的dwDesiredAccess參數(shù)它的取值:SC_MANAGER_ALL_ACCESS、SC_MANAGER_CREATE_SERVICE、SC_MANAGER_ENUMERATE_SERVICE、 SC_MANAGER_QUERY_LOCK_STATUS、SC_MANAGER_ENUMERATE_SERVICE 、SC_MANAGER_QUERY_LOCK_STATUS、 SC_MANAGER_LOCK、SC_MANAGER_CONNECT。例如:我們要枚舉當(dāng)前所有的服務(wù)就必須給dwDesiredAccess參數(shù)指定SC_MANAGER_ENUMERATE_SERVICE;同時(shí)也可以指定其它的值,當(dāng)指寫(xiě)多個(gè)值時(shí)我們要把它用按位或(|)符號(hào)連接起來(lái)。
            與此同時(shí)我們?cè)谡{(diào)用OpenService時(shí)也必須告知SCM我們要對(duì)服務(wù)進(jìn)行的動(dòng)作;它有三個(gè)參數(shù),最后一個(gè)參數(shù)dwDesiredAccess指出要對(duì)服務(wù)進(jìn)行的操作,這些操作的標(biāo)記與CreateService中的dwDesiredAccess參數(shù)標(biāo)記值一樣。當(dāng)我們以SERVICE_ALL_ACCESS訪(fǎng)問(wèn)權(quán)限打開(kāi)服務(wù)后就可以對(duì)它進(jìn)行配置(QueryServiceConfig)、控制(ControlService)、查詢(xún)狀態(tài)(QueryServiceStatus)、設(shè)置狀態(tài)(SetServiceStatus)、刪除(DeleteService)等所有訪(fǎng)問(wèn)操作。下面我們可以來(lái)看兩服務(wù)控制的實(shí)例。
            4.2    枚舉服務(wù)
            我們先來(lái)看EnumDependentStatus函數(shù)原型:
            BOOL EnumServicesStatus (
             SC_HANDLE hService,      // SCM控制句柄
            DWORD dwServiceType,   //要枚舉服務(wù)還有驅(qū)動(dòng)
             DWORD dwServiceState,    // 要枚舉什么狀態(tài)的服務(wù)
             LPENUM_SERVICE_STATUS lpServices,// 存儲(chǔ)枚舉出服務(wù)的內(nèi)存地址
             DWORD cbBufSize,         // lpServices指向內(nèi)存區(qū)大小
             LPDWORD pcbBytesNeeded//實(shí)際需要的內(nèi)存大小
             LPDWORD lpServicesReturned //返回枚舉到服務(wù)年個(gè)數(shù)
            LPDWORD lpResumeHandle //指向下一個(gè)有效的入口
            );
            dwServiceState參數(shù)由:SERVICE_ACTIVE、SERVICE_INACTIVE、SERVICE_STATE_ALL三種值分別枚舉當(dāng)前活動(dòng)、不活動(dòng)、全部的服務(wù)。函數(shù)會(huì)返回一個(gè)ENUM_SERVICE_STATUS數(shù)組,ENUM_SERVICE_STATUS有三個(gè)域分別指出服務(wù)的服務(wù)名、顯示名和當(dāng)前狀態(tài)一個(gè)指針;我們可以根據(jù)服務(wù)名打開(kāi)枚舉出的服務(wù),以得到它更加詳細(xì)的信息。下面具體讓我們看一段程序的例子。
            //清空服務(wù)信息隊(duì)列
            DeletItemAll();
             
            LPENUM_SERVICE_STATUS st=NULL;
            st=NULL;
            DWORD ret=0;
            DWORD size=0;
            ServiceInfo info;
            SC_HANDLE sc=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
            SC_HANDLE sh;
            char* szInfo[1024*8];
            DWORD dwSize=1024*8;
            CString str;
            //第一次調(diào)用來(lái)得到需要多大的內(nèi)存區(qū)
            EnumServicesStatus(sc,SERVICE_WIN32,SERVICE_STATE_ALL,st,size,&size,&ret,NULL);
            //申請(qǐng)需要的內(nèi)存
            st=(LPENUM_SERVICE_STATUS)LocalAlloc(LPTR,size);
            EnumServicesStatus(sc,SERVICE_WIN32,SERVICE_STATE_ALL,st,size,&size,&ret,NULL);
            //開(kāi)始記錄枚舉出服務(wù)的信息
            for(DWORD i=0;i<ret;i++){
                 dwSize=1024*8;
                 ZeroMemory(szInfo,dwSize);
                 info.Name.Format("%s",st[i].lpDisplayName);
                 info.serviceNmae.Format("%s",st[i].lpServiceName);
                 info.State.Format("%d",st[i].ServiceStatus.dwCurrentState);
                 sh=OpenService(sc,st[i].lpServiceName,SERVICE_ALL_ACCESS);
                 //得到服務(wù)描述信息
                 QueryServiceConfig2(sh,SERVICE_CONFIG_DESCRIPTION,(LPBYTE)szInfo,dwSize,&dwSize);
                 info.Desc.Format("%s",((LPSERVICE_DESCRIPTION)szInfo)->lpDescription);
                 //得到服務(wù)的啟動(dòng)賬戶(hù)名
                 ZeroMemory(szInfo,dwSize);
                 dwSize=1024*8;
                 QueryServiceConfig(sh,(LPQUERY_SERVICE_CONFIG)szInfo,dwSize,&dwSize);
                 info.LoginUser.Format("%s",((LPQUERY_SERVICE_CONFIG)szInfo)->lpServiceStartName);
                 CloseServiceHandle(sh);
                 //添加到信息隊(duì)列中
                 ItemAdd(&info);
            }
            CloseServiceHandle(sc);
            return TRUE;
            上面程序中用到了兩個(gè)查詢(xún)服務(wù)當(dāng)前配置的函數(shù)QueryServiceConfig2和QueryServiceConfig。它們有所不同是QueryServiceConfig2可以通過(guò)設(shè)置第二個(gè)參數(shù)是SERVICE_CONFIG_DESCRIPTION還是SERVICE_CONFIG_FAILURE_ACTIONS來(lái)得到服務(wù)的描述信息和失敗的活動(dòng);而QueryServiceConfig則查詢(xún)返回一個(gè)QUERY_SERVICE_CONFIG結(jié)構(gòu),這個(gè)結(jié)構(gòu)存儲(chǔ)了服務(wù)的類(lèi)型、啟動(dòng)類(lèi)型、錯(cuò)誤控制標(biāo)記、服務(wù)文件所在路徑、顯示名等信息詳細(xì)可以查看MSDN。與這個(gè)兩函數(shù)相對(duì)應(yīng)還有兩個(gè)配置函數(shù)ChangeServiceConfig2和ChangeServiceConfig。它們的具體使用方法我們來(lái)看下面的這段程序。
            4.3    配置服務(wù)
            在3.2中我們舉了一個(gè)創(chuàng)建服務(wù)的程序片段,其中我們只是創(chuàng)建服務(wù)并未設(shè)置服務(wù)描述信息,啟動(dòng)服務(wù)的操作。下面的程序片段給出了示例:
            bool RegterService(char* pszServiceName,
                                  char* pszDisplayName,
                                  char* pszServicePath,
                                  char* pszDescription)
            {
                 SC_HANDLE newService,scm;
                 BOOL success = FALSE;
                 SERVICE_STATUS status;
                 SERVICE_DESCRIPTION description;
             
                 if (pszDisplayName==NULL&&pszServiceName==NULL&&pszServicePath==NULL)
                 {
                     return false;
                 }
                 description.lpDescription=pszDescription;
                 scm = OpenSCManager(NULL,NULL,SC_MANAGER_ENUMERATE_SERVICE|SC_MANAGER_CREATE_SERVICE);
                 if (!scm){
                     OUT_DEBUG("OpenSCManager ERROR!");
                     return false;
                 }
                 newService = CreateService(scm,pszServiceName,pszDisplayName,
                     SERVICE_ALL_ACCESS|SERVICE_STOP,SERVICE_WIN32_OWN_PROCESS,
                     SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,pszServicePath,
                     0,0,0,0,0);
                 if (!newService){
                     OUT_DEBUG("CreateService ERROR!");
                      CloseServiceHandle(scm);
                     return false;
                 }
                 if (description.lpDescription!=NULL)
                 {
                     success=ChangeServiceConfig2(newService,
                                   SERVICE_CONFIG_DESCRIPTION,
                                   &description);
                 }
                 success = QueryServiceStatus(newService,&status);
                 if (!success){
                     cout<<"QueryServiceStatus ERROR:"<<GetLastError()<<endl;
                     CloseServiceHandle(newService);
                     CloseServiceHandle(scm);
                     return false;
                 }
                 if (status.dwCurrentState!=SERVICE_RUNNING)
                 {
                     success = StartService(newService,NULL,NULL);
                     if (!success){
                          cout<<"ControlService ERROR:"<<GetLastError()<<endl;
                          CloseServiceHandle(newService);
                          CloseServiceHandle(scm);
                          return false;
                     }
                 }
                 CloseServiceHandle(newService);
                 CloseServiceHandle(scm);
                 return true;
            }
            ChangeServiceConfig函數(shù)可以配置更多關(guān)于服務(wù)的信息,下面列出其原型:
            BOOL ChangeServiceConfig(
             SC_HANDLE hService     // 打開(kāi)服務(wù)時(shí)返回的句柄
             DWORD dwServiceType,   // 服務(wù)的類(lèi)型
             DWORD dwStartType,     // 何時(shí)啟動(dòng)服務(wù)
             DWORD dwErrorControl// 錯(cuò)誤控制代碼
             LPCTSTR lpBinaryPathName// 服務(wù)的路徑
             LPCTSTR lpLoadOrderGroup// 服務(wù)所屬的組
             LPDWORD lpdwTagId,     // 服務(wù)的標(biāo)記
             LPCTSTR lpDependencies,    // 依賴(lài)的其它服務(wù)和組
             LPCTSTR lpServiceStartName,// 服務(wù)的啟動(dòng)用戶(hù)
             LPCTSTR lpPassword,   //服務(wù)啟動(dòng)用戶(hù)的密碼
             LPCTSTR lpDisplayName      // 服務(wù)的顯示名
            );
            大家可以看到ChangeServiceConfigCreateServiceee 有著相似的參數(shù),它們的使用方法也十分相似可以參照CreateServiceee函數(shù)調(diào)用。它主要在服務(wù)安裝完成后,需要對(duì)服務(wù)的配置進(jìn)行修改時(shí)調(diào)用,除了lpDisplayName改變時(shí)需要服務(wù)停止才能生效外,其它都可以運(yùn)行時(shí)動(dòng)態(tài)改變;更詳細(xì)信息可查閱MSDN。
            4.4     控制服務(wù)
            有時(shí)我們要根據(jù)實(shí)際情況啟動(dòng)、暫停、停止一個(gè)服務(wù)。在4.3中的程序示例里面就一個(gè)啟動(dòng)服務(wù)的調(diào)用。這里我們?cè)俸?jiǎn)單介紹一下這個(gè)函數(shù):
            BOOL StartService(
             SC_HANDLE hService,            // 打開(kāi)服務(wù)時(shí)返回的句柄
             DWORD dwNumServiceArgs,        // 服務(wù)程序參數(shù)的個(gè)數(shù)
             LPCTSTR *lpServiceArgVectors   // 存放服務(wù)程序參數(shù)的數(shù)組 
            );
            當(dāng)服務(wù)程序需要配置啟動(dòng)參數(shù)時(shí)就需要使用StartService后面的兩個(gè)參數(shù),服務(wù)程序的入口函數(shù)也就可以接到相關(guān)的參數(shù)了。函數(shù)調(diào)用成功時(shí)返回非零,當(dāng)返回零時(shí)調(diào)用失敗;更詳細(xì)的出錯(cuò)信息可以調(diào)用GetLastError獲得。
                啟動(dòng)后如果我們還要控制其暫停、繼續(xù)、停止的話(huà),還需要另一個(gè)函數(shù)調(diào)用來(lái)完成。在上面刪除服務(wù)的例示程序片段中我們調(diào)用了ControlService函數(shù)來(lái)停止服務(wù),下面我們介紹一下它的詳細(xì)信息:
            BOOL ControlService(
             SC_HANDLE hService// 打開(kāi)服務(wù)時(shí)返回的句柄
             DWORD dwControl,     // 控制代碼
             LPSERVICE_STATUS lpServiceStatus // 服務(wù)的狀態(tài)
            );
            調(diào)用此函數(shù)會(huì)把控制代碼發(fā)給指定服務(wù)程序的Handler處理函數(shù)同時(shí)返回服務(wù)的狀態(tài),服務(wù)程序得到相應(yīng)的控制代碼后根據(jù)協(xié)議要執(zhí)行相應(yīng)的操作;控制代碼就是Handler規(guī)定響應(yīng)的所有代碼。我們不能啟動(dòng)和停止服務(wù)安全描述符不允許的服務(wù)程序。默認(rèn)的安全描述符只允許LocalSystem、 Administrators和 Power Users來(lái)啟動(dòng)和停止服務(wù)程序。服務(wù)的安全描述符來(lái)用SetServiceObjectSecurity來(lái)設(shè)置(更詳細(xì)信息的信息可查閱MSDN)。
            5        結(jié)束語(yǔ)
            我們?cè)谶@里從總體上講述了SCM的工作流程序、服務(wù)程序編寫(xiě)的方法及控制服務(wù)所用到的一些函數(shù)。文中只列出了一部分函數(shù)的信息,更詳細(xì)的信息大家可以查閱MSDN。希望本文講述的內(nèi)容能幫助大家理解服務(wù)程序的編寫(xiě)與控制。

            原文地址:http://www.cnblogs.com/xuyuan77/archive/2008/03/28/1127941.html

            posted on 2013-12-17 17:13 天道酬勤 閱讀(935) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): windows編程

            久久久噜噜噜久久熟女AA片| 2021国产成人精品久久| 亚洲色婷婷综合久久| 亚洲中文字幕无码一久久区| 亚洲国产美女精品久久久久∴ | 日本人妻丰满熟妇久久久久久| 99久久国产宗和精品1上映 | 久久久久国产精品嫩草影院| 久久久国产精品| 99精品国产免费久久久久久下载| 性欧美丰满熟妇XXXX性久久久 | 久久久久无码精品国产不卡| 久久精品国产只有精品2020| 精品久久久久久无码中文字幕 | 国产亚洲精久久久久久无码AV| 久久天天躁狠狠躁夜夜不卡 | 亚洲国产日韩欧美久久| 精品人妻伦九区久久AAA片69| 久久久久久国产精品无码超碰| 夜夜亚洲天天久久| 香蕉久久夜色精品国产2020| 日本欧美久久久久免费播放网| 亚洲国产成人久久精品动漫| 模特私拍国产精品久久| 999久久久无码国产精品| 久久久久噜噜噜亚洲熟女综合| 亚洲国产欧美国产综合久久| a级毛片无码兔费真人久久| 久久无码AV一区二区三区| 狠狠色丁香婷综合久久| 欧美午夜A∨大片久久 | 国产亚州精品女人久久久久久| 影音先锋女人AV鲁色资源网久久| 久久久久久狠狠丁香| 久久久久久精品免费看SSS| 91精品免费久久久久久久久| 国内高清久久久久久| 国内精品久久久久久不卡影院| 人妻少妇久久中文字幕一区二区| 久久久久国产视频电影| 成人国内精品久久久久影院|