29 December 2010

Advanced C# Windows Service Installation

Click this link, to download source code.
Today, I'm going to show you how to efficiently create Windows Services and deployment projects, using C# in Visual Studio 2010.  These techniques illustrate methods of centralizing key information about the services included in the project, to prevent typos and other mistakes attributed to setting similar properties in decentralized locations.

This example is especially useful for projects that contain several services that use common code bases.  For example, I have a service suite that monitors several FTP drop directories on a server.  Each watcher performs different, custom actions; but, 90-95% of the code is common to all services.  For this and other reasons not discussed here, it makes sense to simply package all services in one project.  The more popular alternative is to create a common library, install to GAC, and create individual service solutions.

The bottom line is that I wanted to simplify management and deployment.  To simplify service registration, I created a "ServiceInfo" class attribute into which all service information is entered, instead of mucking about in several ProjectInstaller files.  View the full article, to get source code and a complete walk-through.

UPDATE 13 MAR 2012: I updated the code sections, to use formatted HTML, instead of using JavaScript code coloring.  The code was formatted a bit, for fit, and to make the LINQ more efficient.  Therefore, the code in the download won't be exactly the same, but it works.

16 December 2010

How To Create a C# Windows Service Application

Today, I wrote a Windows service application.  Because part of my day job is teaching C# and .NET, I was inspired to write a "how to" document.  The Walkthrough: Creating a Windows Service Application in the Component Designer article found on MSDN is quite helpful, if you know your way around C# and .NET.  Anyone who doesn't understand C# and .NET enough to make short work of the document arguably has no business writing a service; but, we all have to learn sometime.

UPDATED 29 MAY 2012 - Revised several areas for clarity and simplification, and updated formatting.

06 December 2010

Reporting Services Item Templates

Download source code
Download binary (.EXE)

Problem

Last week, I needed to rebuild a couple of reports on SQL Server 2005 Reporting Services, and found I need item template support in Microsoft reports.  How to use item templates in Reporting Services?  Well, in short, you can't.

30 November 2010

ATOM and RSS Feeds Available

The Curly Brace is now syndicated through Feed Burner, with both ATOM and RSS feeds available.  My public Twitter account is also available, just in case you want to be bored by the details of my life.

29 November 2010

The Importance of Pet Projects

photo by Stephane Mahe, Reuters
Pet projects are important.  Really important.  There is no way to keep your technical edge in the workplace, without researching and practicing in other areas of the .NET Framework, based on day to day work exposure, alone.

16 November 2010

Extract Files From ISO Images Without Additional Software

I decided to write reports for SQL Reporting Services.  They are fast, look great, and are oh-so-easy to access via the Web interface.  However, I discovered that Visual Studio versions must be paired to the SQL Server version.  I needed to install Visual Studio 2005, but had only the .ISO CD-ROM image available, and was unable to install software to extract the files.

09 September 2010

Optional Argument Overload Bear Traps

C# programmers, beware the optional argument overload trap!  If you don't already know about Optional Arguments and Named Arguments, read through my post, Introduction to Optional Arguments and Named Arguments.

Unfortunately, one of my clients is not a C++ programmer, and encountered this issue, when updating a common library.  Realizing this issue may severely impact large applications (i.e. enterprise, B2B, etc.), I thought it best to discuss it openly.  Using optional arguments may be setting yourself a trap that will be difficult to escape, in the future.

Introduction to Optional Arguments and Named Arguments

This is an introduction to Optional Arguments in C# 4.  C# programmers, beware the optional argument overload trap!  If you already know about Optional Arguments and Named Arguments, you may want to check out my post, Optional Argument Overload Bear Traps.

08 September 2010

Mouse Suites Aren't So Sweet

Visual Studio, Microsoft Office, and many other applications making use of docked windows are subject to the rigors of "helpful" mouse software suites, kindly loaded by computer manufacturers.  Such manufacturers include HP / Compaq, IBM / Lenovo, and others.

Visual Studio Wears Sabots

This post addresses the problem of compiler errors reporting missing namespaces for solutions composed of multiple projects.

