简体   繁体   English

我们如何判断C ++应用程序是否作为Windows服务启动?

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

We have a console app which we launch from command prompt for debugging, but we also launch this as an NT service for production. 我们有一个控制台应用程序,我们从命令提示符启动进行调试,但我们也将其作为生产的NT服务启动。

Right now, the code has this logic: 现在,代码有这样的逻辑:

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

Is there a better way to check how the process has been launched? 有没有更好的方法来检查流程是如何启动的? We're an open source project, so every time we get a new Windows developer we have to explain that they must specify the -f arg to stop the app from connecting to the service controller. 我们是一个开源项目,所以每次我们得到一个新的Windows开发人员时,我们必须解释他们必须指定-f arg来阻止应用程序连接到服务控制器。

What about checking the parent process? 那么检查父进程呢?

Update: 更新:

I forgot to mention that we're using C++ (unmanaged). 我忘了提到我们正在使用C ++(非托管)。

You could check whether process parent is services.exe or svchost.exe. 您可以检查进程父级是services.exe还是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. 或者,您可以使用WinApi查询服务控制管理器,无论您的服务是否已启动且当前进程ID是否等于已启动服务的进程ID。

In C# the following code would do that (since it is WinApi-based this should work similarly in C++, sample code here ): 在C#中,下面的代码会这样做(因为它是基于WinApi的,这应该在C ++中类似地工作,示例代码在这里 ):

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: Windows API导入:

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: 相同功能的C ++版本:

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

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 . 如果您想查看完整版本,请查看此处的错误标题,#undefines等道歉。

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;

} }

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. 安装服务时,只需在您向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. 例如,它可以解释用户应该使用-f进行命令行调试, -i用于安装服务, -u用于卸载,并且他们不应该使用-s本身,因为这会使它尝试运行就像来自命令行的服务一样,它不是受支持的用例。 (They should use net start or sc start to start the service instead.) (他们应该使用net startsc start来启动服务。)

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. IIRC服务将作为系统帐户或非常类似的东西运行,我假设您在普通用户帐户下以调试模式运行您的应用程序。 I think OpenProcessToken and GetTokenInformation with TokenUser functions would help here. 我认为使用TokenUser函数的OpenProcessTokenGetTokenInformation会对此有所帮助。

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 C++ 在 Windows 中确定应用程序是否已启动? - How to determine an application is already launched or not in windows using C++? 如何告诉窗口设置盖动作在C ++中什么也不做 - how to tell windows to set lid action to do nothing in C++ 如何读取 windows 服务启动的控制台应用程序的 output - How to read output of a console application launched by a windows service 我如何等待由我的应用程序Qt / C ++启动的另一个应用程序启动的应用程序 - How can I wait for an application launched by another application launched by my application Qt/C++ 如何更改C ++ Windows服务的显示名称? - How do I change the display name of a c++ windows service? 如何使用C ++为Windows应用程序创建GUI? - How do I create a GUI for a windows application using C++? 如何将PDCurses链接到Windows上的C ++应用程序? - How do I link PDCurses to a C++ application on Windows? 通过Windows命令行,我们如何编译Netbeans C / C ++应用程序? - Via Windows command line, how can we compile a Netbeans C/C++ application? 从 c++ windows 服务应用程序使用 web 服务 - consume a web service from a c++ windows service application 如何从Windows Service主要功能启动C ++本机应用程序(该应用程序与控制台交互)? - How can I launch a C++ native application from a Windows Service main function (the application interacts with the console)?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM