Arcane Searching

I think we’d all agree the internet is one of the greatest productivity tools around, allowing us to find vast stores of information. I’m sure you’ve also heard it’s the greatest time waster, with lots of distracting sites or useless pages that get in the way of the results we want.

I find it really valuable to have a good search tool, one that focuses on the content I need, and limits the scope of the search to relevant areas. Of course we’ve all heard of Google, the 500 pound gorilla of search engines. While the do a pretty decent job, when your search phrase returns half a million hits it can be difficult to narrow down.

Recently I’ve found the Microsoft engine, Windows Live ( http://www.live.com/ ), has gotten a lot better, especially when looking for .Net related developer content.

My favorite so far though, is Search.Net ( http://searchdotnet.com/ ), a site put together by coding legend Dan Appleman. Dan ( http://www.desaware.com/ ) created a Google powered site, but maintains the list of sites it searches so you know that you are only combing sites devoted to programming and not Happy Harry’s House of Wild Women.

Another site I just learned about this week is Koders ( http://www.koders.com/ ). It’s a site devoted to searching through source code. It also has some helps that will let you zoom in on what you want. You can pick the language, or specify your search word needs to be in the class name, method name, or interface name. This kind of search is valuable when you are looking for an example, or trying to avoid reinventing the wheel.

A similar site is Krugle ( http://www.krugle.com/ ). It has similar paradigm to Koders, allowing you to search through code.

The final code search tool I’ll mention is Google’s new Code Search engine ( http://www.google.com/codesearch?hl=en ). It allows you to search using regular expression syntax, which is a nice feature (I just wish regular expressions weren’t such a pain in the underwear to use).

I have to give a quick thanks, most of these I learned about through either my listening of Dot Net Rocks ( http://www.dotnetrocks.com/ ) and HanselMinutes ( http://www.hanselminutes.com/ ) or through Scott Hanselman’s new forum site, which I blogged about yesterday.

Those are the list of place I go when I need to find something, how about you?

SQL Server Integration Services and the “The script files failed to load” Error

I have an SSIS job that has been running on our test server since April, with no issues. Last week it just quit working. About the fourth package in I started getting “The script files failed to load” error when loading a very simple VB Script inside a package.

A web search led me to this forum page:

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=267047&SiteID=1

To save you from wading through all the messages, the two causes of this error could be 1) Package’s PreCompile was set to False, or 2) I had breakpoints in the package.

So I check, and nope my PreCompile was set to true (the default) and there were no breakpoints. In addition, most of the people posting in the forum link above couldn’t get their packages to run in the first place, this didn’t seem to occur in packages like mine that had been running fine for a while.

Next I thought, OK what about environmental issues? Mining our extensive logs I was able to determine the time when one moment the scripts work, then 15 minutes later they no longer worked.

With that exact 15 minute interval known, the DBA team reviewed what had happened on the server, and sure enough a security patch for the .Net 1.1 Framework had been applied on the server during that exact time. Bingo!

It took us 2 days to work through the issue, the team, and I have to be honest and include myself here, had the thought process “well, the error is just on the test server, we’ll get to it when we can.” Since it was just a test server, and we weren’t testing anything at the time, we weren’t stressing over it.

We should have been. The very same patch was scheduled to be rolled to our production server just an hour and a half after we found it. Had that scheduled deployement occurred we would have been in deep diaper filling.

Let me make a quick distinction here, in that it’s not necessarily fixing the issue that’s urgent, but identifiying it. Once you have it identified, you can control it. By not quickly identifying the issue, we nearly rolled the same patch into production.

So the first point here, if your SSIS package which has been running for a while suddenly starts getting “The script files failed to load” error, check to see if any .Net Framework 1.1 patches were applied.

Second, this shows you how important good logging can be. With our extensive logging I was able to determine with a fifteen minute window when the problem occurred.

Finally, never treat issues that occur on your test server as if they are not important. We were quite lucky, narrowly avoiding disaster on our production server. Identify the cause quickly, so you can control it.

Update: We found a fix that worked, see https://arcanecode.wordpress.com/2007/07/18/ssis-issue-from-monday/ for more detail.

Arcane Talks

On Thursday July 12th I’ll be speaking at the Birmingham Software Developers Association (BSDA). You can get directions from the club’s website, http://www.bsda.info/ .

I’ll be speaking on the subject of SQL Server Compact Edition. It’s been a while since I blogged about this, so I thought I’d provide a few links for quick reference.

As promised, here is a link to the Power Point presentation (in PDF format) I used during the presentation:

 SSCE presentation for BSDA user group

My very first post was back in January 2007:

http://shrinkster.com/nsk

My next series of posts began on April 10th, and described how to create databases using the various tools available to you.

http://shrinkster.com/qtl

The complete C# and VB.Net code samples were posted April 13th, 2007:

http://shrinkster.com/qtm

And finally, the series of posts I mentioned on system Views started with this post on April 16th, 2007:

http://shrinkster.com/qtn

If you want to see all of my SSCE posts, simply click the SQL Server Compact Edition tag over in the categories area, or use this link:

 https://arcanecode.wordpress.com/tag/sql-server-compact-edition/

Please note each of these links is a starting point, be sure to read the blog for the next few days after each link in order to get the full story.

And now a question, I’m working up material for a new presentation. Debating between SQL Server 2005 Full Text Searching and SQL Server Integration Services. Any opinions?

Eventually I’ll do both, but would like to do the first one based on feedback. Even if you can’t attend please post a comment and let me know where your interests lie.

Getting Started with SQL Server 2005 Full Text Searching: Part 5 – Advanced Searching

Last week we looked at how to search for data by passing a simple search word into a SQL Query that uses Full Text Searching. As you would expect, Full Text Searching is capable of much more powerful searches than a single word.

First, you can search for a phrase. To do so, you include the phrase inside double quotes.

SELECT WebSiteID, WebSiteName
FROM MyTableOfCoolWebsites
WHERE CONTAINS(*, ‘”Arcane Code”’)

Will find occurances of the phrase Arcane Code in your index. Let’s say however, you want to search for either Arcane or Code? FTS supports Boolean searches.

WHERE CONTAINS(*, ‘”Arcane” OR “Code”’)

Will return results where either the word Arcane or Code is in the text. If you want them both, use an AND in place of the OR. Additionally, FTS supports the AND NOT keyword, for when you want the first word but not the second.

Full Text Search also supports something called Proximity searches. In a proximity seach, one word appears near another.

WHERE CONTAINS(*, ‘arcane near code’)

So how near is near? Well the online docs are a bit vague on this, a safe guess is about ten words. You may also see this form of the syntax:

WHERE CONTAINS(*, ‘arcane ~ code’)

~ equates to near, although to me not nearly as readable, be aware though in case you run across it.

Finally, you should be aware that certain words are excluded from searches. Common words such as a, an, the, and so on. Microsoft refers to these as “Noise Words”. You can edit the list of noise words in case you have some words in your environment that wind up being Noise Words. Your company name might be one example.

I found the file in the folder C:\Program Files\Microsoft SQL Server\MSSQL.2\MSSQL\FTData (your milage may vary). The file is named noiseenu.txt. (ENU is for English US, not to be confused with noiseeng.txt which our friends in the British Isles will be using.)

This is also handy to know in case there is a reserverd word you need to remove from the list. In our environment one of the reserve words is also an abbreviation for a piece of our equipment, so I would want to remove this from our list.

Getting Started with SQL Server 2005 Full Text Searching: Part 4 – Valid Data Types

Hopefully everyone had some fun over the weekend with their digital cameras and listening to some good photo podcasts. Today we’ll return to the subject of Full Text Searching (FTS) under SQL Server 2005.

If you’re like me one of your questions is “OK, FTS is great, but exactly what data can I index with it?” Good question, a quck reference of the books online gives us the answer.

Char, nchar, varchar, nvarchar, text, ntext, xml, varbinary(max), and image are the valid data types for FTS. However, according to the documentation text, ntext, and image are going to be removed in a future version of SQL Server, so I’m going to avoid them and so should you.

Char, nchar, varchar, nvarchar are pretty straight forward, and probably expected, so I won’t go into them further. XML is pretty cool, as you may be aware with SQL Server 2005 Microsoft added the ability to store XML in the database. You can then do XML queries into these XML fields, a subject for a future post. However, MS also allows you to search inside these XML fields using Full Text Searching as well.

The final one that needs explanation then is varbinary(max). I’m sure you’re scratching you’re head over this, how can FTS work on binary data? Well remember, the Full Text Search engine is built on top of the Microsoft Indexing Service, which can scan inside Word, Excel, etc files stores on a server.

Thus FTS can actually look inside files stored inside varbinary(max) and if they have the proper extension FTS will look inside them and index the contents of the files. How sweet is that?

Getting Started with SQL Server 2005 Full Text Searching: Part 3 – Using SQL

OK, so you have this spiffy catalog, and you’ve populated it with full text searches for your favorite tables. Now you’d like to actually use those index from within your SQL. There are four new commands you can use in SQL to get to your data. All are used as part of the where clause, and have similar syntax but different results.

The first is the one you’ll probably use the most, it’s the contains command. You simply pass in the column name and what you want to search for.

select col1, col2 from myTable
where contains(fts_column, ‘searchword’)

For fts_column you can use the name of one of the columns that was indexed, or you can use * (and asterisk) to search in all of the columns that were full text search indexed. In the single quotes you put in the word or phrase you want to look for.

Contains searchs for an exact match. It either finds it or it doesn’t, and it has to be an independent word. For example, if your text field contained “I love the Mythbusters every week.” and you searched for ‘Mythbuster’, would NOT return a match.

If you want your text searching to be a little more open minded, use the freetext command instead. The syntax is identical to contains, including the ability to use an asterisk.

select col1, col2 from myTable
where freetext(fts_column, ‘searchword’)

In this case, however, a search of our afore mentioned text field for ‘Mythbuster’ would return a match, as freetext understands that Mythbuster and Mythbusters are essentially the same word.

In your application, you might consider using a check box that says “exact match”. For exact match queires use the contains keyword, when the user does not check you can use the freetext command.

It’s also possible to return a list of results that are sorted by a rank. The rank indicates the strength of the match to the search phrase passed in. To get a list of ranks, use either the containstable or freetexttable commands. Their syntax is like their cousins, as is the method it uses for searching (containstable is exact, freetexttable is more liberal). The only addition is the first parameter must be the name of the table, then comes the column name and search condition.

Instead of rows, what is returned are two columns: key and rank. The rank is a relative score from 0 to 1000 that indicates the strength of the match. A higher value means it’s a better match.

The key is the primary key from the table you’re searching. You can then use this key to pull back the data from the main table. Let’s do a simple example: you want an exact match for all employees who live in Alabama. Unfortunately the DBA who created the table had just come off a three day drinking binge, and instead of separate street / city / state fields, just created a big text field called emp_address.

select rank, emp_id, emp_name from empTable
join freetexttable(empTable, emp_address, ‘Alabama’) ftt
on empTable.emp_id = ftt.[KEY]

This would return something like:

Rank emp_id emp_name
255 12345 Jamie Hyneman
128 45678 Adam Savage

And there you go, four ways you can have your SQL leverage the power of full text searching to return results.

Getting Started with SQL Server 2005 Full Text Searching: Part 2 – The Indexes

Yesterday I introduced you to full text searching, and covered the basics on creating catalogs to hold your full text indexes. A full text search index is a little different than a regular index. First, each table can only have one full text search index created for it. Next, the create syntax is slightly different. OK, in fact it’s a lot different. Let’s take a look:

create fulltext index on my_table_name_here
(column1, column2,…)
key index my_tables_unique_index_name
on my_catalog_name_here
with change_tracking {manual | auto | off}, no population


The first thing is also the most obvious, you need to supply the name of the table in the first line. Note we’re not supplying a name for the full text search index. Since there’s only one per table, SQL Server takes care of creating the full text search index name for us.

Next we need to supply the name of the column or columns we want indexed. These can be any sort of text field. Just list them one after another, separated by commas.

The next item is also required, and sort of tricky. Each row in the table you are doing full text searching on must have a unique index. It makes sense when you think about it, for the text search to be efficient it must be able to quickly move to the row with the word you’re hunting for, and the way to do that is via the unique index.

So for this parameter you’ll need to supply a unique index name for “my_tables_unique_index_name”. Keep in mind this is not the name of the columns from the table. Instead this is the name of a “normal” index (not a full text search index) that is unique for the table.

The “on” parameter is optional, you only need it if you set up multiple catalogs and don’t have a default. If you omit it, it will simply put the new index in the default catalog.

Next you will need to tell SQL Server how often to update the index. You do this through the with change_tracking parameter. OFF turns it off entirely, no updates will be done until you issue a rebuild via the alter syntax I’ll cover momentarily. You might want to use OFF when you have a table that gets updated very rarely.

AUTO, on the other hand is for when you have a table that gets updated frequently. It will update the full text search index when the associated table is updated. The final option, MANUAL will flag changes to the underlying table, but it won’t update the full text search index until you tell it to.

The final parameter, no population, only applies when you use OFF. It tells SQL Server not to populate the index when it’s created. If you omit it, or use AUTO or MANUAL, SQL Server will populate the full text search index when the index is created.

OK, so you’ve got this index created and need to change it, or perhaps you need to work with one that’s already in existence. For this there’s the alter command:

alter fulltext index on my_table_name_here
parameters here

There’s quite a few parameters you can pass, so let’s look at them individually. Just know that when you see them below, they should go where you see “parameters here” above.

set change_tracking {off | auto | manual} – This works the same as with the create command, it lets you change the tracking mode.

disable – Disables the full text search index, it’s not used for searching nor is it updated. However the data is left intact, should you want to turn it back on.

enable – Enables the full text search index after a disable.

add ( column ) – Adds the passed in column to the full text search index.

drop ( column ) – Removes the passed in column from the full text search index.

start full population –This rebuilds the index from the ground up.

start incremental population –This will update the index since the last time it was updated. Note you must have a timestamp column on your table for this to work.

start update population –Remember a moment ago when I talked about the change_tracking manual option? Well this command is how you update an index with manual change tracking.

And finally, you may decide one day you no longer need the full text search index. Since the readers of this blog are the smartest, most intelligent readers on the planet you’ve already figured out we’ll need to use a variant of the drop command:

drop fulltext index on my_table_name_here

And there you go, you now know how to create, change, or remove a full text search index. Now there’s one more piece, you need to know how to use them from within your SQL. But we’ll save that for tomorrow.

Getting Started with SQL Server 2005 Full Text Searching: Part 1 – The Catalog

One of the coolest features of SQL Server 2005 is the ease with which you can implement full text searching. True, it was available in previous versions but 2005 makes it very easy to implement and use.

Full Text Search is an offshoot of the Microsoft Index Server technology. It’s what you could call an “add-on”. By default it’s enabled for every database you create in 2005.

But just having it turned on is not enough, now you have to create a catalog to hold the data for your full text data. The catalog is a separate file from your database, and holds all the key words it finds. The syntax to create a catalog is pretty simple:

create fulltext catalog my_catalog_name_here
in path ‘c:\mysqldata\somesubdirectory’
as default

The ‘in path’ is optional, if you omit it your catalog is created in the same place as the data. For small databases this is fine, for large ones you might actually want to store the catalog on a separate hard disk in order to get a performance boost.

The ‘as default’ clause says this catalog will be the default one used for new full text search indexes, or for searching existing ones. Most times you’ll probably only need one catalog for a database, so you can add this and forget it.

Once you have a catalog created, you may need to tweak it. There’s not a lot of tweaking you can do, just three ways you can alter it, and all are implemented via the alter command.

alter fulltext catalog my_catalog_name_here rebuild

alter fulltext catalog my_catalog_name_here reorganize

alter fulltext catalog my_catalog_name_here as default

The first command, rebuild does just what it says. Your old catalog goes to the great bit bucket in the sky (i.e. it’s deleted) and SQL Server will recreate all of your full text search indexes. And it should be obvious, but remember during this time your full text search will not be available.

Reorganize is something like doing a disk defrag, it cleans up and reorganizes your full text search indexes. While it may not be as efficient as doing a complete rebuild, it does have the advantage of not taking the catalog offline while it does it’s work.

Finally ‘as default’ simply makes the catalog the default, in case you either forgot or were distracted by Mike Rowe doing something nauseating on “Dirty Jobs” (http://www.discovery.com/dirtyjobs) .

OK, you now have a catalog. But the catalog is simply a space to hold your full text search indexes, and those we’ll create in the next post.

Collections in C#: NameValueCollection

In doing some reading I ran across a handy collection called the NameValueCollection. This collection, which resides in the System.Collections.Specialized namespace, allows you to use either a string or an integer index for the key. Further, it allows you to store more than one string value in a key.

Let’s start the code example by creating a simple Console application. I added using references to System.Collections and System.Collections.Specialized namespaces at the top. As a final bit of housekeeping, make sure to add a Console.ReadLine() as the last line of our code, so the console will wait on us to hit the enter key after we read the results. (If you don’t, the program will run so fast you won’t be able to appreciate your fine work.)

Now I’m going to load some data into a new collection called myCollection. For the data, I’ll use a website owner and the website or sites they own.

      System.Collections.Specialized.NameValueCollection myCollection

        = new System.Collections.Specialized.NameValueCollection();

 

      myCollection.Add(“Arcane”, http://arcanecode.com”);

      myCollection.Add(“PWOP”, http://dotnetrocks.com”);

      myCollection.Add(“PWOP”, http://dnrtv.com”);

      myCollection.Add(“PWOP”, http://www.hanselminutes.com”);

      myCollection.Add(“TWIT”, http://www.twit.tv”);

      myCollection.Add(“TWIT”, http://www.twit.tv/SN”);

Next, I’d like to get some data back out. I mentioned you could cycle through the collection using an integer index, so let’s see how that’s done:

      Console.WriteLine(“Key / Value Pairs by Integer Index”);

      for (int i = 0; i < myCollection.Count; i++)

      {

        Console.WriteLine(i.ToString() + ” “

          + myCollection.GetKey(i) + “: “

          + myCollection.Get(i));

      }

 

[Picture of Key/Value pairs by Integer Index]

 

In the above output you can see how I use the GetKey and Get methods to retrieve the key name and value for that key using the loop’s index. Note that when multiple values are associated with a single key, they are returned as a list of comma separated values.

You can also use foreach logic to cycle through the collection. Here I am using the AllKeys property of our collection to get the list of keys. I can then print the key, and also use the key as the indexer into my collection as you can see below.

      Console.WriteLine();

      Console.WriteLine(“Keys / Value Pairs via AllKeys Collection”);

      foreach (string myKey in myCollection.AllKeys)

      {

        Console.WriteLine(myKey + “: “ + myCollection[myKey]);

      }

 

[Picture of Key/Value pairs via AllKeys Collection]

 

Now I, what? Yes, you in the back row, what was your question? Ah, you say lists of comma separated values are OK, but you want to be able to access individual values? Fortunately some nested looping and the GetValues method will satisfy you demanding types.

 

      Console.WriteLine();

      Console.WriteLine(“Keys / Individual Values”);

      foreach (string myKey in myCollection.AllKeys)

      {

        foreach (string myValue in myCollection.GetValues(myKey))

        {

          Console.WriteLine(myKey + “: “ + myValue);

        }

      }

 

[Picture of Keys/Individual Values]

 

This also works great if your data has commas within it. Let’s add two lines back at the top of the program to the collection.

      myCollection.Add(“CommaTest”, “Here is a , in a string”);

      myCollection.Add(“CommaTest”, “Here is another , in a string”);

 
Now run the application again, and lets look at the results.
 
[Picture of Comma Test]

As you can see in the last area “Keys / Individual Values” the GetValues method correctly determined that the commas I had embedded were part of the data and not a delimiter between values.

Whenever you need a good string collection that has the ability to tie multiple values to a single key, the NameValueCollection would be a good class to take a look at.

 

Arcane Tools: Cropper

Well, the uber cool Scott Hanselman has done it again, found another gem. OK, he’s been using it for a while, but in watching his GrokTalk ( see my post on Tuesday ) I learned about Cropper.

Cropper is a screen capture tool. As you can see below, it puts an translucent window on your screen. You can move and resize this window with the mouse, or the keyboard.

[Pic of Cropper in action]

The arrow keys will move the cropper window in 1 pixel increments for fine tuning, or for quick moves combine the arrows with the CTRL key to make 10 pixel jumps. You can also resize, use ALT plus the arrows for 1 pixel resizes, or CTRL+ALT+arrow for 10 pixel resizing jumps.

You have the option to save in a variety of formats, including BMP, PNG, and JPG, and can even select a level of JPG compression. You can also save to the clipboard if you so desire.

To capture an image, simply double click on the translucent cropper window, or press ENTER. When you do, a file is written to your Documents folder in a subfolder called Cropper Captures (although this is user configurable). I like this, as it lets me quickly grab one screen shot after another without having to put a lot of thought into it.

The coolest thing about Cropper though, is it’s entirely written in C#, and open source so you can see all the code. It comes courtesy of Brian Scott, you can see his blog and download Cropper for yourself at http://blogs.geekdojo.net/brian/articles/Cropper.aspx .

The only negative I’ve found is the name. Apparently cropping is also a popular term in the scrapbooking world, so when I started talking about cropper my wife ( http://southerntinkerbelle.com ) got all excited and tought I was getting into scrapbooking! I hated to disappoint her, but on the bright side the sofa really wasn’t all that uncomfortable.

Thanks for coming!

I just wanted to thank everyone who took the effort to come to the presentation I did tonight on SQL Server Compact Edition at the Birmingham Dot Net Users Group ( http://www.bugdotnet.com ). It was a small crowd but very engaged, all in all a very enjoyable evening for everyone.

As promised, here is a link to the Power Point presentation (in PDF format) I used during the presentation:

SSCE presentation for BUG.Net group

The complete C# and VB.Net code samples were posted April 13th, 2007:

https://arcanecode.wordpress.com/2007/04/13/sql-server-compact-edition-with-c-and-vbnet/

And finally, the series of posts I mentioned on system Views started with this post on April 16th, 2007:

https://arcanecode.wordpress.com/2007/04/16/system-views-in-sql-server-compact-edition-tables/

If you want to see all of my SSCE posts, simply click the SQL Server Compact Edition tag over in the categories area, or use this link:

https://arcanecode.wordpress.com/tag/sql-server-compact-edition/
Thanks again to  everyone, I had a great time and hope came away with a better understanding of SQL Server Compact Edition.

Grok

After listening to last weeks DotNetRocks episode with Scott Stanfield ( http://www.dotnetrocks.com/default.aspx?showNum=244 ) I took at look at Grok Talk ( http://groktalk.com/ ). This is a great site filled with a lot of video training material.

The cool thing is each video is a mere 10 minutes in length. This is awesome for the busy programmer on the go. Now you can easily download and learn something new everyday without a huge investment in time. I highly encourage you to take a look, I plan to watch one video every weekday.

Speaking of free training, I have been asked to speak at the Birmingham Dot Net User Group tomorrow, Tuesday June 12th. The meeting starts at 7 pm, and will take place at the New Horizons training center located at 601 Beacon Pkwy W #106, Birmingham AL. The Windows Live map is at http://shrinkster.com/pv8, or the Google map can be found at http://shrinkster.com/pv7 .

I’ll be speaking on SQL Server Compact Edition, showing you how to get started with this cool technology. My notes and demos are already here, just click the SSCE link under categories. Hope to see you there!

Arcane GUI’s: Enabled versus Visible Properties

I was having a discussion with a coworker today about the user interface for his application. There is an Admin menu that the site IT folks will need to setup the application for the first time on a computer.

He was mentioning he was going to make the Admin menu disabled for non IT folks, and instead I suggested he make it invisible. Why? He asked. Good question.

Human nature is the best answer. Your average user is going to be content with what they have, but there will always be those who want more. They are curious about what they are missing out on, or are not satisfied unless they think they are getting the “full” software, even if it’s functions they don’t need.

In large corporations, these folks tend to be, er well rather insistant, and if they have a supervisor who likes to take the easy way out, he may wind up telling IT to grant the user access he shouldn’t have.

Instead, I have a firm design principle: Ignorance is bliss. In this case, if the Admin menu were hidden, the problematic users would never know it even exists, and live in happy igornace, causing problems elsewhere.

So here’s the rule: If there is functionality a user will never have access to, such as an Admin menu, then it should be hidden via the Visible property.

On the other hand, if there is functionality that is enabled or disabled based on the state of the app, use the Enabled property. A good example might be the Copy function on the Edit menu. If no text is selected, then Copy should be disabled as there’s nothing to copy. It servers as a visual cue the user has the application in a state that the Copy function makes no sense. Once text is selected, Copy should be Enabled.

Another example might be a Save function, if the required fields are not completed, disable the Save as a cue to the user he still has work to do.

And there you go, Arcane’s GUI Rule for Enabled versus Visible Properties.

Windows Services in C#: Pulling in the Event Log for your Windows Service (part 8)

Technically this falls in line closer with the EventLog series I did in January. (For more info see my series on event logging, it started on January 16 of this year and ran to the 19th, http://shrinkster.com/p6d). However, in most cases the Event Log is tied in closely with Windows Services since it’s through the event log that services do most of their communication with the outside world. As such it can be important to read in your events back into your control app.

To read the log we’ll use two different methods, just to show multiple techniques. To start, let’s add a text box to the TLManager, under the buttons. I named mine txtEventLog. Next, in the component area of the toolbox find the EventLog component and drop it on the form. Like the timer, it goes in the tray area under the form. I named mine TimerEventLog.

In the properties for the TimeEventLog, there’s a few properties we must set. First, set the Log property to “ArcaneTimeLogger” and the Source to “ArcaneTimeLoggerService”, so it will know to read our log.

Next set EnableRaisingEvents to true. This will allow the EventLog component to raise an event whenever something gets written to our log. Since we set this to true, we must indicate what method is responsible for the event. Click on the Event button (the lightening bolt) in the properties window to switch to events view, then double click the EventWritten to generate a default named event. It will generate TimeEventLog_EntryWritten.

Now switch to code view, and add this code to the event:

    private void TimeEventLog_EntryWritten(object sender, System.Diagnostics.EntryWrittenEventArgs e)

    {

      txtEventLog.Text = e.Entry.Message

        + System.Environment.NewLine

        + txtEventLog.Text;

    }

This will take the latest event log entry, and add it to the text box. Note I put it first, then add back what was already in the text box. This way the most recent event will always be at the top.

That’s it for this part, now everytime something is written to the event log, the EventLog component will catch the event and let us know about it.

Nice, but sometimes you also want to know what’s already there in the log. Instead of using a component let’s see how to do this with code. First, add a “using System.Diagnostics” reference to the header of your class (form).

Now, I think it would look nice when the TLManager loads to already have the text box populated with the past events. The System.Diagnostics library contains an EventLog class we can use to create an object for our particular event log. Once that’s done, we can cycle through the Entries collection to read what’s there. Here’s some code I added to the TLManger_Load event to do just that.

      StringBuilder sb = new StringBuilder();

      EventLog atl = new EventLog(“ArcaneTimeLogger”);

      for (int i = atl.Entries.Count – 1; i > -1; i–)

      {

        sb.AppendLine(atl.Entries[i].Message);

      }

      txtEventLog.Text = sb.ToString();

I start by creating a string builder object to hold all of the events. Next, and the key is the EventLog atl… line. I pass in the name of our log so the atl object will know what log it belongs to.

Next you will see a for loop that cycles through the entries. Note I’m deliberately starting with the last entry, the most recent one, and counting down to the oldest one. This will ensure the most recent event appears first in the text box.

Finally, I copy the data we’ve built into the string builder into the text box. And that’s it, two ways to monitor event logs and pull their data into your application.

Windows Services in C#: Sending Commands to your Windows Service (part 7)

Yesterday we looked at starting and stopping your windows service from another application. It would also be helpful to be able to send other commands beyond the basic set of Start/Stop/Pause etc.

On the good side, there is an API through which it is possible to send a command to your windows service, fairly easily as a matter of fact. The down side is it’s a one way communication, through the built in mechanism it’s not possible to return any data. Even more limiting is the only data you are allowed to pass in is an integer, and it’s value must be between 128 and 255.

Even with those limitations, if all you need to do is execute a few simple commands the built in API can be more than enough to meet your needs.

To illustrate, we’ll expand the windows service we’ve been using as an example. We’ll add the ability to send a command that will force the event log to be updated immediately, rather than waiting on the timer to fire off its event.

First, we’ll add two items to the service. The first is a public enum. Strictly speaking we don’t have to use an enum, but it makes for more readability.

    // Must be int between 128 and 255

    public enum commands

    {

      LogIt = 255

    }

Next we’ll add a new method to the windows service called OnCustomCommand. This is an override to the base classes method. As you can see it first calls the base method, then checks the value of the integer that was passed in, in this case against our enum. If it finds a match, it calls the WriteToLog method immediately. (The WriteToLog was discussed yesterday, so I won’t reiterate here).

    protected override void OnCustomCommand(int command)

    {

      base.OnCustomCommand(command);

      if (command == (int)commands.LogIt)

      {

        WriteToLog(“Arcane LogIt:”);       

      }

    }

OK, that’s all that’s needed for the service. Let’s switch to our program, and add another button called LogIt.

[Pic of TLManager]

Now we’ll add a little code to the click event for the log it button.

    private void btnLogIt_Click(object sender, EventArgs e)

    {

      ServiceController sc = new ServiceController(“ArcaneTimeLogging”);

      sc.ExecuteCommand(255);

    }

Like in our other examples we create a reference to our service by creating a new ServiceController object and passing in the name of our service. Once we have an sc object we call the ExecuteCommand method. This allows us to send messages to a windows service.

Numbers 0-127 are reserved for windows, and are handled in the base class. 128 to 255 are there for your own use. In the example above I used 255 just to show that you could pass an integer value directly without using an enum.

One last small item, we don’t want the LogIt button to be enabled if our service isn’t running. We’ll add a little logic to the SetDisplay, Start and Stop methods to include setting the buttons enabled status properly. Here’s the updated routines.

    private void SetDisplay(ServiceController sc)

    {

      sc.Refresh();

      if (sc.Status == ServiceControllerStatus.Stopped)

      {

        btnStop.Enabled = false;

        btnStart.Enabled = true;

        btnLogIt.Enabled = false;

        lblStatus.Text = “Stopped”;

      }

      if (sc.Status == ServiceControllerStatus.Running)

      {

        btnStart.Enabled = false;

        btnStop.Enabled = true;

        btnLogIt.Enabled = true;

        lblStatus.Text = “Running”;

      }

    }

 

    private void btnStart_Click(object sender, EventArgs e)

    {

      ServiceController sc = new ServiceController(“ArcaneTimeLogging”);

      sc.Start();

      btnStart.Enabled = false;

      btnStop.Enabled = true;

      lblStatus.Text = “Running”;

      sc.Refresh();

    }

 

    private void btnStop_Click(object sender, EventArgs e)

    {

      ServiceController sc = new ServiceController(“ArcaneTimeLogging”);

      sc.Stop();

      btnStop.Enabled = false;

      btnStart.Enabled = true;

      lblStatus.Text = “Stopped”;

      sc.Refresh();

    }

And that’s all there is to it. Compile and reinstall your service, then launch your TLManager program. With the service started, click the LogIt button a few times then go into MMC and take a look at your event log. You should see a new message appear each time you click the LogIt button.