| Previous | Main | Next | Site Index | Download | Disclaimer | Privacy |


 

 

 

 

 

 

MORE WINDOWS SERVICES: A SUPPLEMENTARY NOTE

 

 

 

 

 

 

 

 

Windows Service: Functions used in program examples of Windows Services 1 and Windows Services 2 wherever applicable. To learn about function you can jump to C & C++ function tutorials.

 

The Index:

  1. QueryServiceStatus()

  2. QueryServiceStatusEx()

  3. GetTickCount()

  4. EnumDependentServices()

  5. HeapAlloc()

  6. HeapFree()

  7. GetProcessHeap()

  8. ControlService()

 

 

QueryServiceStatus()

 

Item

Description

Function

QueryServiceStatus().

Use

Retrieves the current status of the specified service. This function has been superseded by the QueryServiceStatusEx() function. QueryServiceStatusEx() returns the same information QueryServiceStatus() returns, with the addition of the process identifier and additional information for the service.

Prototype

BOOL QueryServiceStatus(SC_HANDLE  hService, LPSERVICE_STATUS  lpServiceStatus);

Parameters

hService - [in] Handle to the service. This handle is returned by the OpenService() or the CreateService() function, and it must have the SERVICE_QUERY_STATUS access right.

lpServiceStatus - [out] Pointer to a SERVICE_STATUS structure that receives the status information.

Return value

If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError(). The following error codes can be set by the service control manager. Other error codes can be set by the registry functions that are called by the service control manager.

  1. ERROR_ACCESS_DENIED - The handle does not have the SERVICE_QUERY_STATUS access right.

  2. ERROR_INVALID_HANDLE - The handle is invalid.

Include file

<windows.h>

Remark

The QueryServiceStatus() function returns the most recent service status information reported to the service control manager. If the service just changed its status, it may not have updated the service control manager yet. Applications can find out the current service status by interrogating the service directly using the ControlService() function with the SERVICE_CONTROL_INTERROGATE control code.

 

Table 1.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

QueryServiceStatusEx()

 

Item

Description

Function

QueryServiceStatusEx().

Use

Retrieves the current status of the specified service based on the specified information level. This function supersedes the QueryServiceStatus() function. QueryServiceStatusEx() returns the same information QueryServiceStatus() returns, with the addition of the process identifier and additional information for the service.

Prototype

BOOL QueryServiceStatusEx( SC_HANDLE hService, SC_STATUS_TYPE  InfoLevel,   LPBYTE lpBuffer, DWORD cbBufSize, LPDWORD pcbBytesNeeded);

Parameters

hService - [in] Handle to the service. This handle is returned by the CreateService() or OpenService() function, and it must have the SERIVCE_QUERY_STATUS access right.

InfoLevel - [in] Service attributes to be returned. Use SC_STATUS_PROCESS_INFO to retrieve the service status information. The lpBuffer parameter is a pointer to a SERVICE_STATUS_PROCESS structure. Currently, no other information levels are defined.

lpBuffer - [out] Pointer to the buffer that receives the status information. The format of this data depends on the value of the InfoLevel parameter. The maximum size of this array is 8K bytes. To determine the required size, specify NULL for this parameter and 0 for the cbBufSize parameter. The function will fail and GetLastError() will return ERROR_INSUFFICIENT_BUFFER. The pcbBytesNeeded parameter will receive the required size.

cbBufSize - [in] Size of the buffer pointed to by the lpBuffer parameter, in bytes.

pcbBytesNeeded - [out] Pointer to a variable that receives the number of bytes needed to store all status information, if the function fails with ERROR_INSUFFICIENT_BUFFER.

Return value

See below.

Include file

<windows.h>

Remark

The QueryServiceStatusEx() function returns the most recent service status information reported to the service control manager. If the service just changed its status, it may not have updated the service control manager yet. Applications can find the current service status by querying the service directly by using the ControlService() function with the SERVICE_CONTROL_INTERROGATE control code. 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.

 

Table 2.

 

Return Values

 

If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError(). The following errors can be returned.

 

Return code

Description

ERROR_INVALID_HANDLE

The handle is invalid.

ERROR_ACCESS_DENIED

The handle does not have the SERVICE_QUERY_STATUS access right.

ERROR_INSUFFICIENT_BUFFER

The buffer is too small for the SERVICE_STATUS_PROCESS structure. Nothing was written to the structure.

ERROR_INVALID_PARAMETER

The cbSize member of SERVICE_STATUS_PROCESS is not valid.

ERROR_INVALID_LEVEL

The InfoLevel parameter contains an unsupported value.

ERROR_SHUTDOWN_IN_PROGRESS

The system is shutting down; this function cannot be called.

 

Table 3

 

GetTickCount()

 

Item

Description

Function

GetTickCount().

Use

Retrieves the number of milliseconds that have elapsed since the system was started. It is limited to the resolution of the system timer. To obtain the system timer resolution, use the GetSystemTimeAdjustment() function.

Prototype

DWORD GetTickCount(void);

Parameters

This function has no parameters.

Return value

The return value is the number of milliseconds that have elapsed since the system was started.

Include file

<windows.h>

Remark

The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days. If you need a higher resolution timer, use a multimedia timer or a high-resolution timer. To obtain the time elapsed since the computer was started, retrieve the System Up Time counter in the performance data in the registry key HKEY_PERFORMANCE_DATA. The value returned is an 8-byte value.

 

Table 4.

 

Example Code

 

The following example demonstrates how to use this function to wait for a time interval to pass. Due to the nature of unsigned arithmetic, this code works correctly if the return value wraps one time. If the difference between the two calls to GetTickCount() is more than 49.7 days, the return value could wrap more than one time and this code will not work; use the system time instead.

DWORD dwStart = GetTickCount();

// Stop if this has taken too long

if (GetTickCount() - dwStart >= TIMELIMIT)

    Cancel();

Note that TIMELIMIT is defined as the time interval of interest to the application, in milliseconds.

 

EnumDependentServices()

 

Item

Description

Function

EnumDependentServices().

Use

Retrieves the name and status of each service that depends on the specified service; that is, the specified service must be running before the dependent services can run.

Prototype

BOOL EnumDependentServices( SC_HANDLE hService, DWORD dwServiceState, LPENUM_SERVICE_STATUS lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned );

Parameters

See below.

Return value

See below.

Include file

<windows.h>

Remark

Implemented as Unicode and ANSI versions. The returned services entries are ordered in the reverse order of the start order, with group order taken into account. If you need to stop the dependent services, you can use the order of entries written to the lpServices buffer to stop the dependent services in the proper order.

 

Table 5.

 

Parameters

 

hService - [in] Handle to the service. This handle is returned by the OpenService() or CreateService() function, and it must have the SERVICE_ENUMERATE_DEPENDENTS access right.

dwServiceState - [in] Services to enumerate based on their state. This parameter can be one of the following values.

 

Value

Meaning

SERVICE_ACTIVE

Enumerates services that are in the following states: SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, and SERVICE_PAUSED.

SERVICE_INACTIVE

Enumerates services that are in the SERVICE_STOPPED state.

SERVICE_STATE_ALL

Combines the following states: SERVICE_ACTIVE and SERVICE_INACTIVE.

 

Table 6

 

lpServices - [out] Pointer to an array of ENUM_SERVICE_STATUS structures that receives the name and service status information for each dependent service in the database. The buffer must be large enough to hold the structures, plus the strings to which their members point. The order of the services in this array is the reverse of the start order of the services. In other words, the first service in the array is the one that would be started last, and the last service in the array is the one that would be started first. The maximum size of this array is 64,000 bytes. To determine the required size, specify NULL for this parameter and 0 for the cbBufSize parameter. The function will fail and GetLastError() will return ERROR_MORE_DATA. The pcbBytesNeeded parameter will receive the required size.

cbBufSize - [in] Size of the buffer pointed to by the lpServices parameter, in bytes.

pcbBytesNeeded - [out] Pointer to a variable that receives the number of bytes needed to store the array of service entries. The variable only receives this value if the buffer pointed to by lpServices is too small, indicated by function failure and the ERROR_MORE_DATA error; otherwise, the contents of pcbBytesNeeded are undefined.

lpServicesReturned - [out] Pointer to a variable that receives the number of service entries returned.

 

Return Values

 

If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError(). The following error codes may be set by the service control manager. Other error codes may be set by the registry functions that are called by the service control manager.

 

Return code

Description

ERROR_ACCESS_DENIED

The handle does not have the SERVICE_ENUMERATE_DEPENDENTS access right.

ERROR_INVALID_HANDLE

The specified handle is invalid.

ERROR_INVALID_PARAMETER

A parameter that was specified is invalid.

ERROR_MORE_DATA

The buffer pointed to by lpServices is not large enough. The function sets the variable pointed to by lpServicesReturned to the actual number of service entries stored into the buffer. The function sets the variable pointed to by pcbBytesNeeded to the number of bytes required to store all of the service entries.

 

Table 7

 

HeapAlloc()

 

Item

Description

Function

HeapAlloc().

Use

Allocates a block of memory from a heap. The allocated memory is not movable.

Prototype

LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);

Parameters

See below.

Return value

See below.

Include file

<windows.h>

Remark

See below.

 

Table 8.

 

Parameters

 

hHeap - [in] Handle to the heap from which the memory will be allocated. This handle is returned by the HeapCreate() or GetProcessHeap() function.

dwFlags - [in] Heap allocation control. Specifying any of these values will override the corresponding value specified when the heap was created with HeapCreate(). This parameter can be one or more of the following values.

 

Value

Meaning

HEAP_GENERATE_EXCEPTIONS

The system will raise an exception to indicate a function failure, such as an out-of-memory condition, instead of returning NULL.

HEAP_NO_SERIALIZE

Serialized access will not be used for this allocation. For more information, see Remarks.

To ensure that serialized access is disabled for all calls to this function, specify HEAP_NO_SERIALIZE in the call to HeapCreate(). In this case, it is not necessary to additionally specify HEAP_NO_SERIALIZE in this function call. This value should not be specified when accessing the process heap. The system may create additional threads within the application's process, such as a CTRL+C handler, that simultaneously access the process heap.

HEAP_ZERO_MEMORY

The allocated memory will be initialized to zero. Otherwise, the memory is not initialized to zero.

 

Table 9

 

dwBytes - [in] Number of bytes to be allocated. If the heap specified by the hHeap parameter is a "non-growable" heap, dwBytes must be less than 0x7FFF8. You create a non-growable heap by calling the HeapCreate() function with a nonzero value.

 

Return Values

 

If the function succeeds, the return value is a pointer to the allocated memory block. If the function fails and you have not specified HEAP_GENERATE_EXCEPTIONS, the return value is NULL. If the function fails and you have specified HEAP_GENERATE_EXCEPTIONS, the function may generate the following exceptions.

 

Return code

Description

STATUS_NO_MEMORY

The allocation attempt failed because of a lack of available memory or heap corruption.

STATUS_ACCESS_VIOLATION

The allocation attempt failed because of heap corruption or improper function parameters.

 

Table 10

 

Note:  Heap corruption can lead to either exception. It depends upon the nature of the heap corruption.

If the function fails, it does not call SetLastError(). An application cannot call GetLastError() for extended error information.

 

Some Remarks

 

If HeapAlloc() succeeds, it allocates at least the amount of memory requested. To determine the actual size of the allocated block, use the HeapSize() function. To free a block of memory allocated by HeapAlloc(), use the HeapFree() function. Memory allocated by HeapAlloc() is not movable. The address returned by HeapAlloc() is valid until the memory block is freed or reallocated; the memory block does not need to be locked. Because the system cannot compact a private heap, it can become fragmented. Applications that allocate large amounts of memory in various allocation sizes can use the low-fragmentation heap to reduce heap fragmentation. Serialization ensures mutual exclusion when two or more threads attempt to simultaneously allocate or free blocks from the same heap. There is a small performance cost to serialization, but it must be used whenever multiple threads allocate and free memory from the same heap. Setting the HEAP_NO_SERIALIZE value eliminates mutual exclusion on the heap. Without serialization, two or more threads that use the same heap handle might attempt to allocate or free memory simultaneously, likely causing corruption in the heap. The HEAP_NO_SERIALIZE value can, therefore, be safely used only in the following situations:

  1. The process has only one thread.

  2. The process has multiple threads, but only one thread calls the heap functions for a specific heap.

  3. The process has multiple threads, and the application provides its own mechanism for mutual exclusion to a specific heap.

 

Windows Me/98/95:  The heap managers are designed for memory blocks smaller than four megabytes. If you expect your memory blocks to be larger than one or two megabytes, you can avoid significant performance degradation by using the VirtualAlloc() or VirtualAllocEx() function instead.

 

HeapFree()

 

Item

Description

Function

HeapFree().

Use

Frees a memory block allocated from a heap by the HeapAlloc() or HeapReAlloc() function.

Prototype

BOOL HeapFree( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);

Parameters

See below.

Return value

If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. An application can call GetLastError() for extended error information.

Include file

<windows.h>

Remark

See below.

 

Table 11.

 

Parameters

 

hHeap - [in] Handle to the heap whose memory block is to be freed. This handle is a returned by either the HeapCreate() or GetProcessHeap() function.

dwFlags - [in] Heap free options. Specifying the following value overrides the corresponding value specified in the flOptions parameter when the heap was created by using the HeapCreate() function.

 

Value

Meaning

HEAP_NO_SERIALIZE

Serialized access will not be used. For more information, see Remarks section. To ensure that serialized access is disabled for all calls to this function, specify HEAP_NO_SERIALIZE in the call to HeapCreate(). In this case, it is not necessary to additionally specify HEAP_NO_SERIALIZE in this function call. Do not specify this value when accessing the process heap. The system may create additional threads within the application's process, such as a CTRL+C handler, that simultaneously access the process heap.

 

Table 12

 

lpMem - [in] Pointer to the memory block to be freed. This pointer is returned by the HeapAlloc() or HeapReAlloc() function.

 

Some Remarks

 

You should not refer in any way to memory that has been freed by HeapFree(). After that memory is freed, any information that may have been in it is gone forever. If you require information, do not free memory containing the information. Function calls that return information about memory (such as HeapSize()) may not be used with freed memory, as they may return bogus data. Serialization ensures mutual exclusion when two or more threads attempt to simultaneously allocate or free blocks from the same heap. There is a small performance cost to serialization, but it must be used whenever multiple threads allocate and free memory from the same heap. Setting the HEAP_NO_SERIALIZE value eliminates mutual exclusion on the heap. Without serialization, two or more threads that use the same heap handle might attempt to allocate or free memory simultaneously, likely causing corruption in the heap. The HEAP_NO_SERIALIZE value can, therefore, be safely used only in the following situations:

  1. The process has only one thread.

  2. The process has multiple threads, but only one thread calls the heap functions for a specific heap.

  3. The process has multiple threads, and the application provides its own mechanism for mutual exclusion to a specific heap.

 

GetProcessHeap()

 

Item

Description

Function

GetProcessHeap().

Use

Obtains a handle to the heap of the calling process. This handle can then be used in subsequent calls to the heap functions.

Prototype

HANDLE GetProcessHeap(void);

Parameters

This function has no parameters.

Return value

If the function succeeds, the return value is a handle to the calling process's heap. If the function fails, the return value is NULL. To get extended error information, call GetLastError().

Include file

<windows.h>

Remark

The GetProcessHeap() function allows you to allocate memory from the process heap without having to first create a heap with the HeapCreate() function, as shown in this example:

 

HeapAlloc(GetProcessHeap(), 0, dwBytes);

 

By default, the process heap is a standard heap. To use the low-fragmentation heap, call the HeapSetInformation() function.

 

Table 13.

 

ControlService()

 

Item

Description

Function

ControlService().

Use

Sends a control code to a service.

Prototype

BOOL ControlService( SC_HANDLE hService, DWORD dwControl, LPSERVICE_STATUS lpServiceStatus);

Parameters

See below.

Return value

See below.

Include file

<windows.h>

Remark

See below.

 

Table 14.

 

Parameters

 

hService - [in] Handle to the service. This handle is returned by the OpenService() or CreateService() function. The access rights required for this handle depend on the dwControl code requested.

dwControl - [in] This parameter can be one of the following control codes.

 

Control code

Meaning

SERVICE_CONTROL_CONTINUE

Notifies a paused service that it should resume. The hService handle must have the SERVICE_PAUSE_CONTINUE access right.

SERVICE_CONTROL_INTERROGATE

Notifies a service that it should report its current status information to the service control manager. The hService handle must have the SERVICE_INTERROGATE access right.

SERVICE_CONTROL_NETBINDADD

Notifies a network service that there is a new component for binding. The hService handle must have the SERVICE_PAUSE_CONTINUE access right. However, this control code has been deprecated; use PnP functionality instead. Windows NT:  This value is not supported.

SERVICE_CONTROL_NETBINDDISABLE

Notifies a network service that one of its bindings has been disabled. The hService handle must have the SERVICE_PAUSE_CONTINUE access right. However, this control code has been deprecated; use PnP functionality instead. Windows NT:  This value is not supported.

SERVICE_CONTROL_NETBINDENABLE

Notifies a network service that a disabled binding has been enabled. The hService handle must have the SERVICE_PAUSE_CONTINUE access right. However, this control code has been deprecated; use PnP functionality instead. Windows NT:  This value is not supported.

SERVICE_CONTROL_NETBINDREMOVE

Notifies a network service that that a component for binding has been removed. The hService handle must have the SERVICE_PAUSE_CONTINUE access right. However, this control code has been deprecated; use PnP functionality instead. Windows NT:  This value is not supported.

SERVICE_CONTROL_PARAMCHANGE

Notifies a service that its startup parameters have changed. The hService handle must have the SERVICE_PAUSE_CONTINUE access right. Windows NT:  This value is not supported.

SERVICE_CONTROL_PAUSE

Notifies a service that it should pause. The hService handle must have the SERVICE_PAUSE_CONTINUE access right.

SERVICE_CONTROL_STOP

Notifies a service that it should stop. The hService handle must have the SERVICE_STOP access right.

 

Table 15

 

This value can also be a user-defined control code, as described in the following table.

 

Control code

Meaning

Range 128 to 255.

The service defines the action associated with the control code. The hService handle must have the SERVICE_USER_DEFINED_CONTROL access right.

 

Table 16

 

lpServiceStatus - [out] Pointer to a SERVICE_STATUS structure that receives the latest service status information. The information returned reflects the most recent status that the service reported to the service control manager. The service control manager fills in the structure only when ControlService() returns one of the following error codes: NO_ERROR, ERROR_INVALID_SERVICE_CONTROL, ERROR_SERVICE_CANNOT_ACCEPT_CTRL, or ERROR_SERVICE_NOT_ACTIVE. Otherwise, the structure is not filled in.

 

Return Values

 

If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError(). The following error codes can be set by the service control manager. Other error codes can be set by the registry functions that are called by the service control manager.

 

Return code

Description

ERROR_ACCESS_DENIED

The handle does not have the required access right.

ERROR_DEPENDENT_SERVICES_RUNNING

The service cannot be stopped because other running services are dependent on it.

ERROR_INVALID_HANDLE

The specified handle was not obtained using CreateService() or OpenService(), or the handle is no longer valid.

ERROR_INVALID_PARAMETER

The requested control code is undefined.

ERROR_INVALID_SERVICE_CONTROL

The requested control code is not valid, or it is unacceptable to the service.

ERROR_SERVICE_CANNOT_ACCEPT_CTRL

The requested control code cannot be sent to the service because the state of the service is SERVICE_STOPPED, SERVICE_START_PENDING, or SERVICE_STOP_PENDING.

ERROR_SERVICE_NOT_ACTIVE

The service has not been started.

ERROR_SERVICE_REQUEST_TIMEOUT

The process for the service was started, but it did not call StartServiceCtrlDispatcher(), or the thread that called StartServiceCtrlDispatcher() may be blocked in a control handler function.

ERROR_SHUTDOWN_IN_PROGRESS

The system is shutting down.

 

Table 17

 

Some Remarks

 

The ControlService() function asks the Service Control Manager (SCM) to send the requested control code to the service. The SCM sends the code if the service has specified that it will accept the code, and is in a state in which a control code can be sent to it. The SCM processes service control notifications in a serial fashion, it will wait for one service to complete processing a service control notification before sending the next one. Because of this, a call to ControlService() will block for 30 seconds if any service is busy handling a control code. If the busy service still has not returned from its handler function when the timeout expires, ControlService() fails with ERROR_SERVICE_REQUEST_TIMEOUT. To stop and start a service requires a security descriptor that allows you to do so. The default security descriptor allows the LocalSystem account, and members of the Administrators and Power Users groups to stop and start services. The QueryServiceStatus() or function returns a SERVICE_STATUS structure whose dwCurrentState and dwControlsAccepted members indicate the current state and controls accepted by a running service. All running services accept the SERVICE_CONTROL_INTERROGATE control code by default. Drivers do not accept control codes other than SERVICE_CONTROL_STOP and SERVICE_CONTROL_INTERROGATE. Each service specifies the other control codes that it accepts when it calls the SetServiceStatus() function to report its status. A service should always accept these codes when it is running, no matter what it is doing. The following table shows the action of the SCM in each of the possible service states.

 

Service state

Stop

Other controls

STOPPED (The service is not running.)

(c)

(c)

STOP_PENDING (The service is stopping.)

(b)

(b)

START_PENDING (The service is starting.)

(a)

(b)

RUNNING (The service is running.)

(a)

(a)

CONTINUE_PENDING (The service continue is pending.)

(a)

(a)

PAUSE_PENDING (The service pause is pending.)

(a)

(a)

PAUSED (The service is paused.)

(a)

(a)

 

Table 18

 

Legend:

 (a)

If the service accepts this control code, send the request to the service; otherwise, ControlService() returns zero and GetLastError() returns ERROR_INVALID_SERVICE_CONTROL.

 (b)

The service is not in a state in which a control can be sent to it, so ControlService() returns zero and GetLastError() returns ERROR_SERVICE_CANNOT_ACCEPT_CTRL.

 (c)

The service is not active, so ControlService() returns zero and GetLastError() returns ERROR_SERVICE_NOT_ACTIVE.

 

----------------------------------------End Part 4/4----------------------------------------

 

Further reading and digging:

 

  1. Microsoft Visual C++, online MSDN.

  2. Structure, enum, union and typedef story can be found at C enum, typedef, define etc..

  3. For Multibytes, Unicode characters and Localization please refer to Unicode & Multibyte 1 (Story) and Unicode & Multibyte 2 (Implementation).

  4. Windows data type information is in Windows data type.

  5. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

 

| Previous | Main | Next | Site Index | Download | Disclaimer | Privacy |