15th century Netherlands textile laborers were believed to throw their wooden shoes, called sabots (sah-BOW), into the cogs of mechanized looms, to prevent their occupation from being rendered obsolete by automated processes; hence, the supposed root and historical context of the word, sabotage.

One would expect Visual Studio to be an industrious little worker, and that it is.  In fact, it works so hard to make our development experience easier, it often runs right out of its clogs, which then fall deep into the whirling cogs of the .NET Framework.  In some cases, Visual Studio sabotages a project, resulting in the following symptoms:

25 August 2010

Efficient Use of Nullable Types

I saw a brief post about nullable types, today, and realized (not by fault of the post itself, rather through my own experiences) that a surprising number of .NET developers either don't know they exist, don't use them correctly, or don't use them efficiently.  Furthermore, nullable types were a topic of the C# class I just finished conducting.

23 August 2010

Joining SQL Server Tables on Composite Column Values

I am working with a database server that hearkens from the not-too-distant past, where disk space was extremely costly.  It made sense to combine several fields (columns), to make the file (table) more compact.  The values were split and evaluated by mainframe code, which was less costly, because the logic and database were on the same machine.  These are the fiery brimstone of relational database hell.

20 August 2010

Extensions: Watch Your Step

I had one of those moments where I wonder if I'm really an expert-level C# programmer, and thought I'd share my embarrassing moment, to help other programmers who are new to extensions, are having the same problem, or for a good laugh.

I created the following C# extension, to safely handle conversion of an object that may contain DBNull to a string; because System.Object.ToString() throws an exception when the object is DBNull.

Frustrations with Visual Studio 2010 Extensions

I have become frustrated with the two most popular extensions for Visual Studio 2010.  These are great tools, but also have their issues.  If you, too, are frustrated by these or other issues, please post a comment.

Oh, great creators of VS extensions, hear my rants and fix your code!

09 August 2010

Mouse Scroll Wheel Issues With Visual Studio and Office

I encountered an old foe, on the workstation of my day job, after installing Visual Studio 2010 Premium.  Moving the mouse scroll wheel normally causes my C# code window to scroll vertically.  However, this particular computer randomly decides vertical scrolling is simply too mundane, and starts scrolling tool windows, instead.  I have experienced this problem in the past, after installing Visual Studio 2003 on a HP workstation.  The issue was resolved after I updated the mouse drivers to use the standard Microsoft Windows mouse driver.  The mouse worked beautifully!  But, what was the cause of the problem?

05 August 2010

Practical Uses of Visual Studio 2010 Productivity Power Tool Extensions

This is an unbiased review of practical uses for the new Visual Studio 2010 Productivity Power Tool Extensions.  I waited to post this for a good reason; there are numerous posts reviewing and highlighting the functions of the extension, but none about how they are really useful and actually used to increase your productivity.  Most users will figure these things out in time; however, for those who are unfamiliar with this extension, or extensions in general, it's great to get an idea of what what the Power Tool extension do for you.

28 July 2010

User Control Design Time Detection

Today, I created a wizard application in WPF/C#.  Each "page" of the wizard is a user control.  Each user control is largely autonomous, each having a pointer to a singleton instance DataManager class that persists data between the controls and parent window.  When a control is made visible, it checks the values of the DataManager class, to ensure the requisite values are available, etc.

The "page" controls are not instantiated on-demand, for reasons I will not discuss here.  When viewing the main window in the WPF designer, all of the user controls that perform data verification in their IsVisibleChanged event handler cause the WPF designer rendering to fail, although I did get several MessageBoxes containing the various validation error messages.  (At least I know those work well!)

The solution is to wrap all validation logic in an IF statement that checks to see if the control is being rendered in a designer.  It we are indeed in design time, the validation logic is stepped over.

private void UserControl_IsVisibleChanged(
    object sender, 
    DependencyPropertyChangedEventArgs e)
{
    if (DesignerProperties.GetIsInDesignMode(this)
        || this.Visibility != Visibility.Visible)
        return;

    // Validation logic here...
}

20 July 2010

Reverse TextIndent (Outdent) for FlowDocument

