Enumerate NT Services

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

I recently needed the code which displays all Windows NT
services from the service database (kept in the registry) and for each service some
essential information (service name, status etc.). As always, there are at least 2
possibilities. Instead of coding in the application, I created a small class which returns
a pointer to the list of services. The contents of the list depends on the arguments
supplied when enumerating services.

Class declaration:

class TTrixServiceInfo {
public:
    CString ServiceName;
    CString DisplayName;
    CString BinaryPath;
    DWORD ServiceType;
    DWORD StartType;
    DWORD ErrorControl;
    DWORD CurrentState;
public:
    TTrixServiceInfo();
    TTrixServiceInfo& operator=(const TTrixServiceInfo& source);
    CString GetServiceType(void);
    CString GetStartType(void);
    CString GetErrorControl(void);
    CString GetCurrentState(void);
    static TTrixServiceInfo *EnumServices(DWORD serviceType,
        DWORD serviceState,DWORD *count);
};

Description:

Each object of this class contains information for one
service. To build a list of services, execute the static function EnumServices(). There
are several possibilities:

ServiceType is a bit OR combination of SERVICE_WIN32 and
SERVICE_DRIVER.
ServiceState is a bit OR combination of SERVICE_ACTIVE and SERVICE_INACTIVE.

The static function EnumServices() returns a pointer to a
list of TTrixServiceInfo objects (or NULL in case of any error). The number of objects in
a list is returned via a count argument. Be sure to deallocate the memory when you no
longer need the list. Following is the example:

TTrixServiceInfo *lpservice = NULL;
DWORD count;
lpservice = TTrixServiceInfo::EnumServices(SERVICE_WIN32,SERVICE_ACTIVE|SERVICE_INACTIVE,&count);
if (lpservice) {
    for (DWORD index = 0; index < count; index ++) {
        printf("%d. %s, %sn", index, lpservice[index].DisplayName,
            lpservice[index].GetCurrentState());
    }
    delete [] lpservice;
}

Other member functions return a user friendly name for
various status information. Notice the operator= member function. It is a good practise to
implement this operator in any similar class since it makes it very easy to copy the
contents of the object (in this case returned in a list) to some other application
specific data structure which contains the object of the same class. For example, if your
application needs also the version information about the binary file etc., you probably
declare your class in the following way:

class CServiceInfo {
public:
       CVersionInformation VerInfo;
       TTrixServiceInfo SrvcInfo;
       ...
};

Now, it is easy to populate the object of this class
(pseudo code with no error checking):

TTrixServiceInfo *lpservice = TTrixServiceInfo::EnumServices(....,&count);
int index = findService(name,lpservice,count);
myobj.SrvcInfo = lpservice[index];

Source code:

TTrixServiceInfo::TTrixServiceInfo()
{
    ServiceName.Empty();
    DisplayName.Empty();
    BinaryPath.Empty();
    ServiceType = 0;
    StartType = 0;
    ErrorControl = 0;
    CurrentState = 0;
}

TTrixServiceInfo& TTrixServiceInfo::operator=(const TTrixServiceInfo& source)
{
    ServiceName = source.ServiceName;
    DisplayName = source.DisplayName;
    BinaryPath = source.BinaryPath;
    ServiceType = source.ServiceType;
    StartType = source.StartType;
    ErrorControl = source.ErrorControl;
    CurrentState = source.CurrentState;
    return *this;
}

CString TTrixServiceInfo::GetServiceType(void)
{
    // Winnt.h
    CString str = "UNKNOWN";
    if (ServiceType & SERVICE_WIN32) {
        if (ServiceType &
            SERVICE_WIN32_OWN_PROCESS)
            str = "WIN32_OWN_PROCESS";
        else if (ServiceType &
            SERVICE_WIN32_SHARE_PROCESS)
            str = "WIN32_SHARE_PROCESS";
        if (ServiceType &
            SERVICE_INTERACTIVE_PROCESS)
            str += "(INTERACTIVE_PROCESS)";
    }
    switch (ServiceType) {
    case SERVICE_KERNEL_DRIVER:
        str = "KERNEL_DRIVER"; break;
    case SERVICE_FILE_SYSTEM_DRIVER:
        str = "FILE_SYSTEM_DRIVER";
        break;
    };
    return str;
}

