Windows NT 提供強大的服務架構(如 FTP、WWW、資料庫服務),服務通常在開機自動啟動、關機停止。但開發者常需在程式中動態控制服務(啟動、暫停、停止、查詢狀態),甚至開發自訂服務。本文系統介紹 Service Control Manager (SCM) 核心 API,並提供 C++ Builder 完整範例程式碼,實現服務列舉、啟動、停止功能。
所有服務資訊儲存在 Service Control Manager Database 中,操作流程:開啟 SCM → 開啟特定服務 → 執行控制操作 → 關閉控制代碼。
SC_HANDLE OpenSCManager(
LPCTSTR lpMachineName, // NULL=本機
LPCTSTR lpDatabaseName, // NULL=預設資料庫
DWORD dwDesiredAccess // 權限旗標
);
常用權限:SC_MANAGER_ALL_ACCESS、SC_MANAGER_ENUMERATE_SERVICE、SC_MANAGER_CREATE_SERVICE 等。
SC_HANDLE OpenService(
SC_HANDLE hSCManager,
LPCTSTR lpServiceName, // 服務名稱(區分大小寫)
DWORD dwDesiredAccess // SERVICE_ALL_ACCESS, SERVICE_START, SERVICE_STOP 等
);
BOOL QueryServiceStatus(
SC_HANDLE hService,
LPSERVICE_STATUS lpServiceStatus
);
SERVICE_STATUS 結構包含:dwCurrentState(SERVICE_STOPPED=1, SERVICE_RUNNING=4 等)、dwControlsAccepted、dwCheckPoint、dwWaitHint 等。
BOOL StartService(
SC_HANDLE hService,
DWORD dwNumServiceArgs,
LPCTSTR *lpServiceArgVectors
);
BOOL ControlService(
SC_HANDLE hService,
DWORD dwControl, // SERVICE_CONTROL_STOP, PAUSE, CONTINUE, INTERROGATE
LPSERVICE_STATUS lpServiceStatus
);
BOOL EnumServicesStatus(
SC_HANDLE hSCManager,
DWORD dwServiceType, // SERVICE_DRIVER, SERVICE_WIN32 等
DWORD dwServiceState, // SERVICE_ACTIVE, SERVICE_INACTIVE 等
LPENUM_SERVICE_STATUS lpServices,
DWORD cbBufSize,
LPDWORD pcbBytesNeeded,
LPDWORD lpServicesReturned,
LPDWORD lpResumeHandle
);
關鍵點:通常首次呼叫回傳 FALSE,GetLastError() == ERROR_MORE_DATA,需依 pcbBytesNeeded 配置足夠緩衝區再呼叫第二次取得完整列表。
介面:ListBox1 顯示服務清單,Button1 列舉,Button2 啟動選取服務,Button3 停止選取服務。
#include
class TForm1 : public TForm {
__published:
TButton *Button1, *Button2, *Button3;
TListBox *ListBox1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall Button3Click(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:
SC_HANDLE hSCManager, hService;
SERVICE_STATUS ReturnStatus;
};
void __fastcall TForm1::Button1Click(TObject *Sender) {
DWORD BytesNeeded, ServicesReturned, ResumeHandle = 0;
BOOL ReturnFlag;
ENUM_SERVICE_STATUS *lpServices = new ENUM_SERVICE_STATUS[100];
ENUM_SERVICE_STATUS Service;
ListBox1->Items->Clear();
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (!hSCManager) { ShowMessage("無法開啟 SCM"); return; }
ReturnFlag = EnumServicesStatus(hSCManager, SERVICE_DRIVER, SERVICE_ACTIVE,
&Service, sizeof(Service), &BytesNeeded, &ServicesReturned, &ResumeHandle);
if (!ReturnFlag && GetLastError() == ERROR_MORE_DATA) {
DWORD BufSize = BytesNeeded + sizeof(ENUM_SERVICE_STATUS);
EnumServicesStatus(hSCManager, SERVICE_DRIVER, SERVICE_ACTIVE,
lpServices, BufSize, &BytesNeeded, &ServicesReturned, &ResumeHandle);
for (DWORD i = 0; i < ServicesReturned; i++)
ListBox1->Items->Add(lpServices[i].lpServiceName);
}
delete[] lpServices;
}
void __fastcall TForm1::Button2Click(TObject *Sender) {
for (int i = 0; i < ListBox1->Items->Count; i++) {
if (ListBox1->Selected[i]) {
hService = OpenService(hSCManager,
ListBox1->Items->Strings[i].c_str(), SERVICE_ALL_ACCESS);
if (!hService) { ShowMessage("無法開啟服務"); return; }
QueryServiceStatus(hService, &ReturnStatus);
if (ReturnStatus.dwCurrentState == SERVICE_STOPPED) {
if (StartService(hService, 0, NULL))
ShowMessage("啟動成功!");
else
ShowMessage("無法啟動服務!");
}
CloseServiceHandle(hService);
}
}
}
void __fastcall TForm1::Button3Click(TObject *Sender) {
for (int i = 0; i < ListBox1->Items->Count; i++) {
if (ListBox1->Selected[i]) {
hService = OpenService(hSCManager,
ListBox1->Items->Strings[i].c_str(), SERVICE_ALL_ACCESS);
if (!hService) { ShowMessage("無法開啟服務"); return; }
QueryServiceStatus(hService, &ReturnStatus);
if (ReturnStatus.dwCurrentState == SERVICE_RUNNING) {
if (ControlService(hService, SERVICE_CONTROL_STOP, &ReturnStatus))
ShowMessage("成功停止!");
else
ShowMessage("無法停止服務!");
}
CloseServiceHandle(hService);
}
}
}
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) {
CloseServiceHandle(hSCManager);
}
requireAdministrator。EnumDependentServices)。ServiceMain、HandlerEx、向 SCM 註冊(CreateService),另文詳述。OpenSCManager 指定機器名稱即可管理遠端電腦服務(需 RPC 通訊權限)。GetLastError(),常見錯誤碼:ERROR_ACCESS_DENIED、ERROR_SERVICE_DOES_NOT_EXIST、ERROR_SERVICE_ALREADY_RUNNING。掌握 SCM API,即可在應用程式中無縫整合 Windows 服務管理功能,構建企業級系統管理工具。