Enviar comando para o serviço de C ++
-
22-09-2019 - |
Pergunta
Como posso enviar comando para um serviço do Windows a partir de C ++? O código .NET equivalente é:
ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
Solução
Do C ++ nativo, você precisará:
- Abra uma alça para o gerente de controle de serviço,
- Use o gerenciador de controle de serviço para obter um identificador de serviço para o serviço que você deseja controlar,
- Envie um código ou códigos de controle para o serviço e
- Feche as alças abertas nas etapas 1 e 2.
Por exemplo, esse código reinicia o serviço de sincronização de tempo. Primeiro, crio uma classe de wrapper para as alças de serviço, para fechá -las automaticamente ao sair do bloco.
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;
};
Então, eu abro o gerente de controle de serviço (usando OpenScManager ()) e o serviço que quero controlar. Observe que o parâmetro dwdesiredaccess para OpenService () Deve incluir permissões para cada controle que desejo enviar, ou as funções de controle relevantes falharão.
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;
Para parar o serviço, eu uso ControlService () Para enviar o código Service_Control_Stop e verifique o valor de retorno para garantir que o comando fosse bem -sucedido. Se algum erro que não seja o erro_service_not_active for relatado, presumo que o início do serviço não terá sucesso.
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;
}
}
Depois de instruir o serviço a parar, espero o gerente de serviço relatar que o serviço é de fato parado. Este código possui dois bugs em potencial, que você deseja corrigir para o código de produção:
- O sono (1000) suspenderá o loop da mensagem neste thread, portanto, você deve usar outro método para atrasar a execução se essa função será executada em um thread da interface do usuário. Você pode construir um mole de sono com message adequado usando MsgwaitFormultiPleObjectSex ().
O dword retornou de GetTickCount () irá envolver -se para zero eventualmente; Se isso envolve enquanto essa função estiver esperando, a espera poderá desistir mais cedo do que eu pretendia.
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; }
Finalmente, sabendo que o serviço está em um estado parado, eu ligo Começar serviço() execute novamente.
success = ::StartService(hW32Time, 0, NULL);
if (!success) return FALSE;
return TRUE;
}
Outras dicas
Você usa ControlService, Vejo Solicitações de controle de serviço.
Aqui está um pequeno programa que se conectará a um serviço chamado "MyService" e enviará um comando 141 (que é definido pelo serviço)
// ServiceCommunicator.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
SC_HANDLE managerHandle;
SC_HANDLE serviceHandle;
SERVICE_STATUS controlParms;
DWORD retStatus;
managerHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
if (NULL != managerHandle)
{
serviceHandle = OpenService(managerHandle, L"MYSERVICE", SERVICE_USER_DEFINED_CONTROL | SERVICE_QUERY_STATUS);
if (NULL != serviceHandle)
{
cout << "connected to Service" << endl;
retStatus = ControlService(serviceHandle, 141, &controlParms);
if (retStatus)
{
//Get the return code from the service
cout << "For command 141, return code from service was " << controlParms.dwWin32ExitCode << endl;
}
else
cout << "Sending command 141 failed" << endl;
CloseServiceHandle(serviceHandle);
}
else
{
cout << "could not connect to Service" << endl;
}
CloseServiceHandle(managerHandle);
}
else
{
cout << "could not open service manager" << endl;
}
return 0;
}