Trigger-Start Services Recipe

Trigger-Start Services Recipe

  • Comments 11
  • Likes

Windows services enable code to run in the background, even if no user is logged on. These background services provide a lot of useful functionality while minimizing interference with the user’s desktop.

Services have been around for a long time and have proven to be quite useful. Many application developers, OEMs, and IT organizations use services. On a typical system, this can result in a large number of services running at a given time. For example, my machine has about 110 services running right now. This large number of services is due to multiple beta products, different users, multiple desktop and everything running together at the same time.

Running this many services can lengthen Windows startup time, increase the machine’s attack surface, takes a toll on memory, CPU, and power usage. While developers may have machines that can handle loads like these, my machine is a powerful enough (8G of RAM, 4 cores…) so I don’t really care, consumers probably do.

If you are developing Windows services, be sure to write them in the most efficient way possible. The service should run only when it needs to be running. You could write an on-demand service that can be started and stopped by applications that need it, or configure the service as a delayed auto-start service. “Windows Services Enhancements” by Kenny Kerr covers these useful features, among others.

However, Windows 7 and Windows Server 2008 R2provide a new mechanism for making sure services run only when needed. It is called Trigger-Started Service (TSS). Basically, TSS tells the operating system to start the service under certain conditions, such as when specific devices or types of devices are plugged into the computer, or when a computer is added or removed from a domain. Additional criteria are specified in the services documentation.

So what is the problem?

There is already good documentation for creating or enabling services to run like this, and it’s quite easy to do in C++. However, while there are several examples and code snippets for doing the same thing in managed code, none of these implementations provides a general purpose, easy-to-reuse solution that doesn’t, in most cases, ultimately require a lot of interoperability code to be written or modified.

The Windows 7 Trigger-Start Services Recipe gives managed code developers an easier way to write TSS.

In the recipe, you’ll find the Win7TriggerStartRecipe assembly, which provides a clean .NET interface that can be used to communicate with Windows Service Control Manager (SCM) to configure a service as a TSS. The SCM is a large tool, as it includes the Win 32 API, a command line interface, and reflects some of its functionality in the Windows Control Panel. You will use its API to configure services to use TSS.

Usually, a service is configured to work as a TSS immediately after the service is installed. Since the recommended method of installing a .NET service is to use installutil.exe, bundled with the .NET framework, and this requires that the service have an Installer-derived class, the best place to use the trigger start assembly will be in an AfterInstall handler in the ServiceInstaller implementation. However, it can be used by any application with administrative privileges to register any service.

To use the recipe, you’ll first need to add a reference to the Win7TriggerStartRecipe assembly in your project and then add the following Using statement:

using WindowsRecipes.TriggerStartServices;

Before you can start working, you need to double check that underlying operating system supports this feature. To do so, call the static method IsSupported in the TriggerStart class. This is currently supported only on Windows 7 and Windows Server 2008 R2.

Next, create an instance of the TriggerStart class. You can use one of the three constructors, passing a reference to a specific service implementation to link (bind) the required service to the TriggerStart manager.

After that, you can add (or remove) triggers as necessary from the Triggers collection, and then register these changes with the SCM by calling CommitChangesToService. The following code registers a service to start when a generic USB Drive is plugged into the PC.

   1: try
   2: {
   3:     if (!TriggerStart.IsSupported)
   4:     {
   5:         Console.WriteLine("The current system does not support trigger-start services.");
   6:         return;
   7:     }
   8:     //Without error checking and logging, the following lines of code are 
   9:     //everything necessary to register a service
  10:     TriggerStart ts = new TriggerStart(this.USBServiceInstaller.ServiceName);
  11:  
  12:     //Add a trigger that tells the SCM to start the service when USB device plugged in
  13:     Guid Guid_USBDevice = new Guid("53f56307-b6bf-11d0-94f2-00a0c91efb8b");
  14:     const string DeviceName = @"USBSTOR\GenDisk";
  15:  
  16:     ts.Triggers.Add(
  17:                 new DeviceInterfaceArrivalTrigger(
  18:                         ServiceTriggerAction.ServiceStart,
  19:                         Guid_USBDevice,DeviceName));
  20:     ts.Triggers.Add(
  21:                 new DeviceInterfaceArrivalTrigger(
  22:                         ServiceTriggerAction.ServiceStop, 
  23:                         Guid_USBDevice, 
  24:                         DeviceName));
  25:                                 
  26:     //Commit our changes (this registers these triggers with the SCM)
  27:     ts.CommitChangesToService();
  28:  
  29:     Console.WriteLine("Trigger registration successful.  
  30:                        To manually confirm this, type 'sc qtriggerinfo " 
  31:                        + this.USBServiceInstaller.ServiceName + "'.");
  32: }
  33: catch (Exception ex)
  34: {
  35:     Console.WriteLine("Registering the service for trigger starting failed:");
  36:     Console.WriteLine(ex.Message);
  37: }

