7 votes

Comment savoir si une application C++ est lancée en tant que service Windows ?

Nous avons une application console que nous lançons à partir de l'invite de commande pour le débogage, mais nous la lançons également en tant que service NT pour la production.

Actuellement, le code a cette logique :

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

Existe-t-il un meilleur moyen de vérifier comment le processus a été lancé ? Nous sommes un projet open source, donc à chaque fois que nous recevons un nouveau développeur Windows, nous devons lui expliquer qu'il doit spécifier l'adresse du site web de l'entreprise. -f arg pour empêcher l'application de se connecter au contrôleur de service.

Qu'en est-il de la vérification du processus parental ?

Mise à jour :

J'ai oublié de mentionner que nous utilisons C++ (non géré).

14voto

0xA3 Points 73439

Vous pourriez vérifier si le processus parent est services.exe ou svchost.exe. Vous pouvez également demander au gestionnaire de contrôle des services, à l'aide de WinApi, si votre service est lancé et si l'identifiant du processus actuel est égal à celui du service lancé.

En C#, le code suivant le ferait (puisqu'il est basé sur WinApi, cela devrait fonctionner de manière similaire en C++, exemple de code ici ):

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        if (IsRunningAsService("myServiceName"))
        {
            Console.WriteLine("I'm a service.");
        }
        else
        {
            Console.WriteLine("I'm not a service.");
        }
    }

    static bool IsRunningAsService(string serviceName)
    {
        IntPtr serviceManagerHandle = WinApi.OpenSCManager(null, null, (uint)WinApi.SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
        if (serviceManagerHandle == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        IntPtr serviceHandle = WinApi.OpenService(serviceManagerHandle, serviceName, (uint)WinApi.SERVICE_ACCESS.SERVICE_ALL_ACCESS);
        if (serviceHandle == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        WinApi.SERVICE_STATUS_PROCESS serviceStatus = new WinApi.SERVICE_STATUS_PROCESS();
        byte[] buffer = new byte[1000];
        int bytesNeeded;
        GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            bool success = WinApi.QueryServiceStatusEx(serviceHandle, WinApi.SC_STATUS_PROCESS_INFO, buffer, 1000, out bytesNeeded);
            if (!success)
            {
                throw new Win32Exception();
            }
            IntPtr buffIntPtr = bufferHandle.AddrOfPinnedObject();
            Marshal.PtrToStructure(buffIntPtr, serviceStatus);
        }
        finally
        {
            bufferHandle.Free();
        }

        WinApi.CloseServiceHandle(serviceHandle);
        WinApi.CloseServiceHandle(serviceManagerHandle);

        return Process.GetCurrentProcess().Id == serviceStatus.processID;
    }
}

Importations de l'API Windows :

class WinApi
{
    [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CloseServiceHandle(IntPtr hSCObject);

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ControlService(IntPtr hService, SERVICE_CONTROL dwControl, ref SERVICE_STATUS lpServiceStatus);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool QueryServiceStatusEx(IntPtr serviceHandle, int infoLevel, byte[] buffer, int bufferSize, out int bytesNeeded);

    [Flags]
    public enum SCM_ACCESS : uint
    {
        /// <summary>
        /// Required to connect to the service control manager.
        /// </summary>
        SC_MANAGER_CONNECT = 0x00001,

        /// <summary>
        /// Required to call the CreateService function to create a service
        /// object and add it to the database.
        /// </summary>
        SC_MANAGER_CREATE_SERVICE = 0x00002,

        /// <summary>
        /// Required to call the EnumServicesStatusEx function to list the
        /// services that are in the database.
        /// </summary>
        SC_MANAGER_ENUMERATE_SERVICE = 0x00004,

        /// <summary>
        /// Required to call the LockServiceDatabase function to acquire a
        /// lock on the database.
        /// </summary>
        SC_MANAGER_LOCK = 0x00008,

        /// <summary>
        /// Required to call the QueryServiceLockStatus function to retrieve
        /// the lock status information for the database.
        /// </summary>
        SC_MANAGER_QUERY_LOCK_STATUS = 0x00010,

        /// <summary>
        /// Required to call the NotifyBootConfigStatus function.
        /// </summary>
        SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020,

        /// <summary>
        /// Includes STANDARD_RIGHTS_REQUIRED, in addition to all access
        /// rights in this table.
        /// </summary>
        SC_MANAGER_ALL_ACCESS = ACCESS_MASK.STANDARD_RIGHTS_REQUIRED |
            SC_MANAGER_CONNECT |
            SC_MANAGER_CREATE_SERVICE |
            SC_MANAGER_ENUMERATE_SERVICE |
            SC_MANAGER_LOCK |
            SC_MANAGER_QUERY_LOCK_STATUS |
            SC_MANAGER_MODIFY_BOOT_CONFIG,

        GENERIC_READ = ACCESS_MASK.STANDARD_RIGHTS_READ |
            SC_MANAGER_ENUMERATE_SERVICE |
            SC_MANAGER_QUERY_LOCK_STATUS,

        GENERIC_WRITE = ACCESS_MASK.STANDARD_RIGHTS_WRITE |
            SC_MANAGER_CREATE_SERVICE |
            SC_MANAGER_MODIFY_BOOT_CONFIG,

        GENERIC_EXECUTE = ACCESS_MASK.STANDARD_RIGHTS_EXECUTE |
            SC_MANAGER_CONNECT | SC_MANAGER_LOCK,

        GENERIC_ALL = SC_MANAGER_ALL_ACCESS,
    }

    [Flags]
    enum ACCESS_MASK : uint
    {
        DELETE = 0x00010000,
        READ_CONTROL = 0x00020000,
        WRITE_DAC = 0x00040000,
        WRITE_OWNER = 0x00080000,
        SYNCHRONIZE = 0x00100000,

        STANDARD_RIGHTS_REQUIRED = 0x000f0000,

        STANDARD_RIGHTS_READ = 0x00020000,
        STANDARD_RIGHTS_WRITE = 0x00020000,
        STANDARD_RIGHTS_EXECUTE = 0x00020000,

        STANDARD_RIGHTS_ALL = 0x001f0000,

        SPECIFIC_RIGHTS_ALL = 0x0000ffff,

        ACCESS_SYSTEM_SECURITY = 0x01000000,

        MAXIMUM_ALLOWED = 0x02000000,

        GENERIC_READ = 0x80000000,
        GENERIC_WRITE = 0x40000000,
        GENERIC_EXECUTE = 0x20000000,
        GENERIC_ALL = 0x10000000,

        DESKTOP_READOBJECTS = 0x00000001,
        DESKTOP_CREATEWINDOW = 0x00000002,
        DESKTOP_CREATEMENU = 0x00000004,
        DESKTOP_HOOKCONTROL = 0x00000008,
        DESKTOP_JOURNALRECORD = 0x00000010,
        DESKTOP_JOURNALPLAYBACK = 0x00000020,
        DESKTOP_ENUMERATE = 0x00000040,
        DESKTOP_WRITEOBJECTS = 0x00000080,
        DESKTOP_SWITCHDESKTOP = 0x00000100,

        WINSTA_ENUMDESKTOPS = 0x00000001,
        WINSTA_READATTRIBUTES = 0x00000002,
        WINSTA_ACCESSCLIPBOARD = 0x00000004,
        WINSTA_CREATEDESKTOP = 0x00000008,
        WINSTA_WRITEATTRIBUTES = 0x00000010,
        WINSTA_ACCESSGLOBALATOMS = 0x00000020,
        WINSTA_EXITWINDOWS = 0x00000040,
        WINSTA_ENUMERATE = 0x00000100,
        WINSTA_READSCREEN = 0x00000200,

        WINSTA_ALL_ACCESS = 0x0000037f
    }

    [Flags]
    public enum SERVICE_ACCESS : uint
    {
        STANDARD_RIGHTS_REQUIRED = 0xF0000,
        SERVICE_QUERY_CONFIG = 0x00001,
        SERVICE_CHANGE_CONFIG = 0x00002,
        SERVICE_QUERY_STATUS = 0x00004,
        SERVICE_ENUMERATE_DEPENDENTS = 0x00008,
        SERVICE_START = 0x00010,
        SERVICE_STOP = 0x00020,
        SERVICE_PAUSE_CONTINUE = 0x00040,
        SERVICE_INTERROGATE = 0x00080,
        SERVICE_USER_DEFINED_CONTROL = 0x00100,
        SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
                          SERVICE_QUERY_CONFIG |
                          SERVICE_CHANGE_CONFIG |
                          SERVICE_QUERY_STATUS |
                          SERVICE_ENUMERATE_DEPENDENTS |
                          SERVICE_START |
                          SERVICE_STOP |
                          SERVICE_PAUSE_CONTINUE |
                          SERVICE_INTERROGATE |
                          SERVICE_USER_DEFINED_CONTROL)
    }

    [Flags]
    public enum SERVICE_CONTROL : uint
    {
        STOP = 0x00000001,
        PAUSE = 0x00000002,
        CONTINUE = 0x00000003,
        INTERROGATE = 0x00000004,
        SHUTDOWN = 0x00000005,
        PARAMCHANGE = 0x00000006,
        NETBINDADD = 0x00000007,
        NETBINDREMOVE = 0x00000008,
        NETBINDENABLE = 0x00000009,
        NETBINDDISABLE = 0x0000000A,
        DEVICEEVENT = 0x0000000B,
        HARDWAREPROFILECHANGE = 0x0000000C,
        POWEREVENT = 0x0000000D,
        SESSIONCHANGE = 0x0000000E
    }

    public enum SERVICE_STATE : uint
    {
        SERVICE_STOPPED = 0x00000001,
        SERVICE_START_PENDING = 0x00000002,
        SERVICE_STOP_PENDING = 0x00000003,
        SERVICE_RUNNING = 0x00000004,
        SERVICE_CONTINUE_PENDING = 0x00000005,
        SERVICE_PAUSE_PENDING = 0x00000006,
        SERVICE_PAUSED = 0x00000007
    }

    [Flags]
    public enum SERVICE_ACCEPT : uint
    {
        STOP = 0x00000001,
        PAUSE_CONTINUE = 0x00000002,
        SHUTDOWN = 0x00000004,
        PARAMCHANGE = 0x00000008,
        NETBINDCHANGE = 0x00000010,
        HARDWAREPROFILECHANGE = 0x00000020,
        POWEREVENT = 0x00000040,
        SESSIONCHANGE = 0x00000080,
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct SERVICE_STATUS
    {
        public static readonly int SizeOf = Marshal.SizeOf(typeof(SERVICE_STATUS));
        public SERVICE_TYPES dwServiceType;
        public SERVICE_STATE dwCurrentState;
        public uint dwControlsAccepted;
        public uint dwWin32ExitCode;
        public uint dwServiceSpecificExitCode;
        public uint dwCheckPoint;
        public uint dwWaitHint;
    }

    [Flags]
    public enum SERVICE_TYPES : int
    {
        SERVICE_KERNEL_DRIVER = 0x00000001,
        SERVICE_FILE_SYSTEM_DRIVER = 0x00000002,
        SERVICE_WIN32_OWN_PROCESS = 0x00000010,
        SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
        SERVICE_INTERACTIVE_PROCESS = 0x00000100
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public class SERVICE_STATUS_PROCESS
    {
        public int serviceType;
        public int currentState;
        public int controlsAccepted;
        public int win32ExitCode;
        public int serviceSpecificExitCode;
        public int checkPoint;
        public int waitHint;
        public int processID;
        public int serviceFlags;
    }

    public const int SC_STATUS_PROCESS_INFO = 0;
}

Une version C++ de la même fonction :

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

2voto

nbolton Points 8244

Voici un peu de code que j'ai créé (qui semble bien fonctionner). Je m'excuse pour les en-têtes manquants, les #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;

}

1voto

Rob Kennedy Points 107381

Si le programme s'exécute sans paramètres, vous supposez qu'il s'agit d'un service. Changez que et le reste de vos problèmes de démarrage de service disparaissent. Exiger un paramètre pour que le programme agisse comme un service. Lorsque vous installez le service, il suffit d'inclure ce paramètre dans la ligne de commande que vous enregistrez auprès de Windows.

Sans paramètres, faites en sorte que le programme imprime sa documentation d'utilisation et quitte. Il peut y expliquer, par exemple, que les utilisateurs doivent utiliser le logiciel -f pour le débogage en ligne de commande, -i pour installer le service, et -u à désinstaller, et qu'ils ne devraient pas utiliser -s car cela l'obligerait à essayer de fonctionner comme un service à partir de la ligne de commande, ce qui n'est pas un cas d'utilisation supporté. (Ils devraient utiliser net start o sc start pour démarrer le service à la place).

0voto

Dmitry Points 3946

La vérification d'un compte utilisateur vous aiderait-elle ? IIRC un service doit être exécuté en tant que système ou quelque chose de très similaire et je suppose que vous exécutez votre application en mode débogage sous votre compte utilisateur normal. Je pense OpenProcessToken y GetTokenInformation con TokenUser seraient utiles ici.

0voto

shellster Points 323

Si votre application est exécutée en tant qu'application console (lorsqu'elle n'est pas exécutée en tant que service), une solution simple consiste à vérifier si une console a été allouée :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X