Enumerate NT Services

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, %s\n", 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.



Comments

  • Support for Advaced server and 2003

    Posted by Legacy on 01/29/2004 12:00am

    Originally posted by: kiran napit

    Does this class or API's of Service manager for enumerating support for Advanced win 2000 server and 2003
    kiran

    Reply
  • Integrating the source code into your project

    Posted by Legacy on 11/12/1998 12:00am

    Originally posted by: Thomas Blenkers

    Greetings,

    at first thank you to Zoran for this nice piece of work.

    When it came to me to integrate the source code, I first could not compile the TTrixServiceInfo class and ran into several dozens error messages.

    Yes, your (and mine) guess is right: you need some declarations. And you have to link with the import library for the "Advanced Windows 32 Base API" (advapi32.dll).

    For the API calls used, you have to

    #include <winsvc.h>

    in the approtiate source file (e.g. TTrixServiceInfo.h).

    Next go to the project settings, make sure to enable "all configurations", go to the "Linker" tab, and specify the "Object/Library-module"
    advapi32.lib

    After these changes, my program compiled (and ran!) flawlessly.

    Thanks again to Zoran.

    Thomas Blenkers

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Wednesday, September 24, 2014 8:00 AM - 9:00 AM PDT According to a recent Forrester Research report, many companies are choosing low-code platforms over traditional programming platforms, due to the speed with which low-code apps can be assembled and tested. With customer-facing applications on the rise, traditional programming platforms simply can't keep up with the "short schedules and rapid change cycles" required to develop these applications. Check out this upcoming webinar and join Clay Richardson from …

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds