青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 14, 文章 - 0, 評論 - 3, 引用 - 0
數(shù)據(jù)加載中……

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

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

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

posted on 2013-12-17 17:13 天道酬勤 閱讀(956) 評論(0)  編輯 收藏 引用 所屬分類: windows編程


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


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲欧美亚洲| 国产麻豆精品theporn| 亚洲精选中文字幕| 欧美成人午夜77777| 麻豆精品在线播放| 欧美成在线视频| 91久久国产精品91久久性色| 欧美韩日一区二区三区| 亚洲日本va午夜在线影院| 亚洲六月丁香色婷婷综合久久| 亚洲美女av电影| 亚洲永久免费观看| 久久久亚洲成人| 欧美日韩18| 国产日产欧美a一级在线| 精品999久久久| 国产精品99久久久久久宅男| 久久精品国产精品亚洲| 亚洲激情第一区| 亚洲欧美日韩人成在线播放| 榴莲视频成人在线观看| 国产精品久久久久久久一区探花| 激情一区二区| 亚洲欧美成人在线| 老司机精品久久| 亚洲无限乱码一二三四麻| 久久深夜福利免费观看| 欧美三区美女| 亚洲区欧美区| 久久久久久久网站| 夜夜夜精品看看| 免费成人高清| 国内精品视频在线观看| 一区二区电影免费观看| 久久亚洲色图| 性感少妇一区| 国产精品久久久久久久久久直播 | 国产精品日韩专区| 黄色小说综合网站| 亚洲人www| 久久久久久久久蜜桃| 亚洲一区二区三区免费视频| 欧美四级剧情无删版影片| 国产综合自拍| 欧美一级理论片| 国产欧美一区二区三区久久| 亚洲欧洲精品一区二区三区波多野1战4 | 亚洲在线视频网站| 欧美久久久久久| 亚洲黄色小视频| 欧美成人精品一区二区三区| 欧美成人午夜剧场免费观看| 在线视频欧美精品| 欧美成人免费网站| 亚洲国产另类精品专区| 久久综合九色综合欧美狠狠| 亚洲欧美一区二区视频| 国产精品一区二区三区四区五区 | 亚洲国产成人精品女人久久久| 午夜欧美不卡精品aaaaa| 亚洲免费不卡| 欧美日韩直播| 亚洲欧美日韩国产综合精品二区| 一区二区欧美日韩| 国产精品―色哟哟| 欧美与欧洲交xxxx免费观看| 午夜精品久久久久久久白皮肤| 国产精品女主播一区二区三区| 亚洲欧美国产另类| 午夜视频精品| 在线日韩av片| 亚洲精品国产精品国产自| 欧美另类综合| 欧美一区二区在线免费播放| 久久精品国产清高在天天线 | 国产精品日韩欧美| 久久www免费人成看片高清| 久久九九免费| 亚洲第一页自拍| 亚洲精选在线| 国产精品免费网站在线观看| 久久久久久久999精品视频| 亚洲字幕一区二区| 亚洲一区二区三区在线看| 国产精品h在线观看| 亚洲欧美不卡| 久久久国产精品一区| 在线观看欧美视频| 亚洲精品综合精品自拍| 国产精品一区二区三区乱码| 免费观看欧美在线视频的网站| 欧美a级片网| 午夜精品久久99蜜桃的功能介绍| 先锋资源久久| 99热在线精品观看| 亚洲一区二区免费看| 久久精品亚洲乱码伦伦中文| 国产亚洲成av人在线观看导航| 亚洲欧洲综合另类在线| 亚洲欧美综合另类中字| 亚洲欧美日韩精品一区二区| 亚洲福利视频三区| 亚洲午夜精品17c| 伊人成人开心激情综合网| 亚洲精品影视在线观看| 国内久久婷婷综合| 亚洲免费av网站| 亚洲第一搞黄网站| 亚洲综合色丁香婷婷六月图片| 亚洲国产精品成人一区二区| 亚洲一区综合| 一区二区三区鲁丝不卡| 久久中文字幕一区二区三区| 欧美专区亚洲专区| 欧美午夜精彩| 亚洲黄色一区| 亚洲国产黄色片| 久久久综合激的五月天| 久久国产视频网| 国产精品美女www爽爽爽视频| 亚洲国产精品99久久久久久久久| 国产亚洲观看| 新狼窝色av性久久久久久| 亚洲综合色丁香婷婷六月图片| 欧美极品在线播放| 亚洲高清av| 亚洲欧洲另类| 欧美成人亚洲成人| 欧美刺激午夜性久久久久久久| 国内精品免费在线观看| 欧美一区国产二区| 久久精品午夜| 国产一区二区日韩精品欧美精品| 亚洲一区欧美激情| 午夜精品一区二区三区四区| 国产精品久久久久久久久久久久久久| 亚洲精品一二三区| 国产精品99久久久久久www| 欧美日韩一区二区三区在线视频| 亚洲精品中文字幕在线| 亚洲日韩第九十九页| 亚洲小视频在线| 国产精品视频免费观看| 99re热这里只有精品视频 | 激情亚洲成人| 国产精品久久九九| 99亚洲伊人久久精品影院红桃| 一本色道久久综合亚洲精品高清| 欧美肥婆bbw| 亚洲精品久久久久久下一站| 亚洲视频免费在线| 国产毛片久久| 久久九九精品| 亚洲人成小说网站色在线| 99精品视频免费在线观看| 欧美视频日韩视频在线观看| 亚洲特黄一级片| 久久经典综合| 亚洲人成人77777线观看| 欧美—级a级欧美特级ar全黄| 一本色道久久综合亚洲精品不| 欧美一区二区三区四区在线观看地址| 国产一区二区高清视频| 久久综合网络一区二区| 99热在这里有精品免费| 久久免费视频在线观看| 日韩亚洲欧美成人| 国产精品视频一| 免费不卡视频| 亚洲欧美综合v| 亚洲国产精品成人综合色在线婷婷| 亚洲五月六月| 一区二区在线视频播放| 欧美日韩亚洲一区二区三区在线观看| 亚洲一区二区在| 亚洲国产导航| 久久久久久伊人| 中文精品视频| 亚洲大胆女人| 国产欧美日韩综合一区在线播放 | 国产真实精品久久二三区| 免费久久精品视频| 亚洲欧美日韩在线观看a三区| 欧美风情在线| 久久激情视频免费观看| 中文亚洲免费| 亚洲精品国精品久久99热一| 国产美女高潮久久白浆| 欧美日韩国产色综合一二三四 | 99re视频这里只有精品| 欧美成人免费小视频| 久久国产成人| 亚洲欧美第一页| 亚洲婷婷综合久久一本伊一区| 亚洲丶国产丶欧美一区二区三区| 国产精品狼人久久影院观看方式| 欧美片在线观看| 欧美高清视频一区二区三区在线观看| 午夜精品福利视频| 亚洲少妇最新在线视频|