Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Sergio Pereira

There are no half-solutions because there isn't half a problem

March 2008 - Posts

  • Creating Windows Services

    How to Create Windows Services

    It's not uncommon for an enterprise application to need some form of background processes to do continuous work. These could be tasks such as

    • Cleanup abandoned shopping carts
    • Delete temporary files left behind by some report or image generation feature
    • Send email notifications
    • Create daily reports and send them out
    • Check an email inbox
    • Pull messages from a queue
    • Perform daily or monthly archiving
    • etc

    For many of these things there are dedicated tools that provide that feature, like a reporting service (SSRS or BO,) scripts that run in the email server, or even simple executables that are fired by the Windows Task Scheduler. When you have only one or two background tasks, using something like the task scheduler may be OK, but administration quickly becomes painful when the number of tasks grows. The dedicated services like SSRS or BO can be overkill depending on the size of your application or organization.

    One approach I like to take is to create a Windows Service for the application, grouping all the different background tasks under a single project, a single .exe, and a single configuration file. Visual Studio has always had a Windows Service project type, but the process of creating a working service is not as simple as you would hope, especially when your service performs more than one independent task.

    After creating a couple of services, I realized that I definitely needed to stash all that monkey work somewhere I could just reuse later. I decided to create a helper library to assist creating and maintaining Windows services.

    The library doesn't help with all kinds of Windows services, but has helped me a lot with the type of tasks I explained above. The key to the library is the ITask interface.

    public interface ITask: IDisposable
    {
        bool Started { get; }
        string Name { get; }
        void Start();
        void Stop();
        void Execute();
    }

    This interface shown all that is needed to create a task that can be started, stopped, and invoked by the service process. But this interface has too many members and many tasks are common enough that these members will be implemented almost identically. For example, tasks that execute on a regular interval will be almost identical, the only different member will be the Execute method. That's why the library comes with some handy base classes as shown in this diagram.

    Now when I need to implement a task that runs repeatedly I simply inherit a task from PeriodicalTask or ScheduledTask as seen below. These classes will be part of my service project, from which I remove all the other classes that were added by default.

    class CleanupTask : PeriodicalTask
    {
        readonly static log4net.ILog Log =
            log4net.LogManager.GetLogger(
               System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    
        public override void Execute()
        {
            //time to run....
            //TODO: write the actual code here
            // ShoppingCart.DeleteAbandonedCarts();
            Log.InfoFormat("Executed: {0}", this.GetType().Name);
        }
    }
    
    
    class DailyReportTask : ScheduledTask
    {
        readonly static log4net.ILog Log =
            log4net.LogManager.GetLogger(
                  System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    
        protected override void Execute(DateTime scheduledDate)
        {
            //time to run....
            //TODO: write the actual code here
            // SalesReport.SendDailySummary();
            Log.InfoFormat("Executed: {0}", this.GetType().Name);
        }
    }

    Instead of hard coding the interval or the scheduled time of the above tasks, we use the service's .config file for that:

    <WindowsService>
        <tasks>
            <task name="CleanupTask" interval="600"  />
            <task name="DailyReportTask" time="21:30"  />
        </tasks>
    </WindowsService>

    There are only a few more things we need to do to get this service ready. First we need to add a new WindowsService item. Here we are naming it MyAppService and making it inherit from from SPServiceBase.

    partial class MyAppService : SPServiceBase
    {
        public const string MyAppSvcName = "MyAppSVC";
        public MyAppService()
        {
            InitializeComponent();
            //Important.. use the constant here AFTER 
            //   the call to InitializeComponent()
            this.ServiceName = MyAppSvcName;
        }
    }

    We also need to add an Installer Class, which I'll name simply Installer and which will be invoked during the service installation phase to add the appropriate registry entries to make the service be listed in the Services applet. Here's how this class looks like. Note that it inherits from another base class from the library.

    [RunInstaller(true)]
    public class Installer : SergioPereira.WindowsService.ServiceInstaller
    {
        //That's all we need. Hooray!
    }

    I mentioned that the installer will add the necessary registry information. Some of that are the name and description of the service. We provide that with an assembly attribute that you can put in the AssemblyInfo.cs or anywhere you like in a .cs file (outside any class or namespace.)

    [assembly: ServiceRegistration(
        SampleService.MyAppService.MyAppSvcName, // <-- just a string constant
        "MyApp Support Service",
        "Supports the MyApp application performing several " + 
               "critical background tasks.")
    ]

    A Windows service is compiled as an .exe, so it needs an en entry point, a static Main function. Let's add a Program.cs like this:

    class Program
    {
        static void Main(string[] args)
        {
            if (!SelfInstaller.ProcessIntallationRequest(args))
            {
    
                MyAppService svc = new MyAppService();
    
                svc.AddTask(new CleanupTask());
                svc.AddTask(new DailyReportTask());
                //add more tasks if you have them
    
                svc.Run();
            }
        }
    }

    The code above is pretty simple, we are creating the tasks and telling our service to take care of them. Then we start the service. The interesting thing is the call to ProcessIntallationRequest. This is where we added the self-installing capability of the service. If you wrote a service in the past, you know that they get installed by using InstallUtil.exe. One potential problem is that InstallUtil.exe may not be present on the server or not in the PATH, making an scripted installation a little more complicated. Instead, by using the that call from SelfInstaller, we enabled our service to be invoked like the following to install or uninstall it (remember to execute as an Administrator).

    SampleService.exe -i[nstall]
    SampleService.exe -u[ninstall]

    After installing it, you should see the service in the Services applet.

    Here's the final structure of our project.

    If you want, download the library source code along with a sample service project. There's more in the library than I have time to explain here, including an auto-update task and extra configuration properties for each task.

  • Edit and Continue

    I may very well be the last one to figure this one out, but I always thought that the Edit and Continue option in Visual Studio 2005 and 2008 was a myth. I always made certain that the Enable Edit and Continue check-box was firmly checked, as shown below, but I had never gotten the feature to work.

    With that option enabled any time I attempted to alter the code during debug, to add a quick comment or fix a small typo, the dreaded message would pop up, mocking me for not losing that habit.

    Only recently I was told that the darn thing works the other way around. I don't know if I'm reading this wrong, but it seems counter-intuitive that un-checking that option enables you to edit the source file during a debug session. I un-checked it and to my shock it worked. I felt that anger of several years of being deprived of that feature boiling inside of me and I had to take a deep breath to avoid a nervous breakdown.

    Exaggerations aside, this one goes into my list of why wasn't I told that before along with:

  • The Long Road To Rescuing Wally

    Seriously, how can someone not be thrilled to work as a software developer? Think about this, how many jobs out there give you the ultimate blank canvas where you constantly discover a new and better way to get things done? It's hard for me to consider this just another 9 to 5 job.

    On the other hand, I can see how someone lands in an environment when such freedom or room for exploration is not granted, and settles for stability and a decent paycheck in exchange for suppressed dreams and aspirations. People have their families and different priorities than I and I fully sympathize with that compromise. That doesn't stop me from trying to bring them back, though.

    It should be no news to you that people work better when they like what they are doing and who they work with. I haven't seen any effective way to make people start to like working with each other, but getting someone to restore the lost interest in the job they once loved: that sounds doable. The key here is that many of our seemingly uninterested fellow programmers one day chose this career and went into it with high hopes. At some point down the line, something caused them to slip into this low energy state where the clock ticks slower and learning something doesn't seem worthwhile.

    You know him I think nothing explains the above behavioral pattern better than reading and observing Wally. I bet you have a Wally working with you right now or at least had one in the past, in that previous job that you are glad you left behind. As the Wikipedia article shows, even a nerve-racking co-worker like Wally can have very active and promising past (as twisted as that sounds.)

    I'll have to take a break and acknowledge that some people just don't have any interest in changing their attitude and I'll be respectful of that. I won't be that annoying car salesman that keeps coming back to offer the car you don't need.

    But back to the question, how can someone like Wally regain his original momentum? I think there isn't one big solution to that. Instead there are lots of small things that accumulate over time and can effect change. Most of these things relate more to ourselves than to the intended peer. It all revolves around attitude.

    Positive attitudes are often contagious, the problem is that the incubation period can be long. You have to hang in there. Throughout the entire process you cannot refrain from displaying your enthusiasm with the tasks you complete or with new things you learn. Keep sharing interesting and reusable information. Show that you are always available to help by the simple fact that it is a pleasure for you to engage on a challenging debugging session or after-hours discussions on emerging technologies.

    Some people would say it's not a programmer's job to deal with motivational issues of other developers. I think this is a very short sighted view of your role in the team. If one team member is no longer bringing his A-game, the other members will have to pick up the slack, which is not exactly fair.

    Wally is a bright guy, it's to our own advantage to rescue him from zombie-land.

  • Automation FTW

    One of the things I often discuss with other developers is how little (on average) we automate our work-related tasks.

    Think about this. We make our living automating our users' activities:

    • We write applications to replace manual processes.
    • Our programs send notifications so that they users don't need to waste time checking for process status.
    • We make our programs talk to each other and to third party systems so that the slow and expensive human interaction is avoided,
    • In more general terms, we go through great lengths to ensure our users are adequately empowered and hopefully more productive.

    Contrast that to the way we historically see developers conducting their daily chores.

    • We often work with incredibly powerful IDEs or text editors with macro support or even an extensibility model. When was the last time you employed a macro?
    • How many times will that manager will have to send you that updated Excel spreadsheet or CSV file until you write a script to verify each line instead of the tiresome and error-prone manual check you have been doing?
    • Aren't you tired of keeping track of which files changed since last time you updated the application? Have you ever thought your Source Control Management tool could make this much simpler?
    • Are you kidding me? You're writing that same SQL query for the Nth time? How hard is to save this query for a change?
    • If your work cycle looks like this: Save, Build (wait), Debug (wait), Click, Click, Click, Type, Type, Click, Verify result, OK;  someone needs to introduce you to a unit testing tool immediately.

    I'm as guilty as the next guy of falling in one of the the above anti-patterns. The way I found to make these traps the exception, not the norm, was to make them less comfortable than the automated alternative. The key in the entire process is lowering the automation entry barrier by learning. It also helps if you cultivate a positive attitude toward learning.

    Here's my laundry list of steps to make those CPU cores earn their salt and leave my own processing power for more challenging things. I have some of these items pegged but I'm not done with all of them yet.

    • Take the time to explore and learn the capabilities of your IDE/text editor. Find a cheat sheet and print it. Learn one or two interesting keyboard shortcuts every day. Learn how to write/record a macro, it's usually so easy that you'll be ashamed you hadn't done that before. Some tools even have strong user communities sharing macros. See how your IDE can be extended, if it's beyond your reach, maybe there are third party productivity add-ons that will boost your productivity and pay for themselves quickly (if not already free.)
    • Other applications, like MS Office apps, also have a rich extensibility and automation model behind them. If that application insists in being part of your workflow, investigate how you can show it who the boss is.
    • One of the best things I've done in the recent years was to add a scripting language to my toolbox. I highly recommend that. If possible pick a scripting language that is reasonably different from your primary development language, it will make the learning phase more interesting. I'm currently working with C#, so I thought Ruby would be an interesting language for scripting tasks, and that's what I use most of the time.
    • Be adamant about using your SCM as part of your deployment process. Learn how to do labeling/tagging, diffs, updates, etc not only using the GUI but also through an API or the command line. You'll be writing scripts against your SCM in no time and the weight of tracking changed files will be lifted.
    • I tend to include a tools directory within each project directory structure where I keep (among other things) all the scripts I create to assist development and maintenance of the application. These files can be SQL scripts, Ruby scripts, .bat files, and anything that can be repeatedly executed. Some scripts turn out to be generic enough that I keep them in a common location, such as c:\projetcs\tools.
    • I won't attempt to get yo hooked on TDD or Continuous Integration here, that's something for another day. I will say, though, that if you don't already use an automated unit testing framework, you owe yourself a closer look into that. Why write several test applications or run the entire application all the time to manually verify that everything still works when there are tools to automate this? Take a look at JUnit/NUnit (the xUnit family), RSpec or whatever there is in your language of choice.
    • No text about software development automation would be complete without at least mentioning build automation tools. Tools like Ant (NAnt), MSBuild, Make, Rake go a long way toward providing complete solutions for automating the build, testing, packaging/deploying of an application. Very few things are more satisfying than executing go.bat and seeing your application being compiled, configured, and tested in a fraction of the time that it used to take when you did all this manually.

    As many things in life, the hardest part is getting started. Once you have your first one or two tasks automated, you will be more aware of tasks that can be automated and you will be more confident that you can indeed automate this task that you were just about to start for the second time.

  • Nice to meet you

    Starting a blog is something that had been on my to-do list for quite some time but I had never really gotten around to actually doing it. Today I'm happy to check that off and begin catching up to the lost time.

    Thanks Derik for the welcome and introduction and thanks Devlicio.Us for being so brave to allow me to participate here.

    For my friends and colleagues that decide to read my blog, I'd like to encourage you to subscribe to the entire Devlicio.Us feed and enjoy the great content that has been poured here for quite some time. Take some time to also browse the archives.

    For existing Devlicio.Us subscribers and bloggers, I'll promise to do my best to keep up and be a good devlitzen.

    I have been writing software for a living since 1997. My background goes all the way back to VB4, through ASP classic, .Net since the alpha bits, and more recently I managed to add a little bit o Ruby to my repertoire.

    I don't know exactly what content you can expect to see here, and I don't want to start off with broken promises, but my idea is to write about software development, the developer challenges, sharpening the saws, and anything new that I learn that I think it's worth sharing. What exactly all that will turn out to be is still to be seen. I hope you stick around.

More Posts

Our Sponsors

Red-Gate!

Proudly Partnered With