When the word multitasking comes into mind, we always think of foreground processes that is opened in the
taskbar. But this is not true. There are two types of tasks that runs on every computer.
First is foreground Tasks, and another is background tasks.
Foreground tasks are the process which have user interfaces so that user can easily communicate with the process easily using the application.
On the other hand, Background processes are generally hidden processes that runs in our machine and invokes specific
requirement of the system to enhance the user experience.
Windows Service is the most common way of developing a background process in Windows Environment.

There are lots of services that we all the time while we are logged into windows. To name a few:

Server: It is the main service that enables you to share files, printers etc over network.

Shell Hardware Detection: It invokes the hardware detection wizard automatically when new hardware is attached with your computer.

There are lots of services there in our windows environment which works in background, polls throughout the session with few active
long running threads.This results in slower performance, larger number of useless process running, killing up a portion of CPU, and hence slow
performance of the overall user experience.

Windows 7 (Later with Windows Server 2008) puts forth a new concept to start services based on some events. The background services once configured
to the computer will automatically start itself when the event occurs to the system. Thus eliminates the concept of running
background services infinitely. Thus we will have lesser number of background threads running on the system and also lesser amount of memory is
blocked, and hence better performance of Foreground process. Removing background threads also reduces the overall
power consumption of the system as well.

To elaborate the scenario, lets say you are about to build a service that gets some updates from the
server only when the computer is connected to the Internet. So what will be your steps :

Create a service and override the OnStart event of the Service do an Infinite loop and check Whether network is available.

Ping the Server if service is available or not.

If service is available do the respective updates, otherwise poll it again and do the same job over and over.

Now if you look into the service, you would have noticed that you are running the service throughout the day without running a single
line of statement. I mean if the computer doesnt connect to the internet, every time the Ping to server fails and
hence the loop is going on without anything to do. In such a scenario, Start triggers comes into play.

In this article you will learn how to configure the Service Triggers, so that it enables and disables your service
according to your need.

By the type of Service Events I mean the types of events that currently the API supports that you can use in your application.

Device Join : This trigger is fired when any device is joined to the system. Say printer is attached, USB device is
installed like (Pen Drive, Bluetooth Dongle etc)

Domain Join : This trigger is fired when the computer is attached to a Domain. Generally we track
this event to do specific tasks which you want to execute when computer domain server is changed.

Firewall Event : Say you have exempted one port in the Firewall. This event will be triggered when any change
in firewall settings is made.

Group Policy Change : Group policies defines the actual restrictions to the system that windows will impose during
the session is on progress. If certain Group policies are modified, this event gets triggered automatically.

Network IP Availability : This event is triggered when the first IP address on the TCP/IP networking stack becomes available to the system. You might look into the events like
FirstIPAvailable, or LastIPAvailable and run /stop your service accordingly.

Custom Event : You can also define your own custom events which is generated by Event Tracing of windows.

Windows 7 and Windows 2008 introduced a new option to Service Configuration tool sc.exe to configure and query
services that have supported triggers associated with it. Open CMD and type sc triggerinfo you will see
all the options that are available to handle trigger info.

Usage:sc <server> triggerinfo [service Name] <option1> <option2>...
The server is optional which defaults to Local Computer.

The options that you might use are :

start/device/UUID/HwId1/.. : Specifies a start of a service when specific device of UUID (ClassID) and HwId1, HwId2 ... are attached.

start/custom/UUID/data0/.. : Specifies a start of a custom service provided by Event Tracer based on hexadecimal data passed.

stop/custom/UUID/data0/.. : Stops the custom ETW service

start/strcustom/UUID/data0/.. : Specifies a start of a custom service when data is passed as string

stop/strcustom/UUID/data0/.. : Stops the custom string based custom service

start/networkon :Determines the start of a service when first IP is available

stop/networkoff : Stops the service when last IP is unavailable

start/domainjoin : Starts the service when domain is joined

stop/domainleave : Stops the service when Domain is left

start/portopen/parameter : Configures the service to start when a specified port is opened through
the firewall. You can mention the parameter with ;(semicolon) delemeter. viz. portnumber;protocolname;imagepath;servicename

stop/portclose/parameter : Stops the service when port is unavailable.

start/machinepolicy : Starts a service when machine policy is modified.

start/userpolicy : Starts a service when user group policy is modified

Therefore to configure a service that will start automatically when the system is attached to a Domain server you can just
execute:sc triggerinfo myservice start/domainjoin
The service "myservice" will automatically configured to start when domain is joined to the local computer.

To query if the service is configured properly as Triggered Start you can use qtriggerinfo switch with the servicename in sc.exe.

Usage:sc <server> qtriggerinfo [service Name] <buffer size>

Thus if you callsc qtriggerinfo myservice

