Friday, November 11, 2005

For a limited time, Microsoft is offering TONS of free training on the new VS.NET 2005.  These courses will not be free forever, so if you're interested check 'em out and get some great training from Microsoft eLearning.

[Just a quick update.  I saw but failed to mention that there is also free training for SQL Server 2005.  Check them out...they're great resources to have - especially for the up-and-coming developer on the .NET 2.0 platform]

Friday, November 11, 2005 8:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, November 10, 2005

I was just perusing one of my long-time favorite blogs and found a reference to a utility for capturing windows called Window Clippings by Kenny Kerr.  What a slick little tool!  It's funny how the stars have aligned (but just a tad late on this one).  I was doing exactly this earlier today - capturing windows regions for a help document, and I had to go into each one and 'clean-up' the upper right and left corners because the XP-themed windows used a non-rectangular region.

One fantastic aspect of this utility is that though it ultimately captures a rectangular block, it identifies the window region and marks the rest of the image as transparent (for PNG images).  I've written many applications that use irregular regions and this utility will save me a lot of clean-up efforts on my screen captures.  Kudos!

[Update: My apologies to Scott Hanselman for the repetitive trackbacks...I've had to repost this a few times.]

Thursday, November 10, 2005 4:01:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback

Wow, we had a great .NET User Group event tonight.  Scott Golightly, though pressed for time, covered a great amount of material on SQL Server 2005.  We had about 50 people in attendance (which is precisely what we had estimated).  Scott covered the gamut of the new features in SQL Server 2005, from schemas, to Xml, to SQLCLR, to Service Brokers, to Notification Services.  It was a lot of fun and very well received.

We had some great giveaways as well, with more great ones in the coming months (hint: several VS.NET 2005/SQL Server 2005 licenses, etc).  Stay tuned.

Thanks, Scott, for a job well done!

Thursday, November 10, 2005 3:37:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, November 08, 2005

As many may already be aware, MS had their Worldwide launch of VS.NET 2005, SQL Server 2005, and Biztalk Server 2006 in San Fransisco yesterday (November 7th, 2005).  I had the opportunity yesterday to attend the event which was pretty awesome!  (I'll be blogging more on that again later tonight as I have more time)  I did want to post this quick tidbit, so I wouldn't forget.

There are several incarnations of the VS.NET and SQL products being released.  In an effort to get product into everyone's hands, and to promote the revolution that is .NET 2.0, Microsoft is offering the Express versions of VS.NET 2005 and SQL Server 2005 for FREE for a year.  Get it now and use it if you don't already.  Awesome power at your fingertips!

Tuesday, November 08, 2005 11:04:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, November 04, 2005

Many of you may have read by now Sony's irresponsible foray into DMR (Digital Rights Management).  I discovered this article by reading Scott Golightly's blog the other day, but didn't actually delve into reading it until today.  Scott mentions an article written by Mark Russinovich of SysInternals wherein he discovered a RootKit (an application that hide various parts of your computer such as files, system objects, registry keys).  Mark did some fantastic detective work (which is fully outlined in the article) and discovered the source of the problem being a CD manufactured by Sony.

I agree with Scott that Sony (or anyone else) are well within their rights to protect their property, be it music, software, etc, but taking that a step further and subversively installing software on a machine that masks itself, continually consumes resources, and hides other files, directories, and devices is plain wrong.  This is what spyware and malware do in an attempt to hide their presence.  Sony has way overstepped its bounds with this one.

I'm definitely gonna have to keep my eye out when purchasing a CD (or other form of media) that is labeled as copy protected.  I'll have to seriously consider ever making that purchase.

Friday, November 04, 2005 3:36:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, November 03, 2005

This has been a thorn in my side now for quite some time and I'm glad to say that it's finally been plucked.  After pleading and begging the original CAPTCHA control designer to fix the glaring issues with the control and never getting so much as a response nor an acknowledgement of any correspondance, he finally decided to release the source about a month ago.  I downloaded it immediately, intent on exploring what the cause of the issues might be, but didn't even crack the .zip until today - it's been that busy.

Well, I dove in to see what the issue was.  First of all there were two glaring issues that people constantly complained to me about:

  1. Users would frequently get a red 'X' image in place of the CAPTCHA image.  This actually only happened if the user were to browse to a post on my blog that had a querystring or a hash (such as the #Feedback anchor reference).  Even though this was identified as a fixed bug on the source website, I know I'm not alone in continuing to experience it.
  2. An invalid CAPTCHA code entered would post back and subsequently cause the form to clear and lose all changes and comments made.  This was most infuriating.

These issues were the biggest ones.  I recompiled and quickly fixed the first issue - not a problem at all.  The second one was a little trickier, mostly due to how the control responded to an invalid postback.  The control would Redirect back to itself, effectively negating any form content, and thereby clearing out the controls.  I saw that Miguel had written and commented out some code that attempted to resolve the issue, but it didn't work (hence it was commented out).

Ultimately, I fixed the control so it would leverage session state (if it is available - .Text blogs have session state off by default and I didn't want to enable it just for a silly control, but wanted to support it if it were present).  In the event that session state is not available, a cookie gets created that persists form values until they are posted with a valid CAPTCHA code.  There are still a few things that I'd like to address, but it's working MUCH better now.  Additionally, I fixed a few more bugs, made the code interfaces a bit cleaner, and tidied up the code where I saw fit.  Perf is a little better now too, as well as a few minor memory issues have been resolved.

In the coming posts, I'll be commenting on how I got it to work properly and how I worked around some of the interesting issues in the control.

I'll be submitting my source code back to Miguel here shortly for him to release, seeing that the code is his baby.  Hopefully, this makes posting comments on my blog a bit easier. :-)

Thursday, November 03, 2005 5:35:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, October 31, 2005

Having been turned on to a review by Orson Scott Card (one of my favorite authors - Ender's Game, anyone?) several weeks ago, I decided to go out an check out the movie. Oh my goodness!  It was awesome!  I've rarely had a movie experience quite like that - fantastic!  Even not knowing the backstory one bit I was completely engrossed in the film and quickly fell in love with the characters.

After seeing it we decided to go out and purchase the complete series of Firefly, the FOX television series on which the movie was based.  I was very impressed!  I'm now an addict of the series and really want to see the movie again (though I'll probably wait until it comes out on DVD unless someone wants to go with me [hint, hint]).  It's too bad they didn't keep up a good series, though FOX, it seems, does that: throws out good, quality stuff for trash.  I can't think of the last time I actually watched something on FOX since the X Files or the occasional The Simpsons episode.

Oh well, Firefly is beautiful...what a cool show!

Monday, October 31, 2005 3:14:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

<sarcasm>Well, today's a funny day.</sarcasm>  I was starting to reset an older machine (one that I've had for several years), when I heard the now-all-too-familiar 'click, click, whirrr, click'.  Well, chalk that up to another hard drive gone...I have a stack of 5 hard drives on my desk now that have failed within the past several months.  Se la vi.  Oh well.  I guess I'm beyond feeling now - this crash didn't even affect my disposition today, except that it made me laugh.

Fortunately, I had backed up all valuable information from the drive - so no data lost (this time).

Monday, October 31, 2005 6:15:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Saturday, October 29, 2005

This is, by now, old news, but if you're an MSDN subscriber, you'll definitely want to go get the RTM (Release To Manufacturer) versions of VS.NET 2005 and SQL Server 2005.  If not, but you anticipate running .NET 2.0 applications (which many undoubtedly will), the released 2.0 Framework is now available for download.

This is an exciting time!  I can't wait for the launch event.  I'm heading to San Francisco for the launch on the 7th.  If you're going to be there, drop me a line and let's get together!

Saturday, October 29, 2005 5:53:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, October 24, 2005

Several months back I wrote an article wherein I described creating an IDE.  This IDE was (and still is) pretty cool.  It has the ability to take an ASP.NET .ascx file, parse it for custom controls, render the controls using alternative text, and associate property values with what I refer to as a meta-instance.  In fact, that is one of the cool, geeky elements of this application; the application creates instances of these controls dynamically without having a container for the controls and can map the control text in the textbox back to an actual in-memory instance of a control that isn't defined within the code.

The magic that facilitates this capability is dynamic IL generation via the System.Reflection and System.Reflection.Emit namespaces.  I will not take the time to illustrate how these work or even my motivations in designing the application in this way.  For those explanations, please see the original posts (Part I and Part II).

I had to make some alterations of the code and wanted to post some insights and strategies that I employed to achieve my goals.  Firstly, the IL that I spit in the previous iteration had some pretty simple and brainless methods.  In other words, the property methods were simply getters and setters - they took the value and assigned it to the private field or returned the private field's value, as in the following code.

NOTE:  All of the code in this article has been greatly simplified and altered to protect NDA and IP.  While this code is pretty specific and not very extensible, the actual code is much more extensible and reusable.

// the getter method which is functionally equivalent to the following:
// public int Age {
//    get { return _age; }
// }
//
// variables already initialized:
// tb == TypeBuilder
// pb == PropertyBuilder
// fb == FieldBuilder
//

MethodBuilder getter = tb.DefineMethod("getAge", MethodAttributes.Public, typeof(int), Type.EmptyTypes);
ILGenerator il = getter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fb);
il.Emit(OpCodes.Ret);
pb.SetGetMethod(getter);

Similarly, my setter method was very vanilla in that all it did was accept a value and blindly assign it to the underlying field:

// the setter method which is funcionally equivalent to the following:
// public int Age {
//    set { _age = value; }
// }
//

MethodBuilder setter = tb.DefineMethod("setAge", MethodAttributes.Public, null, new Type[] { typeof(int) });
ILGenerator il = setter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fb);
il.Emit(OpCodes.Ret);
pb.SetSetMethod(setter);

This is all well and good - especially for properties that simply contain their assigned values.  No sweat.  However, what happens in the event that we need to perform validation on the values being assigned?  The proposition gets trickier, because now we need to emit IL that is a tad more complicated and we have to understand how to manipulate the stack in IL and know when to push and when to pop...that, or know how to interpret IL (via ILDASM.exe) and retrofit it into our code - something that is quite easy, but fraught with a learning curve.

Due to the nature of this application, I could make some allowances and take some shortcuts.  Suppose I want the following code:

public int Age{
   set {
      if ( isValidAge(value) )
         _name = value;
      else
         MessageBox.Show("Invalid age: " + value);
   }
}

Now I have to perform validation on the arguments, call a function, concatenate a string, pass it to the message box, etc.  The ante has been raised, but it's not something that can't be overcome.  Notice the isValidName() function.  I don't want to have to emit a validation method in the dynamic assembly; instead, I can create a generic validation method in my source and have my dynamic assembly call back into the host assembly.  This would, of course, require that we know, upon creating the dynamic method, what function to call, what parameters to supply, and how to validate the value.

For this purpose, I propose to create a class in the host assembly that performs this validation in a generic way.  We could then provide the builder with the metadata necessary to call the appropriate validation function, and have it return the appropriate result.  Following the pattern outlined above, we might end up with the following:

public sealed class Validators {
   public static bool IsValidAge(int value) {
      return ( value < 0 ) ? false : true;
   }  
}

Ok, very contrived, but I'm illustrating a point...and a point that we'll rectify here momentarily.

The IL necessary to generate the new setter that calls this function is substantially more involved, but we'll illustrate it with comments:

// get the pointer to the methods.
// in order to call the various methods, we need to first have the appropriate metadata so the
// emitter can effectively map the calls
//

MethodInfo methodValidator = typeof(Validators).GetMethod("IsValidAge", BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, new Type[] { typeof(int) }, null);
MethodInfo methodStringConcat = typeof(string).GetMethod("Concat", BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, new Type { typeof(string), typeof(string) }, null);
MethodInfo methodMessageBox = typeof(System.Windows.Forms.MessageBox).GetMethod("Show", BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, new Type[] { typeof(string) }, null);

// now we need to create the jump points to control program flow.
// Despite the simplicity of the code we're emitting, we have a conditional block
// which requires application branching and flow control.
//
Label labelInvalidProp = il.DefineLabel();
Label labelEndOfMethod = il.DefineLabel();

il.Emit(OpCodes.Ldarg_1);                              // .. push the value parameter
il.EmitCall(OpCodes.Call, methodValidator, null);      // if ( isAgeValid(...) ) {
il.Emit(OpCodes.Brfalse_S, labelInvalidProp);          //
il.Emit(OpCodes.Ldarg_0);                              // .. push the this parameter
il.Emit(OpCodes.Ldarg_1);                              // .. push the value parameter
il.Emit(OpCodes.Stfld, fb);                            //    _field = value;
il.Emit(OpCodes.Br_S, labelEndOfMethod);               // }
il.MarkLabel(labelInvalidProp);                        // else {
il.Emit(OpCodes.Ldstr, "Invalid age: ");               // .. push the first string to concat
il.Emit(OpCodes.Ldarg_1);                              // .. push the value parameter to concat
il.EmitCall(OpCodes.Call, methodStringConcat, null);   // .. push the result of the concatenation
il.EmitCall(OpCodes.Call, methodMessageBox, null);     //    MessageBox.Show(...);
il.Emit(OpCodes.Pop);                                  // .. ignore return value
il.MarkLabel(labelEndOfMethod);                        // }
il.Emit(OpCodes.Ret);

This code is pretty slick, but it isn't without its problems and issues.  These issues are made manifest mostly by the problem domain of the issues that I'm solving.  First of all, the only mechanism that I'm enabling for changing property values is a PropertyGrid control.  If I display my own message box, I steal focus from the control and it makes the application more difficult to use.

Second, this method of data validation is not very 'OO'.  I have to create a validator method for each property that I want to validate that falls outside the scope of the object.  What if I had another object with an Age property and I needed to validate it differently, or against a different range?  Using this method, I'd have to create a separate method for each object, for each property to be validated.  If I did that, I might as well forego the entire dynamic code generation approach and create hard classes with their own properties and validators...but that violates my original design.  I want to keep this cleaner.

Therefore, I've come up with a solution that is substantially more flexible, leverages the functionality of the PropertyGrid control, is ultimately easier with respect to the validator methods, and the IL is cleaner.  All in all, it sure seems like a win-win.  In order to implement it properly, I have to go back to how the properties are created in the first place.  After reading Posts I and II, you'll undoubtedly recall that I have an interface (IToolPropertyDefn) that provides a mechanism for referencing properties of the control/object.  Well, in order to provide control-specific validation, I've extended the interface to support a new property called ValidationData.  This class contains information regarding the method to call and the parameters needed.

internal sealed class ValidationData {
   private MethodInfo   _validatorMethod;
   private Type[]       _parameters;
   private object[]     _values;
  
   private ValidationData(string methodName, Type[] parameters, object[] values) {
      _validatorMethod = typeof(Validators).GetMethod(methodName, BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, parameters, null);
      _parameters = parameters;
      _values = values;
   }
  
  
   internal static ValidationData GetIntRangeValidator(int min, int max) {
      return new ValidationData("ValidateIntRange", new Type[] { typeof(int), typeof(int), typeof(int) }, new object[] { min, max });
   }
  
   // methods and properties to return private fields
   // ...Type[] GetParameters();
   // ...object[] GetValues();
   // ...MethodInfo ValidatorMethod;
}


public sealed class Validators {
   public static void ValidateIntRange(int value, int min, int max) {
      if ( value < min || value > max )
      throw new ArgumentException(string.Format("Invalid value.  Value must be between {0} and {1}.", min, max));
   }
}

I am, of course, simplifying my code substantially for a variety of reasons mentioned above, but am still leaving the gist of it so that it's understandable.  The idea is the ValidationData constructor takes a method name, parameters, and the set of values to be passed to the validator.  The constructor then resolves to the actual method (found in a different class) to perform the validation.  At this point, we must change the generated IL code to call this new validation method.

Note also that the validator method no longer returns a boolean value but instead throws an exception if the value fails validation.  This takes advantage of the functionality built into the PropertyGrid that if an exception is thrown from the property setter the PropertyGrid will catch it and display an 'Invalid property value' error message with the description of the exception in the 'Details'.  Additionally, the focus will remain on the PropertyGrid which is a good thing.

The updated IL generator then looks more reminiscent of the following where defn is the IToolPropertyDefn implementation (note: greatly simplified for clarity):

MethodInfo setter = tb.DefineMethod("set" + defn.Name, MethodAttributes.Public, null, new Type[] { defn.DataType });
ILGenerator il = setter.GetILGenerator();
if ( null != defn.ValidateData ) {
   il.Emit(OpCodes.Ldarg_1);                              // .. push the value parameter
                                                          // .. push the other parameters
   object[] values = defn.ValidateData.GetValues();
   Type[] parameters = defn.ValidateData.GetParameters();
   if ( null != values ) {
      for ( int i = 0; i < values.Length; i++ ) {
         // evaluate the type of parameters and push them onto the stack appropriately
         // for simplicity, show just the 'int' variety for illustration purposes:

         il.Emit(OpCodes.Ldc_I4, (int)values[i]);
      }
   }
   il.EmitCall(OpCodes.Call, defn.ValidateData.ValidatorMethod, null);
}
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fb);
il.Emit(OpCodes.Ret);
pb.SetSetMethod(setter);

Again, the code has been greatly simplified, but it makes for a much more extensible model.

Monday, October 24, 2005 2:56:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Saturday, October 22, 2005

Last night was a blast!  About a month and a half ago, two former co-workers and I put the plans in motion to have a PPI reunion of sorts wherein we would invite the various trainers and employees of PPI (Productivity Point International) who worked together 6 years ago when the Utah branch was closed down.  We had so much fun together and had created such strong bonds that we thought it would be a great event.

Well, six of us got together last night and spent about 3 1/2 hrs catching up and having a ball.  All told, those present included me (Aaron Zupancic), Dale Byrd, Joyce Raymundo, Wendy Bloomquist, Marja Larsen, and Kelland Coleman.  What a fun time!  We're planning on getting back together again in March, hopefully this time with a greater turnout.

Thanks all for coming!

Saturday, October 22, 2005 7:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback