2009-10-20 5 views

Répondre

3

De C++ natif, vous devez:

  1. Ouvrir une poignée au gestionnaire de contrôle de service,
  2. Utilisez le gestionnaire de contrôle de service pour obtenir une poignée de service pour le service que vous souhaitez contrôler,
  3. Envoyer un code de contrôle ou codes au service et
  4. Fermer les poignées ouvertes dans les étapes 1 et 2.

Par exemple, ce code redémarre le service de synchronisation horaire. Tout d'abord, je crée une classe wrapper pour les handles de service, pour les fermer automatiquement en quittant le bloc.

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; 
}; 

Ensuite, j'ouvre le gestionnaire de contrôle de service (en utilisant OpenSCManager()) et le service que je veux contrôler. Notez que le paramètre dwDesiredAccess à OpenService() doit inclure des autorisations pour chaque contrôle que je veux envoyer ou les fonctions de contrôle pertinentes échoueront.

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; 

Pour arrêter le service, j'utilise ControlService() pour envoyer le code SERVICE_CONTROL_STOP, puis vérifier la valeur de retour pour vous assurer que la commande a réussi. Si une erreur autre que ERROR_SERVICE_NOT_ACTIVE est signalée, je suppose que le démarrage du service n'aboutira pas.

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; 
     } 
    } 

Après avoir demandé au service d'arrêter, j'attends le gestionnaire de services pour signaler que le service est en fait arrêté. Ce code a deux bugs potentiels, que vous voudrez peut-être corriger le code de production:

  1. sommeil (1000) suspendra la boucle de message sur ce fil, vous devez donc utiliser une autre méthode pour retarder l'exécution si cette fonction se déroulera sur un fil d'interface utilisateur. Vous pouvez construire une boucle sleep-with-message appropriée en utilisant MsgWaitForMultipleObjectsEx().
  2. Le DWORD retourné à partir de GetTickCount() finira à zéro par la suite; Si elle est enroulée pendant que cette fonction est en attente, l'attente peut être abandonnée plus tôt que prévu.

    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; 
    } 
    

Enfin, sachant que le service est dans un état arrêté, je l'appelle StartService() exécuter à nouveau.

success = ::StartService(hW32Time, 0, NULL); 
    if (!success) return FALSE; 

    return TRUE; 
} 
Questions connexes