It will return you the info regarding how the service is configured to Trigger start when domain is joined.

Services can also be configured to trigger by invoking ChangeServiceConfig2 API method with SERVICE_CONFIG_TRIGGER_INFO.
The SERVICE_TRIGGER_INFO structure points to an array of SERVICE_TRIGGER structures, each specifying one trigger event.

The structure of SERVICE_TRIGGER and SERVICE_TRIGGER_INFO looks like:

publicstruct SERVICE_TRIGGER
{
/// The trigger event type.
public ServiceTriggerType dwTriggerType;
/// The action to take when the specified trigger event occurs.
public ServiceTriggerAction dwAction;
/// Points to a GUID that identifies the trigger event subtype. The value
/// of this member depends on the value of the dwTriggerType member.
publicIntPtr pTriggerSubtype;
/// The number of SERVICE_TRIGGER_SPECIFIC_DATA_ITEM structures in the
/// array pointed to by pDataItems.
publicuint cDataItems;
/// A pointer to an array of SERVICE_TRIGGER_SPECIFIC_DATA_ITEM
/// structures that contain trigger-specific data.
publicIntPtr pDataItems;
}
publicstruct SERVICE_TRIGGER_INFO
{
/// The number of triggers in the array of SERVICE_TRIGGER structures
/// pointed to by the pTriggers member.
publicuint cTriggers;
/// A pointer to an array of SERVICE_TRIGGER structures that specify the
/// trigger events for the service.
publicIntPtr pTriggers;
/// This member is reserved and must be NULL.
publicIntPtr pReserved;
}

The structure is predefined and should not be changed. Now to call the API we will declare the API methods and corresponding structures. We will use the
ServiceNative.cs provided by Microsoft which you can find in the sample code associated with this application.
It uses advapi32.dll to P/Invoke native methods likeChangeServiceConfig2, QueryServiceConfig2 etc
and also defines related structs and enumerations like SERVICE_TRIGGER, SERVICE_TRIGGER_INFO, SERVICE_TRIGGER_SPECIFIC_DATA_ITEM etc.
You can call ChangeServiceConfig2 to configure the service and hence check if the service is properly installed or not using QueryServiceConfig2.

SERVICE_TRIGGER contains few important important members. dwTriggerType defines the event type that you are about to define
for which the service is going to react. The value of dwTriggerType can be :

SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL (1): The event is triggered when a specific device type defined in pTriggerSubtype as
interface guid that arrives

SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY (2) : The event is triggered when first Network IP is available and vice versa.

SERVICE_TRIGGER_TYPE_DOMAIN_JOIN (3) : The event is triggered when system is joined to a Domain. pTriggerSubtype should be DOMAIN_JOIN_GUID (1ce20aba-9851-4421-9430-1ddeb766e809) or
DOMAIN_LEAVE_GUID(ddaf516e-58c2-4866-9574-c3b615d42ea1).

SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT (4) : This event is triggred when firwall specific port is opened or 60 seconds after the port is blocked. The pTriggerSubtype should be
specified with FIREWALL_PORT_OPEN_GUID(b7569e07-8421-4ee0-ad10-86915afdad09) or FIREWALL_PORT_CLOSE_GUID(a144ed38-8e12-4de4-9d96-e64740b1a524). You can use pDataItems to
define port, protocol or executable path.

SERVICE_TRIGGER_TYPE_GROUP_POLICY(5) : This event triggered when Machine Group policy is modified. pTriggerSubtype can have MACHINE_POLICY_PRESENT_GUID(659FCAE6-5BDB-4DA9-B1FF-CA2A178D46E0) or USER_POLICY_PRESENT_GUID(54FB46C8-F089-464C-B1FD-59D1B62C3B50).

SERVICE_TRIGGER_TYPE_CUSTOM(20) : This event is triggrred by Event Tracer. pTriggerSubtype could be used to define event provider's GUID.

The dwAction defines the action that we are going to take when the event is triggered. dwAction can have two values :

Now lets create a service that will detect if network is connected and based on which It will produce a log entry(to make it the most simple) in text file.
We can check the log file and see our custom message for testing. In your original application, the corresponding code will be executed on these events.

This function will write the message with DateTime to the system folder. From the OnStart and OnStop event of the service
we wrote some silly messages to detect actually what is happenning.

Finally we need to configure the service by calling ChangeServiceConfig2 after the service gets installed in the machine.
To do this the best candidate should be the AfterInstall event of the ProjectInstaller. The event occurs Just after the service is
installed and thus it is the most right place to do this.

Right click on the Designer view of the and add Installer.

A project installer will be added In the project installer. The project installer will have two objects created within it.
Lets rename the ServiceInstaller1 to triggerStartServiceInstaller (You can leave it as it is) and change the
ServiceName to WindowsTriggerService.

Open the code window and write the code below inside the AfterInstall of the Project installer.

You should note that Trigger start services are introduced in Windows 7 and you can use it in Windows 2008 R2. Thus we need to avoid installing it in lower version of windows.
IpArrivalTriggerCapture is my own function that
is used to configure the service. triggerStartServiceInstaller represents the serviceInstaller.
You need to configure the Serviceinstaller 's servicename property to your own service.

To create the configuration section first, we need to Marshall the structs that points to the Network FirstIP arrival GUID and LastIP Removal GUID to its
native pointers. Lets allocate a pointer and Marshall the structure to a pointer using the following code :

We use AllocateServiceTrigger to get the SERVICE_TRIGGER. We define ServiceTriggerAction, ServiceTriggerType and its SubType.
The Device Data can also be configured here using SERVICE_TRIGGER_SPECIFIC_DATA_ITEM to pDataItems member, but it is not required in current context.

In this method I have allocated SERVICE_TRIGGER_INFO structure, marshall it to the memory and return back the pointer to the base location.
Finally lets look into the IPArrivalTriggerConfigure method :

In the above code we first create pointers to the respective GUIDs to invoke the NETWORK_MANAGER_FIRST_IP_ADDRESS_ARRIVAL_GUID. The pointer is used
to AlloacateServiceTrigger object. As we are registering the service to start and stop both, we have to create two object of SERVICE_TRIGGER.
We Allocate an array of SERVICE_TRIGGER and get the pointer to the structure.
Finally we create a pointer to the SERVICE_TRIGGER_INFO using GetServiceTriggerInfo method.

The API ChangeServiceConfig2 is called to configure the Service with ServiceTriggerInfoPointer.

The first argument is Service Handle pointer. We will get the ServicePointer from ServiceController.ServiceHandle,
we pass SERVICE_CONFIG_TRIGGER_INFO as second argument as it determines that the Service we are now going to
configure is ServiceTrigger. And finally ServiceTriggerInfoPointer is the pointer to configure the service as ServiceTrigger.

If you inspect the above method, I have called the API QueryServiceConfig2 two times, the first call is made with null pointer. This call will fail as we are not passing any service handler to it and this is basically to determine how many bytes needed to allocate for the current system to query the service.

For the next call, which will give you the actual result we first allocate a buffer with the same size as retrieved from the first call with Null Pointer.

We pass the Service handle to the API as first argument. The second argument is the Trigger Info Configuration(0x00000008) which is already defined in the ServiceNative. pBuffer is the allocated Buffer pointer with the outBytes.

The API will return 1 if the Trigger is found, otherwise returns 0. To get the actual TriggerInfo object we Marshall the pBuffer to SERVICE_TRIGGER_INFO as below :

There are two option to Configure a Service to Trigger Start, First using sc.exe with Triggerinfo attribute, and another programmatically.
As InstallUtil which installs .NET services does not support TriggerInfo switch, we did using ChangeServiceConfig2 API method defined in advapi32.dll

Install the application.
You can either use Installutil.exe windowstriggerservice.exe, (if current windows supports Trigger Start service, the command prompt will print "Configuring trigger-start service")
or you can use Setup Application. After you install you can inspect if the service console if the service is installed properly
or not. You can see the service listed in the Services.msc applet.

Check in Windows\System32 folder and check the file TriggerServiceLog.txt is present.

Now to check even further, open Command Prompt and type sc qtriggerinfo WindowsTriggerService

You can see if the Appropriate Triggers are installed as shown in the figure

Now, Open Network properties and Disable Network.

Inspect the TriggerServiceLog.txt file to see if there is any changes made. You will find a message
"Service Stopped as LastIP removed"

Finally, Enable the Network again and inspect the file. You will see that the trigger is started automatically again.

Share

About the Author

Oh, lets go a bit further to know him better. Visit his Website : www.abhisheksur.com to know more about Abhishek.

Abhishek also authored a book on .NET 4.5 Features and recommends you to read it, you will learn a lot from it.http://bit.ly/EXPERTCookBook

Basically he is from India, who loves to explore the .NET world. He loves to code and in his leisure you always find him talking about technical stuffs.

Presently he is working in WPF, a new foundation to UI development, but mostly he likes to work on architecture and business classes. ASP.NET is one of his strength as well. Have any problem? Write to him in his Forum.

Well, I was thinking about the same. It is the first question that came to my mind as well. Actually Custom Events are invoked using Event Tracer. I think you can easily configure it using sc.exe tool as I mentioned in the article, but as It is very new to me too, I havent developed any Custom Event Triggers using Managed API.

It is in my mind, and I would definitely try to give one example of Custom Event Trigger as soon as possible.