How do we tell if a C++ application is launched as a Windows service?

前端 未结 5 1425
我寻月下人不归
我寻月下人不归 2020-12-16 04:55

We have a console app which we launch from command prompt for debugging, but we also launch this as an NT service for production.

Right now, the code has this logic:<

相关标签:
5条回答
  • 2020-12-16 05:10

    Would checking a user account help you? IIRC a service would be run as system account or something very similar and I assume you run your application in debug mode under your normal user account. I think OpenProcessToken and GetTokenInformation with TokenUser functions would help here.

    0 讨论(0)
  • 2020-12-16 05:10

    If your application is running as a console application (when not running as a service), a simple solution is to check whether a console has been allocated:

    if(GetConsoleWindow())
    {
        //Running as console Application
    }
    else
    {
        //Running as Service
    }
    
    0 讨论(0)
  • 2020-12-16 05:17

    Here's some code I created (seems to work nicely). Apologies for missing headers, #defines, etc. If you want to see the full version, look here.

    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 讨论(0)
  • 2020-12-16 05:18

    You could check whether process parent is services.exe or svchost.exe. Or you could query the service control manager using WinApi whether your service is started and the current process id is equal to the one of the started service.

    In C# the following code would do that (since it is WinApi-based this should work similarly in C++, sample code here):

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

    Windows API imports:

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

    A C++ version of the same function:

    #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;
    }
    
    0 讨论(0)
  • 2020-12-16 05:21

    If the program runs without parameters, you assume it's a service. Change that, and the rest of your service-startup problems go away. Require a parameter for the program to act like a service. When you install the service, simply include that parameter in the command line that you register with Windows.

    Without parameters, make the program print its usage documentation and exit. There it can explain, for example, that users should use -f for command-line debugging, -i to install the service, and -u to uninstall, and that they should not use -s themselves because that would make it try to run like a service from the command line, which isn't a supported use case. (They should use net start or sc start to start the service instead.)

    0 讨论(0)
提交回复
热议问题