Windows 服務程序(一)


Windows 服務程序簡介:

Windows服務應用程序是一種需要長期運行的應用程序,它對於服務器環境特別適合。
它沒有用戶界面,並且也不會產生任何可視輸出。任何用戶消息都會被寫進Windows事件日志。
因為這些特性,所以特別適用於后台程序。
計算機啟動時,服務會自動開始運行。它們不要用戶一定登錄才運行,它們能在包括這個系統內的任何用戶環境下運行。
通過服務控制管理器, Windows服務允許用戶創建可在其自身的 Windows 會話中長時間運行的可執行應用程序。
這些服務可在計算機啟動時自動啟動,可以暫停和重啟,並且不顯示任何用戶界面。
 Windows服務應用程序通常可以在本地和通過網絡為用戶提供一些功能:
例如客戶端/服務器應用程序、Web 服務器、數據庫服務器以及其他基於服務器的應用程序。
Windows 服務程序如下圖右方:
 
 
編寫 Windows 服務程序:
Windows服務程序有着固定的模式,它一般由四個部分組成:main(), ServiceMain(), ServiceHandle(), MyWork()。
main() 僅負責創建服務分派表並啟動控制分派機制,典型代碼如下:
int main(void)
{
    SERVICE_TABLE_ENTRYA ServTable[2];    // 創建服務分派表。
    ServTable[0].lpServiceName = "Test";  // 服務名稱。類似於 Win32 應用程序的窗口名稱。每個服務代表一項功能。
    ServTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;  //服務入口函數。
    ServTable[1].lpServiceName = NULL;  // 每一個服務分派表代表一個服務,分派表的最后一項必須是服務名和服務主函數域的 NULL 指針。
    ServTable[1].lpServiceProc = NULL;   // 我們稱之為“哨兵”(所有值都為NULL),表示該服務表末尾。
    StartServiceCtrlDispatcher(ServTable);  // 啟動控制分派機制。
return 0; }

 

SERVICE_TABLE_ENTRYA 結構體:

typedef struct _SERVICE_TABLE_ENTRYA {
  LPSTR                    lpServiceName;   // 服務名稱。
  LPSERVICE_MAIN_FUNCTIONA lpServiceProc;   // 服務入口函數。
} SERVICE_TABLE_ENTRYA, *LPSERVICE_TABLE_ENTRYA;

StartServiceCtrlDispatcher() 介紹 :

功能:連接程序主線程到服務控制管理程序。

BOOL StartServiceCtrlDispatcher(
  CONST SERVICE_TABLE_ENTRYA *lpServiceStartTable // SERVICE_TABLE_ENTRYA 結構體變量。
);                     

返回值:非零表示成功,零表示失敗。

 

服務控制管理器 (SCM:Services Control Manager) 是一個管理系統所有服務的進程。

當 SCM 啟動某個服務時,它等待某個進程的主線程來調用 StartServiceCtrlDispatcher 函數,

將分派表傳遞給 StartServiceCtrlDispatcher。

這將把調用進程的主線程轉換為控制分派器。該分派器啟動一個新線程,

該線程運行分派表中每個服務的 ServiceMain 函數分派器還監視程序中所有服務的執行情況。

然后分派器將控制請求從 SCM 傳給服務。

分派表中所有的服務執行完之后,或者發生錯誤時。StartServiceCtrlDispatcher 調用返回。然后主進程終止。

SCM 開始啟動一個服務程序時,總是等待這個服務程序去調用 StartServiceCtrlDispatcher()。

而當服務開始運行時,main() 將會調用 ServiceMain(), 直到 ServiceMain() 執行完畢或發生錯誤而退出,

StartServiceCtrlDispatcher() 返回,主線程將會終止。

 

ServiceMain() 服務入口函數,它的作用就是:將你需要執行的任務放到該函數中循環執行即可。這就是服務程序的工作函數。

在ServiceMain執行你的任務前,需要給SERVICE_TABLE_ENTRY 分派表結構體進行賦值。

典型代碼如下:

