Search code examples
cwinapiservicepid

Why is process ID of running Windows services zero (QueryServiceStatusEx)?


I need to determine the process ID of a Windows service (I'm using C). Seeing as I have access to the SC_HANDLE of the service, QueryServiceStatusEx is exactly the function I need. However, when accessing the dwProcessId field in the SERVICE_STATUS_PROCESS structure returned by QueryServiceStatusEx, it contains 0. Here is my code:

unsigned int get_svc_pid (SC_HANDLE svc) {

    unsigned int pid = 0;
    BYTE *svc_info = NULL;
    DWORD buf_size = 0;
    DWORD bytes_needed = 0;
    //determine svc_info size
    QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO, svc_info, buf_size, &bytes_needed);
    svc_info = malloc(bytes_needed);
    buf_size = bytes_needed;
    //get pid
    if(QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO, svc_info, buf_size, &bytes_needed) != 0){
        pid = (*(SERVICE_STATUS_PROCESS *)svc_info).dwProcessId;
    }else{
        fprintf(msglog, "ERR: QueryServiceStatusEx failed\nGLE: %u\n", (unsigned int) GetLastError());
    }
    free(svc_info);
    return pid;
}

Also: I've used EnumServiceStatusEx to enumerate all running services and output their names and PIDs contained in the ENUM_SERVICE_STATUS_PROCESS structures. The majority of the PIDs were 0. Only a few PIDs in the structures matched those listed in ProcessHacker/TaskManager.

I'm at a loss as to why the dwProcessId fields contain 0, when clearly that can't be the valid PID. Thank you in advance.


Solution

  • Per the QueryServiceStatusEx() documentation:

    The process identifier returned in the SERVICE_STATUS_PROCESS structure is valid provided that the state of the service is one of SERVICE_RUNNING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED, or SERVICE_CONTINUE_PENDING. If the service is in a SERVICE_START_PENDING or SERVICE_STOP_PENDING state, however, the process identifier may not be valid, and if the service is in the SERVICE_STOPPED state, it is never valid.

    Just because you can obtain an SC_SERVICE handle to a service does not guarantee that the service is actually running. QueryServiceStatus/Ex() tells you if the service is running or not in the dwCurrentState field.

    You are also not doing any error handling on the first QueryServiceStatusEx() call.

    Try something more like this:

    unsigned int get_svc_pid (SC_HANDLE svc)
    {
        DWORD pid = 0;
        BYTE *buf = NULL;
        DWORD buf_size = 0;
        SERVICE_STATUS_PROCESS *svc_info = NULL;
    
        //determine svc_info size
        QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO, NULL, 0, &buf_size);
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
        {
            fprintf(msglog, "ERR: QueryServiceStatusEx failed\nGLE: %u\n", GetLastError());
            return 0;
        }
    
        buf = malloc(buf_size);
        if (!buf)
        {
            fprintf(msglog, "ERR: malloc failed\nsize requested: %u\n", buf_size);
            return 0;
        }
    
        //get pid
        if (!QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO, buf, buf_size, &buf_size))
        {
            fprintf(msglog, "ERR: QueryServiceStatusEx failed\nGLE: %u\n", GetLastError());
        }
        else
        {
            svc_info = (SERVICE_STATUS_PROCESS*) buf;
            switch (svc_info->dwCurrentState)
            {
                case SERVICE_RUNNING:
                case SERVICE_PAUSE_PENDING:
                case SERVICE_PAUSED:
                case SERVICE_CONTINUE_PENDING:
                    pid = svc_info->dwProcessId;
                    break;
            }
        }
    
        free(buf);
        return pid;
    }