windows服務管理操作

來源:互聯網
上載者:User

標籤:

服務程式是windows上重要的一類程式,它們雖然不與使用者進行介面互動,但是它們對於系統有著重要的意義。windows上為了管理服務程式提供了一個特別的程式:服務控制管理程式,系統上關於服務控制管理的API基本上都與這個程式打交道。下面通過對服務程式的操作來說明這些API函數

擷取系統服務的資訊

在windows系統中有專門用來儲存服務資訊的資料庫,而擷取系統服務資訊主要是通過在這樣的資料庫中尋找。所用到的函數主要有:

OpenSCManager:開啟資料庫
SC_HANDLE WINAPI OpenSCManager(  __in          LPCTSTR lpMachineName,  __in          LPCTSTR lpDatabaseName,  __in          DWORD dwDesiredAccess);

這個函數主要用來串連特定電腦上的服務控制管理員,並開啟服務控制管理員的資料庫。
函數的參數有:
lpMachineName:主機名稱
lpDatabaseName:主機中服務資料庫的名稱
dwDesiredAccess:以何種許可權開啟服務程式
前兩個參數都可以為NULL,如果第一個參數為NULL,則表示在本機上擷取,第二個參數為NULL表示從註冊表中擷取,第三個參數的主要傳入如下值:
SC_MANAGER_ALL_ACCESS (0xF003F) :預設擁有所有許可權
SC_MANAGER_CREATE_SERVICE (0x0002):具有建立服務的許可權
SC_MANAGER_CONNECT (0x0001):串連的權利
SC_MANAGER_ENUMERATE_SERVICE (0x0004) 枚舉裡面資訊的許可權
後面的就不再一一說明了,詳細資料請看MSDN的記錄。在程式中為了方便一般採用SC_MANAGER_ALL_ACCESS 參數
函數如果調用成功,則會返回一個操作資料庫的控制代碼,以後的關於服務的操作都已這個參數作為第一個參數。

EnumServicesStatus:枚舉系統服務
BOOL WINAPI EnumServicesStatus(  __in          SC_HANDLE hSCManager,  __in          DWORD dwServiceType,  __in          DWORD dwServiceState,  __out         LPENUM_SERVICE_STATUS lpServices,  __in          DWORD cbBufSize,  __out         LPDWORD pcbBytesNeeded,  __out         LPDWORD lpServicesReturned,  __in_out      LPDWORD lpResumeHandle);

hSCManager:服務資料庫控制代碼
dwServiceType:枚舉服務的類型,主要有:SERVICE_DRIVER(驅動類型服務)、SERVICE_WIN32(win32類型的服務)
dwServiceState:表示枚舉哪中狀態的服務,主要有:SERVICE_ACTIVE(已啟動的服務)、SERVICE_INACTIVE(未啟動的服務)、SERVICE_STATE_ALL(所有服務)
lpServices:這個參數主要是作為一個緩衝區,用來返回服務資訊,類型ENUM_SERVICE_STATUS主要儲存的是服務名稱、顯示名稱以及一個SERVICE_STATUS 結構體,該結構體的原型如下:

typedef struct _SERVICE_STATUS{      DWORD dwServiceType; //服務類型    DWORD dwControlsAccepted;//目前狀態    DWORD dwServiceSpecificExitCode;    DWORD dwCheckPoint;      DWORD dwWaitHint;} SERVICE_STATUS,  *LPSERVICE_STATUS;

cbBufSize:緩衝區的大小
pcbBytesNeeded:實際需要緩衝區的大小
lpServicesReturned:服務的傳回值
lpResumeHandle:額外的控制代碼
每一個ENUM_SERVICE_STATUS結構體儲存的是一個服務的資訊,但是我們事先並不知道有多少個服務,因此不知道該定義多大的服務資訊數組,但是windows考慮到了這一點,當函數調用失敗時利用GetLastError返回ERROR_MORE_DATA時表示提供的緩衝區不夠,這個時候參數pcbBytesNeeded會返回正確的大小,所以使用這個函數一般會經過兩個調用第一次lpServices = NULL, cbBufSize = 0,這個時候函數出錯並返回所需要的實際大小,然後根據大小動態分陪一個記憶體緩衝區或者提供一個數組,並傳入實際大小,以擷取所有服務的資訊。下面提供一個具體的例子:

    SC_HANDLE scHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);    if(NULL == scHandle)    {        return FALSE;    }    LPENUM_SERVICE_STATUS pServices = NULL;    DWORD dwByteNeed = 0;    DWORD dwServiceReturn = 0;    LPDWORD lpResumeHandle = NULL;    //第一次調用,將緩衝區設定為NULL並將緩衝區大小設定為0    BOOL  bRet = ::EnumServicesStatus(scHandle, SERVICE_WIN32, SERVICE_STATE_ALL, pServices, 0, &dwByteNeed, &dwServiceReturn, lpResumeHandle);       if(!bRet)    {    //如果是因為緩衝區大小不夠        if(ERROR_MORE_DATA == GetLastError())        {        //儲存緩衝區的真實大小            DWORD dwRealNeed = dwByteNeed;            //多分配一個是為了儲存字串末尾的0            pServices = (LPENUM_SERVICE_STATUS)new char[dwRealNeed + 1];            ASSERT(NULL != pServices);            ZeroMemory(pServices, dwRealNeed + 1);            bRet = ::EnumServicesStatus(scHandle, SERVICE_WIN32, SERVICE_STATE_ALL, pServices, dwRealNeed + 1, &dwByteNeed, &dwServiceReturn, lpResumeHandle);            //通過上述代碼可以擷取到服務的相關資訊        }    }
擷取服務的主程式所在路徑、啟動類型以及依賴項

上述代碼只能擷取到系統服務的部分資訊,比如服務的名稱,顯示名稱,等等至於其他的資訊需要調用另外的API函數擷取

OpenService擷取具體服務的控制代碼
SC_HANDLE WINAPI OpenService(  __in          SC_HANDLE hSCManager, //服務資料庫的控制代碼  __in          LPCTSTR lpServiceName,//服務的名稱  __in          DWORD dwDesiredAccess//以何種許可權開啟,為了方便一般填入SERVICE_ALL_ACCESS所有許可權);
QueryServiceConfig 查詢系統服務資訊
BOOL WINAPI QueryServiceConfig(  __in          SC_HANDLE hService,  __out         LPQUERY_SERVICE_CONFIG lpServiceConfig,  __in          DWORD cbBufSize,  __out         LPDWORD pcbBytesNeeded);

這個函數的第二個參數是一個結構體指標這個結構體的定義如下:

typedef struct _QUERY_SERVICE_CONFIG {      DWORD dwServiceType;  //服務類型    DWORD dwStartType;  //啟動類型    DWORD dwErrorControl;//錯誤碼,服務執行出錯時返回,作業系統根據這個錯誤碼來做相應的處理    LPTSTR lpBinaryPathName;//主程式所在路徑    LPTSTR lpLoadOrderGroup;    DWORD dwTagId;    LPTSTR lpDependencies; //依賴項    LPTSTR lpServiceStartName;    LPTSTR lpDisplayName; //顯示名稱} QUERY_SERVICE_CONFIG,  *LPQUERY_SERVICE_CONFIG;

這個函數的調用方式與EnumServicesStatus相同,也是通過兩次調用,第一次獲得所需的空間大小,這個大小通過第四個參數返回。
下面的代碼展示了如何調用這兩個函數

//第一個參數是通過OpenSCManager函數擷取得到的SC_HANDLE h_SCService = OpenService(h_SCHandle, pSrvItem->strSrvName, SERVICE_ALL_ACCESS);    if(NULL == h_SCService)    {        CloseServiceHandle(h_SCHandle);        return FALSE;    }    LPQUERY_SERVICE_CONFIG pSrvConfig = NULL;      DWORD dwBuffSize = 0;    DWORD dwBuffNeed = 0;    BOOL bRet = QueryServiceConfig(h_SCService, pSrvConfig, dwBuffSize, &dwBuffNeed);    if(!bRet)    {        if(ERROR_INSUFFICIENT_BUFFER == GetLastError())        {            DWORD dwRealNeed = dwBuffNeed;            pSrvConfig = (LPQUERY_SERVICE_CONFIG)new char[dwRealNeed + 1];            ASSERT(NULL != pSrvConfig);            bRet = QueryServiceConfig(h_SCService, pSrvConfig, dwRealNeed, &dwBuffNeed);        }    }
擷取服務的描述資訊

描述資訊一般是有服務開發人員提供,以便解釋服務程式的作用等等資訊,這些資訊在注入服務時由系統記錄,並呈現給使用者。擷取系統服務主要使用的API函數是QueryServiceConfig2

BOOL WINAPI QueryServiceConfig2(  __in          SC_HANDLE hService,  __in          DWORD dwInfoLevel,//將擷取何種資訊在這我們需要填寫SERVICE_CONFIG_DESCRIPTION表示擷取描述資訊  __out         LPBYTE lpBuffer,  __in          DWORD cbBufSize,  __out         LPDWORD pcbBytesNeeded);

這個函數不想上面的QueryServiceConfig一次可以擷取服務的多項資訊,它是根據第二個參數指定需要擷取哪項資訊,然後返回到第3個參數提供的緩衝區中,這個緩衝區是一個BYTE類型的指標,調用者需要根據具體的情況進行類型轉化。同樣這個函數需要進行兩次調用。

BOOL bRet = QueryServiceConfig2(h_SCService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpByte, cbBufSize, &dwBuffNeed);    if(!bRet)    {        if(ERROR_INSUFFICIENT_BUFFER == GetLastError())        {            DWORD dwRealNeed = dwBuffNeed;            //LPSERVICE_DESCRIPTION結構體中只儲存了一個字串指標lpDescription            lpByte = (LPSERVICE_DESCRIPTION)new char[dwRealNeed + 1];            ASSERT(NULL != lpByte);            bRet = QueryServiceConfig2(h_SCService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpByte, dwRealNeed, &dwBuffNeed);            if(!bRet)            {                delete[] lpByte;                goto __ERROR_RET;            }            pSrvItem->strSrvDescrible = lpByte->lpDescription;            delete[] lpByte;            CloseServiceHandle(h_SCService);            CloseServiceHandle(h_SCHandle);            return TRUE;        }    }__ERROR_RET:    CloseServiceHandle(h_SCService);    CloseServiceHandle(h_SCHandle);    return FALSE;
服務的控制

服務控制主要控制服務的啟動,暫停,恢複,停止等等。

StartService啟動服務
BOOL WINAPI StartService(  __in          SC_HANDLE hService,  __in          DWORD dwNumServiceArgs,//啟動參數的個數  __in          LPCTSTR* lpServiceArgVectors//參數列表指標);

這個函數有點類似於main函數,main函數可以傳遞命令列參數給程式,以便實現程式與使用者的互動,這裡同樣可以傳遞參數,以便服務完成特定的功能,當第二個參數為0時第三個參數為NULL

ControlService 控制服務
BOOL WINAPI ControlService(  __in          SC_HANDLE hService,  __in          DWORD dwControl,//新狀態  __out         LPSERVICE_STATUS lpServiceStatus//服務的原始狀態);

這個函數用來完成除了啟動之外的服務控制,其中第二個參數是服務的狀態,它可使用的參數主要有如下幾個:

取值 含義
SERVICE_CONTROL_CONTINUE 繼續運行
SERVICE_CONTROL_PAUSE 暫停
SERVICE_CONTROL_STOP 停止

其餘的部分不是很常用,故不在這裡一一列舉
下面是一個具體的例子,由於要考慮當前的狀態以及服務是否支援這種狀態,因此這部分的代碼多了許多判斷的部分

SERVICE_STATUS ServiceStatus = {0};//擷取當前的狀態    BOOL bRet = QueryServiceStatus(h_SCService, &ServiceStatus);    ASSERT(bRet);    if(ServiceStatus.dwCurrentState == dwNewStatus)    {        goto __RETURN;    }//如果當前服務正處在啟動和暫停之中,但是沒有完成這個動作時不允許改變服務狀態    if(ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING || SERVICE_PAUSE_PENDING ==ServiceStatus.dwCurrentState ||        SERVICE_START_PENDING == ServiceStatus.dwCurrentState || SERVICE_STOP_PENDING == ServiceStatus.dwCurrentState)    {        bRet = FALSE;        goto __RETURN;    }//如果服務處在暫停狀態,則只允許繼續運行和停止    if(SERVICE_PAUSED == ServiceStatus.dwCurrentState)    {        if(SERVICE_CONTROL_CONTINUE == dwNewStatus || SERVICE_CONTROL_STOP == dwNewStatus)        {            bRet = ControlService(h_SCService, dwNewStatus, &ServiceStatus);            goto __RETURN;        }else        {            bRet = FALSE;            goto __RETURN;        }    }//如果服務正在運行,則運行暫停和停止    if(SERVICE_RUNNING == ServiceStatus.dwCurrentState)    {        if(SERVICE_CONTROL_PAUSE == dwNewStatus || SERVICE_CONTROL_STOP == dwNewStatus)        {            bRet = ControlService(h_SCService, dwNewStatus, &ServiceStatus);            goto __RETURN;        }else        {            bRet = FALSE;            goto __RETURN;        }    }//如果服務處於停止狀態,則允許運行操作    if(SERVICE_STOPPED == ServiceStatus.dwCurrentState)    {        if(SERVICE_RUNNING == dwNewStatus)        {            bRet = StartService(h_SCService, 0, NULL);            goto __RETURN;        }else        {            bRet = FALSE;            goto __RETURN;        }    }__RETURN:    if(bRet)    {        pIter->pNode->dwCurrentStatus = dwNewStatus;    }    CloseServiceHandle(h_SCService);    CloseServiceHandle(h_SCHandle);    return bRet;

當前服務的狀態可以使用EnumServicesStatus函數擷取,但是這個函數是擷取系統中記錄的所有的服務程式,在這樣的情況下,我們只需要擷取一個服務的狀態,調用這個函數總有殺雞用牛刀的意思,所以採用的是另外一個函數QueryServiceStatus,服務程式的主要狀態有如下幾種:

取值 狀態原因
SERVICE_RUNNING 已啟動
SERVICE_STOPPED 已停止
SERVICE_PAUSED 已暫停
SERVICE_CONTINUE_PENDING 正在恢複
SERVICE_PAUSE_PENDING 正在暫停
SERVICE_START_PENDING 正在啟動
SERVICE_STOP_PENDING 正在停止

其中前幾個是完成時,也就是完成了從一個狀態到另一個的轉化,而後面幾個是進行時,正在這兩種狀態之間

擷取服務的可控類型

在控制服務時不光要考慮服務程式當前的狀態,還要考慮它是否支援這種狀態,比如有的服務就不支援暫停和恢複操作

QueryServiceStatus 查詢服務的狀態資訊

這個函數主要傳遞一個SERVICE_STATUS結構體的指標。這個結構體的定義如下:

typedef struct _SERVICE_STATUS {    DWORD dwServiceType;//服務類型    DWORD dwCurrentState; //目前狀態    DWORD dwControlsAccepted;//允許的操作    DWORD dwWin32ExitCode;    DWORD dwServiceSpecificExitCode;    DWORD dwCheckPoint;    DWORD dwWaitHint;} SERVICE_STATUS,  *LPSERVICE_STATUS;
改變服務的啟動類型

服務的啟動類型主要有開機啟動、手動啟動、以及禁止啟動等項。改變啟動類型的函數主要是:ChangeServiceConfig。函數原型如下:

BOOL WINAPI ChangeServiceConfig(  __in          SC_HANDLE hService,  __in          DWORD dwServiceType,//服務類型  __in          DWORD dwStartType,//啟動類型  __in          DWORD dwErrorControl,  __in          LPCTSTR lpBinaryPathName,//服務的主程式路徑  __in          LPCTSTR lpLoadOrderGroup,  __out         LPDWORD lpdwTagId,  __in          LPCTSTR lpDependencies,//依賴項  __in          LPCTSTR lpServiceStartName,//服務名稱  __in          LPCTSTR lpPassword,//服務密碼,主要用於控制服務  __in          LPCTSTR lpDisplayName//服務的顯示名稱);

函數中傳遞的都是服務的新資訊,如果希望改變則填入相應的值,如果不想改變則對於DWORD類型的成員來說填入SERVICE_NO_CHANGE,對於指標類型的只需要填入NULL即可。

建立服務

建立服務主要使用函數CreateService,該函數的原型如下:

SC_HANDLE WINAPI CreateService(  __in          SC_HANDLE hSCManager,  __in          LPCTSTR lpServiceName,//服務名稱  __in          LPCTSTR lpDisplayName,//顯示名稱  __in          DWORD dwDesiredAccess,//服務許可權  __in          DWORD dwServiceType,//服務類型  __in          DWORD dwStartType,//服務啟動類型  __in          DWORD dwErrorControl,  __in          LPCTSTR lpBinaryPathName,//主程式路徑  __in          LPCTSTR lpLoadOrderGroup,  __out         LPDWORD lpdwTagId,  __in          LPCTSTR lpDependencies,//依賴項  __in          LPCTSTR lpServiceStartName,啟動名稱  __in          LPCTSTR lpPassword//密碼);在啟動時需要填入一些資訊系統的服務控制管理員儲存這些資訊,並根據其中的某些資訊來啟動這個服務,有的選項是必填的,比如服務名稱,這個是用來唯一標識一個服務的,服務所在路徑告知服務控制管理員啟動哪個程式,而向依賴、密碼等等資訊可以不用填寫。下面是調用的例子```cppp    SC_HANDLE hRet = ::CreateService(h_SCManager, lpServiceName, lpDisplayName,     dwDesiredAccess, SERVICE_WIN32_OWN_PROCESS, dwStartType, SERVICE_ERROR_NORMAL,         lpBinaryPathName, NULL, NULL, lpDependencies, lpServiceStartName, lpPassword);<div class="se-preview-section-delimiter"></div>

SERVICE_WIN32_OWN_PROCESS表示服務類型是win32類型擁有獨立進程的服務
SERVICE_ERROR_NORMAL表示服務程式返回的錯誤碼是系統預設的錯誤碼

刪除服務

刪除服務使用的函數是DeleteService,這個函數主要傳入的是服務的控制代碼,這個控制代碼是由函數OpenService返回的。另外需要注意的是這個函數只對已停止的服務起作用,所以在刪除之前需要將服務停止。
“`

SERVICE_WIN32_OWN_PROCESS表示服務類型是win32類型擁有獨立進程的服務SERVICE_ERROR_NORMAL表示服務程式返回的錯誤碼是系統預設的錯誤碼## 刪除服務刪除服務使用的函數是DeleteService,這個函數主要傳入的是服務的控制代碼,這個控制代碼是由函數OpenService返回的。另外需要注意的是這個函數只對已停止的服務起作用,所以在刪除之前需要將服務停止。```cppBOOL bRet = FALSE;    DWORD dwSrvAcceptCtrl = GetSrvCtrlAccept(pIter->pNode->strSrvName);    if(0 == (dwSrvAcceptCtrl & SERVICE_ACCEPT_STOP))            //服務不能被刪除    {        goto __RETURN;    }    //停止服務,函數CtrlService是我之前封裝的用於控制服務。    bRet = CtrlService(pIter, SERVICE_CONTROL_STOP);    if(!bRet)    {        goto __RETURN;    }    bRet = ::DeleteService(h_SCService);    if (bRet)    {        DeleteItem(pIter->pNode);    }__RETURN:    CloseServiceHandle(h_SCService);    CloseServiceHandle(h_SCManager);    return bRet;

windows服務管理操作

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.