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 {
    CString ServiceName;
    CString DisplayName;
    CString BinaryPath;
    DWORD ServiceType;
    DWORD StartType;
    DWORD ErrorControl;
    DWORD CurrentState;
    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);


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,
    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 {
       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:

    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 &
            str = "WIN32_OWN_PROCESS";
        else if (ServiceType &
            str = "WIN32_SHARE_PROCESS";
        if (ServiceType &
            str += "(INTERACTIVE_PROCESS)";
    switch (ServiceType) {
        str = "KERNEL_DRIVER"; break;
        str = "FILE_SYSTEM_DRIVER";
    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[] = {
        "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
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;
    if (scman) {
        ENUM_SERVICE_STATUS service, *lpservice;
        BOOL rc;
        DWORD bytesNeeded,servicesReturned,resumeHandle = 0;
        rc = ::EnumServicesStatus(scman,serviceType,serviceState,&service,sizeof(service),
        if ((rc == FALSE) && (::GetLastError() == ERROR_MORE_DATA)) {
            DWORD bytes = bytesNeeded + sizeof(ENUM_SERVICE_STATUS);
            lpservice = new ENUM_SERVICE_STATUS [bytes];
            *count = servicesReturned; // Not a chance that 0 services is returned
            info = new TTrixServiceInfo [servicesReturned];

            TCHAR Buffer[1024];
            // Should be enough for service info
            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;
            delete [] lpservice;
    return info;

Last updated: 20 September 1998.


  • 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

  • Integrating the source code into your project

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

    Originally posted by: Thomas Blenkers


    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"

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

    Thanks again to Zoran.

    Thomas Blenkers

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

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds