IIS Automation (2/6)

The w3svc Service

The IIS server is just another Windows service. It appears in the Services control panel under the name "World Wide Web Publishing Service" as shown on Figure 1.

IIS in the Services control panel
Figure 1: IIS in the Services control panel

"World Wide Web Publishing Service" is the human-friendly name shown in the control panel. If we open the Properties dialog for the service we can see its service name is in fact "W3SVC" (service names are case-insensitive). The Properties dialog is shown on Figure 2.

Details of the IIS service in the Services control panel
Figure 2: Details of the IIS service in the Services control panel

As any other service the w3svc service can be stopped and started from the Services control panel and be managed from code using the various APIs that exist to access Windows services.

Controlling w3svc from the command line

Services can be started and stopped from the command line using the net start and net stop commands [1] as shown below. This requires administrator privileges.

Stopping and starting the w3svc service
C:\>net stop w3svc
The World Wide Web Publishing Service service is stopping.
The World Wide Web Publishing Service service was stopped successfully.


C:\>net start w3svc
The World Wide Web Publishing Service service is starting.
The World Wide Web Publishing Service service was started successfully.

Controlling w3svc from C#

The ServiceController class [2] can be used to start and stop services from C# as shown in the example below. The program will require administrator privileges to run successfully.

Stopping a service that is already stopped or in the process of being stopped will return an error. The example below assumes the w3svc is running, if it is not it will throw an exception.

The Start and Stop functions are non-blocking and return immediately without waiting for the service operation to have completed. The WaitForStatus function is used to wait until the state of the service indicates that the operation has completed.

File: Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceProcess;

namespace IISStartStop
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceController controller = new ServiceController("w3svc");
            Console.Out.WriteLine("Status of w3svc service: " + controller.Status.ToString());

            try
            {
                Console.Out.WriteLine("Stopping w3svc service...");
                controller.Stop();

                controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(5));
                Console.Out.WriteLine("Status of w3svc service: " + controller.Status.ToString());

                Console.Out.WriteLine("Starting w3svc service...");
                controller.Start();

                controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(5));
                Console.Out.WriteLine("Status of w3svc service: " + controller.Status.ToString());
            }
            catch (Exception e)
            {
                Console.Out.WriteLine(e.ToString());
            }
        }
    }
}

The program needs to be run with administrator privileges and should produce the following output. The program also assumes w3svc is running when the program is started as it doesn't check the state of the w3svc service before trying to stop it.

Program execution
C:\>IISStartStop.exe
Status of w3svc service: Running
Stopping w3svc service...
Status of w3svc service: Stopped
Starting w3svc service...
Status of w3svc service: Running

C:\>

Controlling w3svc from C++

The normal functions to manipulate services [3] can be used to start and stop IIS. In particular MSDN has two good examples on how to start a service [4] and how to stop one [5].

The example below shows how to use these functions to start and stop the w3svc service. When stopping a service the dependent services must be stopped first but there are no dependent services in the w3svc case so dependent services are not stopped in the example below unlike the general example on the MSDN site.

Stopping a service that is already stopped or in the process of being stopped will return an error. The example below assumes the w3svc is running, if it is not it will return an error.

The StartService and ControlService functions are non-blocking and return immediately without waiting for the service operation to have completed. The QueryServiceStatusEx function is used to poll the state of the service until it indicates that the operation has completed. The SERVICE_STATUS_PROCESS.dwWaitHint variable suggests the time interval between two poll attempts. The GetWaitTime function in our example follows the recommendation found on MSDN for that time interval.

File: main.cpp
#include <windows.h>
#include <iostream>
#include <string>

DWORD GetWaitTime(DWORD waitHint)
{
    DWORD waitTime = waitHint / 10;
    if (waitTime < 1000)
    {
        waitTime = 1000;
    }
    else if (waitTime > 10000)
    {
        waitTime = 10000;
    }
    return waitTime;
}

bool WaitForServiceState(SC_HANDLE service, DWORD desiredState)
{
    DWORD startTime = GetTickCount();
    
    SERVICE_STATUS_PROCESS serviceStatus;
    DWORD bytesNeeded;
    while (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&serviceStatus,
                                sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded))
    {
        if (serviceStatus.dwCurrentState == desiredState)
        {
            break;
        }
            
        Sleep(GetWaitTime(serviceStatus.dwWaitHint));
            
        // 30-second time-out
        if ((GetTickCount() - startTime) > 30000)
        {
            std::cout << "Timed out waiting for state changed" << std::endl;
            break;
        }
    }

    return (serviceStatus.dwCurrentState == desiredState);
}

std::string ServiceStateToString(DWORD state)
{
    std::string result;
    switch (state)
    {
    case SERVICE_CONTINUE_PENDING:
        result = "SERVICE_CONTINUE_PENDING";
        break;

    case SERVICE_PAUSE_PENDING:
        result = "SERVICE_PAUSE_PENDING";
        break;

    case SERVICE_PAUSED:
        result = "SERVICE_PAUSED";
        break;

    case SERVICE_RUNNING:
        result = "SERVICE_RUNNING";
        break;

    case SERVICE_START_PENDING:
        result = "SERVICE_START_PENDING";
        break;

    case SERVICE_STOP_PENDING:
        result = "SERVICE_STOP_PENDING";
        break;

    case SERVICE_STOPPED:
        result = "SERVICE_STOPPED";
        break;
    }
    return result;
}

bool StartService(SC_HANDLE service)
{
    std::cout << "Stopping w3svc service..." << std::endl;

    if (StartService(
            service,    // handle to service 
            0,            // number of arguments 
            NULL))        // no arguments
    {
        if (WaitForServiceState(service, SERVICE_RUNNING))
        {
            SERVICE_STATUS_PROCESS serviceStatus;
            DWORD bytesNeeded;
            if (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&serviceStatus,
                sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded))
            {
                std::cout << "Status of w3svc service: " << ServiceStateToString(serviceStatus.dwCurrentState) << std::endl;
                return true;
            }
            else
            {
                std::cout << "Failed to get service status" << std::endl;
            }
        }
        else
        {
            std::cout << "Service state change wait failed" << std::endl;
        }
    }

    return false;
}

bool StopService(SC_HANDLE service)
{
    std::cout << "Stopping w3svc service..." << std::endl;

    SERVICE_STATUS_PROCESS serviceStatus;
    DWORD bytesNeeded;
    if (ControlService(service, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&serviceStatus))
    {
        if (WaitForServiceState(service, SERVICE_STOPPED))
        {
            if (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&serviceStatus,
                sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded))
            {
                std::cout << "Status of w3svc service: " << ServiceStateToString(serviceStatus.dwCurrentState) << std::endl;
                return true;
            }
            else
            {
                std::cout << "Failed to get service status" << std::endl;
            }
        }
        else
        {
            std::cout << "Service state change wait failed" << std::endl;
        }
    }
    
    return false;
}

int main(int argc, char* argv[])
{
    // Open the Service Control Manager
    SC_HANDLE serviceControlManager = OpenSCManager(
        NULL,    // Connect to the Service Control Manager on the local machine
        NULL,    // Open the SERVICES_ACTIVE_DATABASE database
        0
        );
    
    if (serviceControlManager)
    {
        SC_HANDLE service = OpenService(serviceControlManager, L"W3SVC", SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP);
        if (service)
        {
            SERVICE_STATUS_PROCESS serviceStatus;
            DWORD bytesNeeded;
            if (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&serviceStatus,
                                     sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded))
            {
                std::cout << "Status of w3svc service: " << ServiceStateToString(serviceStatus.dwCurrentState) << std::endl;

                if (StopService(service))
                {
                    if (!StartService(service))
                    {
                        std::cout << "Failed to start service" << std::endl;
                    }
                }
                else
                {
                    std::cout << "Failed to stop service" << std::endl;
                }
            }
            else
            {
                std::cout << "Failed to get service status" << std::endl;
            }

            CloseServiceHandle(service);
        }
        else
        {
            std::cout << "Failed to open Service handle" << std::endl;
        }

        CloseServiceHandle(serviceControlManager);
    }
    else
    {
        std::cout << "Failed to open Service Control Manager handle" << std::endl;
    }

    return 0;
}

The program needs to be run with administrator privileges and should produce the following output. The program also assumes w3svc is running when the program is started as it doesn't check the state of the w3svc service before trying to stop it.

Program execution
C:\>IISStartStop.exe
Status of w3svc service: SERVICE_RUNNING
Stopping w3svc service...
Status of w3svc service: SERVICE_STOPPED
Stopping w3svc service...
Status of w3svc service: SERVICE_RUNNING

C:\>

References

  1. Microsoft TechNet: Start, stop, pause, resume, or restart a service
  2. MSDN: ServiceController class documentation
  3. MSDN: Service Functions documentation
  4. MSDN: Starting a Service
  5. MSDN: Stopping a Service

blog comments powered by Disqus

Copyright(c) 2006-2015 Xavier Leclercq | Privacy policy

Home
Contact Us
Search