如何使用WinAPI函数检查当前进程是否正在作为Windows Service运行?

时间:2019-04-04 20:52:32

标签: c++ winapi windows-services

我有一个可以作为简单控制台应用程序运行或可以注册为Windows Service的程序。我想在main()函数中检测当前运行的上下文:

#include <windows.h>

BOOL IsWindowsService()
{
    ???
}

int main(int argc, char** argv)
{
    if (IsWindowsService())
    {
        // Running as Windows Service...
        RunService();
        return;
    }

    // Running as console application...    
    return 0;
}

主要用例是具有一个exe文件,该文件可以安装并作为Windows服务运行,并带有'--install'和'--start'参数,或者在控制台模式下无需任何参数即可执行(例如,从VS运行)调试器)。

可以帮我实现IsWindowsService()函数吗?

5 个答案:

答案 0 :(得分:3)

int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
    { 
    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        { SERVICE_NAME,(LPSERVICE_MAIN_FUNCTION)ServiceMain },
    { NULL,NULL }
    };
    if (StartServiceCtrlDispatcher(ServiceTable))
        //service
    else app; // last error ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
}


VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{...}

文档https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-startservicectrldispatchera

答案 1 :(得分:0)

通常这样做是为了易于调试代码。您需要阅读this MSDN page,有关日志记录的注释非常重要,因为这些printf()语句现在非常吸引人,但是当它作为服务运行时,您将视而不见。

最后一段告诉您如何确定您的程序是否实际上作为控制台应用程序运行。报价:

  

有时,出于调试目的,可能有必要将服务作为控制台应用程序运行。在这种情况下,StartServiceCtrlDispatcher函数将返回ERROR_FAILED_SERVICE_CONTROLLER_CONNECT。因此,请确保您的代码结构合理,以便在返回此错误时不调用特定于服务的代码。

如此简单,总是调用StartServiceCtrlDispatcher()并注意FALSE返回值和GetLastError()返回代码。

答案 2 :(得分:0)

似乎我找到了解决该问题的好方法,不需要提供特殊的命令行参数来处理(@RbMn提供的解决方案):

BOOL IsWindowsService()
{
    DWORD sessionId = 0;
    ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
    return !sessionId;
}

此解决方案之所以有效,是因为all Windows Services run in Session 0 along with applications

答案 3 :(得分:-1)

一般方法是:首先,使用GetCurrentProcessId获取当前进程ID。然后使用EnumServicesStatusEx检索所有正在运行的服务的列表,以查看当前pid是否与pid匹配。

BOOL IsWindowsService()
{
    LONG lRet = 0;
    BOOL bRet = FALSE;
    SC_HANDLE hSCM = NULL;
    char *pBuf = NULL;
    DWORD dwBufSize = 0;
    DWORD dwBufNeed = 0;
    DWORD dwNumberOfService = 0;
    ENUM_SERVICE_STATUS_PROCESS *pServiceInfo = NULL;

    hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
    if (NULL == hSCM)
    {
        printf("OpenSCManager error.\n");
        return false;
    }
    EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, dwBufSize, &dwBufNeed, &dwNumberOfService, NULL, NULL);
    dwBufSize = dwBufNeed + sizeof(ENUM_SERVICE_STATUS_PROCESS);
    pBuf = (char *)malloc(dwBufSize);
    memset(pBuf, 0, dwBufSize);
    bRet = EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, (LPBYTE)pBuf, dwBufSize, &dwBufNeed, &dwNumberOfService, NULL, NULL);
    if (bRet == FALSE)
    {
        printf("EnumServicesStatusEx error.\n");
        ::CloseServiceHandle(hSCM);
        free(pBuf);
        return false;
    }
    CloseServiceHandle(hSCM);
    pServiceInfo = (LPENUM_SERVICE_STATUS_PROCESS)pBuf;
    DWORD id = GetCurrentProcessId();
    for (unsigned int i = 0; i < dwNumberOfService; i++)
    {
        if (pServiceInfo[i].ServiceStatusProcess.dwProcessId == id)
            return true;
    }
    free(pBuf);
    return false;
}

答案 4 :(得分:-1)

您可以使用 system() 功能。它将执行可以在命令提示符下运行的任何命令。像这样使用它:

system("tasklist > tasks.txt");

这会将所有正在运行的任务存储到 tasks.txt 。然后,您可以通过在文件中搜索来检查程序是否正在运行。

有关 任务列表 的更多信息,运行命令提示符,执行以下操作:

tasklist /?