7
votes

Comment pouvons-nous dire si une application C ++ est lancée en tant que service Windows?

Nous avons une application de console que nous avons lancé à partir d'une invite de commande pour le débogage, mais nous le lancons également comme un service NT pour la production.

En ce moment, le code a cette logique: p>

if (__argc <= 1) {
  assumeService();
} else {
  assumeForgound();
}


2 commentaires

Quelle langue de développement utilisez-vous?


C ++ (le titre est maintenant fixé).


5 Réponses :


14
votes

Vous pouvez vérifier si les processus parents sont des services.exe ou svchoste.exe. Ou vous pouvez interroger le gestionnaire de contrôle de service à l'aide de WinapI si votre service est démarré et que l'ID de processus actuel est égal à celui du service démarré.

en C # Le code suivant le ferait (puisqu'il s'agit de Winapi, cela devrait fonctionner de manière similaire en C ++, exemple de code ICI ): P>

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <aclapi.h>
#include <stdio.h>

bool IsRunningAsService(const TCHAR* szSvcName)
{
    SERVICE_STATUS_PROCESS ssStatus; 
    DWORD dwBytesNeeded;

    SC_HANDLE schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // servicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 

    if (NULL == schSCManager) 
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return false;
    }

    // Get a handle to the service.
    SC_HANDLE schService = OpenService( 
        schSCManager,         // SCM database 
        szSvcName,            // name of service 
        SERVICE_ALL_ACCESS);  // full access 

    if (schService == NULL)
    { 
        printf("OpenService failed (%d)\n", GetLastError()); 
        CloseServiceHandle(schSCManager);
        return false;
    }    

    // Check the status in case the service is not stopped. 

    if (!QueryServiceStatusEx( 
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // information level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // size needed if buffer is too small
    {
        printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return false; 
    }

    return GetCurrentProcessId() == ssStatus.dwProcessId;
}


2 commentaires

Ah désolé. Nous n'utilisons pas c # - mais votre réponse s'applique de toute façon je pense.


Veuillez noter que cette approche nécessite des autorisations, le processus actuel doit pouvoir interroger le gestionnaire de services de service.



0
votes

Vérifierait qu'un compte d'utilisateur vous aidait? IIRC Un service serait exécuté sous le nom System ou quelque chose de très similaire et je suppose que vous exécutez votre application en mode de débogage sous votre compte d'utilisateur normal. Je pense que openprocessToken et getTokenInformation avec Tokenuser fonctionnerait ici.


1 commentaires

Les services peuvent être exécutés sous des comptes d'utilisateurs



2
votes

Voici un certain code que j'ai créé (semble fonctionner bien). Toutes mes excuses pour les en-têtes manquants, #defines, etc. Si vous voulez voir la version complète, Regardez ici .

bool
CArchMiscWindows::wasLaunchedAsService() 
{
    CString name;
    if (!getParentProcessName(name)) {
        LOG((CLOG_ERR "cannot determine if process was launched as service"));
        return false;
    }

    return (name == SERVICE_LAUNCHER);
}

bool
CArchMiscWindows::getParentProcessName(CString &name) 
{   
    PROCESSENTRY32 parentEntry;
    if (!getParentProcessEntry(parentEntry)){
        LOG((CLOG_ERR "could not get entry for parent process"));
        return false;
    }

    name = parentEntry.szExeFile;
    return true;
}

BOOL WINAPI 
CArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry)
{
    // get entry from current PID
    return getProcessEntry(entry, GetCurrentProcessId());
}

BOOL WINAPI 
CArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry)
{
    // get the current process, so we can get parent PID
    PROCESSENTRY32 selfEntry;
    if (!getSelfProcessEntry(selfEntry)) {
        return FALSE;
    }

    // get entry from parent PID
    return getProcessEntry(entry, selfEntry.th32ParentProcessID);
}

BOOL WINAPI 
CArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID)
{
    // first we need to take a snapshot of the running processes
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE) {
        LOG((CLOG_ERR "could not get process snapshot (error: %i)", 
            GetLastError()));
        return FALSE;
    }

    entry.dwSize = sizeof(PROCESSENTRY32);

    // get the first process, and if we can't do that then it's 
    // unlikely we can go any further
    BOOL gotEntry = Process32First(snapshot, &entry);
    if (!gotEntry) {
        LOG((CLOG_ERR "could not get first process entry (error: %i)", 
            GetLastError()));
        return FALSE;
    }

    while(gotEntry) {

        if (entry.th32ProcessID == processID) {
            // found current process
            return TRUE;
        }

        // now move on to the next entry (when we reach end, loop will stop)
        gotEntry = Process32Next(snapshot, &entry);
    }

return FALSE;


0 commentaires

1
votes

Si le programme fonctionne sans paramètres, vous supposez que c'est un service. Changer que et le reste de vos problèmes de démarrage de service disparaissent. nécessite un paramètre pour que le programme agisse comme un service. Lorsque vous installez le service, incluez simplement ce paramètre dans la ligne de commande que vous vous enregistrez avec Windows.

Sans paramètres, créez le programme Imprimer sa documentation d'utilisation et quitter. Là, il peut expliquer, par exemple, que les utilisateurs doivent utiliser -f pour le débogage de ligne de commande, -i pour installer le service, et -u Pour désinstaller, et qu'ils ne doivent pas utiliser -s car cela permettrait d'essayer de fonctionner comme un service de la ligne de commande, qui n'est pas un cas d'utilisation pris en charge. (Ils doivent utiliser net Démarrage ou SC Démarrer pour démarrer le service à la place.)


3 commentaires

Rob, comment puis-je commencer un service avec argways? Nous utilisons CreateService, que je crois ne vous donne pas l'option ... msdn.microsoft.com/en-us/library/ms682450 (vs.85) .aspx


Lisez ce lien plus attentivement. En particulier, lisez tout le texte décrivant le paramètre LPBinairePathName. Cela démontre spécifiquement des arguments de passage au programme.


Ah, "Le chemin peut également inclure des arguments pour un service de démarrage automatique". J'ai été induisée par une mauvaise documentation dans notre projet qui a déclaré que "les services de Windows ne prennent pas arguments" que j'aurais dû ignorer au lieu de la lire Verbatim. L'ancien code va même dans l'ampleur de la création d'une clé Reg spécifiquement pour les arguments, ha!



0
votes

Si votre application est en cours d'exécution d'une application de console (lorsqu'elle n'est pas utilisée comme service), une solution simple consiste à vérifier si une console a été allouée:

if(GetConsoleWindow())
{
    //Running as console Application
}
else
{
    //Running as Service
}


0 commentaires