WPF FlowDocuments are extremely useful and format very nicely, so long as you are writing plain text.  The tag kindly provides a TextIndent attribute, to indent the first line of the paragraph.  But, what about formatting for nested code?

Margin doesn't look kindly upon inserting negative values, but TextIndent gladly accepts them.  Using a combination of a negative TextIndent and a matching, positive left margin, the Paragraph is coaxed into outdenting the first line.  This visually appears that the first line is even with the other paragraphs, and the following lines are indented.

<paragraph 
            margin="25,0,0,0"
            textindent="-25"
            FontFamily="Courier">
    First Line
    Second line
    Third Line
</paragraph>

Produces the following:

First Line
    Second Line
    Third Line

Admittedly, this works well for the first indent, but any further indentations must be handled with a bit of creativity. I will leave you to decide how to best handle that situation.

Passing ORDER BY to SQL Server Stored Procedures as a Parameter

Just about anyone who has worked with legacy code, especially when modifying the functionality of compiled assemblies, has run into this problem.  You can't modify the majority of the business code, and inheriting and overriding the code is not feasible due to impact on the system as a whole.

In the case that inspired this post, I needed to populate a simple ASP.NET 1.1 data repeater with data in a specific order, with minimal impact to the existing code.  The existing code did not contain DataView or DataAdapter ADO.NET objects.  Although I could have composed a SQL statement and switched the SqlCommand object to text query mode, I knew that is a bad idea, because:

  1. Composed text queries are begging for an injection attack, and introduce a massive security hole
  2. Stored procedures are much more efficient, because they are pre-compiled
My solution was to create a stored procedure on SQL Server, that performs the sorting for me.  All I had to do was pass the order by column to a varchar input parameter.  The trouble with using a parameter in an ORDER BY statement is that it simply doesn't work. The following script WILL NOT WORK.

SELECT * FROM Products ORDER BY @orderByColumn

The solution is to use a bit of both worlds.  Pass the column name as a parameter to the stored procedure, and then compose it into a varchar parameter.  Finally, execute the parameter containing the composite query.

DECLARE @orderByColumn    varchar(50) -- (THE INPUT PARAMETER)
        @sql              varchar(255)

-- Be sure to include a space after ORDER BY
SET @sql = 'SELECT * FROM Products ORDER BY ' + @orderByColumn

EXEC(@sql)

Receiving data in the order you need saves you the trouble of adding a DataTable, DataView, DataAdapter, and possibly other objects, and allows you to bind the command results directly to a data repeater.

15 July 2010

Format Document - Run Frustrations

I have been using FlowDocument, to create C# training materials, because it looks nice as a presentation, but can include interactive controls embedded in BlockUIContainer.  It looks great, the interactive portions are perfect for the students, and everything is peaches and cream. That is, until I press Ctrl+E Ctrl+D to clean up the XAML using Visual Studio's Format Document tool.

I am using , etc., to reflect the default text colors seen in Visual Studio.  Format Document insists on inserting a white space following every tag.  This does a great job of reducing readability of things like array declarations:

This:  string[] s = new string[]{...}
Becomes: string [] s = new string []{...}

I know one line of code doesn't look like a big deal, but when you have something more complex, such as the following tuple declaration, everything gets jumbled together in a hurry.  This is not good for my students, who are unfamiliar with syntax and proper code formatting.

Tuple &lt; int , string , double , Tuple &lt; string [], int []> > t =
    new Tuple &lt; int , string , double , Tuple &lt; string [], int []> >(
        13, "Friday the 13th", 65.0185d,
        Tuple .Create&lt; string [], int []>(
            new string [] {"Jason", "Freddy", "Michael"},
            new int []{13, 14, 152}
        )
    );

If you have any solutions, please post them here, and I will include it in this post!

01 July 2010

How To Find USB Device Vendor and Product ID Codes

Creating an interface for the Missile Launcher USB device (manufactured by Dream Cheeky, available at ThinkGeek.com $25 USD) is a great way to entice programming students to develop their UI skills.  (I will post links to the .NET 4.0 device command API, when I get it working.)  Because similar devices have been made by several manufacturers, since about 2004 to present, most of the USB human interface device (HID) interface code available on the Web was targeted at older models from other vendors.

When interfacing with a USB HID, you must first locate the device -- the HID could be plugged into any USB port, or could have been moved to another port during use.  We locate the correct HID by iterating through attached USB devices, until we find one that matches the Vendor ID (VID) and Product ID (PID) in which we are interested.

How do I find the USB HID Vendor ID and Product ID?
  1. Open the Windows Device Manager
  2. Expand the Human Interface Devices node
  3. Double-click the device of interest -- the USB Human Interface Device Properties window appears
  4. Click the Details tab
  5. In the Property drop-down box, select Hardware Ids
The USB HID VID and PID are displayed.  My Cheeky USB Missile Launcher displays:
USB\VID_0A81&PID_0701&REV_0001
USB\VID_0A81&PID_0701

The hexadecimal Vendor ID is highlighted in red, Product ID in green, and product revision number in blue.  Other details obtained through a USB monitoring application:

DEVICE DESCRIPTOR
  • USB Version 1.10
  • Device class; 0x0 (defined at interface level)
  • Device subclass: 0x0 (Unknown)
  • Device protocol: 0x0 (Unknown)
  • Control pipe max size: 8 bytes
  • Vendor ID: 0xA81 (Chesen Electronics Corp.)
  • Product ID: 0x701 (USB Missile Launcher)
  • Product version: 0.1
  • Manufacturer: Dream Link
  • Product: USB Missile Launcher v1.0
  • Serial number: not specified
  • Configurations: 1
CONFIGURATION DESCRIPTOR
  • Number of interfaces: 1
  • Configuration value: 0x1
  • Attributes: Remote wakeup, Bus powered
  • Max power: 100 mA
INTERFACE DESCRIPTORL0, ALTERNATE SETTING: 0
  • Number of endpoints: 1
  • Interface class: 0x3 (Human Interface Device)
  • Interface subclass: 0x0 (None)
  • Interface protocol: 0x0 (None)
  • Endpont address 0x1, Input, Interrupt, max packet size: 1 bytes, update interval: 16 1-millisecond frames

Masking Drag & Drop Hit Test in WPF

While creating WPF demonstration code, I decided to use the classic DoDrag() method, to perform drag and drop functions.  When dropping objects on both a Canvas and Border object, DoDrag() unexpectedly and consistently returned Effects.None.

The solution was easy: the Canvas and Border objects had no Fill.  DoDrag's hit test was failing, because there was no physical rendering of the Canvas or Border at the mouse position!  The object must be filled, to return a positive hit test.  Using the "Transparent" brush  or a solid color with 00 alpha works, too.

Note, borders are considered filled areas, and return a positive hit.  You can use this to your advantage, but I'm not sure why you would ever drag and drop something on a border, unless it is very thick.  You could use a border as a drop mask, to return different Effects result, over certain areas of an object.

22 June 2010

Querying Extended ASCII Characters in SQL Server

Part of a project requires conversion of ADABAS to SQL Server.  ADABAS hearkens from the day when storage space was a precious commodity, so the "Packed" data types were invented.  These compress the values stored in the column, to maximize storage utilization.

When converting packed ADABAS File fields to SQL Server relational table columns, some of the packed data was not unpacked(?) correctly, resulting in some interesting characters appearing in SQL Server.  The entire data content of a field needs not be packed; ADABAS allows you to leave the first N characters unpacked, and then pack the remainder of the field, and other such options.

It was my job to find all records across the entire database (we're talking millions of records per table) that contain ASCII characters that do not appear on a standard, 108 key, US English, QWERTY keyboard.  Constructing a query that iterates through all tables and columns that are varchar data type is easy.  However, the SQL Management Studio query editors don't display extended ASCII characters.

The solution was pretty simple.  Cast a byte value to a character type, to specify the extended character ranges.

SELECT RecordID
FROM MyTable
WHERE ((patindex('%[' + char(0) + '-' + char(31) + ']%', ColumnName COLLATE Latin1_General_BIN2) &lt;> 0)
      OR (patindex('%[' + char(127) + '-' + char(255) + ']%', ColumnName COLLATE Latin1_General_BIN2) &lt;> 0))

This selects records from the table where the number of extended ASCII (key codes 0-31, and 127-255) characters in a specific column is not 0.

Threading and Static Generic.Dictionary Issues

I ran into this issue, recently, and was inspired by uber-tester Tess Ferrandez*, to blog it, myself.

Generally, one remembers to lock() or Monitor.TryEnter() their resources, in a multi-threaded situation.  Collections are often needed by multiple threads, and can be made static, to make them generally accessible to all threads.  Furthermore, collections support concurrent readers, making them ideal for multi-thread access.

So, what happens if another thread writes to the static dictionary, while reading?  Well, it isn't a disaster, nor a deadlock, but it sure gets slow!  The problem is the reader(s) contend with the writer, as the dictionary keys are being updated.  If several threads are attempting to traverse the keys, performance suffers greatly, and you'll eventually receive an InvalidOperationException.

In the threading section of the Dictionary&lt;T> MSDN documentation, we find:
"Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
A Dictionary<TKey, TValue> can support multiple readers concurrently... blah blah blah"
Many programmers (unfortunately, myself included) stop reading at that point, and return to coding with a smile, because they discovered a thread safe, concurrent read collection.  If we keep on reading, we find the answer to the problem:
A Dictionary<TKey, TValue> can support multiple readers concurrently, ...as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
Simply locking the resource prevents the whole issue.  It is well worth the time to insert Monitor.TryEnter statements throughout the code, rather than suffer intermittent performance issues.

*Check out the Tess Ferrandez's blog post, High CPU in .NET app using a static Generic.Dictionary.  Her post is directly related to this one, and provides debug details.

Fire & Forget BackgroundWorker.RunAsync()

The BackgroundWorker is an extremely handy threading tool.  However, stopping and then immediately starting the BackgroundWorker isn't something built in to the class.  The problem is that you can not call BackgroundWorker.RunAsync() when the worker is already busy (.IsBusy) or is busy and pending cancellation (.CancellationPending).  You must wait until the worker is no longer busy.

Simply use a timer, to periodically check the status of the worker.  This code is able to handle any number of background workers.  This approach has been very helpful, when building services that monitor and maintain other processes.  Remember, IsBusy returns true until the thread terminates; therefore, when CancellationPending returns true, we know IsBusy will also returns true.

If you find yourself in this situation of handling mutually exclusive BackgroundWorkers or need to restart a background worker, chances are you have over-complicated your code and need to re-engineer your architecture. I strongly suggest you do that, before implementing this solution. The other, more remote possibility is you have a very unique situation that requires advanced coding.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Timers;

BackgroundWorker MyWorker;
List<backgroundworker> WorkersToStart;
System.Timers.Timer timStartWorkerTimer;

void Main(string[] args) {
    timStartWorkerTimer = new System.Timers.Timer(1000);
    timStartWorkerTimer.Enabled = false;
    timStartWorkerTimer.Interval = 1000;
    timStartWorkerTimer.Elapsed += 
        new ElapsedEventHandler(timStartWorkerTimer_Elapsed);

    MyWorker = new BackgroundWorker();
    MyWorker.WorkerSupportsCancellation = true;
}

private void StartBackgroundWorker(
        ref BackgroundWorker bgw, bool cancelIfRunning) {
    // When bgw.CancellationPending is true,
    // bgw.IsBusy is also true.
    if (!bgw.IsBusy)    
        bgw.RunWorkerAsync();
    else if (cancelIfRunning) {
        bgw.CancelAsync();
        WorkersToStart.Add(bgw);
        timStartWorkerTimer.Start();
    } // Else, do nothing. The worker is already
      // started and cancelIfRunning == false.
}

void timStartWorkerTimer_Elapsed(
        object sender, ElapsedEventArgs e) {
    foreach (BackgroundWorker bgw in WorkersToStart)
        if (!bgw.IsBusy) bgw.RunWorkerAsync();

    if (WorkersToStart.Count == 0)
        timStartWorkerTimer.Stop();
}

17 June 2010

Installing an Event Source

Writing events to the event log is easy, but you must register an event source with the system.  Because these are simply registry keys, the application must be executing under administrative privileges, to create the keys.  This is a problem frequently encountered by client-server, service, and middleware .NET programmers.

The one time you are guaranteed to have administrative control over the registry is during installation of the application.  Adding a custom action to a Visual Studio Setup project will create your EventSource, during installation, without encountering permissions issues.

The Installui.exe utility that we run from the Visual Studio Command Line, especially when testing services, executes during the setup process.  We can tell it to look for RunInstaler attribute decorated classes, whose input parameter value is true, and call the method.  (Cool!)

Configuration Steps:
  1. Add a class to your application named InstallActions.cs
  2. The file content should resemble this:



    using System.ComponentModel;
    using System.Configuration.Install;
    using System.Diagnostics;
    
    [RunInstaller(true)]
    public class InstallEventLog : Installer
    {
        public const string EventSource = "MyEventSource";
        public InstallEventLog()
        {
            var eventLogInstaller = new EventLogInstaller();
            eventLogInstaller.Source = EventSource;
            Installers.Add(eventLogInstaller);
        }
    }
    

    Notice the method is decorated with the RunInstaller attribute. This tells the compiler the method is to be accessible to the setup project. Of course, this class may be included in any project in the solution.
  3. Add a Visual Studio Setup project to your solution, if one doesn't already exist.


  4. Right-click the Application Folder node, in the left window pane
    (The context menu appears)
  5. In the context menu, select Add > Project Output...
    (The Add Project Output Group dialog appears)

  6. Select the relevant output required for installation (typically Primary Output and Active configuration)
  7. Click the OK button
  8. Right-click the setup project node in the Solution Explorer
    (The context menu appears)
  9. In the context menu, select View > Custom Actions


  10. Right-click the Install node
    (The context menu appears)
  11. In the context menu, select Add Custom Action...
    (The Select Item in Project dialog appears)
  12. In the dialog window, double-click the Application Folder node, or select it from the drop-down list
  13. In the dialog window, double-click select the Primary output from PROJECT_NAME (Active) node
    (The dialog closes, and a new child node appears under the Install node, entitled, Primary output from WindowsService1 (Active))
You are done!  The setup project is now configured to execute all classes inheriting Installer, and have the RunInstaller(true) attribute.

I generally keep the EventSource name stored in an application setting (app.config) file.  Retrieving this value for use in the source code provided above takes a little more effort; because, the InstallEventLog class is not in a namespace.

16 June 2010

ASP.NET 1.1 Response.Redirect Exception

If you were/are an ASP.NET 1.1 developer, this exception probably looks familiar to you:

System.Threading.ThreadAbortException: Thread was being aborted

This exception occurs when you call Response.Redirect() or Response.Transfer().  You are pretty much guaranteed an exception, when these calls are inside a Try block!  This is due to how the .NET 1.1 framework handles Try blocks.

This is how I understand the situation: the compiler puts try block code into a virtual method.  Like the Main() method, it returns a result code, and a result code is expected to be returned.  Response.Redirect() and Response.Transfer() cause the current thread to be aborted.  It the thread aborts while in the Try block, the virtual method is unable to return a result code.  There is doubtless some complications with handling Try block code as a virtual method, which is not conducive to stopping the thread.

Solution:
  1. You should include the optional, second variable in your code, whenever possible.  This second input parameter is a boolean value, indicating whether the page should finish rendering.  (I haven't encountered any situations where I do!)
  2. Take the redirect or transfer call out of the Try block.

15 June 2010

Enterprise Mouse Gestures

I normally only post after a coding experience, when I have hard code to deliver right to your monitor.

Today, however, is an exciting deviation from the norm.  While talking with a developer about top-down development practices (designing the user interface first, and then the database and business logic) for an enterprise class project, my thoughts turned to the greatest power users of all time: customer service agents.

The applciation we were discussing provides the representitive access to a suprisingly large number of data entry and reporting interfaces.  Wrangling all of these into an easily navegatable menu presents a substantial challenge, considering Fitts's Law (time to click a control is a function of the distance to the control and its size).

Then, I thought...
"What if you don't have to click a button?  What if the application knows what you want, without having to target anything with the mouse?"

I immediately thought of pattern-based solutions.  Most humans remember phone numbers by the pattern they generate on the keypad, which is why typing phone numbers you have memorized on a keyboard keypad is often more difficult.  I thought of these two solutions:
  1. Make a circular menu appear under the mouse (or close by).  Categories are organized radially, and open their own bubbles when the mouse enters them.
  2. Power users frequent perhaps 15 forms.  Allow the user to associate a mouse gesture with the form.
Solution #1 is a practical, pattern-based method of fast navigation, as the user remembers the direction of travel through the menu, to arrive at the desired form.  However, the pattern is subject to change, when the menu options are updated.  Also, the menus need to be visually targeted.  Not ideal.

Solution #2 hits the bulls eye.  Mouse gestures are intelligent (they use an artificial neural network to match the user's actual motions to what they intend to motion), and customizeable (the user may associate a pattern to any form, assign to a keyboard gesture, and/or create a custom pattern).

Another benefit of employing mouse gestures, especially in an enterprise class GUI, is they do not interfere with pre-established keyboard gestures.  It is possible to assigning a mouse gesture to a general keyboard gesture (<Ctrl><S> for Save) that may or may not be supported by the current form, assign the same mouse gesture to form-specific keyboard gestures, and/or fire event calls to the code-behind (be it WebForm postback, WinForm handler call, or MVC controller).

Other, common actions could be employed.  Drawing a C copies selected text, and drawing a P pastes the content.  Drawing an @ opens a new e-mail message.  There are countless general applications.

Needless to day, I am excited about supproting mouse gestures for power users.  The level of use provides more then enough opportunity for the neural network to learn, and the savings in time is invaluable.


Check out the Mouse Gestures for .NET project at Codeplex: http://mousegestures.codeplex.com/  (current as of 15 JUN 2010)  I will be following this project, and hope to get invoved with its development.

09 June 2010

Event Log Jam

While testing a service I wrote, I kept running into an exception in the area that creates a custom event log:
Only the first eight characters of a custom log name are significant, and there is already another log on the system using the first eight characters of the name given. Name given: 'MyNewService', name of existing log: 'MyNewServiceTEST'.

I made a test log, to write debug messages to the log, so i don't clutter up the production log. I have no idea why "Only the first eight characters of a custom log name are significant" in this age where 64-bit OS is the standard, but that's how it is.

The issue was resolved simply by deleting the log. Here's the syntax used to create a custom log, write an event, and delete the custom log. I placed the source and custom log names in string value types, because you'll likely want to use these throughout the application. I usually store these in a settings file, for ease of maintenance.

WARNING! Do not delete system logs! You can delete these, and will probably be displeased if you do.


Create Event Log Source:

string sourceName = "MyApp";
string logName = "MyAppLog";

// The event source must exist, before
// a custom log may be created for your application!
// This must be run using administrative privelages,
// otherwise an "access denied" exception is thrown.
if (!EventLog.SourceExists(sourceName))
{   
    EventSourceCreationData esData = 
        new EventSourceCreationData(sourceName, logName);
    EventLog.CreateEventSource(esData);
}

Write Event Log:

EventLog evt = new EventLog(logName, ".", sourceName);
evt.WriteEntry(
    String.Format(
        "Test log entry submitted at: {0} {1}",
        DateTime.Now.ToShortDateString(),
        DateTime.Now.ToShortTimeString()), 
    EventLogEntryType.Information);

Delete Custom Log:
Note, you must be running with administrative privelages, otherwise you receive an exception stating, "Requested registry access is not allowed."

EventLog.Delete("MyCustomLog");

Writing to the Event Log is simple and easy!  Keep the first 8 characters of your custom event log unique, and you'll be in great shape.

Curly Brace Rebooted

Hello, and welcome to the all-new Curly Brace blog!

This a a complete reboot of the old Burly Brace blog, which has been accumulating information that is now defunct, outdated, and just plain un-cool.  Should this blog should rival the likes of Scott Gu, I'll be quite impressed.

~Mike