繁体   English   中英

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

[英]How to check that the current process is running as Windows Service using WinAPI functions?

我有一个程序可以作为简单的控制台应用程序运行,也可以注册为 Windows 服务。 我想在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 文件,该文件可以作为带有“--install”和“--start”参数的 Windows 服务安装和运行,或者在控制台模式下不带任何参数执行(例如从 VS 调试器)。

你能帮我实现IsWindowsService()函数的可能实现吗?

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

通常这样做是为了便于调试代码。 你需要阅读这个MSDN页面 ,关于日志记录的注意事项是有点重要的,因为那些printf()语句现在非常有吸引力,但是当它作为服务运行时你会像蝙蝠一样盲目。

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

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

如此轻松,只需始终调用StartServiceCtrlDispatcher()并注意FALSE返回值和GetLastError()返回代码。

似乎我找到了一个优雅的解决方案,不需要提供特殊的命令行参数来处理(@RbMn提供的解决方案):

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

此解决方案有效,因为所有Windows服务都在会话0中与应用程序一起运行

一般方法是:首先,使用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;
}

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

system("tasklist > tasks.txt");

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

有关tasklist的更多信息,请运行命令提示符,执行以下命令:

tasklist /?

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM