tag:blogger.com,1999:blog-11829059512267337192009-06-15T10:41:42.813-04:00Enthusiastic Programmingthe zealous pursuit of code, family, church, music... life.steve brownellhttp://www.blogger.com/profile/10796687712198259408noreply@blogger.comBlogger2125tag:blogger.com,1999:blog-1182905951226733719.post-39480160610774693702009-06-11T22:38:00.017-04:002009-06-15T10:41:43.126-04:00Making a better start for a .net windows serviceThe general windows project templates that ship with visual studio give the bare essentials for the projects they create. That's nice, but I've found with windows services I was always writing the same basic structure before I got to the point of writing the working end of the service. So, I've created a VS2008 project template that puts in all the things that I always want in my windows service. I'll discuss each piece of additional code so you can see why it's there. That way you can make any adjustments you like with confidence. This blog will use C#, but I've included a project template for VB as well.<br /><br /><a href="http://sites.google.com/site/enthusiasticprogramming/Home/EnthusiasticServiceCS.zip">C# Template Link</a><br /><a href="http://sites.google.com/site/enthusiasticprogramming/Home/EnthusiasticServiceVB.zip">VB Template Link</a><br /><br /><strong>Service Configuration</strong><br />Services in general do not have a user interface. Now, it's true that I've seen many people write user interface forms for services, and yes, those forms are displayed on the console session. However, I consider this a bad design for services. You're service should not depend on any user interaction. The better way is to separate user input from service messaging. In this implementation of a windows service, I won't get into how you should enter or store configuration information. You can store it any number of ways: database, registry, local file, etc. How you put information into that store should be done with a separate application.<br /><br /><strong>Base Template</strong><br />Ok, now let's take a brief look at the service template that ships with visual studio. The base project contains the following files: Program.cs and Service.cs. There are several things that need to be done, and many of them are outlined in this <a href="http://msdn.microsoft.com/en-us/library/zt39148a(VS.80).aspx">msdn article</a>. You should refer to this article for a detailed explanation of adding the installer objects. Also, the base template uses "Service1" as the class name for the service. I haven't changed that, but you can do the replacements yourself if you want to name it something else. Look at the general service properties to discover all the settings that will make your installed service easier to identify.<br /><br />A quick note about security and impersonation. Since the service is not running in session it will have its own security context. I typically leave the default set to "local system". This is fine for most configurations. But if your service will access something on another machine (like a file share), then you will need to change it to run under another domain account that has permission to access whatever it is on the other machine. When this happens I usually have the network admin create a separate windows account for the service, and have them set the password to never expire. There's nothing worse than having your service stop working because the account password changed or expired. The general application eventlogs are very misleading when this happens.<br /><br /><strong>Conditional Dependency on MSSQLServer</strong><br />I made one addition to the installer code: a pre-installation routine that will make SQL server a service dependency if it exists. Most of the time my services need to access a database, and that's pretty much always SQL server. Now I may chose to install the service on an independent machine or the main server. If I'm putting it on a machine that has SQL server on it, then I will almost surely use the database on that machine. Therefore, it's useful to make the service dependant on SQL server.<br /><br /><a href="http://4.bp.blogspot.com/_BHiTdIBQKXg/SjKRGD-LTtI/AAAAAAAAAA0/0mOpGw6huo8/s1600-h/fig1_servicedependancy.jpg"><img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 144px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5346495240973536978" border="0" alt="" src="http://4.bp.blogspot.com/_BHiTdIBQKXg/SjKRGD-LTtI/AAAAAAAAAA0/0mOpGw6huo8/s400/fig1_servicedependancy.jpg" /></a><br />Now when the machine reboots and the service (which is set to automatic start) starts up, it will make sure SQL server has started before my service starts. But if SQL server doesn't exist on the machine, then we don't want the dependency.<br /><br /><strong>Event Logging</strong><br />I find it very useful to make a separate eventlog for my service. You can by default write entries to the general application eventlog, but it's much more convenient to write to your own log. To do this you can use the System.Diagnostic.Eventlog object and all the associated code that goes with that, but I've found it useful to wrap that code in my own classes: SystemEventLogFactory and SystemEventLog. The factory uses a thread safe singleton pattern to deliver the instance of the eventlog that we will create. Each class can then make it's own pointer to this instance. Since we're really only writing eventlogs the threading isn't all that critical, but it's good practice nonetheless.<br /><br />The other reason I make the wrapper class is that it provides me with a flagging system that I can use to distinguish between different kind of eventlogs. After you've had to live with these services for a while, you'll come to appreciate the idea of changing how much logging is needed. When my service is running fine, I'll turn back the logging to only create the minimum number of log entries that are needed. But invariably something happens down the road and I wish that I could get more information. By adding additional eventlogs it's kind of like having a debug.writeline that I can turn on or off as needed.<br /><br />The SystemEventLog class has a public enumeration that you can redefine and extend. You'll notice that it uses the [Flags] modifier which allows us to use bitwise operators. So the MessageFlagComposite represents a single integer that is the bitwise or () of all the flags you want set. While I've hard coded the flags into the factory class for this template, you'll want to pull your composite setting from your configuration store.<br /><br /><a href="http://1.bp.blogspot.com/_BHiTdIBQKXg/SjZawtEaIuI/AAAAAAAAAA8/A5nld5bDwdg/s1600-h/fig2_messageflagenumeration.jpg"><img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 263px; DISPLAY: block; HEIGHT: 227px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5347561400327348962" border="0" alt="" src="http://1.bp.blogspot.com/_BHiTdIBQKXg/SjZawtEaIuI/AAAAAAAAAA8/A5nld5bDwdg/s400/fig2_messageflagenumeration.jpg" /></a><br /><br />Now when ever I use my mxLog to write an entry I can specify what flag is associated with that entry at design time. At run time that flag is compared to the MessageFlagComposite to see if that particular flag is actually set, and only writes the eventlog if it is. Thus we can add lots of eventlog entries into our code (and leave them there), but only write the ones we want at run time.<br /><br />Finally, when choosing strings for LogName and Source, you should keep a couple things in mind. First, a source can only be used in one eventlog, the by default the general application log will your your compiled executable name as the source for this application. Therefore, you should not use a source name that is the same as your compiled executable. By default when you use this template the word "source" is appended to the end of your project name to use as the source. The logname is set to the "safe" version of your project name. Of course you can change these if you wish. Also by default I've set the machine name to ".". This assumes that you want your eventlog on the same machine where it has been installed. I've never known of a good reason not to do this. If you want to access the eventlog from another machine there are several ways to do that.<br /><br /><strong>Main Service Code</strong><br />The main service class now has two private class level objects: mxLog (an instance of our custom eventlog), and mxTimerBasedMonitor. The later just one type of class object that will carry out the work we want done by the service. I'll cover the several base types of monitor objects in follow up blog entries.<br /><br />At this time the only thing to note is that all our work will be done on background threads to the main service thread. The main reason I do this is because it's the simplest way to quickly get through the OnStart event. Just about any help article you will read on services will stress how important it is that your OnStart event be optimized to complete quickly. The main reason for this is so that you won't have your service routinely winding up in the "starting" state. The WindowsOS only gives you 60 seconds to complete your OnStart event before it complains. This will lead the users of your code to think something is wrong when the reality is that you're just taking a long time to get things going.<br /><br />For similar reasons I have a tendency to wrap all my methods in try/catch blocks. So you will note that the OnStart also has a try/catch block. The affect of this is that the service will always reach the started state. You will need to actually check the eventlog to see if it started successfully. There's nothing worse than a hung service that you have to kill because you hit an unhandled error in your code.<br /><br /><strong>MonitorBase</strong><br />Class MonitorBase is what all the different types of service working classes will inherit from. It makes an instance of the Log and give a method to start and stop the monitor. This is all well and good, but we haven't done anything yet!<br /><br />We'll cover the different monitor classes in subsequent blogs... stay tuned.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1182905951226733719-3948016061077469370?l=enthusiasticprogramming.blogspot.com'/></div>steve brownellhttp://www.blogger.com/profile/10796687712198259408noreply@blogger.com0tag:blogger.com,1999:blog-1182905951226733719.post-51928723842893326642009-03-28T09:04:00.011-04:002009-03-30T20:55:09.497-04:00Application Negotiation - a field guide to interacting with the clientYou love programming and writing code. I get it, but at some time or other you're going to actually talk to a client. In the end this will likely have more impact on the success or failure of your project than the technical aspects of writing code. I've lumped these soft skills into the term "application negotiation". So as the project leader what's this all about? (<span class="blsp-spelling-error" id="SPELLING_ERROR_0">btw</span>, if you're the only technical person on the project, then you're the project leader.) Well, it's not about the details of how you manage the to-do list. It is about communication and leadership.<br /><br /><strong><span style="font-size:130%;">Early Agreements</span></strong><br />You probably fall into one of two situations: you're employed by a company full-time to do programming work or lead a programming team, or you're selling your services to a company from outside that company. Either way you need to pay some attention to the <span class="blsp-spelling-corrected" id="SPELLING_ERROR_1">agreements</span> that you make when you first start talking about a new project.<br /><br />The client chose you for a reason. Find out why. If you work for a company, the reasons have more to do with why they want the project than why they chose you personally. They probably didn't have a lot of choice about using you for the project. Do your best to find out the pressures that are on them. I found that all too often, they've come to you with only one of many possible solutions to their situation. Try to get them to define the project in terms of the root problem you're trying to solve.<br /><br />If you've been hired from outside the company, learn why they chose you. This will give you some insight into their expectations about the project. Try not to be lulled into a straight time and materials viewpoint. You'll do better in the long run if you can build a relationship of trust. Very few projects are so cut and dry that you can just do what was asked of you and walk away.<br /><br />It's in your best interest to focus on the long term. You have your reputation to protect. Taking any old project may help provide some immediate cash flow, but it's the long term relationships that add stability to your efforts. Beware the client that is only interested in you writing the code that you're told to write. In the end they probably won't like what they get and they'll blame it on you.<br /><br />The client will undoubtedly have expectations for the project, but you have expectations too. Make sure to set some agreements before you start working, and I do mean working, not programming. Define how you will work together. Do your best to gain direct access to the people who will use the final product. Set regular times when you will meet to review the project. Give them a vision of what it will be like to work together. Actually talk through how you will get work done.<br /><br /><span style="font-size:130%;"><strong>Project Research</strong></span><br />Learn your client's world. Write your project documentation in their terms using their jargon, not yours. Have the client verbally explain the environment where the work products of this project will be used. Their business environment is likely very different than your programming environment. If you can become the person that is comfortable working in both worlds, then you will become very <span class="blsp-spelling-corrected" id="SPELLING_ERROR_2">successful</span>.<br /><br />Spend the time talking through who will use the product and how it will be used. Do your best to pull in all affected parties, even if that means you do the "leg work" to interview people separately. Use this early time to help people visualize the solution they are asking for. Dig deep to find consequences to their actions. One of the areas that comes up most often is in setting requirements for data collection. Many times the sponsor of the project wants to make most data input fields required, but you should talk it through and make sure that the user will always have the answer to every required field.<br /><br />The client doesn't care about your technical prowess. Don't use it as a smoke screen. I've seen many eager young programmers suggesting technical solutions in terms so technical it was impossible for the client to understand them. In the end the client doesn't usually care how you're going to solve the problem. If they talk like they want to know how you're going to accomplish the project, keep your answers simple. Let the client probe you for more information if they want it. After many years of programming and leading projects large and small, I still struggle with this one.<br /><br />I've found that some of the best approaches to problem solving in this environment comes from some math teachers. Here's some links to articles I've found useful:<br /><br /><ul><li><a href="http://mathforum.org/pd/process/documents/understanding_the_problem.pdf">Understanding the problem</a> from <span class="blsp-spelling-error" id="SPELLING_ERROR_3">mathforum</span>.org</li><li><a href="http://www.edutopia.org/measuring-what-counts-memorization-versus-understanding">Measuring what counts: memorization vs. understanding</a> from <span class="blsp-spelling-error" id="SPELLING_ERROR_4">edutopia</span>.org</li><li><a href="http://www.refsum.no/publications/Refsum_Tsukuba2003.pdf">Understanding a Conflict</a> by Grete <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Refsum</span></li></ul><br /><span style="font-size:130%;"><strong>Managing Expectations</strong></span><br />Let nothing go unsaid that needs to be said. If you have doubts, you need to express them. However, don't dwell on what won't work. Spend most of your time exploring ideas that you can implement. If your project meeting is turning into a gripe session, turn the tide by exploring possible solutions. If they refuse to acknowledge that anything will help, try focusing on the requirements. Defining and redefining requirements can help them to focus on what they want, not on what they think is impossible.<br /><br />Reiterate your understanding of the expectations at the end of every interaction with the client. Verbal <span class="blsp-spelling-corrected" id="SPELLING_ERROR_6">repetition</span> helps you solidify your understanding of the problem and gives the client a chance to clarify. Always make an appointment for your next interaction, and don't tie it to project milestones. If you make your next appointment tied to the accomplishment of a task and that task gets delayed, it only frustrates the client.<br /><br />Learn your client's tolerance for project updates. "I'm working on it" is never an appropriate measure of progress or project status. When the client asks for project projections, avoid projecting beyond their tolerance for updates. When I was at my last job, their tolerance was about three weeks. I needed to try to keep any work items broken down into things that could be accomplished in three weeks or less. If you reach the end of your client's tolerance for updates, and your answer is the dreaded "I'm working on it", then the client will start to set their own timeline for progress based on how long they think it should take to get the task done. Then they'll hold you to it.<br /><br /><strong><span style="font-size:130%;">Dealing with Shifting Project Definitions</span></strong><br />People change their minds. Get over it. Learn to effectively lead people through their decisions. They won't always know the consequences of their choices. It's up to you to lay it out for them. After that, all you can do is give them what they want.<br /><br />Admit your mistakes early. You're only wasting time by delaying the inevitable.<br /><br />Get help. When you're unclear of your direction, <span class="blsp-spelling-corrected" id="SPELLING_ERROR_7">bounce</span> your ideas off a trusted colleague. <span class="blsp-spelling-error" id="SPELLING_ERROR_8">Facebook</span>, Twitter, and USENET groups can be <span class="blsp-spelling-corrected" id="SPELLING_ERROR_9">valuable</span> resources.<br /><br /><strong><span style="font-size:130%;">Dealing with People</span></strong><br />Stay in control of your emotions even when the client is all over the map with theirs. You don't have to take their stress. Here's a <a href="http://ezinearticles.com/?Understanding-Emotional-Triggers-Means-Customer-Satisfaction-and-Greater-Sales&id=1983924">link</a> to a good article on understanding people's emotions as a way of providing good customer service.<br /><br />Learn to deal with angry clients. When they start to vent. Let them. The client has the right to feel the way they feel, so don't tell them their feelings aren't valid. After you have listened, repeat their main points back to them. This let's them know that they've been heard. Here's two links on dealing with angry clients:<br /><br /><ul><li><a href="http://www.businessknowhow.com/marketing/diffuse-anger.htm">Diffusing Anger</a> from <span class="blsp-spelling-error" id="SPELLING_ERROR_10">BusinessKnowHow</span>.com</li><li><a href="http://www.gaebler.com/Handling-Angry-Customers.htm">Handling Angry Customers</a> from <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Gaebler</span>.com</li><li><a href="http://books.google.com/books?id=1MvgpGfcMRwC&pg=PA32&lpg=PA32&dq=understanding+their+problems+to+find+an+answer&source=bl&ots=Ok-E5XG-RE&sig=cdwDGlJC7yRpgG2paXjvrtNT0wE&hl=en&ei=_CS3SZ67HZaitgewwcmuCQ&sa=X&oi=book_result&resnum=5&ct=result#PPA34,M1">Boys and Their Toys</a> by Bill Adler, Jr.</li></ul><p><br /></p><strong><span style="font-size:130%;"></span></strong><p><strong><span style="font-size:130%;">How to Improve Your Skills</span></strong> </p><ul><li>Look for courses in facilitative leadership. </li><li>Practice public speaking. </li><li>Read. There is an almost unlimited library of advice on this topic on the web.</li></ul><p></p><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1182905951226733719-5192872384289332664?l=enthusiasticprogramming.blogspot.com'/></div>steve brownellhttp://www.blogger.com/profile/10796687712198259408noreply@blogger.com0