如何將命令從C++發送到Windows服務?等效.NET代碼:從C++發送命令到服務
ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
如何將命令從C++發送到Windows服務?等效.NET代碼:從C++發送命令到服務
ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
從本地C++,你將需要:
例如,此代碼重新啓動時間同步服務。首先,我爲服務句柄創建一個包裝類,在離開塊時自動關閉它們。
class CSC_HANDLE
{
public:
CSC_HANDLE(SC_HANDLE h) : m_h(h) { }
~CSC_HANDLE() { ::CloseServiceHandle(m_h); }
operator SC_HANDLE() { return m_h; }
private:
SC_HANDLE m_h;
};
於是,我打開服務控制管理器(使用OpenSCManager()),我想控制服務。請注意,OpenService()的dwDesiredAccess參數必須包含我要發送的每個控件的權限,否則相關控制功能將失敗。
BOOL RestartTimeService()
{
CSC_HANDLE hSCM(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, GENERIC_READ));
if (NULL == hSCM) return FALSE;
CSC_HANDLE hW32Time(::OpenService(hSCM, L"W32Time", SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS));
if (NULL == hW32Time) return FALSE;
要停止該服務,我用ControlService()發送SERVICE_CONTROL_STOP代碼,然後檢查返回值,以確保該命令成功。如果報告ERROR_SERVICE_NOT_ACTIVE以外的任何錯誤,我認爲啓動服務不會成功。
SERVICE_STATUS ss = { 0 };
::SetLastError(0);
BOOL success = ::ControlService(hW32Time, SERVICE_CONTROL_STOP, &ss);
if (!success)
{
DWORD le = ::GetLastError();
switch (le)
{
case ERROR_ACCESS_DENIED:
case ERROR_DEPENDENT_SERVICES_RUNNING:
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_SERVICE_CONTROL:
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
case ERROR_SERVICE_REQUEST_TIMEOUT:
case ERROR_SHUTDOWN_IN_PROGRESS:
return FALSE;
case ERROR_SERVICE_NOT_ACTIVE:
default:
break;
}
}
指示服務停止後,我等待服務管理器報告服務實際上已停止。這段代碼有兩個潛在的bug,你不妨以糾正產品代碼:
從GetTickCount()返回的DWORD最終將回到零;如果它在這個函數等待的時候迴繞,等待可能會比我預期的更早放棄。
DWORD waitstart(::GetTickCount());
while (true)
{
ZeroMemory(&ss, sizeof(ss));
::QueryServiceStatus(hW32Time, &ss);
if (SERVICE_STOPPED == ss.dwCurrentState) break;
::Sleep(1000);
DWORD tick(::GetTickCount());
if ((tick < waitstart) || (tick > (waitstart + 30000))) return FALSE;
}
最後,知道服務處於停止狀態,我稱之爲StartService()再次運行。
success = ::StartService(hW32Time, 0, NULL);
if (!success) return FALSE;
return TRUE;
}