As you can see from the above code, after checking that TSS is supported, you create a new instance of the TriggerStart class, passing a specific service implementation – this.USBServiceInstaller.ServiceName.

Next you define a GUID that is associated with a USB device, and provide a DeviceName. Then add specific triggers like DeviceInterfaceArrivalTrigger, passing a specific trigger action like ServiceTriggerAction.ServiceStart that starts the service when a generic USB storage device is plugged into the computer USB port. The DeviceInterfaceArrivalTrigger activates when the specified device or type of device is connected to the system or on startup if the device is already connected. You will need to specify the DeviceInterfaceClass GUID that identifies the class of device you are looking for, and at least one Hardware ID or Compatible ID that identifies a list of matching device names to watch for.

The next trigger defines the stop condition when the USB key is pulled out, as there is no need to continue running the service if the USB is not plugged in and the service will start again the next time you plug in a generic USB storage device.

Lastly, you need to call the CommitChangesToService method, to save any changes to the service configuration.

That’s all there is to it. Again, when first associated with a service, the Triggers collection contains any triggers currently associated with the service. Modify this collection as necessary, commit the changes, and you’re done.

DeviceInterfaceArrivalTrigger, is just one of many triggers that you can use, here is a list of the implemented triggers in this recipe:

  • DomainEventTrigger: This trigger activates when the computer joins or leaves a domain. All you need to do is specify which action in the constructor or by using the EventType member.
  • FirewallEventTrigger: This trigger activates when one of a group of specified ports is opened or closed on the firewall. You’ll start by specifying a list of the ports (FirewallPort) you’re interested in monitoring by using the Ports member. You’ll then specify whether you’d like the trigger to activate when they open or close with the EventType member. The FirewallPort structure contains optional ServiceName and ServicePath members if you’d like to specify the service listening in on the event.
  • GroupPolicyEventTrigger: This simple trigger fires when changes are made to group policy. The trigger simply needs to specify whether to monitor user or machine policy through the EventType member.
  • IPAddressAvailabilityTrigger: This trigger fires when the computer joins its first network, or leaves its last network. You specify which by using the EventType member.
  • CustomEventTrigger: This trigger mostly exists for the assembly to be able to modify services with existing triggers of this type. You can use it to add your own custom trigger, but because the expected data is unknown unmanaged data provided by a specified Events for Windows (EFW) provider, you’ll need to marshal it yourself. Still, if the data is easily marshaled, you may find this helpful for adding your own custom triggers.

If you want to understand how the recipe works, please download and review the recipe code and documentation

Here is a short video by Kate Gregory, explaining this in great detail.

Follow Yochay on Twitter

(Post edited by Barbara Alban)

11 Comments
You must be logged in to comment. Sign in or Join Now
  • Villard
    57 Posts

    For most of the people, the ability of keeping real warm and durability are the features besides the prices of the Moncler articles. www.kissmonclerjackets.com

  • Clarissat
    38 Posts

    Review of Moncler shop in Paris, France- Moncler shop in Paris for best skiing jacket. http://bit.ly/uET5Ss

  • Clarissat
    38 Posts

    For the most part, these will be unique to each individual budget and lifestyle, but there are also some general goals that everyone should include. <a href="www.bestmonclercoats.com/.../a>

  • You will need to specify the DeviceInterfaceClass GUID that identifies the class of device you are looking for, and at least one Hardware ID or Compatible ID that identifies a list of matching device names to watch for.www.rosettastonev3.com/rosetta-stone-arabic-p-2.html.

  • This large number of services is due to multiple beta products, different users, multiple desktop and everything running together at the same time. www.rosettastonev3.com/rosetta-stone-arabic-p-2.html.

  • For example, my machine has about 110 services running right now. This large number of services is due to multiple beta products, different users, multiple desktop and everything running together at the same time.http://www.rosettastonev3.com/.

  • Villard
    57 Posts

    Modify this collection as necessary, commit the changes, and you’re done.www.bestmonclercoats.com

  • Villard
    57 Posts

    Modify this collection as necessary, commit the changes, and you’re done.www.bestmonclercoats.com

  • Arvid
    22 Posts

    Rosetta Stone takes the drudgery out of putting together a grammatically correct sentence in another language. Instead of laboriously calculating the correct person, number, gender, and/or case of a phrase, you learn the correct phrasing naturally from ample listing, speaking, reading, and writing exercises in Rosetta Stone. This training then starts to seem more like solving a challenging puzzle than rote memorization, http://www.ofrosettastone.com/

    and you are now repeating the process of learning a second language as you did your own mother tongue.

  • arekpio
    13 Posts

    very usefull article, good jobs, http://www.firmyw1miejscu.pl

  • Very useful article, looks quite handy for me!!..

    webhostingreview.info/business-hosting