CString TTrixServiceInfo::GetStartType(void)
{
    // Winnt.h
    TCHAR *types[] = {
        "BOOT_START", // 0
        "SYSTEM_START", // 1
        "AUTO_START", // 2
        "DEMAND_START", // 3
        "DISABLED" // 4
    };
    return CString(types[StartType]);
}

CString TTrixServiceInfo::GetErrorControl(void)
{
    // Winnt.h
    TCHAR *types[] = {
        "ERROR_IGNORE", // 0
        "ERROR_NORMAL", // 1
        "ERROR_SEVERE", // 2
        "ERROR_CRITICAL" // 3
    };
    return CString(types[ErrorControl]);
}

CString TTrixServiceInfo::GetCurrentState(void)
{
    // Winsvc.h
    TCHAR *types[] = {
        "UNKNOWN",
        "STOPPED", // 1
        "START_PENDING", // 2
        "STOP_PENDING", // 3
        "RUNNING", // 4
        "CONTINUE_PENDING", // 5
        "PAUSE_PENDING", // 6
        "PAUSED" // 7
    };
    return CString(types[CurrentState]);
}

// ServiceType = bit OR of SERVICE_WIN32, SERVICE_DRIVER
// ServiceState = bit OR of SERVICE_ACTIVE, SERVICE_INACTIVE
TTrixServiceInfo *TTrixServiceInfo::EnumServices(DWORD serviceType,DWORD
                                                 serviceState,DWORD *count)
{
    // Maybe check if serviceType and serviceState have at least one constant specified
    *count = 0;
    TTrixServiceInfo *info = NULL;
    SC_HANDLE scman = ::OpenSCManager(NULL,NULL,SC_MANAGER_ENUMERATE_SERVICE);
    if (scman) {
        ENUM_SERVICE_STATUS service, *lpservice;
        BOOL rc;
        DWORD bytesNeeded,servicesReturned,resumeHandle = 0;
        rc = ::EnumServicesStatus(scman,serviceType,serviceState,&service,sizeof(service),
                                  &bytesNeeded,&servicesReturned,&resumeHandle);
        if ((rc == FALSE) && (::GetLastError() == ERROR_MORE_DATA)) {
            DWORD bytes = bytesNeeded + sizeof(ENUM_SERVICE_STATUS);
            lpservice = new ENUM_SERVICE_STATUS [bytes];
            ::EnumServicesStatus(scman,serviceType,serviceState,lpservice,bytes,
                &bytesNeeded,&servicesReturned,&resumeHandle);
            *count = servicesReturned; // Not a chance that 0 services is returned
            info = new TTrixServiceInfo [servicesReturned];

            TCHAR Buffer[1024];
            // Should be enough for service info
            QUERY_SERVICE_CONFIG *lpqch = (QUERY_SERVICE_CONFIG*)Buffer;
            for (DWORD ndx = 0; ndx < servicesReturned; ndx++) {
                info[ndx].ServiceName = lpservice[ndx].lpServiceName;
                info[ndx].DisplayName = lpservice[ndx].lpDisplayName;
                info[ndx].ServiceType = lpservice[ndx].ServiceStatus.dwServiceType;
                info[ndx].CurrentState = lpservice[ndx].ServiceStatus.dwCurrentState;
                SC_HANDLE sh = ::OpenService(scman,lpservice[ndx].lpServiceName,SERVICE_QUERY_CONFIG);
                if (::QueryServiceConfig(sh,lpqch,sizeof(Buffer),&bytesNeeded)) {
                    info[ndx].BinaryPath = lpqch->lpBinaryPathName;
                    info[ndx].StartType = lpqch->dwStartType;
                    info[ndx].ErrorControl = lpqch->dwErrorControl;
                }
                ::CloseServiceHandle(sh);
            }
            delete [] lpservice;
        }
        ::CloseServiceHandle(scman);
    }
    return info;
}

Last updated: 20 September 1998.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read