// SERVICE_STATUS ServiceStatus;  //這段代碼應聲明為全局變量,因多處使用。
// SERVICE_STATUS_HANDLE hStatus; //這段代碼應聲明為全局變量,因多處使用。
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv) { // 初始化狀態設置。 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_START_PENDING; // 即服務目前狀態為 正在初始化。 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
// 這個通知 SCM 服務接收哪個域。本例包含 停止,暫停和繼續,關機等命令。 ServiceStatus.dwWin32ExitCode
= 0; // 這個域在終止服務並報告細節時很有用。下面這四個值一般不用關心,通常設置為 0。 ServiceStatus.dwCheckPoint = 0; // 用來報告它當前的事件進展情況的。 ServiceStatus.dwServiceSpecificExitCode = 0; // 這個域在終止服務並報告細節時也很有用。 ServiceStatus.dwWaitHint = 0; // 根據初始化過程的長短而定。 // 注冊控制函數。 hStatus = RegisterServiceCtrlHandler("ServiceName", // 注冊服務控制程序,注冊成功后將會返回一個服務狀態句柄。 (LPHANDLER_FUNCTION)ServiceHandler); // ServiceHandle 是服務控制程序。類似與 Windows 應用程序的窗口過程。 if (!hStatus)
{
printf("Register Service Error!\n");
system("pause");
return;
}
SetServiceStatus(hStatus, &ServiceStatus);  // 設置服務狀態。通過調用 SetServiceStatus 函數,向 SCM 報告服務的狀態。
if (GetLastError != NO_ERROR) // 返回調用線程最近的錯誤代碼值,檢查是否出錯。 { // 如果出錯,將其重設為停止狀態。 ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus, &ServiceStatus);
printf("Start Error!\n");
system("pause");
return; }
// 沒有錯誤就繼續運行。
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;
    SetServiceStatus(hStatus, &ServiceStatus);
    // 創建線程執行特定功能
    HANDLE hThread = CreateThread(NULL, 0, MyWork, NULL, 0, NULL); // MyWOrk 是特定功能的函數,如后門程序。
    if(hThread = NULL)
         return; }

 

SERVICE_STATUS 結構體:

typedef struct _SERVICE_STATUS {
  DWORD dwServiceType;   // 服務類型。
  DWORD dwCurrentState;   // 服務的當前狀態。
  DWORD dwControlsAccepted;  // 服務在其處理函數中接受和處理的控制代碼。
  DWORD dwWin32ExitCode;    // 服務用於報告啟動或停止時發生錯誤的錯誤代碼。
  DWORD dwServiceSpecificExitCode;  // 服務在啟動或停止時發生錯誤時返回的錯誤代碼。
  DWORD dwCheckPoint;   // 服務在長時間啟動、停止、暫停或繼續操作期間定期遞增以報告其進度的檢查點值。
  DWORD dwWaitHint;    // 將要進行 開始、停止、暫停或繼續服務 操作所需的估計時間 (以毫秒為單位)。
} SERVICE_STATUS, *LPSERVICE_STATUS;

 

dwServiceType 的值:(組合)

含義
SERVICE_FILE_SYSTEM_DRIVER 該服務是一個文件系統驅動程序
SERVICE_KERNEL_DRIVER 該服務是一個設備驅動程序
SERVICE_WIN32_OWN_PROCESS 服務在自己的進程中運行(通常使用這個)
SERVICE_USER_OWN_PROCESS 該服務在登錄用戶帳戶下在其自己的進程中運行

 

dwCurrentState 的值:

含義
SERVICE_CONTINUE_PENDING 服務處於從暫停狀態恢復的過程中
SERVICE_PAUSE_PENDING 服務正在暫停過程中,但還有沒完全進入暫停狀態
SERVICE_PAUSED 服務已經暫停了
SERVICE_RUNNING 服務正在運行了
SERVICE_START_PENDING 服務在啟動過程中,但還沒有准備好對請求進行響應
SERVICE_STOP_PENDING 服務正在停止過程中,但還沒有完全進入停止狀態
SERVICE_STOPPED  服務已經停止了

 

dwControlsAccepted 的值: (組合)

含義
SERVICE_ACCEPT_NETBINDCHANGE 該服務是一個網絡組件,並且能夠在服務在服務不重啟的情況下,改變其所網絡接收的綁定,接收SERVICE_CONTROL_NETBINDADD、SERVICE_CONTROL_NETBINDREMOVE、SERVICE_CONTROL_NETBINDENABLE、SERVICE_CONTROL_NETBINDDISABLE 的通知
SERVICE_ACCEPT_PARAMCHANGE 服務在不重啟的情況下能夠重新讀取其配置參數,接收 SERVICE_CONTROL_PARAMCHANGE 通知
SERVICE_ACCEPT_PAUSE_CONTINUE 服務支持暫停和重啟,服務能夠接收到 SERVICE_CONTROL_PAUSE 和SERVICE_CONTROL_CONTINUE的通知
SERVICE_ACCEPT_PRESHUTDOWN 系統在關閉前,能夠收到系統的 SERVICE_CONTROL_PRESHUTDOWN 通知,用來處理一些關閉前的清理,xp之前不支持此控制碼
SERVICE_ACCEPT_SHUTDOWN 能夠接收系統退出時的 SERVICE_CONTROL_SHUTDOWN 的通知,以便處理一些回收
SERVICE_ACCEPT_STOP 能夠接收 SERVICE_CONTROL_STOP 的通知來處理一些回收任務

 

SetServiceStatus() 介紹:

功能:更新服務控制管理器調用服務的狀態信息。

函數原型:BOOL SetServiceStatus(
                  SERVICE_STATUS_HANDLE hServiceStatus, //服務狀態信息結構的句柄, 由 RegisterServiceCtrlHandler() 返回。
                  LPSERVICE_STATUS  lpServiceStatus   // 指向 SERVICE_STATUS 結構的指針包含呼叫服務的最新狀態信息。
                  );

返回值:非零表示成功,零表示失敗。

 

RegisterServiceCtrlHandler() 介紹:

功能:注冊一個函數以處理服務控制請求。

函數原型:SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
                  LPCSTR  lpServiceName, //調用線程運行的服務的名稱, 即在 CreateService() 中指定的服務控制程序的服務名稱。
                  LPHANDLER_FUNCTION lpHandlerProc   // 指向要注冊的處理程序的函數。
                  );

返回值:如果函數成功, 則返回值為服務狀態句柄。如果函數失敗, 返回值為零。

 

CreateThread() 介紹:

功能:該函數在 主線程 的基礎上創建一個新線程。線程 終止運行后,

          線程對象仍然在系統中,必須通過 CloseHandle() 來關閉該線程對象。

函數原型:HANDLE CreateThread(
                  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
                  SIZE_T   dwStackSize,
                  LPTHREAD_START_ROUTINE  lpStartAddress,
                  LPVOID  lpParameter,
                  DWORD    dwCreationFlags,
                  LPDWORD  lpThreadId
                  );

參數:

lpThreadAttributes

指向 SECURITY_ATTRIBUTES 結構的指針, 它確定返回的句柄是否可以由子進程繼承。

如果 lpThreadAttributes 為 NULL, 則無法繼承句柄。

dwStackSize

堆棧的初始大小 (以字節為單位)。系統將此值舍入到最近的頁面。

如果此參數為零, 則新線程將使用可執行文件的默認大小。

lpStartAddress

指向要由線程執行的應用程序定義的函數。此指針表示線程的起始地址。

lpParameter

指向要傳遞給線程的變量的指針。

dwCreationFlags

控制線程創建的標志。通常情況下為 0。

lpThreadId

指向接收線程標識符的變量的指針。如果此參數為 NULL, 則不返回線程標識符。

返回值:如果函數成功, 則返回值是新線程的句柄。如果函數失敗, 返回值為 NULL。

 

ServiceHandle() 服務控制函數,典型代碼如下:

void WINAPI ServiceHandler(DWORD fdwControl)
{
    switch (fdwControl)
    {
    case SERVICE_CONTROL_PAUSE:
        ServiceStatus.dwCurrentState = SERVICE_PAUSED;
        break;
    case SERVICE_CONTROL_CONTINUE:
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        break;
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
        SetServiceStatus(hStatus, &ServiceStatus);
        return;
default: break; } SetServiceStatus(hStatus, &ServiceStatus); // 重設服務狀態。 return; }

 

 入門代碼如下:(目前的代碼仍然不能當做服務來運行,還需要 SCM 進行安裝)

#include<stdio.h>
#include<Windows.h>
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
void WINAPI ServiceHandler(DWORD fdwControl);
DWORD WINAPI MyWork(LPVOID lpParam);
int main(void) { SERVICE_TABLE_ENTRY ServTable[2]; ServTable[0].lpServiceName = (LPSTR)"Test"; ServTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; ServTable[1].lpServiceName = NULL; ServTable[1].lpServiceProc = NULL; StartServiceCtrlDispatcher(ServTable); return 0; } void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_START_PENDING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; ServiceStatus.dwWin32ExitCode = 0; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwServiceSpecificExitCode = 0; ServiceStatus.dwWaitHint = 0; hStatus = RegisterServiceCtrlHandler("ServiceName", (LPHANDLER_FUNCTION)ServiceHandler); if (!hStatus)
    {
        printf("Register Service Error!\n");
        system("pause");
        return;
     }

SetServiceStatus(hStatus, &ServiceStatus);
if (GetLastError() != NO_ERROR) { ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus, &ServiceStatus);
printf("Start Service Error!\n");
system("pause");
return; }
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus, &ServiceStatus);
// 從這里開始可以放入你想服務為你所做的事情。
HANDLE hThread = CreateThread(NULL, 0, MyWork, NULL, 0, NULL);
if(hThread = NULL)
return; } void WINAPI ServiceHandler(DWORD fdwControl) {
switch (fdwControl) { case SERVICE_CONTROL_PAUSE: ServiceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: ServiceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwWaitHint = 0; SetServiceStatus(hStatus, &ServiceStatus); return;
default: break; } SetServiceStatus(hStatus, &ServiceStatus); return; }

DWORD WINAPI MyWork(LPVOID lpParam)
{
 return 0;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM