Monday, August 24, 2009

Here's something I threw together the other day to help me organize my ripped audio books.  I have several audio books (Harry Potter, LOTR, Dresden Files, etc) that I've been ripping to file so that I could gather an entire 10-20 CD set onto a single disc for easy listening while driving without needing to switch discs.  I like to use WMA files for easy ripping (via Media Player) and the small footprint.

I've found, much to my chagrin, that the information retrieved online for these books (via CDDB, FreeDB, or All Music Guide (which WMP uses)) is unreliable, frequently contains typos and misspellings, and is extremely inconsistent with respect to formatting and convention.

I found that when I inserted a CD I would spend a good amount of time entering the book name, the genre, the year it was recorded, the author, and the performer, and I'd take the time to rename the tracks.  After a handful of CDs this was almost unbearable and extremely tedious.

I therefore set out to write a script that would do this, updating all of the ID3 tags after the fact.  The only thing I would need to concern myself with was that the book name (I like the form Book Name - Disc XX) was correct.  This script belows makes a few assumptions about the folder and file names:

  • each folder contains a single disk and that they are named according to the pattern "Book Name - Disc XX"
  • all books will be collected into a single folder with the name pattern "Book Set - Book Name"

For example, I ripped a book by Jim Butcher named Small Favor.  Each disc went into a folder named "Small Favor - Disc 01", "Small Favor - Disc 02", etc.  I would then collect all the ripped files into a single directory named "Dresden Files - Book 10 - Small Favor".  This way, when all was said and done, I'd have all the books in order and grouped.

I decided to use PowerShell as the language of preference for automating this procedure.  Not only does it provide a very powerful scripting environment, but I can also leverage the .NET Framework (which I love dearly).  As such, I was able to take advantage of TagLib#'s ability to edit the tags within the files.  This script assumes that the taglib-sharp.dll is in the same directory as the .ps1 script file.

The following PowerShell scripts performs all the collecting, enumerating, and tagging of my files.  Feel free to adapt it and tweak it according to your own preferences.  If you have suggestions, I'd welcome them.  Like I said, I threw it together in a very short time so it's probably weak in many regards but seems to get the job done.

Taking my example above, my command line would be:

.\collectaudiobook "Small Favor" "Dresden Files - Book 10" "Jim Butcher" "James Marsters" 2008

Now I don't need to worry about what information was loaded from the CDDB-esque service, the script updates it all after-the-fact. :)

CollectAudioBook.ps1

$a = $args.length

if ( $a -lt 2 ) {
  write @"

USAGE:

`t.\CollectAudioBook.ps1 titlePrefix targetFolderPrefix [author] [artist] [year]

REQUIRED PARAMETERS:

titlePrefix
`tSpecifies the subfolders to process as a single book.
`t(e.g., 'Small Favor' would locate 'Small Favor - Disc 01', 'Small Favor - Disc 02', etc)
`tThe folders are processed in order by name, assuming that to be the proper sequencing of the resulting files.

targetFolderPrefix
`tSpecifies the target folder to which all tracks are collected.  If this
`tfolder doesn't exist, it is created.
`tNOTE: The folder's full name will be comprised of the targetFolderPrefix, a hyphen, and the titlePrefix.
`t(e.g., a titlePrefix of 'Small Favor' and a targetFolderPrefix of 'Dresden Files - Book 10' becomes
`tDresden Files - Book 10 - Small Favor)

author
`tIdentifies the author of the book.

artist
`tIdentifies the name of the performer.

year
`tIdentifies the year of the recording.

"@
  exit
}

$bookName = $args[0]
$bookSet = $args[1] + ' - ' + $bookName
$author = $args[2]
$artist = $args[3]
$year = $args[4]

$target = '.\' + $bookSet
$partNum = 0

# load taglib-sharp.dll which allows for the manipulation on the media tags in the files
$asm = [Reflection.Assembly]::LoadFrom((Resolve-Path ".\taglib-sharp.dll"))

# create the target directory
$dir = New-Item -path "$target" -type directory -force

# enumerate all files in each folder that start with the prefix, copying and renaming each file
foreach ( $folder in ( Get-ChildItem .\$bookName* | Where-Object { $_.Mode.StartsWith('d') } | Sort Name ) ) {
  foreach ( $file in ( Get-ChildItem $folder | Sort Name ) ) {
    ++$partNum

    $targetPrefix = 'Part ' + $partNum.ToString("00")
    $targetFileName = $targetPrefix + '.wma'
    write "Copying $file`t-->`t$targetFileName"
    Copy-Item $folder\$file $target\$targetFileName

    # update the media information in the file
    $media = [TagLib.File]::Create((Resolve-Path "$target\$targetFileName"))
    $media.Tag.Title = $bookName + ' - ' + $targetPrefix
    if ( $author -ne $null ) { $media.Tag.AlbumArtists = $author }
    if ( $artist -ne $null ) { $media.Tag.Performers = $artist }
    $media.Tag.Genres = { Audio Book }
    if ( $year -ne $null ) { $media.Tag.Year = $year }
    $media.Save()
  }
}

Monday, August 24, 2009 10:20:53 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, April 06, 2009

I was putting together a WinForms utility application today that consists of a TabStrip control on the main dialog box.  On a themed system the TabStrip displays a nice gradual gradient on the page.  As part of this application I wanted to output statistical and detailed information about the user's selections in a readonly fashion, but I wanted to have the text appear as though it were written directly on the TabStrip page rather than within a control.

Of course my first thought was to use a Label control, but I almost immediately wrote that off as the amount of information to be displayed was prohibitive and variable.  The data being displayed might exceed the space on the TabStrip, so scrolling the information is a must.

Next I thought of using a TextBox control, but without some pretty intense subclassing and overriding, I wouldn't be able to achieve the 'written-on' look I was after.  The TextBox will always render an opaque background color.  Sure, there are ways of faking transparency, like overriding TextBox and adding a PictureBox and telling the parent control to WM_PRINT into it, but that seems to be a kludgy way of getting the results I wanted.

Additionally, there are mechanisms to use a RichTextBox (v5.0) to achieve transparency that are well documented online, but try as I might with these implementations, I found them to be quite buggy.  In particular, the appearance of the scrollbars was erratic and sometimes didn't render properly with the control was resized.  After battling with it for a few hours I gave up.  It wasn't for lack of desire, it just wasn't worth it given the scope of the project I was working on.

So this morning I went back to the drawing board and came up with a solution in about 45 minutes that seems to do exactly what I wanted.  I've called this control the ScrollableLabel.  Essentially, it offers the readonlyness of a Label control and the scrollability of a TextBox.  This implementation here is very fundamental.  I plan on enhancing it some more to incorporate user-defined colors, headers, and much more.

I made some executive decisions about how this control should render.  For instance, it will always use your system's colors for text and I set the BackColor property of the UserControl to Transparent via the designer.  For both of these I plan on making them user-selectable choices, but for not, they're the look I was after.

public partial class ScrollableLabel : UserControl {
   public ScrollableLabel() {
      InitializeComponent();
      SetStyle(ControlStyles.Selectable, false);
      // the preferred mechanism for enabling double-buffering is to set the DoubleBuffered property
      // of the control rather than the equivalent SetStyle(DoubleBuffer | UserPaint | AllPaintingInWmPaint, true).
      DoubleBuffered = true;
      AutoScroll = true;
      AutoScrollMinSize = Size.Empty;
   }

   private SizeF _actualTextSize;

   public override string Text {
      get { return base.Text; }
      set {
         base.Text = value;
         calcLabelDimensions();
         Invalidate();
      }
   }

   protected override void OnScroll(ScrollEventArgs se) {
      base.OnScroll(se);
      Invalidate();
   }

   public void Clear() {
      Text = string.Empty;
   }

   #region Hidden design-time properties
   [Browsable(false)]
   public override bool AutoScroll {
      get { return true; }
      set { /* do nothing for now; control is always AutoScroll */ }
   }


   [Browsable(false)]
   public override Color ForeColor {
      get { return SystemColors.ControlText; }
      set { /* do nothing for now; only ControlText is currently supported */ }
   }
   #endregion

   private void calcLabelDimensions() {
      using ( Graphics g = CreateGraphics() ) {
         // assume that no text should wrap
         _actualTextSize = g.MeasureString(Text, Font);
         AutoScrollMinSize = Size.Round(_actualTextSize);
      }
   }

   protected override void OnPaint(PaintEventArgs e) {
      RectangleF rct = new RectangleF(AutoScrollPosition, _actualTextSize);
     
Brush br = Enabled
                    ? SystemBrushes.ControlText
                    : SystemBrushes.GrayText;
      e.Graphics.DrawString(Text, Font, br, rct);
   }
}

When this control is added to a Form it will automatically add scrollbars if the contents exceed the dimensions of the control.  At design-time, however I wanted to make sure that I could see where the ScrollableLabel was positioned so I created a simple Designer.

public sealed class BorderlessBorderDesigner : ControlDesigner {
   protected override void OnPaintAdornments(PaintEventArgs pe) {
      base.OnPaintAdornments(pe);
      if ( BorderStyle.None == getBorderStyle() ) {
         Rectangle rct = Control.ClientRectangle;
         rct.Width -= 1;
         rct.Height -= 1;
         using ( Pen p = new Pen(SystemColors.ControlDark) ) {
            p.DashStyle = DashStyle.Dash;
            pe.Graphics.DrawRectangle(p, rct);
         }
      }
   }

   private BorderStyle getBorderStyle() {
      UserControl ctl = Control as UserControl;
      if ( null != ctl ) return ctl.BorderStyle;
      return BorderStyle.None;
   }
}

Then, I added [Designer(typeof(BorderlessBorderDesigner)] to my ScrollableLabel class declaration.

It's a simple control that achieves a simple goal.  I'll post my updates to it as I get a chance to modify it and make it all the more flexible and powerful.

Enjoy!

.NET | C# | WinForms | Controls
Monday, April 06, 2009 9:19:13 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, December 09, 2008

Alright, this has been a downright stumper and I'm currently at a loss.  For this reason, therefore, I'm opening up to the community to see if anyone out there has any recommendations to this dilemma.

The issue revolves around running a 32-bit application on a 64-bit version of Windows and the application needs to pull values from the Registry.  One thing that 64-bit Windows will do, to help achieve compatibility with 32-bit applications by transparency, is remap (redirect) registry calls.  In other words, when I request "HKLM\SOFTWARE\Test Company\Application Name\Key Name" it actually redirects this to "HKLM\SOFTWARE\Wow6432Node\Test Company\Application Name\Key Name" for storage and retrieval.  So far so good.

Well, I have an old 32-bit application written in VB 6.0 that writes a value to the registry that my .NET application must read.  For argument sake, let's say that the value is ultimately written to the HKLM\SOFTWARE\Wow6432Node\Company\Application\Settings key with a name of Value01.  This is precisely what I'd expect.  However, when I attempt to read this value in my .NET application (I'm using VS 2008, .NET 3.5) I'm getting erratic results.

This is what I have:

My application is explicitly compiled as a 32-bit application.  To do this, I've set the Platform target project property to x86.  I've also confirmed this using the CorFlags.exe, even going to far as using the /32BIT+ switch to ensure the setting.

I have a few varieties of code that seem like they should successfully read the value:

Take 01:

private static string getValue01() {
   return getCompanySetting("Application", "Settings", "Value01", "Not found");
}

private static string getCompanySetting(string appName, string section, string key, string defaultValue) {
   string subKey = string.Format(@"SOFTWARE\Company\{0}\{1}", appName, section);
   return getRegValue(subKey, key, defaultValue);
}

private static string getRegValue(string subKey, string key, string defaultValue) {
   RegistryKey key = Registry.LocalMachine.OpenSubKey(subKey);
   if ( null != key ) {
      object val = key.GetValue(key);
      return ( string.IsNullOrEmpty(val) ? string.Empty : val.ToString();
   }
   else {
      return new Win32Exception(Marshal.GetLastWin32Error()).Message;
   }
}

Take 02:  This is almost the same except that I use the WinAPI directly to read the value rather than the managed objects.

private static string getValue01() {
   return getCompanySetting("Application", "Settings", "Value01", "Not found");
}

private static string getCompanySetting(string appName, string section, string key, string defaultValue) {
   FieldInfo fi = Registry.LocalMachine.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)[0];
   SafeHandle hKey = ( SafeHandle )fi.GetValue(Registry.LocalMachine);
   string subKey = string.Format(@"SOFTWARE\Company\{0}\{1}", appName, section);
   return getRegValue(hKey, subKey, key, defaultValue);
}

private static string getRegValue(SafeHandle hKey, string lpszSubKey, string szKey, string szDefaultValue) {
   StringBuilder szBuffer = new StringBuilder(255);
   uint lBufferSize = ( uint )szBuffer.Capacity;

   int lResult;
   IntPtr phkResult;
   uint lpType;

   lResult = WinApi.RegOpenKeyEx(hKey, lpszSubKey, 0, 1, out phkResult);
   if ( WinApi.ERROR_SUCCESS != lResult )
      return new Win32Exception(Marshal.GetLastWin32Error()).Message;

   lResult = WinApi.RegQueryValueEx(phkResult, szKey, 0, out lpType, szBuffer, ref lBufferSize);
   WinApi.RegCloseKey(phkResult);

   return ( WinApi.ERROR_SUCCESS == lResult )
               ? szBuffer.ToString()
               : szDefault;
}

internal static class WinApi {
   internal const int ERROR_SUCCESS = 0;
   internal const uint KEY_WOW64_64KEY = 0x0100;
   internal const uint KEY_WOW64_32KEY = 0x0200;

   [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegOpenKeyEx")]
   internal static extern int RegOpenKeyEx(SafeHandle hKey, string subKey, uint options, uint sam, out IntPtr phkResult);

   [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
   internal static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, out uint lpType, StringBuilder lpData, ref uint lpcbData);
 
   [DllImport("advapi32.dll", SetLastError = true)]
   internal static extern int RegCloseKey(IntPtr hKey);
}

In both of these cases, the code runs flawlessly when I run it from Visual Studio with the debugger attached (e.g., F5).  However (and here's the rub) it fails to retrieve the value when I run it directly within Windows or in VS without the debugger (e.g., CTRL+F5).

In all my investigations, it appears that the application IS indeed a 32-bit image (Process Explorer confirms it), but it will simply not redirect to the \Wow6432Node unless it's running with the debugger attached.

Any thoughts of how to address it?  It's driving me nuts.

Tuesday, December 09, 2008 7:37:44 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Friday, June 27, 2008

I was putting together a small application today that presents data in a TextBox.  Now while this isn't too uncommon :), the text gets added to the TextBox a line at a time via the TextBox's .AppendText() method.  I needed to do this repeatedly and in succession.  As it turns out, an unwelcome side effect of AppendText is that it scrolls the TextBox as the text is appended.  This resulted in the TextBox getting cleared and then 'wiping' down as the text was added.  Not only was this visually unappealing, but I wanted the insertion point to stay at the top of the contents.

To my knowledge, which is quite flawed and limited, there's not a built-in mechanism in .NET that provides this functionality.  Sure, I could override the TextBox control and manage the WndProc method and/or use SetStyle to make the control user drawn.  But I didn't want to do that.  Well, accomplishing this is quite easy.

There are a few ways we can do it:

  1. Use the LockWindowUpdate() API function.
  2. Use SendMessage() API function with the WM_SETREDRAW message.

LockWindowUpdate() is slick and arguably easier to use, but it's intended purpose isn't to suppress the redrawing of controls in this manner.  Plus, you can only have one window locked at a time.  So I threw that one out.

I wrapped the logic to lock and unlock the redrawing of the control in an IDisposable object so I wouldn't have to worry about remembering to clean up after myself.  My class is as follows:

public class LockVisualUpdate : IDisposable {
   public LockVisualUpdate(IWin32Window control) {
      _hWnd = control.Handle;
      SendMessage(control.Handle, WM_SETREDRAW, 0, 0);
   }


   private readonly IntPtr _hWnd;
   private const int WM_SETREDRAW = 0x000B;


   [DllImport("user32.dll")]
   private static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);


   [DllImport("user32.dll")]
   private static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);


   public void Dispose() {
      SendMessage(_hWnd, WM_SETREDRAW, 1, 0);
      InvalidateRect(_hWnd, IntPtr.Zero, false);
   }
}

Really simple to use.  To consume it, I simply have to do the following:

using ( new LockVisualUpdate(textBox1) ) {
   textBox1.AppendText(...);
   // ...repeated as often as necessary to populate the control

   // when finished, position the insertion point to the top of the control
   textBox1.SelectionStart = 0;
   textBox1.ScrollToCaret();
}

.NET | C#
Friday, June 27, 2008 1:28:14 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, June 23, 2008

A few days ago I realized that my blog wasn't working properly.  This came with more than a bit of frustration because I had upgraded ito to DasBlog on May 25th (almost 1 month ago exactly!).  What clued me off was the fact that I wasn't getting any comments to prior posts.  While the vast majority of comments came from my download control and my rating control (neither of which has yet been ported over), I would get the occasional comment through the blog directly.  But since the upgrade nothing.  Well, that's not true, I got two comments on my transition post but I didn't get either one because I didn't set up my mail setting correctly.  That has since been fixed.

As it turns out, the code that I originally used to port the blog site over was flawed, but I didn't realize it until it was too late.  Pretty much all of my google links where my blog was the top page or in the top few have all but disappeared :(.  I have, however, updated the code (which I present below) in the event there is any other poor soul out there needing to migrate from .Text to DasBlog.

Comments should now work on the blog.

I hope this works better for everyone moving forward:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using newtelligence.DasBlog.Runtime;

namespace ConvDotTextToDasBlog {
   internal class Program {
      private class EntryData {
         public EntryData(string id, string title) {
            Id = id;
            Title = title;
         }

         public readonly string Id, Title;
      }

      private static readonly Dictionary<int, EntryData> _postDict = new Dictionary<int, EntryData>();

 
     private static void Main() {
         string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "output");
         Directory.CreateDirectory(path);

         IBlogDataService dataService = BlogDataServiceFactory.GetService(path, null);
         string connStr = @"Initial Catalog=Blog; Data Source=(local)\SQLEXPRESS; Integrated Security=True";

         using ( SqlConnection conn = new SqlConnection(connStr) ) {
            conn.Open();
            using ( SqlCommand cmPosts = new SqlCommand("SELECT COUNT(ID) FROM blog_Content WHERE PostType=1; SELECT * FROM blog_Content WHERE PostType=1", conn) )
            using ( SqlDataReader drPosts = cmPosts.ExecuteReader() ) {
               drPosts.Read();
               int totalCount = drPosts.GetInt32(0);
               drPosts.NextResult();

               int currIndex = 0;
               while ( drPosts.Read() ) {
                  int postId = drPosts.GetInt32(0);
                  string postTitle = drPosts.IsDBNull(1) ? string.Empty : drPosts.GetString(1);
                  DateTime dtCreated = drPosts.GetDateTime(2);
                  DateTime dtModified = drPosts.GetDateTime(10);
                  string postText = drPosts.GetString(12);
                  string postAuthor = drPosts.GetString(5);

                  // catalog the id, assigning it a guid
                  string newPostId = Guid.NewGuid().ToString().ToLowerInvariant();
                  string newPostTitle = ( postTitle.Length > 0 ? postTitle : postText.Substring(0, Math.Min(20, postText.Length)) );
                  _postDict.Add(postId, new EntryData(newPostId, newPostTitle));

                  Console.WriteLine("Processing Post #{0} ({1} of {2})", postId, ++currIndex, totalCount);
                  Entry entry = new Entry();
                  entry.CreatedUtc = dtCreated;
                  entry.ModifiedUtc = dtModified;
                  entry.Title = newPostTitle;
                  entry.Content = postText;
                  entry.EntryId = newPostId;
                  entry.Categories = getPostCategories(postId, connStr);
                  entry.Author = postAuthor;
                  dataService.SaveEntry(entry);
               }
            }

            using ( SqlCommand cmComments = new SqlCommand("SELECT COUNT(ID) FROM blog_Content WHERE PostType=3; SELECT * FROM blog_Content WHERE PostType=3", conn) )
            using ( SqlDataReader drComments = cmComments.ExecuteReader() ) {
               drComments.Read();
               int totalCount = drComments.GetInt32(0);
               drComments.NextResult();

               int currIndex = 0;
               while ( drComments.Read() ) {
                  int commentId = drComments.GetInt32(0);
                  int refPostId = drComments.GetInt32(13);
                  DateTime dtCreated = drComments.GetDateTime(2);
                  string commentAuthorName = drComments.IsDBNull(5) ? string.Empty : drComments.GetString(5);
                  string commentAuthorIp = drComments.IsDBNull(7) ? string.Empty : drComments.GetString(7);
                  string commentAuthorUrl = drComments.IsDBNull(11) ? string.Empty : drComments.GetString(11);
                  string commentText = drComments.GetString(12);

                  EntryData refEntry;
                  if ( !_postDict.TryGetValue(refPostId, out refEntry) )
                     Console.WriteLine("Error processing comment #{0} ({1} of {2}); post {3} was not resolved.", commentId, ++currIndex, totalCount, refPostId);
                  else {
                     Console.WriteLine("Processing Comment #{0} ({1} of {2})", commentId, ++currIndex, totalCount);
                     Comment comment = new Comment();
                     comment.EntryId = Guid.NewGuid().ToString().ToLowerInvariant();
                     comment.CreatedUtc = dtCreated;
                     comment.ModifiedUtc = dtCreated;
                     comment.TargetEntryId = refEntry.Id;
                     comment.TargetTitle = refEntry.Title;
                     comment.Author = commentAuthorName;
                     comment.AuthorHomepage = commentAuthorUrl;
                     comment.AuthorIPAddress = commentAuthorIp;
                     comment.Content = commentText;
                     dataService.AddComment(comment);
                  }
               }
            }
         }
      }

      private static string getPostCategories(int postId, string connStr) {
         const string sql = "SELECT cat.Title FROM blog_Links AS links INNER JOIN blog_LinkCategories AS cat ON links.CategoryID = cat.CategoryID WHERE links.PostID = @PostID";

         List<string> categories = new List<string>();
         using ( SqlConnection cn = new SqlConnection(connStr) )
         using ( SqlCommand cm = new SqlCommand(sql, cn) ) {
            cn.Open();
            cm.Parameters.Add("@PostID", SqlDbType.Int).Value = postId;
            using ( SqlDataReader dr = cm.ExecuteReader(CommandBehavior.CloseConnection) ) {
               while ( dr.Read() ) {
                  string category = dr.GetString(0);
                  categories.Add(category);
               }
            }
         }
         return string.Join(";", categories.ToArray());
      }
   }
}

.NET | DasBlog | Journal
Monday, June 23, 2008 9:46:47 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, June 14, 2008

I was experiencing a most perplexing issue yesterday and the day prior that I *finally* resolved late last night.  In essence, I have a Windows Service that I wrote that hosts an adapted Cassini server which, in turn, hosts an ASP.NET website product running on the client's computer.  The issue was the classic case of 'it works on my machine'.

I could install my service, configure it, and run an ASP.NET website on my computer.  It was a beautiful and exciting thing to witness.  When I went to execute it on another computer, however, the service would start up perfectly but when I made the first request to the website the service would crash (immediately stop) and log an error to the system's Application event log.  The error was a FileNotFoundException.

After troubleshooting the obvious stuff for a bit (folder permissions (the Service runs as NETWORK SERVICE), executables in the proper places, comparing systems, etc), I resorted to more drastic means. :)  Actually, this is technique I frequently employ when troubleshooting issues loading assemblies.

You can troubleshoot Fusion binding issues by simply setting up some registry keys on the machine in question.  Fusion, in short, is the name given by Microsoft for their assembly loading and binding technology in .NET.  Because I don't have the .NET SDK or Visual Studio on the target machine in question, I had to set it up manually.  I added the following registry keys:

HKLM\SOFTWARE\Microsoft\Fusion\LogPath (string) with value pointing to my desired output folder (C:\(TEMP)\FusionLog).
HKLM\SOFTWARE\Microsoft\Fusion\LogFailures (DWORD) with a value of 1.

I restarted the service and made another web request to witness the failure for the umpteenth time.  Sure enough I got some output in my FusionLog folder:

*** Assembly Binder Log Entry  (6/13/2008 @ 9:18:40 PM) ***

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable  C:\Program Files\Devstone\WebHostSvc\WebHostSvc.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = NT AUTHORITY\NETWORK SERVICE
LOG: DisplayName = WebHostSvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx
 (Fully-specified)
LOG: Appbase = file:///C:/Documents and Settings/All Users/Application Data/Devstone/Web/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = f9861834
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Documents and Settings\All Users\Application Data\Devstone\Web\web.config
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Post-policy reference: WebHostSvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx
LOG: GAC Lookup was unsuccessful.
ERR: No codebases found to download from.
ERR: Unrecoverable error occurred during pre-download check (hr = 0x80070002).

The thing that immeidately stood out was the fact that the Initial PrivatePath, Dynamic Base, and Cache Base were all NULL.  On my development machine these all referenced the /Web/bin folder.  Why wasn't it looking in the \bin folder?

At this point I began debugging the service, actually stepping into the .NET Framework code itself to see if I could figure out the issue.  Delving into the HttpRuntime.cs file I could see that the \bin folder gets appended to the app domain's private paths in the InitFusion() method, just as I had expected.  However, somehow the code wasn't getting that far.  Unfortunately, I couldn't step through some of the code leading up to this point so it was difficult to see exactly why it wasn't getting here.  Actually, in retrospect, I should have looked at the code a little longer - the answer was right in front of me.

I noticed from the fusion log that it was performing a GAC Lookup for the assembly.  Now the assembly isn't GAC-registered on my development machine but I was at my wit's end with the issue.  So I GAC'd it:

gacutil /i WebHostSvc.exe

Immediately upon the next request I got the error I was looking for in the browser:

The current identity (NT AUTHORITY\NETWORK SERVICE) does not have write access to 'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files'.

Aha!  The one thing I didn't check but should have before.  Granting NETWORK SERVICE rights to that folder fixed the problem straight away.

I then removed it from the GAC and removed the Temporary ASP.NET Files that were created, restarted the service, and ran it again to verify that it was running properly.

At this point I went back to the source code and sure enough I saw the line I was looking for.  When initializing, it invokes a method called SetUpCodegenDirectory (which maps to the Temporary ASP.NET Files folder).  This method ensures that the caller has write access to it.

So the moral of the story: make sure that the user account that you're using for ASP.NET has write access to the Temporary ASP.NET Files folder and you'll be a much happier developer.

.NET | Web
Saturday, June 14, 2008 9:42:17 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, June 09, 2008

Almost immediately following yesterday's post I got to thinking about supporting static classes / methods.  I see static classes all the time where the class represents a set of related-but-operationally-distinct methods, often utilities.  The post yesterday really had two aspects about it: 1) the retrieval / extraction and the hosting of an assembly embedded as a resource within another assembly and 2) the creation of a proxy type by wrapping the logic surrounding using Reflection to dig into the guts of the referenced object in a base class.

The ante is raised (only slightly) when dealing with static objects.  Static objects have no instance methods and therefore have no inheritance support and no constructors (the static .ctor is really a type initializer and it's quite the same thing).  Our simple example yesterday defines an abstract base class (ProxyClassBase) that encapsulates the logic for creating the proxy instance and invoking the methods within it.

If we were to mimic the structure of the reference object (the object from the extracted assembly) in our proxy class we would be unable to inherit ProxyClassBase because our proxy would be static.  Allow me to flesh out an example illustrating this:

Example 01: Static Proxy

For this to work, I'd have to create a new class.  Let's name it StaticProxyClass.  This class would, in large measure, be quite similar to the ProxyClassBase base type we defined yesterday with a few distinctions:  1) we wouldn't need an Instance (all methods are static) and 2) we can't have a protected .ctor (our proxy would be static and would be unable to inherit the class).

Our type might resemble the following:

namespace HostAsm {

   internal class StaticProxyClass {
      internal StaticProxyClass(string assemblyName, string typeName) {
         _type = AssemblyLoader.GetType(assemblyName, typeName);
      }

      private readonly Type _type;

      internal void InvokePublicVoidMethod(string methodName, object[] parameters) {
         Type[] types = getTypes(parameters);
         MethodInfo method = _type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Standard, types, null);
         method.Invoke(null, parameters);
      }

      private static Type[] getTypes(object[] parameters) {
         Type[] result;
         if ( null == parameters || 0 == parameters.Length )
            result = Type.EmptyTypes;
         else {
            result = new Type[parameters.Length];
            for ( int i = 0; i < parameters.Length; i++ ) {
               result[i] = parameters[i].GetType();
            }
         }
         return result;
      }
   }
  
}

Note that I've included a method (getTypes) that I didn't include in yesterday's example but may well belong in the ProxyClassBase type as well.  This method will assist in resolving overloaded methods (methods with the same name but with differing parameters).

Our static proxy class would effectively wrap an instance of this type and delegate calls to the reference object through it.

namespace HostAsm {

   internal static class CustomStaticClass {
      static CustomStaticClass() {
         _proxy = new StaticProxyClass("EmbeddedAsm.dll", "EmbeddedAsm.CustomStaticClass");
      }

      private static readonly StaticProxyClass _proxy;

      internal static void StaticMethod(string data) {
         _proxy.InvokePublicVoidMethod("StaticMethod", new object[] { data });
      }
   }

}

This is all well and good and it works perfectly well.  There's something missing, however, and a few things bug me about it.

First of all, it bugs me most that we'd end up having to duplicate (in large part) the functionality in one class (ProxyClassBase) in another (StaticProxyClass).  Secondly, we're omitting one major part of the discussion: objects that have both instance methods and static methods.  To me, this is the deal breaker.

To support this and to leverage our ProxyClassBase type even for static methods, I'd do away with the StaticProxyClass and add the methods therein to the ProxyClassBase.  This will necessitate that our proxy type for static types be non-static, but we can pseudo-simulate that by making its .ctor private.  For these, we will have to encapsulate the actual proxy within our proxy type.  In fact, we'd have to take that one more level by creating delegate methods on our proxy class that direct the calls to the base type.  I'd rather do this than muck with the scope of the base class's methods to keep my intentions pure with the base class.

Example 02: Enhancing the ProxyClassBase

The ProxyClassBase will get the functionality we just added to StaticProxyClass as well as a flag indicating whether the wrapped type is a static type.

using System;
using System.Reflection;

namespace HostAsm {

   internal abstract class ProxyClassBase {
      protected ProxyClassBase(string assemblyName, string typeName, bool isStatic) {
         ObjType = AssemblyLoader.GetType(assemblyName, typeName);
         Instance = isStatic ? null : Activator.CreateInstance(ObjType);
      }

      protected Type ObjType { get; private set; }
      protected object Instance { get; private set; }
      protected bool IsStatic { get; private set; }

      protected T InvokePublicMethod<T>(string methodName) {
         MethodInfo method = ObjType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Standard, Type.EmptyTypes, null);
         return ( T )method.Invoke(Instance, null);
      }

      protected void InvokePublicStaticVoidMethod(string methodName, object[] parameters) {
         Type[] types = getTypes(parameters);
         MethodInfo method = ObjType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, types, null);
         method.Invoke(null, parameters);
      }

      private Type[] getTypes(object[] parameters) {
         Type[] result;
         if ( null == parameters || 0 == parameters.Length )
            result = Type.EmptyTypes;
         else {
            result = new Type[parameters.Length];
            for ( int i = 0; i < parameters.Length; i++ ) {
               result[i] = parameters[i].GetType();
            }
         }
         return result;
      }
   }

}

Then, to finish the puzzle, our proxy class that wraps the static type gets a face lift:

using System;

namespace HostAsm {

   internal class CustomStaticClass : ProxyClassBase {
      private CustomStaticClass()
         : base("EmbeddedAsm.dll", "EmbeddedAsm.CustomStaticClass", true) {
      }

      private static readonly CustomClass2 _proxy = new CustomClass2();

      internal static void StaticMethod(string data) {
         _proxy.invokePublicStaticVoidMethod("StaticMethod", new object[] { data });
      }

      private void invokePublicStaticVoidMethod(string methodName, object[] parameters) {
         base.InvokePublicStaticVoidMethod(methodName, parameters);
      }
   }

}

I've not given this approach a great deal of scrutiny.  At first glance I can see it being somewhat fragile.  If the base class changes you have to proliferate changes to the inheriting types.  Then again, I don't see this base class like this changing much if at all except for new additions.

Monday, June 09, 2008 8:20:56 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, June 08, 2008

I've recently developed an application that has the requirement of not having any external dependencies.  That is, the application must, for a variety of reasons, be a stand-alone executable without any bundled libraries or assemblies.  I've already developed some of the functionality that this application relies on, however, and it's packaged in other small utility libraries.  Referencing these assemblies would be the natural decision if I wanted to leverage the functionality.  But that breaks rule #1: no external dependencies.

That aside, I might consider having my project link to the source code files to the other libraries and effectively compile the code directly into my application.  That might, in a simplistic case, be reasonable, but it could easily be unwieldy and unmanagement in the long term.  Not only would I be bringing in classes from another quite distinct namespace into my project, but there's no real tie between them.  A developer may change the source file in a way that breaks my application, introducing other previously-unexpected dependencies.  The list goes on and on as to why this may be a bad idea.

The next thought that came to mind was "What if I were to take the assembly I would otherwise reference and embed it within my application as a resource?  I could load it dynamically at runtime without physically deploying a separate library."  This approach does require a little more work on the part of the developer.

This isn't a new concept.  In fact, several years ago I'd written some code that does this in a similar fashion.  I'm pleased with how effortless it was, however, and I managed to throw this code together in about 20 minutes.

For purposes of the illustration and to test the concept, I created a blank solution in Visual Studio 2008 called 'LoadAssemblyFromResource'.  To this solution, I then added three new projects: EmbeddedAsm (class library), HostAsm (class library), and ClientApp (console application).  EmbeddedAsm is the assembly that will be embedded within HostAsm.  HostAsm represents my application that can't have any external dependencies.  ClientApp is my test harness that will invoke methods on HostAsm.  In turn, HostAsm will leverage the functionality in EmbeddedAsm at runtime.

To set this up, I first created EmbeddedAsm and set the output folder for all configurations to bin\.  Then I and compiled it.  Having functionality within it was not necessary at this point.  Basically, I simply needed to be able to reference the output.  Also, I like having a single target to reference.  This makes the embedding of the assembly easier without having to deal with the bin\Debug or bin\Release directories.

Then, in HostAsm I created a folder called Resources.  I right-clicked that folder and chose 'Add --> Existing Item'.  I then browsed to the bin\ folder of EmbeddedAsm and selected the .dll.  Important: I selected Add As Link rather than Add to add the assembly to the Resources folder.  This enables me to make updates to the EmbeddedAsm and have the HostAsm be updated with the changes.  Were I to have selected Add a copy of the .dll at that point in time would be copied into my Resources folder and any updates to the EmbeddedAsm would be ignored; I'd have to re-add it.

ClientApp has a reference to HostAsm.

The final step in the setup, then, was to right-click on the solution (in the Solution Explorer) and select Project Build Order.  Because HostAsm doesn't actually have a reference to EmbeddedAsm, I had to ensure that it has a dependency on in (on the Dependencies tab).  This forces Visual Studio to compile EmbeddedAsm first so when it gets around to compiling HostAsm it embeds the latest version.

Now for the code...

In EmbeddedAsm I created a very simple class with a simple method:

EmbeddedAsm:

namespace EmbeddedAsm {
   public class CustomClass {
      public string GetMessage() {
         return "Hello from CustomClass";
      }
   }
}

I wrote HostAsm to be a little more robust and reusable.  Rather than simply loading the embedded assembly and running with it, I wanted to put together a bit of a framework (albeit a simple one) to handle the loading of multiple embedded assemblies.

HostAsm:

namespace HostAsm {

   internal static class AssemblyLoader {
      private static Dictionary<string, Assembly> _loadedAssemblies = new Dictionary<string, Assembly>();

      internal static Assembly LoadAssembly(string assemblyName) {
         if ( _loadedAssemblies.ContainsKey(assemblyName) )
            return _loadedAssemblies[assemblyName];

         byte[] bytes;
         using ( Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("HostAsm.Resources." + assemblyName) ) {
            bytes = new byte[(int)stream.Length];
            stream.Read(bytes, 0, bytes.Length);
         }

         Assembly asm = Assembly.Load(bytes);
         if ( null == asm )
            throw new ArgumentException("Unable to load assembly: " + assemblyName);

         _loadedAssemblies.Add(assemblyName, asm);
         return asm;
      }


      internal static Type GetType(string assemblyName, string typeName) {
         Assembly asm = LoadAssembly(assemblyName);

         Type type = asm.GetType(typeName);
         if ( null == type )
            throw new ArgumentException(string.Format("Unable to locate type {0} in assembly {1}", typeName, assemblyName));

         return type;
      }
   }

}

The AssemblyLoader class is responsible for the loading of assemblies (naturally) and for retrieving types from the embedded assemblies.  It loads the assemblies by accessing the manifest resource stream of the executing assembly (HostAsm) and fully-qualifying the reference to the embedded library.  Note that the assembly's root namespace is 'HostAsm' and the embedded library is in the Resources folder so the fully-qualified name to the .dll is HostAsm.Resources.EmbeddedAsm.dll.  We then use the Assembly.Load() method to load the assembly from bytes read from the stream.  The assembly is catalogued so we can avoid quickly retrieve an already-loaded library quickly in a subsequent call.  The assembly is then returned to the caller.

The approach I took for accessing and calling the types in the embedded assembly is that of a proxy class.  In this case, the proxy class is coded such that it's methods match those in the class to be called from the embedded assembly.  Also, I wanted to genericize the proxy type so I could, in a repeatable form, invoke methods on other types in a similar fashion.  Therefore, I created a ProxyClassBase type which abstracts away the reflection plumbing necessary to make the calls.  These classes also exist in the HostAsm library.

ProxyClassBase.cs

namespace HostAsm {

   internal abstract class ProxyClassBase {
      protected ProxyClassBase(string assemblyName, string typeName) {
         InstanceType = AssemblyLoader.GetType(assemblyName, typeName);
         Instance = Activator.CreateInstance(InstanceType);
      }

      protected Type InstanceType;
      protected object Instance;

      protected T InvokePublicMethod<T>(string methodName) {
         return (T)InstanceType.GetMethod(methodName).Invoke(Instance, null);
      }
   }

}

CustomClassProxy.cs

namespace HostAsm {

   /// <summary>
   /// Proxy class for EmbeddedAsm.CustomClass.
   /// </summary>

   internal class CustomClass : ProxyClassBase {
      public CustomClass()
         : base("EmbeddedAsm.dll", "EmbeddedAsm.CustomClass") {
      }

      public string GetMessage() {
         return InvokePublicMethod<string>("GetMessage");
      }
   }

}

Note that the CustomClass proxy class invokes the base class's .ctor which in turn uses the functionality in the AssemblyLoader class to encapsulate the creation and management of the type being wrapped.  The method on the CustomClass proxy matches that of the actual CustomClass in EmbeddedAsm except that it defers to the base class's implementation to invoke the method via Reflection.

Then, within my HostAsm I can simply consume the method as though the object were local and participated in the same namespace of my application:

namespace HostAsm {

   public class Worker {
      public void DoWork() {
         CustomClass cc = new CustomClass();
         Console.WriteLine(cc.GetMessage());
      }
   }

}

It's actually quite easy and straightforward.  Not only does this approach help me in my predicament of not being able to support any external dependencies, but I can see how it might be useful in situations where assemblies are packaged and deployed together, used as Add-Ins, etc.  It's pretty cool stuff.

Sunday, June 08, 2008 3:03:26 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, May 25, 2008

This marks a significant moment in my blog's lifetime.  Today I (finally) took the time to migrate the blog from .Text .95 to DasBlog 2.0.

I have long desired to move off of .Text for a couple of reasons: 1) it has long not been updated (yes, I know about the SubText branch) and 2) it runs on the .NET Framework 1.1.  In and of itself, .NET 1.1 is not bad.  In fact, I think it was a pretty rockin' platform 7 years ago.

I have recently acquired Back in February I acquired a replacement server machine for two workstations that I was using as my web servers.  This machine has, except for a few moments of glory, sat still, quiet, and off in my office just waiting to run.  Well, today, I finally had a moment to dedicate to get the DasBlog engine running and all of my websites transferred over to the new machine.  This new box is running Windows Server 2008 and IIS 7.0 (another reason I wanted to move my blog over to the newer .NET platform).  Thus far, DasBlog is running great and I'm very much enjoying it.

Despite conducting several searches I couldn't find any real help or canned utilities for taking .Text content and migrating it.  Pretty much everything led to a dead-end (404) or a SourceForge project that had no downloads.  I did, however, stumble upon some code written by Scott Hanselman that illustrated in a few simple steps the process of creating DasText entries and associated comments.  I've taken this code and tailored it for .Text.  If you're migrating from a .Text with multiple blogs you'll have to customize it a bit, but it should be pretty simple.

I've updated the code to extract blog posts, associated categories, and comments.  NOTE: This code was not written to be elegant; it's really just a brute-forced-extraction of the data.  That's all I was interested in.  I only ever needed to run it once and then I was done.

NOTE: THIS CODE IS OBSOLETE AND HAS BEEN REPLACED AND UPDATED IN THIS POST.

using System;
using
System.Collections.Generic;
using
System.Data;
using
System.Data.SqlClient;
using
newtelligence.DasBlog.Runtime;

namespace
ConvDotTextToDasBlog {
   class Program {
      static void Main(string[] args) {
         IBlogDataService dataService = BlogDataServiceFactory.GetService(AppDomain.CurrentDomain.BaseDirectory, null);
         string connStr = @"Initial Catalog=Blog; Data Source=(local); Integrated Security=True";
         using ( SqlConnection conn = new SqlConnection(connStr) ) {
            conn.Open();
            using ( SqlCommand cmPosts = new SqlCommand("SELECT COUNT(ID) FROM blog_Content WHERE PostType=1; SELECT * FROM blog_Content WHERE PostType=1"
, conn) ) {
               using ( SqlDataReader drPosts = cmPosts.ExecuteReader() ) {
                  drPosts.Read();
                  int totalCount = drPosts.GetInt32(0);
                  drPosts.NextResult();
                  int currIndex = 0;
                  while ( drPosts.Read() ) {
                     int postId = drPosts.GetInt32(0);
                     string postTitle = drPosts.IsDBNull(1) ? string.Empty : drPosts.GetString(1);
                     DateTime dtCreated = drPosts.GetDateTime(2);
                     DateTime dtModified = drPosts.GetDateTime(10);
                     string postText = drPosts.GetString(12);
                     string postAuthor = drPosts.GetString(5);
                     Console.WriteLine("Processing Post #{0} ({1} of {2})"
, postId, ++currIndex, totalCount);
                     Entry entry = new Entry();
                     entry.CreatedLocalTime = dtCreated;
                     entry.ModifiedLocalTime = dtModified;
                     entry.Title = ( postTitle.Length > 0 ? postTitle : postText.Substring(0, Math.Min(20, postText.Length)) );
                     entry.Content = postText;
                     entry.EntryId = postId.ToString();
                     entry.Categories = getPostCategories(postId, connStr);
                     entry.Author = postAuthor;
                     dataService.SaveEntry(entry);
                  }
               }
            }

            using ( SqlCommand cmComments = new SqlCommand("SELECT COUNT(ID) FROM blog_Content WHERE PostType=3; SELECT * FROM blog_Content WHERE PostType=3"
, conn) ) {
               using ( SqlDataReader drComments = cmComments.ExecuteReader() ) {
                  drComments.Read();
                  int totalCount = drComments.GetInt32(0);
                  drComments.NextResult();
                  int currIndex = 0;
                  while ( drComments.Read() ) {
                     int commentId = drComments.GetInt32(0);
                     int refPostId = drComments.GetInt32(13);
                     DateTime dtCreated = drComments.GetDateTime(2);
                     string commentAuthorName = drComments.IsDBNull(5) ? string.Empty : drComments.GetString(5);
                     string commentAuthorIp = drComments.IsDBNull(7) ? string.Empty : drComments.GetString(7);
                     string commentAuthorUrl = drComments.IsDBNull(11) ? string.Empty : drComments.GetString(11);
                     string commentText = drComments.GetString(12);
                     Console.WriteLine("Processing Comment #{0} ({1} of {2})"
, commentId, ++currIndex, totalCount);
                     Comment comment = new Comment();
                     comment.CreatedLocalTime = dtCreated;
                     comment.ModifiedLocalTime = dtCreated;
                     comment.TargetEntryId = refPostId.ToString();
                     comment.Author = commentAuthorName;
                     comment.AuthorHomepage = commentAuthorUrl;
                     comment.AuthorIPAddress = commentAuthorIp;
                     comment.Content = commentText;
                     dataService.AddComment(comment);
                  }
               }
            }
         }
      }

      private static string getPostCategories(int postId, string connStr) {
         string sql = "SELECT cat.Title FROM blog_Links AS links INNER JOIN blog_LinkCategories AS cat ON links.CategoryID = cat.CategoryID WHERE links.PostID = @PostID"
;
         List<string> categories = new List<string>();
         using ( SqlConnection cn = new SqlConnection(connStr) )
         using ( SqlCommand cm = new SqlCommand(sql, cn) ) {
            cn.Open();
            cm.Parameters.Add("@PostID"
, SqlDbType.Int).Value = postId;
            using ( SqlDataReader dr = cm.ExecuteReader(CommandBehavior.CloseConnection) ) {
               while ( dr.Read() )
                  categories.Add(dr.GetString(0));
            }
         }
         return string.Join(";"
, categories.ToArray());
      }
   }
}

For the most part the site is up and running, but I'd like to migrate my rating system and my download manager to this new platform.  This hasn't been straightforward as .Text is SQL Server-based and DasBlog is file-based, so I'll have to come up with something...but I look forward to exploring the DasBlog object model to see if I can come up with something that works.

.NET | DasBlog | Journal
Sunday, May 25, 2008 7:43:52 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, May 09, 2008

I've just posted my code that I wrote both in preparation for my presentation and during my presentation on Windows Communication Foundation at the Utah Spring Code Camp 2008 a few weeks back.  You can download the code here or via the link on my blog page.  I had a great time at Code Camp and look forward to the next one; we've had a blast the last few years putting them on.

I feel this presentation was unique in that I took an approach on WCF that I hadn't seen done before, and being that I had never presented on WCF before I wanted to take a different slant on it.  Rather than start with how to create WCF services and consume them by generating a proxy class and using configuration files (which is a snap really), I started with how to consume WCF services via code and what's entailed.  Knowing it from the code side first helps me appreciate the flexibility, elegance, beauty, and simplicity of the configuration file route.  I think starting to understand WCF from the configuration file route is fine, but you miss out on some of the finer details of how it works.  Both techniques have their places, of course.

Friday, May 09, 2008 4:57:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, May 08, 2008

We were planning on conducting the Utah .NET User Group meeting tonight for our regular monthly meeting, but due to a variety of circumstances, we are needing to postpone our user group meeting to next Tuesday.  This is the email sent out to the group:

We sincerely apologize for the extreme lateness of this notice, but due to a variety of circumstances beyond our control we are needing to postpone our regularly scheduled Utah .NET User Group meeting to this coming Tuesday, May 13th.

We hope that this doesn't disrupt your plans and/or schedules too much and that we can convene at our regularly scheduled time.

Thank you for your understanding.

Please pass the word along to anyone who you may know that was planning on attending the meeting tonight so that we might all be informed of the change

Topic: Windows Server 2008
Date: Tuesday, May 13th, 2008
Time: 6:00 PM
Place: Digital Draw Network - Suite 300 (10897 South River Front Parkway, South Jordan, UT)
Thursday, May 08, 2008 7:42:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, April 22, 2008

Sticking with the tradition established over the past few years, we have another great opportunity to touch base with developers across the valley at our bi-annual Utah Code Camp.  The Spring Code Camp will be held this coming Saturday (April 26th from 9:00 AM to 5:00 PM) at Neumont University.

These events have been getting better and better each time.  There is a great group of speakers this time as well (not that we didn't have a good group in years past :), but it seems that the Code Camp is diversifying as well - which is great!  We have talks on Scrum, CakePHP, Developing on Windows Vista, TSQL, Windows Services, XNA development, Silverlight, Mac OSX programming, and much more!  Personally, I'll be sharing my thoughts on Windows Communication Foundation (WCF).

So, come on out and join the vibrant Salt Lake community of developers and have a great time!

What: Utah Spring Code Camp
When: April 26th, 2008, 9:00-5:00
Where: Neumont University (10701 South River Front Parkway, South Jordan, UT 84095)
Registration:
http://utcodecamp.com/event/

Tuesday, April 22, 2008 2:02:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, April 10, 2008

That's right, folks, we have the distinct opportunity to once again gather as a community of developers and share our insights and thoughts on the world of .NET.  The time has come once again to convene for our monthly .NET User Group Meeting!

This month's meeting is sure to be an exciting one as we're hosting a Heroes Community Launch! With this particular event we have volunteers from the group willing to share on three distinct topics: Visual Studio 2008, and SQL Server 2008. You won't want to miss it!

In the coming months we'll take the various topics and do some additional deep dives into each one.

Come on out, invite your friends and co-workers and be among the first to receive technical demos, readiness kits, and much more. We have several Not-For-Resale copies of each product to be given out as well to some lucky winners!

Time: 6:00 PM
Date: Thursday, April 10th, 2008
Place: Digital Draw Network (10897 South River Front Parkway, South Jordan, Suite 300)

 

Thursday, April 10, 2008 4:25:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, April 03, 2008

Today I set out to create a TextBox that would provide the user with a built-in prompt.  I didn't want a TextBox control that included a Label.  Instead, I wanted something akin to the password field in Windows Vista's logon screen; the field has the prompt built-into the TextBox in a faint, perhaps italic font when the control is empty.  Once the user performs an action that causes text to appear (i.e., typing, pasting, etc) the faint prompt disappears to accommodate the text.

Now there may be tons of implementations of a similar control out there; I didn't look.  Instead I wanted to 'reinvent the wheel' as it were and figure it out for myself.  Fortunately it only cost me between 5 to 10 minutes and I had something up and working.  The code I've included below is very raw and doesn't include many customizations (you're on your own there :-)

public sealed class PromptingTextBox : TextBox {
  public PromptingTextBox() {
     userPaint(true);
  }

  private Font _promptFont = new Font("Arial", 9.0F, FontStyle.Italic);
  private string _promptText = "Enter text here...";

  ///


  /// Sets or returns the text to display when the TextBox is empty.
  ///

  public string PromptText {
     get { return _promptText; }
     set { _promptText = value; }
  }

  protected override void Dispose(bool disposing) {
     if ( disposing )
        _promptFont.Dispose();
     base.Dispose(disposing);
  }

  protected override void OnTextChanged(EventArgs e) {
     base.OnTextChanged(e);
     userPaint(0 == TextLength);
  }

  private void userPaint(bool enabled) {
     if ( enabled != GetStyle(ControlStyles.UserPaint) ) {
        SetStyle(ControlStyles.UserPaint, enabled);
        Refresh();
     }

  }

  protected override void OnPaint(PaintEventArgs e) {
     Graphics g = e.Graphics;
     g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
     g.DrawString(_promptText, _promptFont, SystemBrushes.ControlLight, 0.0F, 0.0F);
  }
}

That's all there is to it - at least for a simple start. Enjoy!

Thursday, April 03, 2008 4:02:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, March 17, 2008

In the world of AJAX and JavaScript, JSON (JavaScript Object Notation) is ubiquitous; that is, it's everywhere.  JSON is, in a nutshell, a terse, convenient mechanism for representing objects and their property values in a manner that the browser (via JavaScript itself) can reconstruct as an object.  I'm not going to go into the details of JSON and its intracacies here as there are plenty of other discussions on the topic online elsewhere.

I just wanted to point out that there is some first-class support for JSON serialization in the .NET framework v3.5.  I bring this up because a while back I was about to create my own attribute-based JSON serializer but I figured there had to be something already in place for it.  I'm glad I looked.

Using the [DataContractAttribute] and [DataMemberAttribute] which are usually associated with Windows Communication Foundation (WCF) you can easily serialize your objects to/from JSON form.  The object that provides this support (not coincidentally named DataContractJsonSerializer) is found after referencing the System.ServiceModel.Web assembly in the System.Runtime.Serialization.Json namespace.  When (de)serializing your objects, you do so against a stream.

Say, for purposes of the discussion, that you wanted to create an HTTP Handler that returned product information to the caller.  You could very easily serialize your product set in the following manner:

void IHttpHandler.ProcessRequest(HttpContext context) {
  HttpResponse res = context.Response;
  res.ContentType = "application/json";
  DataContractJsonSerializer jsSer = new DataContractJsonSerializer(typeof ( Product[] ));
  jsSer.WriteObject(res.OutputStream, getProducts());
}

The JavaScript client could then parse the result and do with it as it sees fit.  Pretty handy!

Monday, March 17, 2008 6:04:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, February 22, 2008

I've been putting together a small WCF service that allows a client to upload a file to a server for processing.  While the details of that are for another discussion, I want to focus on what should have been a simple operation but turned out to take up most of my afternoon and some of my evening: the generation of a client proxy class and configuration file.

By way of background, this particular WCF service is hosted by a simple Windows Service, runs (by default) under the Network Service account, and exposes a TCP end point for communication.  I have a setup project that takes care of the installation and registration of the Windows Service and auto-runs it when installation is complete.  This has been running great.

However, anytime I attempted to generate a proxy class for my service via the following command line, I would get some nasy error indicating that it “Cannot obtain Metadata from net.tcp://localhost:9999/myservice”.

svcutil net.tcp://localhost:9999/myservice/mex

My configuration file was as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <system.serviceModel>
      <behaviors>
         <serviceBehaviors>
            <behavior name="mex">
               <serviceMetadata />
            </behavior>
         </serviceBehaviors>
      </behaviors>
      <services>
         <service name="MyService.ExampleSvc" behaviorConfiguration="mex">
            <host>
               <baseAddresses>
                  <add baseAddress="net.tcp://localhost:9999/myservice" />
               </baseAddresses>
            </host>
           
            <!-- default endpoint -->
            <endpoint
               binding="netTcpBinding"
               contract="MyService.IExampleSvc" />
           
            <!-- metadata exchange (MEX) endpoint -->
            <endpoint
               address="mex"
               binding="mexTcpBinding"
               name="MEX"
               contract="IMetadataExchange" />
         </service>
      </services>
   </system.serviceModel>
</configuration>

However, as I mentioned, everytime I attempted to retrieve the metadata via the /mex endpoint it failed.  Right off the bat I suspected that the error had to do with the types used in the [ServiceContract] class, but I brushed that idea aside thinking that for sure it was something in the configuration file.  So I kept on battling with it.

To that end, I created a very simple WCF service (in the same application) this time with an HTTP endpoint.  I had to change the Windows Service credentials to a user with elevated permissions to register the HTTP channel (Network Service won't cut it) because I didn't want to manually mess with my HTTP security settings.  Anyway, this was just a test.  To my chagrin, it worked first try; I was able to retrieve the metadata.

So I returned to the TCP service.

Fortunately (and I'm no WCF whiz...yet :), I found a helpful behavior that I could add to the .config file that saved the day for me.  By simply adding the <serviceDebug /> element with the includeExceptionDetailInFaults attribute to the mex behavior element I was able to get some diagnostics.

<serviceBehaviors>
    <behavior name="mex">
       <serviceDebug includeExceptionDetailInFaults="true" />
       <serviceMetadata />
    </behavior>
</serviceBehaviors>

Now, in the big, fat, honkin' exception stack that was output I saw immediately that one of the embedded types used in my [ServiceContract] implementation had a nested type that was not serializable.  Once I added [Serializable()] to the offending types (there were a few of them) and recompiled it worked like a champ!  So it turns out that my initial suspiscion was correct...I need to listen to my intuition more often!

Friday, February 22, 2008 1:19:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, February 20, 2008

I ran into a peculiar behavior that Visual Studio 2008 was exhibiting today that I suspect is a bug.  If it's not a bug, then there must be a setting somewhere of which I'm not aware.

I have a couple of projects that I've developed initially in VS2003 (targeting the .NET 1.1 framework), maintained in VS2005 (targeting the .NET 2.0 framework), and recently upgraded to VS2008 (targeting the .NET 3.x framework).  These projects, rather than embedding image resources natively (that is, embedding the .png, .gif, and .jpg files as independent resources) I have some .resources files that contain string and image data.  Up to this point they've worked great.

However, I discovered today that once upgraded to VS2008 the resources were not loading.  My code for loading the resources is pretty simple:

ResourceManager mgr = new ResourceManager("MyApp.Res", Assembly.GetExecutingAssembly());
ResourceSet res = mgr.GetResourceSet(CultureInfo.InvariantCulture, true, false);

Note that my embedded resource file is named 'Res.resources', so the namespace for the ResourceManager is my application namespace followed by 'Res' because it's in the root of the application.

Now, with VS2008, the mgr.GetResourceSet() call was returning a NULL value.  Somehow my resources were not resolving! After digging a bit, I found that the '.resources' extension was being truncated off of my resource file during the embedding process so though it was present in the .exe, it's name didn't have the '.resources' extension appended!

My solution was to rename the file 'Res.resources.resources' and it worked.  Interestingly, the compiler doesn't exhibit this same behavior for the .resources files that it creates.  Has anyone else run into this or is there a setting for it?  Fortunately, I only lost about 15 minutes from discovering the issue to having it fixed, but it really feels like a hack more than a fix.

Wednesday, February 20, 2008 3:50:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Tuesday, February 12, 2008

It is once again time for our very own Utah .NET User Group monthly meeting! In fact, in lieu of this Thursday being Valentine's Day, we've bumped the meeting date up to Tuesday (TODAY - 02/12/2008), same time, same place. We realize this is short notice (shame on us). We posted the meeting on the calendar several days ago, but failed to send out the email reminder until now for which we apologize.

This month, grab your canoes, oars, and life preservers and get ready to talk about STREAMS. Streams, though fundamental and routinely used, come in many different flavors. We'll be exploring streams from the ground up; from the ever-so-commonly utilized MemoryStream and FileStream types to the not-so-frequently used CryptoStream. Hey, we may even venture into the realm of the NetworkStream, the NamedPipeServerStream and NamedPipeClientStream. Time permitting, we will delve into the very cool compression streams (such as DEFLATE and GZIP).

Time: 6:00 PM
Date: Tuesday, February 12, 2008
Place: Digital Draw Network (10897 South River Front Parkway, South Jordan) Suite 300

This month's meeting is generously sponsored by TEKsystems.

Come out and enjoy the food, the friendship, and the fun!

Tuesday, February 12, 2008 3:00:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, February 07, 2008

If you ever incorporate XmlSerialization into your applications, you're probably aware of the SGEN.exe utility.  If not, you should become familiar with it.  Without going into details about it, suffice it to say that by using sgen.exe you can greatly improve the performance of your applications that rely on serialization (e.g., Web Service clients, et al).  Without it, your applications suffer in part because the .NET Framework generates a dynamic assemblies at runtime for the Xml-Serializable types in your application when first serialized.  Note that this hit is taken 'up front' and doesn't affect the runtime performance as much as the initialization performance of your application.

Well, since VS 2005 MS has included an option within the GUI on the Build tab called “Generate serialization assembly” with three options: Auto, On, and Off.  Unfortunately, though the intentions were there, this option is largely unusable for several reasons.  The same problems have carried over to VS 2008 with no changes.  On the bright side, however, there are other options at our disposal.

  1. Use the sgen.exe from the command line.  Fun an exciting indeed!
  2. Set up a Post-build event command line referencing sgen.exe.  Even more exciting!
  3. The best option is to use the <SGen> MSBuild task which automates the process for us.  Awesome!

In fact, the main reason to use the <SGen> MSBuild task is to turn off the /proxytypes switch that the GUI throws in for us when the “Generate serialization assembly” is set to “On” which only has any meaning for Web Services.  Edit your .csproj file, dropping the following XML:

<Target Name="GenSerializationAssembly"
        DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource"
        Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"
        Outputs="$(OutputPath)$(_SGenDllName)">
  <SGen BuildAssemblyName="$(TargetFileName)"
        BuildAssemblyPath="$(OutputPath)"
        References="@(ReferencePath)"
        ShouldGenerateSerializer="true"
        UseProxyTypes="false"
        KeyContainer="$(KeyContainerName)"
        KeyFile="$(KeyOriginatorFile)"
        DelaySign="$(DelaySign)"
        ToolPath="$(SGenToolPath)">
    <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
  </SGen>
</Target>
<Target Name="AfterBuild" DependsOnTargets="GenSerializationAssembly" />

Now, when you build your project you'll get another DLL  with the name “assemblyname.XmlSerializers.dll”.  Simply deploy this DLL with your application to take advantage of the pre-generated serialization library. :-)

Note:  Thanks to Kiwidude for the pointer on the MSBuild task.

Thursday, February 07, 2008 3:47:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, January 10, 2008

Tonight we, at the Utah .NET User Group, will be putting on a special event: Visual Studio 2008 InstallFest.

We're very excited to be able to put on such an event; a big thanks goes out to Craig Berntson for working with the folks at Microsoft to acquire the DVDs.  In fact, we were able to acquire 35 such copies to give out to attendees.  So, bring your laptop and a power strip and come join in the festivities, it'll be a great time!

Place: Digital Draw Network - Suite 300 (10897 South River Front Parkway, South Jordan)
Time:  6:00 PM
Date:  Thursday, January 10th, 2008 (tonight)

Thursday, January 10, 2008 2:57:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, January 01, 2008

Alright, there has been a particular issue with the Regular Expression Builder that I've known about since its creation and it's long bugged me.  I haven't, until today, taken the time to address the issue.  Essentially, in order to maintain the root namespace within the regex assembly created, I had created a custom Attribute (AssemblyBaseNamespaceAttribute) that was baked into the target DLL.  This hasn't truly proven to be an issue (at least when running on Windows), though the compiler would warn you when referencing it because the RegexAssemblyBuilder.exe application wasn't to be found.  It was more of an annoyance.  However, as I learned today, it does seem to be an issue when running on Linux/Mono.

Now I had the incentive I needed to fix the problem.

So, today I present a minor (but important) update to the Regular Expression Builder.  Version 2.0.0.1 provides two updates:

  • The ability to save a 'Release' version of the assembly.  This version is saved to a \Release folder beneath the project path and is created without the AssemblyBaseNamespaceAttribute so the dependency is no longer there.  Applications should reference this DLL and this is the one you should deploy.  However, when making changes, use the original as it still has the attribute.
  • You can now TAB through the pattern field in the regex editor form.  If you want, however, you can still insert TABs (ASCII 9) within your regular expression in the Zoom window via CTRL+TAB.  This is particularly helpful if you want your regular expression to be broken up onto multiple lines for readability and/or to comment it and use the IgnorePatternWhitespace option.

Download it here

Tuesday, January 01, 2008 3:15:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
I received an email today indicating that I have been named a Microsoft MVP in the area of Visual Developer - ASP.NET.  This is exciting news for me and marks the third straight year of such an honor.  Thanks to everyone's support and the awesome community we've been nurturing here in Utah.  Let's make 2008 even better!
Tuesday, January 01, 2008 2:00:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, December 31, 2007

Due to popular demand I (finally) got around to updating my Regular Expression Assembly Builder Tool.  If you're not familiar with it, you can read more about it and its history here and here, but suffice it to say, it's a handy little utility that compiles regular expressions into their own assembly (dll).  There is a lot of power behind this, not the least of which is that you can effectively pre-compile your regular expressions for your application and gain some performance improvements.  You can read more about it in my aforementioned posts.

The updates to the utility include the following:

  • NEW: Upgraded to version 2.0.0.0.
  • NEW: Upgraded to VS.NET 2008, .NET 3.5.
  • UPD: Added resizability to regex dialog, supporting a larger text field for regex entry, line breaks, tabs (with a tabstop of 3), etc.
  • NEW: Added scrollability to editor panels rather than not at all.
  • NEW: Added support for a command-line-specified DLL to load.
  • NEW: Added support for hierarchical (namespace) representation of regular expressions.
  • UPD: Context menus add regular expressions to the hierarchy based on the user's selection.
  • FIX: Fixed issue involving the various RegexOptions checkboxes where an invalid configuration of options could be selected (particularly involving ECMAScript, Multiline, and Singleline.

So, apart from some nicer UI interactions than in previous versions, this version most notably includes

  1. Organization of your regular expressions my namespace.
  2. Alphabetical listing of regular expressions.
  3. The ability to provide an argument on the command-line designating the DLL to load.

Let me know what you like and/or don't like about it and I'll see what I can do to improve it for you!

Download it here

Enjoy!

Monday, December 31, 2007 5:03:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, December 24, 2007

I was working with a customer today that, after upgrading to the latest version of our software, was running into an error that I had never seen before.  The product, as you may be aware, is a web-based Product Configurator for Microsoft CRM.  Historically, our upgrades have been very clean and easy, but today the customer received the following error message when attempting to launch the application:

The file or directory is corrupted and unreadable.

The error was occurring at launch (during the initialization of ASP.NET) and was caused by an IOException.  Inspecting the call stack it was apparent that it was happening during the System.Web.HttpRuntime.FirstRequestInit() method while loading the assemblies from the \bin folder (System.Web.HttpRuntime.PreloadAssembliesFromBin() method).

My first thought was that something security-wise was amiss.  So, via Windows Explorer, we inspected the security properties on the folder for the website.  Sure enough, some credentials were messed up so we fixed those; but the application continued to fail with the same error.

Something in the back of my mind told me it was still security related.  After considering it a bit the answer was obvious.  We proceeded to open \Windows\Microsoft.NET\Framework\vX.X.X\Temporary ASP.NET Files to the folder for the application.  Upon drilling down the folder tree Windows reported the exact same error.

To fix it, all we had to do was fix the security settings on the folder, perform a CHKDSK, and delete the application's folder in the \Temporary ASP.NET Files folder.  After that, everything ran beautifully.

Monday, December 24, 2007 9:07:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, December 19, 2007

As you're probably already aware, Microsoft recently RTM'd Visual Studio 2008.  Well, I had a moment to come up for air the other day and decided it was time to upgrade my machine to this newest encarnation.  I must say that the installation was probably one of the easiest I've ever done.  The product installed very smoothly without even a hiccup.  Also, much to my delight, ReSharper 3.0 continues to work flawlessly with VS2008! :-)

Having played around with the Beta and CTP versions of VS2008 I was very excited to move onto the released version.  It's flat out awesome!

Highly recommended!

Wednesday, December 19, 2007 2:47:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, November 26, 2007

I have a website that I created that uses an ASP.NET Login control to assist in user authentication on the site.  I've used the Login control on other sites and it's provides a very convenient mechanism for authenticating a user.  On this site particular site I have the Login control contained within a wrapper control (which I called LoginBox).  Due to the nature of this site, the LoginBox does not exist on the page in the ASP.NET HTML markup but rather is added to the page dynamically.  As such I don't have the convenience of editing the HTML markup of the asp:Login control directly but must interact with it programmatically.

The Login control provides several properties through which I can achieve a nice look and feel.  It contains child controls for the title, labels, textboxes, required field validators, checkboxes, and buttons.  Each of these can be customized via CSS styles or by directly assigning values to the controls' properties.

In this particular site I needed to extend the appearance of the control, adding some textboxes to the control.  At first this wasn't very intuitive, but I'll show you that it is, in fact, quite easy to achieve.  You can easily add fields to the control (e.g., a 'verify password' field, an 'email' field, a CAPTCHA control, etc) by customizing the LayoutTemplate of the Login control.

Were I to have had the control in the HTML markup of my page this would be a piece of cake.  Note, via the designer, you can simply click the Login control's smart tag and click “Convert to Template“ or create it manually:

<asp:Login runat="server" id="login">
  <LayoutTemplate>
    <!-- HTML template here -->
  </LayoutTemplate>
</asp:Login>

However, as the control is being generated entirely in code, the template must be created programmatically.  As a bare minimum, you need two controls in your template that implement the ITextControl interface for the control to render; otherwise, you'll get an exception (TextBoxes will suffice or any control you create that implements that interface).  These controls are UserName and Password.  I recommend adding the login button as well (Button, LinkButton, or ImageButton) with a CommandName set to “Login“.  Using a template, you can fully customize the look and feel of the output.  Note, that when you go the route of creating a custom template, you are solely responsible for the output.  In other words, you're completely replacing the output that the Login control generates; if you want field validators, you need to add them.

// somewhere in the page (e.g., Page.Init)
Login login = new Login();
login.LayoutTemplate = new LoginTemplate();
Controls.Add(login);

internal class LoginTemplate : ITemplate {
  public void InstantiateIn(Control container) {
    TextBox txtUserName = new TextBox();
    txtUserName.ID = "UserName";
    container.Controls.Add(txtUserName);

    TextBox txtPassword = new TextBox();
    txtPassword.ID = "Password";
    container.Controls.Add(txtPassword);

    ImageButton btnLogin = new ImageButton();
    btnLogin.ID = "LoginButton";
    btnLogin.ImageUrl = "~/images/LoginButton.gif";
    btnLogin.CommandName = "Login";
    container.Controls.Add(btnLogin);
  }
}

With this, I have an extremely boring-looking Login control, but you get the idea.

When the page is posted back, you can easily retrieve the contents of any control you added to the template by calling the .FindControl() method, referencing it by name:

string emailAddress = ( ( TextBox )login.FindControl("txtEmail") ).Text;

Monday, November 26, 2007 3:36:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Saturday, November 03, 2007

I had the opportunity to present at the Utah Code Camp today on the topic of Regular Expressions.  This topic is near and dear to my heart, but (being my worst critic) I feel that I couldn't get my act together.  In part I feel that the presentation went very poorly; it was definitely among my worst.  I felt my explanations lacking, incomplete, or erroneous, my examples unsuccessful or poor, and my 'togetherness' absent.  It was a very far cry from a similar presentation I had given back in Sept 2006 to the Utah .NET User Group which was among my best I feel.  That made it all the more disappointing for me.

I guess on the positive side I've updated my RegexTester application and it's available for download here.  Thanks to all that came and participated.  We had a great turnout to the event so that was very positive.  I just wish my presentation hadn't ruined my day...it's been lousy ever since.  I guess you can't win them all.

Saturday, November 03, 2007 1:54:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Wednesday, October 31, 2007

Please join us at the Utah Fall Code Camp this coming Saturday, November 3rd, 2007.  This event is going to be excellent!  Come check out the site, register, browse the schedule and more.  Here's the official announcement:

Utah Fall Code Camp

Nov 3rd 2007

Neumont University

Salt Lake City, UT

www.utcodecamp.com

 

The local Users Group Community is conducting a “Code Camp” for local software programmers at Neumont University. This Camp is Free to all.

 

We have a diverse topic range for this code camp. You can see the full list of sessions and speakers at www.utcodecamp.com We will be giving away at least one IPOD, software, Shirts, Books and Lunch and Breakfast will be provided. 

 

You will need to register on the site to be eligible for some giveaways so you need to go register now at www.utcodecamp.com if you haven’t already. If you registered on www.msutahevents.com you will need to re-register at the code camp site.

Wednesday, October 31, 2007 7:08:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, October 19, 2007

If you're developing ASP.NET applications on Windows Vista that are utilizing the Visual Studio-provided web server (a.k.a., Cassini) then you may have run into an issue where, when attempting to debug your website, Visual Studio immediately drops out of debugging once your application starts.  That is, you press F5, the browser pops up and displays the website and Visual Studio stops debugging and drops back into its standard development mode and the browser continues on running.  Your breakpoints are no longer active and you essentially can't utilize your development environment to troubleshoot issues with the site.

This has been happening to me of late.  I don't usually use the debugger in that sense to 'walk through' my code for a variety of reasons, but tonight I needed to.  Sure, I could attach to the process (WebDev.WebServer.exe), but that's a pain and I get tired of constantly attaching and detaching.

The culprit in my case was that http://localhost was in my Local Intranet zone in IE and, using IE 7 and Vista, I'm not authorized to debug intranet sites.  The solution is an easy one: move http://localhost to the Trusted Sites list in Internet Explorer.  Now the debugger attaches and remains attached, greatly simplifying the debugging process.

Friday, October 19, 2007 4:45:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [12]  |  Trackback

Thanks to everyone that came out to the Utah .NET User Group lunch today at Olive Garden.  We had a great turnout and a great time.  I think in the future we'll be holding the lunch around I-15 and I-215 as it's a major intersection and a great place to gather.  Any time we've attempted to hold the lunch elsewhere has resulted in just a small handful of people (3-4) whereas all of our lunches in the Fashion Place area have resulted in 10+ (today we had 14 if I recall).

Anyway, it was great to see everyone and we'll be doing it again in the future - maybe at the new Cheesecake Factory that opens next month...we'll keep everyone posted.

Friday, October 19, 2007 7:17:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, October 16, 2007

Later tonight (as it is now officially tomorrow being 3:26 AM) I have the opportunity to present on the topic of Design Patterns at the Utah County .NET User Group in Provo, UT.  This will largely be an adaptation of my Sept 2007 talk to the Utah .NET User Group, but I might throw in some new stuff.  I'm looking forward to it as I always am.  Come join us and see you there!

Time: 6:00 PM
Date: Oct 17th, 2007
Place: NuSkin building (1175 S 350 E, Provo)

Tuesday, October 16, 2007 8:26:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, September 28, 2007

Boy, looking back at the past several months I really haven't done much in the way of keeping my blog up to date.  Perhaps I just needed a reprieve or perhaps it was too much Lord of the Rings Online.  It was probably a combination of the two.  Anyway, I have an update that I want to provide as it's plagued me in the past and been a point of repeated and renewed frustration.

I frequently create custom websites in development and uniquely identify them via a custom host header (entered into IIS either during site creation or after the fact).  Then I'd edit my hosts file to point that host header to my machine.  NOTE: If you use DNS and create a CNAME to the machine none of these problems exist.

When I attempt to open the website via IE 7.0 I am greeted with an authentication dialog box.  None of the credentials I enter will take, and after three tries I get a standard 401.1 (Unauthorized: Logon Failed) HTTP error page.  My first line of defense is to add the host header to IE's list of local intranet sites, but that doesn't have any effect - it still prompts me to log in.  None of my standard debugging techniques seem to yield any meaningful results.

As it turns out, Windows XP SP2 and Windows Server 2003 SP1 both include a loopback check security feature that is designed to help prevent reflection attacks on your computer.  This update causes authentication to fail if the FQDN or custom host header does not match the local computer name.

An article on Microsoft Support identifies this issue and how to work around it.  For my test cases, I tried the first method (”Disable the loopback check”) and that fixed it right off the bat.  Also note, that even though the instructions tell you to, I didn't need to reboot.  A simple IISRESET fixed the issue.

Essentially all I needed to do was edit the registry by creating the following DWORD value and assigning it a value of 1:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\DisableLoopbackCheck = 1

I suppose that if I needed tighter control I might have opted for method 2 by specifying host names, but such wasn't the case.

I'm glad that got to the bottom of it.

Friday, September 28, 2007 9:02:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, September 13, 2007
Tonight I'll be out at the Utah .NET User Group giving a presentation on Design Patterns.  I've had the opportunity to present on Design Patterns in years past and thoroughly enjoyed it and I'm really looking forward to tonight.  Come on out and show your support for the community and group and enjoy the free pizza and drinks...oh, and I hope you like the presentation as well.  The event starts at 6:00 PM.  Then afterwards join us for dinner/drinks/dessert at the nearby Denny's off of I-15 and 106 S.
Thursday, September 13, 2007 2:32:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, August 09, 2007

Tonight we're switching up the .NET User Group meeting format somewhat, hopefully to the approval of all.  We're conducting what we're calling a “Presenter's Challenge”.  We're giving the opportunity to 4-5 presenters to take approximately 20-30 minutes each and present on various topics (we have LINQ, Mono, WPF, etcetra already scheduled).  Following the meeting we're giving everyone the chance to vote online for the best presentations.  The winners will be announced and awarded in September.

This should be a blast of an event and I hope it goes well - we'd like to have more variety in the meetings like this in the future.

Date: Thursday, August 9th, 2007
Time: 6:00 PM
Place: Digital Draw Network - Suite 300 (10897 South River Front Parkway, South Jordan, UT)

Don't miss it!

Thursday, August 09, 2007 2:42:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, July 24, 2007

I've been developing an ASP.NET application for quite some time now without a single problem and all of the sudden when I attempt to open the project in VS 2003 (it's an ASP.NET 1.1 app) I am greeted with the following dialog box:

I've been coping with the issue for a few weeks as I'm able to simply open the application directly in IE and attach the debugger as necessary.  Something must have changed of which I'm unaware, but this had me confused.  Needless to say, the very first thing I tried upon seeing this message was to register ASP.NET 1.1:

C:\>cd\windows\Microsoft.NET\Framework\v1.1.4322
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322>aspnet_regiis -i

Investigating it beforehand with the -lv and -lk revealed that it was indeed already registered with ASP.NET 1.1, but I figured redoing it wouldn't hurt.

When I tried opening the project in Visual Studio, still the problem persisted.

The only solution that I could come up with was to create the file that VS looks for when opening a web project (get_aspx_ver.aspx) explictly in my web application's root, even though it's not expecting to find the file in the first place.

Has anyone else seen that and/or have a better solution?

Tuesday, July 24, 2007 7:09:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback

I was greeted rather abruptly today by an error message that resembled the following:

The module 'XXX' is already in the application and cannot be added again.

This surprised me as I hadn't made any changes to my web.config file's <httpModules /> section.  In fact the module in question is only in my web.config file one time.

I have a web configuration file editor that I had written that updates config files and it automatically adds the <httpModules /> section if necessary, wiring up the httpModule in question. As it turns out, I had inadvertently updated the web.config in my root website which was causing the module to be loaded globally.  Removing the erroneous information from my root web.config file fixed the problem straightaway.

Tuesday, July 24, 2007 6:43:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, July 03, 2007

I've been working with the Visual Studio deployment projects for years now, but always in a very simplistic manner.  That is, I've used them to deploy my applications, install my services, etc, but I've never needed to get into custom development.

Sure, I've long leveraged .NET's ability to have Installer classes to execute custom code when the application in installed to perform tasks such as Event Log creation and/or registration.  This is quite easy, in fact.

Without diving too deeply into it right now, a simple form comes down to the following tasks:

  1. Create a public class that derives from System.Configuration.Install.Installer.
  2. Add the [RunInstaller(true)] attribute to the class.
  3. Add the appropriate code to your Install, Rollback, and Commit method overrides.
  4. Add your application's output to your setup project's Install and Uninstall Custom Actions.

That's it - the installer takes care of everything.

However, my needs with a recent pet project involved passing data to my installer class from the setup project.  This is easily accomplished by following these steps:

  1. Add a dialog to your installer (e.g. there's one called "Textboxes (A)" that will work fine).
  2. Each of these basic dialogs (A, B, and C) contains 4 edit fields.  In order to pass the values from the edit fields to your installer you must name them.  You can use the defaults, but EDITA1, EDITA2, etc don't tend to make much sense.  You name them by changing the EditxProperty property on the dialog.  This is now the variable name you can reference in your code.
  3. Pass the value to your installer by selecting the 'Primary output from XXX (Active)' node in the appropriate Custom Action and setting the CustomActionData property.  This string is defined as a space delimited parameter list.

    For instance, if you named a property 'ROOTFOLDER', you could define the parameter as follows:
    /rootFolder=[ROOTFOLDER]

    If you want to pass more than one parameter, you space delimit them:
    /rootFolder=[ROOTFOLDER] /userName=[USERNAME]

    Of course, if there's a space (or the potential for a space) in the value, you'll need to enclose the property in quotes:
    /rootFolder="[ROOTFOLDER]"
  4. You can then reference the parameters by name in your installer code:
    public override void Install(IDictionary stateSaver) {
       base.Install(stateSaver);
       try {
          string rootFolder = this.Context.Parameters["rootFolder"];
          // do something, such as create the folder, write a configuration file, etc
       }
       catch ( Exception ) {
          // do some logging here
       }
    }

All in all it's pretty straightforward and easy to do.

Happy coding!

Tuesday, July 03, 2007 4:28:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Saturday, June 30, 2007

Well, I must say that I'm happy about our very first .NET User Group Lunch which took place a couple of days ago (06/28/2007).  We had about 15 people show up to Ruby Tuesday in Jordan Landing and we had a great time.  I really enjoy the commaradie of the group.  We're going to shoot for another lunch in a few weeks and we're looking for suggestions for locations (both to maximize accessibility and therefore attendance to these events).

If you have thoughts, please follow up on the User Group forums.

Saturday, June 30, 2007 4:19:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, June 25, 2007

This week we're starting what we hope will be a long and fun tradition for the Utah .NET User Group: A .NET User Group Luncheon.

Here are the details as outlined on the website:

By popular demand, we're starting up what we hope will be the first of many .NET User Group lunches.  This is a time where the members of the group can gather, collaborate collectively, frolic and fraternize, and consume comestibles.  We've had a great time gathering after the monthly meetings each month and would like to continue that tradition during the daylight hours when (hopefully) more people can make it..

As a tentative schedule, we're thinking of having a lunch gathering every two weeks at different points throughout the valley.  That way, if you can't make one lunch, hopefully you can make the next one.

This first one will be in Jordan Landing at Ruby Tuesday.  To accomodate everyone's disparate work hours and various commutes, we're allotting 1.5 hrs for lunch from 11:30 AM - 1:00 PM.  You don't need to come for the entire time, show up whenever you like.  Again, things may and probably will change as we get this underway (such as times, dates, and locations), but we just want to get the ball rolling.

We love this group and hope to have a good turnout to this, the inaugural lunch.

If you plan on attending, please check the appropriate option below so we can arrive in time enough to warn them :)

Come one and all, good times are to be had!

Date: Thursday, June 28th, 2007
Time: 11:30 AM - 1:00 PM
Place: Ruby Tuesday in Jordan Landing (
http://www.rubytuesday.com/locator.asp?template=map_search&pWidth=339&pHeight=299&transaction=locMap&recordId=7132)

If you'd like to attend, please visit the post and sign up on the poll, indicating you'll be there.

It should be a lot of fun!

Monday, June 25, 2007 2:47:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, June 20, 2007

Tonight I had the distinct pleasure of giving a presentation at the Utah County .NET User Group on the topic of Silverlight - specifically Silverlight 1.1 Alpha.  It was a blast!  Honestly, I was a little nervous going into it, having never really presented on the topic and being relatively new to it myself, but I had a great time.  We talked about Silverlight (previously known as WPF/E) and used Visual Studio “Orcas” with Expression Blend and did some really cool things.  We talked about XAML, created custom controls, retrieved a file from the server and read its contents, automated the HTML DOM from the Silverlight .NET code, did the reverse (called .NET methods from JavaScript), and much more!

If you missed it, never you fear because I'll be addressing the topic at the September 2007 Utah .NET User Group meeting.

Thanks all for coming and to Scott Golightly for giving me this opportunity to present.

Wednesday, June 20, 2007 3:39:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Wednesday, June 13, 2007

Well, the time has once again arrived, my friends, for our monthly Utah .NET User Group meeting.

We will be pleased to have Craig Campbell stepping up to the plate to discuss Team Foundation Server (TFS).  We know that this has been a topic long desired and we're excited to be able to bring it to you.

Topics of discussion will include (but are not limited to):

  • What is TFS (in relation to SCM)?
  • Source Control
    • Comparisons
    • Features
    • Branching/Merging/Shelving
    • Visual Studio Integration/Other integration/Command line
  • Build
    • Team Build
    • Continuous Integration
    • Orcas changes (v2 of TFS)
  • Project Features
    • Work items
    • Methodologies offered
    • Source control integration
  • Customizations/Extensibility

So bring your friends, family, fellows, and fraternities...you won't want to miss this event.  And though there is no need to mention this, I will:  don't forget it's FREE and open to all.

Date: Thursday, June 14th, 2007
Time: 6:00 PM
Place: Digital Draw Network - Suite 300 (10897 South River Front Parkway, South Jordan)

We'll see you there!

Wednesday, June 13, 2007 4:23:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, June 12, 2007

Recently I had the need to create a component in .NET that would be callable as a COM component (not unlike many other such objects in the past).  This one, however, was peculiar in that I also needed to be able to call it from a web page running in IE (due to constraints already on the application, cross-browser support is not a concern in this case).  I didn't want the users to be burdened with dialog boxes or even require that they change their security settings.  I wanted to create the object as 'safe for scripting'.

There are two approaches that I know of that address the issue: implementing IObjectSafety (a COM interface) or directly managing the object via the Component Category Manager (ICatRegister COM interface) and registering the CAT_SafeForScripting GUID.

The first presumes that I have access to and can compile the code, because the IObjectSafety interface must be implemented directly on the object in question.  The second solution can be done after the fact (e.g., within an installer).

Due to the nature of the project, I opted for the first option because, frankly, it was the easier of the two to implement given my time constraints.

The IObjectSafety interface is not native to the .NET world.  As such, in order to implement a COM interface on a .NET object the interface must be defined within the managed code and then decorated with the appropriate attributes.

Here's my implementation of the IObjectSafety interface in managed code:

[ComImport()]
[Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IObjectSafety {
   [PreserveSig()]
   int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions);
                    
   [PreserveSig()]
   int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions);
}

The ComImportAttribute identifies that the interface was previously defined in COM with the IID specified by the GuidAttribute.  The PreserveSigAttribute is important on each of the methods to tell the .NET runtime to not suppress the HRESULT when the method is called.

Once defined, it's then a simple matter to implement the interface on my class, and indicate that it's safe for scripting:

[ClassInterface(ClassInterfaceType.AutoDispatch)]
[Guid("43B6ADAA-6DE7-43c2-9206-3389C94B9531")]
[ProgId("DemoLib.DemoObject")]
public sealed class DemoObject : IObjectSafety {
   private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
   private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
   private const int S_OK = 0;

   public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions) {
      pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
      pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
      return S_OK;
   }

   public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions) {
      return S_OK;
   }
}

Once registered (via Regasm.exe), you can invoke methods on an instance of the object via your script:

var obj = new ActiveXObject("DemoLib.DemoObject");
obj.CallSomeMethod();

Tuesday, June 12, 2007 11:06:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, June 07, 2007

I hate little hacks like this, but here it is nonetheless.  If someone has a better solution I would love to hear it.

I've been tinkering with the WinForms TreeView control a bit and ran into a documented (but undesirable) behavior.  I'm wanting to display my hierarchical node structure in the tree, but I want to 'highlight' some of them for one reason or another.  My preferred highlight in this case is to use a bolding effect on the font and to change the text color.

The undesired effect, however, is when you apply a font to the node that is larger than the base font of the node the text will clip.  Well, bolded text is larger - it's wider than the text and as a result the right-most character(s) get clipped and it looks really tacky.

My hack isn't without it's limitations and considerations, but all I need to do to 'workaround' this issue is to apply some non-breaking spaces in the text to display such that they get clipped and not the actual text.  This is definitely error-prone as well because just one nbsp doesn't necessarily address the issue (but does in my situation for most texts).

string text = “This is ridiculous” + (char)160;
TreeNode node = new TreeNode(text);

I can see this being quite an issue with non-latin-based fonts, though I've nothing to substantiate that claim.

One side-effect of doing this is that the 'clip box' around the text grows to accommodate the non-breaking spaces.  You can surely set your TreeView to use .FullRowSelect, and to turn off .ShowLines, but that may not be desirable in some situations.

Does anyone have/know of a better solution with the plain, vanilla TreeView?  I've tried other tactics but none led to any better results.  Maybe I'm missing something blatently obvious.

Thursday, June 07, 2007 7:29:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, May 30, 2007

Just the other day I posted some code wherein an MSMQ queue is created and the Network Service account was granted full control to the queue.  Though I didn't mention it, this was necessary because the Windows Service runs under that account and would need access to the queue.

As it turns out the code sample has issues when running on Windows XP.  Windows XP doesn't allow the NT AUTHORITY\NETWORK SERVICE account to be associated with the permissions on a message queue (I've not looked into why yet).  When the code runs no error is thrown and it happily attempts to grant the permissions.  This results in a queue whose permissions are corrupted (not even an administrator can access it) and all users are denied access.  The only way I've found to remove such a queue is to uninstall MSMQ and reinstall it.  Does anyone know of another solution to that?

Therefore, in my code a put a rudimentary workaround to the problem.  I perform a simple check to see if the OS is Windows XP and if so, not set permissions.  This works in this case (though I hate to rely on these behaviours) because the default permissions on a queue in XP is Everyone:Full Control, so the service can see the queue just fine.

if ( !isWindowsXP() ) {
   const string accountName = @"NT AUTHORITY\NETWORK SERVICE";
   Trustee trustee = new Trustee(accountName, null, TrusteeType.User);
   MessageQueueAccessRights rights = MessageQueueAccessRights.FullControl;
   MessageQueueAccessControlEntry ace = new MessageQueueAccessControlEntry(trustee, rights);
   queue.SetPermissions(ace);
}

...

private static bool isWindowsXP() {
   OperatingSystem os = Environment.OSVersion;
   return ( PlatformID.Win32NT == os.Platform && 5 == os.Version.Major && 0 != os.Version.Minor );
}

Again, very simple and potentially error prone (what about Windows 2000 Pro?), but in my case it suffices. 

Wednesday, May 30, 2007 6:33:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, May 27, 2007

In terms of keeping this blog up-to-date on my goings on, the month of May has been a dismal one.  There has been a lot going on, but I've simply not made the time to log my activities and/or code snippets and it's eating at me.

Recently, I've been writing some code that needed to run in response to certain activities that would be triggered by another application.  These applications would not be connected, per se, in the sense that one would 'hook' into the other and respond to events.  Rather, one application would set a flag, send a message, etc such that the second application would recognize the notification and run some activity in response.

The logical choice for such an architecture is a messaging-based system.  In this case, I decided upon MSMQ/Queued Components and boy am I happy about that!  I hadn't seriously worked in MSMQ for years (though I used to do quite a bit of it) and it was fun to romp around my old playing grounds, but now with a .NET engine helping out.  Tons of fun!

This particular application is structured such that I have a web-based application (ASP.NET in this case) that will send certain messages to a message queue when the user completes some task.  Then I have a Windows Service running that will read messages from the queue and run a configurable response.  As part of the setup, I have written a few installer classes that will 1) create the message queue and 2) install the Windows Service and 3) run it automatically after install.

The task of creating a message queue installer is quite simple and straightforward.  I wanted my installer to detect the existance of the queue and create it if it wasn't found.  Upon rollback or uninstall, the queue would be deleted.  Here's an example from the Install method of my Installer class:

public override void Install(IDictionary stateSaver) {
   base.Install(stateSaver);

   try {
      const QueueName = @".\Private$\TestQueue";
      if ( !MessageQueue.Exists(QueueName) ) {
         MessageQueue queue = MessageQueue.Create(QueueName, true);
         Trustee trustee = new Trustee(@"NT AUTHORITY\NETWORK SERVICE");
         MessageQueueAccessRights rights = MessageQueueAccessRights.FullControl;
         MessageQueueAccessControlEntry ace = new MessageQueueAccessControlEntry(trustee, rights);
         queue.SetPermissions(ace);
      }
   }
   catch ( Exception er ) {
      // log the error to the event log
      throw;
   }
}

At this point, I'm ready to start interacting with the message queue by sending and receiving messages.

To simplify this process and to make it more structured, I decided to create a simple class called QueueItem.  This class provides the structure and data needed by the recipient application so that it can effectively run in response to the website's activity.  This class is also decorated with the appropriate attributes that enable it to be serialized to XML.  To further simplify the process of interacting with the message queue, the QueueItem class contains a method that allows the caller to send the message to the queue; all of the logic necessary is enclosed therein:

[XmlRoot("queueItem")]
public sealed class QueueItem {
   public QueueItem() { /* parameterless ctor for serialization support */ }
   public QueueItem(string id, string action) {
      _id = id;
      _action = action;
   }

   private string _id, _action

   [XmlElement("id")]
   public string Id {
      get { return _id; }
      set { _id = value; }
   }

   [XmlElement("action")]
   public string Action {
      get { return _action; }
      set { _action = value; }
   }

   public void SendToQueue(string label) {
      QueueItem.SendToQueue(this, label);
   }

   public static void SendToQueue(QueueItem item, string label) {
      MessageQueue queue = new MessageQueue(@".\Private$\TestQueue", QueueAccessMode.Send);
      queue.Formatter = new XmlMessageFormatter(new Type[] {typeof ( QueueItem )});

      using ( MessageQueueTransaction trans = new MessageQueueTransaction() ) {
         trans.Begin();
         queue.Send(item, label, trans);
         trans.Commit();
      }
   }
}

The Windows Service, now, is responsible for watching the queue and running the appropriate response to messages that find their way into the queue.  To accomplish this, I decided to have the service spin off a secondary thread at start up.  It's this thread that is responsible for processing messages off of the queue.

protected override void OnStart(string[] args) {
   try {
      Thread th = new Thread(new ThreadStart(processMessages));
      th.IsBackground = true;
      th.Name = "QueueListener";
      th.Start();
   }
   catch ( Exception er ) {
      EventLog.WriteEntry(Service.ActualName,
                          string.Format("Unable to start service\n\n{0}", er),
                          EventLogEntryType.Error);
      this.ExitCode = 1;
      this.Stop();
      return;
   }
}

private void processMessages() {
   try {
      using ( MessageQueue queue = new MessageQueue(@".\Private$\TestQueue", QueueAccessMode.Receive) ) {
         queue.Formatter = new XmlMessageFormatter(new Type[] {typeof ( QueueItem )});

         while ( true ) {
            using ( MessageQueueTransaction trans = new MessageQueueTransaction() ) {
               trans.Begin();
               Message msg = queue.Receive();
               trans.Commit();
               QueueItem item = msg.Body as QueueItem;
               if ( null != item ) {
                  EventLog.WriteEntry(Service.ActualName,
                                      string.Format("Message received: {0}, {1}", item.Id, item.Action),
                                      EventLogEntryType.Information);
               }
               else {
                  EventLog.WriteEntry(Service.ActualName,
                                      string.Format("Invalid message datatype received: {0}.  Expected: QueueItem.", msg.Body.GetType().FullName),
                                      EventLogEntryType.Warning);
               }
            }
         }
      }
   }
   catch ( Exception er ) {
      EventLog.WriteEntry(Service.ActualName,
                          string.Format("Critical shutdown\n\n{0}", er),
                          EventLogEntryType.Error);
      this.ExitCode = 1;
      this.Stop();
      return;
   }
}

The beauty here is that I don't have to poll.  Rather, I have the background thread call .Receive() on the queue.  This will effectively block the thread indefinitely or until a message is pushed into the queue.  At that point, it unblocks and begins processing the message.  Once finished it calls .Receive() again.  If there's a message, it gets processed, if not, it blocks.  And thus the cycle repeats itself.

Using MSMQ can be very rewarding and a lot of fun, and this little project has reminded me of that fact.

Sunday, May 27, 2007 6:51:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Tuesday, May 15, 2007

I have a very small utility application that I wrote that takes an XML string and submits it as a query against a web service, displaying the result.  Very simple, and just for testing purposes.  There are times when the XML string that I want to use as the web service query is encoded.  That is, it's littered with &lt; and &gt; strings in place of the XML '<' and '>' respectively.  Were these strings succinct it wouldn't be a big deal, but there are times where the query string is several thousand characters.

In the past I've resorted to opening a text editor and performing a find/replace on each of the escaped sequences, and then copying and pasting the result into my application to run the test.  Simple but cumbersome.

Well, I finally got tired of jumping through those hoops and decided to make the application a bit smarter in that I wanted to have it identify when encoded XML was being pasted into it, and have the application automatically format the result performing the string replacements automatically.  This is quite trivial, but I wanted to share the result as it may be informative.

My 'algorithm' for determining whether the text being pasted is in fact XML is quite rudimentary and basic, but it suffices in this instance.  I'm simply checking for '&lt;' at the start of the string.  If that sequence is found, it determines, hey, this is an XML string, and attempts to perform the replacement.

It occurred to me as well while writing this that XML isn't the only format that I may want to custom-paste into a TextBox.  Therefore, I abstracted the code such that you can write your own special formatting code and simply 'plug it in' to your application.  NOTE: I did NOT want to derive from the TextBox to create a special 'CustomPasteTextBox' control as that would further restrict its usefulness.

The base class (which I called PasteFormatterBase) takes a TextBox and a callback.  The operation is wrapped within a NativeWindow-derived SubClasser class; when the WM_PASTE message is received, a call is made back out to the callback to handle it.

public abstract class PasteFormatterBase {
   public PasteFormatterBase(TextBox txt, MethodInvoker callback) {
      _subClass = new SubClasser(callback);
      _subClass.AssignHandle(txt.Handle);
   }

   private SubClasser _subClass;

   private class SubClasser : NativeWindow {
      private const int WM_PASTE = 0x0302;
      private MethodInvoker _callback;

      public SubClasser(MethodInvoker callback) {
         _callback = callback;
      }


      protected override void WndProc(ref Message m) {
         if ( m.Msg == WM_PASTE && null != _callback ) {
            _callback();
            return;
         }

         base.WndProc(ref m);
      }
   }
}

Then, the class that performs the actual XML decoding of the string is quite simple:

public sealed class PasteXmlFormatter : PasteFormatterBase {
   public PasteXmlFormatter(TextBox txt)
      : base(txt,
             delegate {
                   IDataObject obj = Clipboard.GetDataObject();
                   string data = obj.GetData(DataFormats.Text) as string;
                   if ( null != data && data.StartsWith("&lt;") ) {
                      data = data.Replace("&lt;", "<").Replace("&gt;", ">");
                      txt.SelectedText = data;
                   }
                })
   { }
}

There may be better ways to write this (it's just a quick first pass), but it's elegant in its simplicity.

Tuesday, May 15, 2007 8:15:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, April 26, 2007

The other day I posed a question regarding the processing of data from a web service where the structure (i.e., the names and quantities of the fields) varies.  The fictitious example had us suppose that we wrote a component designed to plug into and consume the services of an application that may be installed across a wide customer base where each customer might customize (albeit indirectly) the results of the web service.  The web service, of course, would purely be an abstraction of the customer's customizations and business model.  For instance, a customer may decide to add a special/custom property to an 'Account' within the system that helps him track some aspect of the Account that isn't out-of-the-box.  This may, as a byproduct, affect the structure of the web service such that the new field is now present.

Part of our solution is to be accepting of such a change in structure in a manner that crosses any and all customer needs.  One customer may customize the heck out of an entity (such as Account) while another may make just a few changes.  Others will leave it alone entirely.  Our product must be able to operate on and affect any and all fields (including custom ones).

We discussed that there are some considerations to make in order to support this environment.

The question posed, then, was "How would you approach this problem?"  How would you design a system whereby you can consume all of the attributes of the entity (e.g., Account) across the board?

From within Visual Studio you can "Add Web Reference".  From the command-line (and my preferred mechanism) you use WSDL.exe.  Regardless of route you take (heck, you can even do it manually - I've done that in the past too), you'll end up with a proxy class with method calls to the web service and classes representing the various structured types that the web service exposes.

This isn't enough when you want to support a structure that will vary from implementation to implementation; the service won't have any inkling of any of the custom fields.

Alright, enough setup.  How would you solve this potential issue?

There are a couple of approaches worth investigating, each with their own varying degrees of complexity and control.  And probably many more that are eluding me and are simpler still.

First of all you can rewrite all of the traffic to/from the web service via a SoapExtension.  This approach may ultimately yield the greatest flexibility, but at the expense of a lot of work.  Essentially, with a SoapExtension you can inject your code into the web service pipeline and read, alter, or even rewrite completely all of the traffic to and from the web service before it is ultimately returned to you (or delegated on to another SoapExtension in the chain).  In fact, when I was first experimenting with this issue of dynamically structured data this was my first approach.  I have the history documented here if you're interested in reading more about it.

A second approach takes a different angle on this idea.  It involves defining a "bucket" into which all fields not formally defined within your proxy class are grouped.  To achieve this we must alter the generated proxy class by adding a custom field (note, don't re-gen the proxy class once you do this or you'll lose your changes).

Recall the original example:

[XmlType(Namespace="http://schemas.devstone.com/svc")]
public abstract class customer : BusinessObj {
   public string id;
   public string name;
}

Let's add our "bucket" and we'll call it "custom":

[XmlType(Namespace="http://schemas.devstone.com/svc")]
public abstract class customer : BusinessObj {
   public string id;
   public string name;
   [XmlAnyElement()] public XmlElement[] custom;
}

This very small change is what works the magic for us.  The XmlAnyElementAttribute identifies that the "custom" member will represent any XML elements within the results from the web service that don't have a corresponding member in the object.  That's pretty cool.

Ok, this is only part of the problem.  This gives us the ability reference potentially anything that the web service may throw our way, but how do we make use of this in our application?

I like to define a wrapper class for the object that encapsulates the business object:

public sealed class DynamicObj {
   public DynamicObj(BusinessObj busObj) {
      _busObj = busObj;
      _busType = busObj.GetType();  // cache the type for repetitive use
   }

   BusinessObj _busObj;
   Type _busType;

   // expose the business object directly so we can continue to interact with the web service with
   // the actual object (not the wrapped one)

   public BusinessObj Object {
      get { return _busObj; }
   }

   // provide a indexer mechanism to read/write field values
   public object this[string fieldName] {
      get { return getFieldValue(fieldName); }
      set { /* I'll leave this as an exercise for you, the reader - it's not difficult at all */ }
   }

   // returns the value associated with the specified field name
   public object getFieldValue(string fieldName) {
      FieldInfo field = _busType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
      if ( null != field )
         return field.GetValue(_busObj);
      else {
         XmlElement customField = getCustomField(fieldName);
         return ( null != customField ? customField.InnerText : null );
      }
   }

   // returns simply the number of fields defined by the business object
   public int FieldCount {
      get {
         FieldInfo[] fields = getDefinedFields();
         XmlElement[] elems = getCustomFields();
         return fields.Length + elems.Length - 1;  // subtract 1 to account for the presence of the 'custom' field
      }
   }

   // returns an array consisting of all of the fields within the business object
   // NOTE: depending on usage, we might want to cache the result of this method so
   // we're not constantly regenerating it.
   public string[] GetFieldNames() {
      string[] ret = new string[FieldCount];
      int i = 0;
      foreach ( FieldInfo field in getDefinedFields() )
         if ( "custom" != field.Name ) ret[i++] = field.Name;
      foreach ( XmlElement elem in getCustomFields() )
         ret[i++] = elem.Name;
      return ret;
   }

   // returns an array of all fields formally defined on the business object
   private FieldInfo[] getDefinedFields() {
      return _busType.GetFields(BindingFlags.Public | BindingFlags.Instance);
   }

   // returns a list of all fields found within the 'custom' field.
   private XmlElement[] getCustomFields() {
      FieldInfo customField = _busType.GetField("custom", BindingFlags.Public | BindingFlags.Instance);
      ifnull == customField )
         throw new NotImplementedException("The BusinessObj does not support the 'custom' field.");
      else
         return customField.GetValue(_busObj) as XmlElement[];
   }

   private XmlElement getCustomField(string fieldName) {
      foreach ( XmlElement elem in getCustomFields() ) {
         if ( fieldName == elem.Name )
            return elem;
      }
      return null;
   }
}

As you can see, it's pretty simple and removes a lot of the complexity of dealing with an object of whose structure you're not aware.  There is a whole lot more you can do with this object than I'm letting on, but it may be a starting point.  There are a few issues that this approach does not address (such as the notion of 'type').  For example, there's no way of knowing whether you should be dealing with an int, a string, a DateTime, etc - but I'll leave that implementation up to you.

What do you think? Plausable? Idiotic? Cool?

I'd like your feedback and input.

Thursday, April 26, 2007 4:12:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, April 24, 2007

With .NET, consuming web services is a snap.  Heck, it's not all that difficult without .NET, but it sure makes it even easier.  In general, and for simple cases, all you need to do is add a web reference to the target .asmx, have the proxy generated (via VS.NET or wsdl.exe), and you're off to the races.

Some additional care and consideration should be taken, however, when the data returned from the web service can vary in format and structure.  Allow me illustrate this with an example:

Suppose you are developing an application that must consume some data from a web service.  This web service returns structured data that, via the auto-generated proxy, is represented in a class.  For argument sake, this class is Customer (which derives from BusinessObj) and it has a few properties as identified below:

[XmlInclude(typeof(customer))]
public abstract class BusinessObj { }

[XmlType(Namespace="http://schemas.devstone.com/svc")]
public abstract class customer : BusinessObj {
   public string id;
   public string name;
}

Furthermore, this web service is part of a larger application that a company will purchase and deploy internally.  In fact the customer may customize each type of data (e.g., Customer) by adding custom fields and attributes.  Your component is designed as a 3rd-party add-in for this application and may be deployed across this varied environment.

Some customers will leave the base application alone (not customizing it) where other will 'go-to-town' and change the schema entirely to fit their business needs.

The question is this: how can your application operate in such a varied environment, consume the web service, and process all of the customizations that the customer has implemented?

You cannot, for instance simply create your web service proxy against the default schema and expect that to work.  The reason being that once you deploy your component into a customized environment, your proxy will not know about the custom fields and cannot serialize/deserialize them.  You won't get an error, per sé, but any custom fields will be ignored and not made available to your application.

This could be disastrous in the event that you needed to populate or read the custom fields.

How would you approach this problem?

NOTE: I'll post my answer tomorrow (or perhaps tonight) of how I've implemented it in the past to great success.

Tuesday, April 24, 2007 3:50:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, April 16, 2007

Phew! What a crazy, fun weekend!  I'm finally getting caught up.

This past weekend (April 14th, 2007) we had the opportunity to host and participate in the second Code Camp in Utah officially entitled “Utah Spring Code Camp 2007”.  Our goal is to put these on every six months, the next one sometime between Sept and Nov of this year.

Nonetheless, this event was a lot of fun.  There were many in attendance, though not quite as many as we were hoping for.

I had the opportunity speak on .NET Serialization.  It was a variation on a presentation that I had prepared a few months back for the Utah .NET User Group and this time, after rewriting the presentation and code samples from scratch it went MUCH better.  I am quite happy with how it came out (I hope those in attendance gleaned some nuggets of information).

As it turns out one presenter was not able to make it out to the Code Camp (calling in around 10:30 or 11:00 in the morning) so we had to scramble a bit.  I had some ideas up my sleeve based on what I had trimmed from my presentation and we focused the second hour on XML Serialization.  That was a lot of fun! :-)

The third block was a shortened (30 min) presentation in which I discussed an evolution in accessing application configuration files (e.g., web.config, app.exe.config, et al).  Beginning with the most basic access method via AppSettings, we progressively made the transition to ConfigurationSectionHandlers and XML Serialization.  We used this technique to deserialize a block of XML from the configuration file into an object in code that we can access.

All in all, I'm quite happy with the Code Camp, though I wish I could have attended more of the presentations.  I guess that's a drawback from having multiple tracks running simultaneously - you have to pick and choose what you're gonna see.

The code for my presentations can be downloaded here.  Enjoy!

Monday, April 16, 2007 4:00:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, April 09, 2007

There's gonna be a fun MSDN event this coming Thursday, April 12th (same day as our Utah .NET User Group meeting - reminders going out shortly) from 1:00 PM - 5:00 PM at Jordan Landing in West Jordan on ASP.NET AJAX and CardSpace.  I'll be there - it should be a great time.

Here's the event link with all the details:  http://msevents.microsoft.com/cui/EventDetail.aspx?culture=en-US&EventID=1032326499

Monday, April 09, 2007 7:07:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, April 05, 2007

As a reminder to everyone, we've having our second Utah Code Camp this next Saturday (April 14th, 2007) at Neumont University.  The last event was a great success and a ton of fun.  Please set aside the date and register.  Here's the official blurb:

What: Utah Spring Code Camp
When:  April 14th 2007 9:00 AM - 5:00 PM
Where: Neumont University
Registration:
http://utahcodecamp.eventbrite.com

The local .NET Users Group and SQL Server Users Group is conducting a “Code Camp” for local software programmers next month at Neumont University.  The code camp is by the community for the community.  Always free and Always for the community.

The Saturday, April 14th event is scheduled from 9:00 AM to 5:00 PM. The conference is free please register at. http://utahcodecamp.eventbrite.com
Lots of Sponsors and Lots of software and Tech Gadgets to giveaway!
You can check out
www.msutahevents.com  for a session schedule and speaker list for the Code Camp.

Thursday, April 05, 2007 6:12:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, April 03, 2007

It was requested to keep this information confidential until 9:00 AM PDT today, but the hour has past. :)

I recently received word that Microsoft has retracted its initial plan of not including the Expression suite of tools (e.g. Expression Web, Expression Blend, etc) with MSDN.  After the barrage of concerned voices and complaints against this decision, Microsoft has decided to make Web and Blend available to all MSDN Premium subscribers (Web is available now to MSDN downloads).

For further reading see Somasegar's WebLog.

Tuesday, April 03, 2007 3:34:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, March 30, 2007

Having recently moved my primary development environment over to Windows Vista I've been presented with a small learning curve when developing my ASP.NET applications. It's been good, however, as I've been forced to delve into IIS 7.0 and explore some of its capabilities and learn some of its nuances. To be sure, I'm very much enjoying working with it…but there's a lot to learn and become acquainted with.

I have a web application that I'd been working on in an IIS 5.5 (Windows XP) environment successfully prior to the migration so I thought I'd set it up and take it for a test drive in the IIS environment. I was pleased that I had but a few changes to make to make it operational. Getting it run from Visual Studio 2005 was a different story however.

I found that anytime I attempted to run my website by pressing F5 within Visual Studio I was presented with a dialog explaining that it was "Unable to start debugging on the web server. An authentication error occurred while communicating with the web server. Please see Help for assistance." I could, however, attach to the running process (w3wp.exe) and debug that way. In my case, while that would have worked most of the time, it didn't provide me access to some of the methods that I'd want to debug (like BeginRequest) and is a bit more cumbersome. Well, this was a bit disconcerting, but after a few minutes of tinkering in IIS to attempt to resolve it I resorted to running a quick Google search. It turns out I wasn't alone on this issue, not by a long shot. The first blog post I found was by Rajiv Popat and it was quite helpful, but not quite what I was looking for. In his post he recommends using the Classic Mode rather than the new Integrated Mode. NOTE: The Classic Mode that is provided with IIS 7.0 provides a runtime environment that mimics IIS 6.0. I could have used that happily, but as I'm not generally satisfied with workarounds or just settling for the first solution, er, band-aid that I find, I wanted to dig deeper and get my application working in the newer environment.

This is when I found a great blog post by Mike Volodarsky (a Program Manager on the IIS team). In his post he mentioned the key characteristic as to why I couldn't debug my site via F5: I had a global.asax file (though I deleted it – it wasn't necessary in my app) and a custom HttpModule that attaches to the HttpApplication.BeginRequest method. Apparently "there is a bug in ASP.NET Integrated mode that prevents authentication from taking place correctly for the debug auto-attach feature". Well, removing the custom HttpModule was not an option because it does some pretty slick URL rewriting for the site and is critical for its operation. That, combined with the fact that Classic Mode didn't really give me the warm fuzzies, led me to adopt his Debugging Assistant (64-bit Version). Man, what a life saver! This worked – though it didn't work at first for me when I tried it yesterday, but I hunkered down and resolved to figure out why tonight.

When running in IIS 7.0's Integrated Mode, the web server will not rely on settings within your section of the web.config file as it does in Classic Mode. Rather it relies on the new section. As per the instructions on Mike's site, I added the special debug module and handler to the appropriate sections in the web.config file, but I continued to receive the "An authentication error occurred communicating with the web server" error. It turns out that I was clearing the modules and not adding back in a module that is required for debugging: WindowsAuthenticationModule. Hey, I'm still learning IIS 7.0 here J This is approximately how my web.config appears:

<system.webServer>
   <modules>
      <clear />
      <add name="debugassistant" />
      <add name="WindowsAuthenticationModule" />
   </modules>
   <handlers>
      <add name="DebugHandler" path="DebugAttach.aspx" verb="DEBUG" type="System.Web.HttpDebugHandler" />
   </handlers>
</system.webServer>

It turns out the fix was quite easy once I knew what I was doing.

Friday, March 30, 2007 7:44:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [7]  |  Trackback
 Tuesday, March 27, 2007

As a reminder, we're having a special .NET Bring Your Family event this Thursday, March 29th, 2007 at our local Microsoft Offices.

The event runs from 6:00 PM to 9:00 PM and is intended for you, your spouse/significant other, and children ages 6 or 7 and up. Sure, there's a geek aspect of the event, as there will be discussions on Office 2007 and Windows Vista for the adults/spouses but there is also a fun side with Lego Mindstorm Robotics stuff for the kiddos (and probably some adults too) with a Lego Mindstorm Robot as a raffle giveaway.

Food (probably pizza) and drinks will be provided by Microsoft.

So prepare for a fantastic time and come one and all. With your participation we can definitely plan on having a lot of fun!

Time: 6:00 PM - 9:00 PM
Date: Thursday, March 29th, 2007
Place: SLC Microsoft Offices (123 Wright Brothers Dr., Suite 100, SLC, UT 84116)

See you there!

Tuesday, March 27, 2007 2:42:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I had the fortune (or misfortune depending on how you look at it) of completely rebuilding my development machine from scratch last week.  The unfortunate circumstance of having a corrupted registry and destroyed network settings befell my laptop due to a bad install of a VPN client.  I was able to rescue 100% of my data however, off-loading it to an external USB drive which I love.  I've long been wanting to make the move to Windows Vista but for a grundle of reasons couldn't validate the time it would take to move to the new environment.  Perhaps this was the kick in the pants that I needed.

Once I blew the machine away (paved it, as they say) I set forth to install Windows Vista Ultimate on my Dell Precision M90.  The install went extremely smoothly - I had an operating machine in about 30-45 minutes.  With the exception of the built-in laptop monitor, I was very pleased that the OS correctly identified 100% of my devices.  It was peculiar that Vista insisted in setting the display rotated 90° counter-clockwise after a few reboots.  Until I installed the correct drivers I couldn't shake it of this habit.  Also, it's working great with my secondary Dell 2407WFP monitor :-)  Dual 1920x1200 resolution :-)

I've also installed my customary applications (Office 2007, VS Team Suite 2005 SP1 w/Vista Patch, XML MissionKit, SQL Server Express 2005 SP2 (I decided to use SQL Express rather than the full blown SQL Server AND Express for development purposes - it's proving to be perfect), Photoshop CS2, WinRar 3.7 (you'll need 3.7 for better Vista integration), Resharper, Invirtus VM Optimizer, et al).  Much of my development also requires VS2003 (.NET 1.1) which is not supported by MS on Vista.  I've therefore established a Virtual PC running XP SP2 as my primary .NET 1.1 dev platform and it's working great.  I have it open and running all day long with ne'er a hiccup.

All in all I can say that I'm very pleased with the upgrade and it wasn't the least bit painful.  Also, I can honestly say that I enjoy using Vista as a non-administrator (something I had been doing religiously for about 2 years on XP).  The periodic UAC warnings aren't the nuisance everyone seems to think they are.

That said, it is important to recognize when things are running under admin privileges and when they're not.  For instance, if an application launches following the install, chances are it's running under admin privileges so any settings made aren't going to be for the logged-on user but rather for the administrative account.

I'm loving developing on Vista - it's awesome! :-)

Tuesday, March 27, 2007 1:35:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, March 17, 2007

A small application that I wrote some time ago just started acting up on me yesterday and today in a manner heretofore not seen.  The program utilizes WSE 2.0 and DIME to perform file transfers between the client and server.  The error was the following:

An unhandled exception of type 'System.Configuration.ConfigurationException' occured in microsoft.web.services2.dll

WSE032: There was an error loading the microsoft.web.services2 configuration section

I felt it bizarre that I'd never seen this error before, despite having installed and run my application hundreds of times in the past.  I was also having some networking trouble with my VPC (on which I was running this application) which correlates to the solution that I found online:

  1. Ohad recommends adding your local computer's host name (can easily be discovered/remembered by typing HOSTNAME at a command prompt) to the %WINDIR%\System32\drivers\etc\hosts. file.  (BTW: This fixed it for me)
  2. Also, if you have a configuration file (e.g. for controlling the timeToleranceInSeconds settings, et al), you need to ensure that you're using a fully qualified type name to the Microsoft.Web.Services2.Configuration.WebServicesConfiguration type.
Saturday, March 17, 2007 7:16:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 08, 2007

Well, the last of the big hurdles that I was waiting for before moving to Vista on my primary machine was released a couple of days ago.  Microsoft released the Visual Studio.NET 2005 SP1 for Vista.  Now that SQL Server 2005 SP2 has been released, along with this service pack, I just want to get a good, stable Windows XP SP2 VPC representing my current development environment so that I can continue to support .NET 1.1 development (in VS2003) because that will not be supported on Vista.

If you're using VS.NET 2005 on Vista, you'll want to get this patch.  Download it here.

Thursday, March 08, 2007 8:18:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 01, 2007

On March 8th, 2007 there's going to be a special event at the local, Salt Lake City Microsoft Offices: Windows Vista for Developers Clinic.  If you're using Vista, planning on using Vista, writing software that needs to run on Vista, or generally want to know more about it, please register and attend.

The event is an all-day event, running from 9:00 AM - 5:00 PM (just in time to get out to head to the Utah .NET User Group :)).  I'll be there and would love to see anyone else there as well.

(NOTE: I just found out about this event today - sorry for the late notice, but as I understand it not many are yet registered, so hurry up.  It should be a fun time.)

Here are the details:

Salt Lake City – March 8, 2007
Salt Lake City Microsoft Office
123 Wright Brothers Drive, Suite 100
Salt Lake City, UT
Registration: 8:30 am
Event: 9:00 am - 5:00 pm
Register:
http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032329708&Culture=en-US
Event ID: 1032329708

Windows Vista for Developers Clinic

For developers who want to create connected applications with visually appealing and highly-differentiated user experiences, Windows Vista provides the most productive, secure and reliable platform.  Developers can take advantage of the .NET Framework for rapidly building connected and secure applications or websites, leverage native Windows APIs for additional functionality and performance or freely mix between the two models.

This one-day instructor-led clinic and hands-on lab for developers introduces students to high-level information and facilitated discovery on the Microsoft Windows Vista platform, maintaining and enhancing their productivity and driving them to further study and adoption of Windows Vista as their preferred application development platform.

AGENDA

Session 1: Introduction to Windows Vista Application Development

The session provides an overview of the major goals and benefits related to developing applications for Windows Vista. The session focuses on the new features available in Windows Vista and on an overview of the application compatibility issues for deploying existing applications that might not be Windows Vista compatible in a corporate environment.

Session 2: Introduction to Microsoft .NET Framework 3.0 Technologies

The session provides an overview of the .NET Framework 3.0 programming model. After explaining the basic concepts of .NET Framework 3.0, the session describes how to develop applications using Windows Communication Foundation and Windows Workflow Foundation as well as how to use CardSpace.

Session 3: Introducing Windows Presentation Foundation

The session provides an overview of how the Windows Presentation Foundation provides the foundation for building applications and high fidelity experiences in Windows Vista, blending together application user interfaces (UIs), documents, and media content.

Session 4: Introducing the Windows Vista APIs

The session provides an overview of the new Windows Vista APIs and demonstrates how to use the new APIs to develop applications for the RSS platform and to query the Windows search engine.

Hands-on Lab: Developing Applications for Windows Vista

The hands-on lab provides students an opportunity to work with developing applications for Windows Vista in a hands-on environment. This lab focuses on new application development features in Windows Vista.

Thursday, March 01, 2007 7:41:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, February 22, 2007

I ran into an interesting issue today when working on an ASP.NET 2.0 Web Application Project that I had not heretofore seen.  Very frequently, but not 100% of the time, upon debugging my application from Visual Studio 2005 I would encounter an error message resembling "Cannot create/shadow copy 'XXX' when that file already exists." where 'XXX' is the name of some resource in my project.  I hadn't seen this error before installing SP1, leading me to believe it has something to do with that upgrade.  However, almost all of my Web Application Projects are done with IIS and not the Cassini web server - so maybe it's Cassini related.

Nonetheless, it's pretty easy to fix.  You simply have to tell ASP.NET not to shadow copy the project assemblies to the ASP.NET temporary folders file by updating your web.config with the following entry:

<configuration>
   <system.web>
      <hostingEnvironment shadowCopyBinAssemblies="false" />
   </system.web>
</configuration>

Then just restart your Cassini web server or simply right-click it and select 'Stop'.  That cleared it up for me.

Thursday, February 22, 2007 3:42:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [15]  |  Trackback
 Monday, February 19, 2007

Just a few days ago Microsoft released SP2 of it's SQL Server product.  Download it here.  If you're running Vista you'll want this update as pre-SP2 isn't supported on Vista.  Now I'm waiting for one more update (the VS.NET 2005 SP1 for Vista) before I officially make the switch on my primary development machine.

Monday, February 19, 2007 4:02:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, February 12, 2007

Deploying a web application and a database to a single server is pretty straightforward.  In fact, it's quite common.  However, for a variety of reasons, it may be necessary to separate the database from the web application into two (or more) physically separate machines.  Many a developer may be caught unawares by potential database authentication issues that may arise in this scenario.

If your web application accesses the database directly, with its own set of credentials, your task may not be too complicated.  You may simply need to make sure that Sql Authentication is enabled on the SQL Server.  If Windows Integrated Security is required, there are ways you can accomplish this as well, such as by programmatically impersonating a user, or perhaps by using a COM+ package with a specific identity.

If, on the other hand, your application needs to flow the end user's credentials (e.g. the browsing user's credentials) to the database there are some other things you'll need to consider.  This type of application is common place in a corporate intranet where a user logs on to his machine and can then navigate to a company portal without requiring an additional logon.  The authentication simply happens behind the scenes between the browser and the site.

Due to restrictions imposed by how NTLM works, however, if you must use integrated security and your database server is physically separate from your web application you will be unable to access the database through the website (though you would if you connected directly).  This is because NTLM causes the server to authenticate the client.  The client security token is authenticated by the web server.  When the time comes to connect to the SQL Server as the user, the database server will attempt to authenticate the client (in this particular connection the web server is the client).  The web server will not have the client token so authentication fails.  Essentially, you're given one network/machine “hop“ with your credentials.

This is where Kerberos comes into play.  Kerberos will allow your credentials to flow further, giving the database server the ability to ascertain your security token and grant you access.

If you're developing an ASP.NET application and need this functionality, there are a few things you should have in place to allow for the client credentials to flow across multiple machine boundaries like this:

  • Make sure impersonation is set in the web.config via <identity impersonate=“true“ />
  • Make sure that the website is set for Windows Authentication / Integrated Security (not anonymous).  If you set for Basic Authentication, the client will be prompted to login upon connecting and the web server will contain the security token and will then, upon attempting to query the database, make its one allotted network “hop“...but that defeats the purpose of the single sign-on we achieve with Windows Authentication.
  • Make sure that the machine is trusted for delegation.  This is accomplished in the Active Directory Users and Computers console.
  • Make sure that your connection to the remote database is using Named Pipes.

While troubleshooting this scenario you may encounter an error such as “Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'”.  In such an event it is possible that Kerberos is enabled but you're accessing the remote SQL Server via TCP/IP.  Make sure that your connection string specifes to use Named Pipes.

Monday, February 12, 2007 5:08:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, February 08, 2007

Tonight's User Group presentation went better than last month's, though still not as smoothly as I would have liked.  The demo gods weren't on my side several times tonight.  Despite that, the event worked out for the best.  We held the Utah .NET User Group meeting at a new facility (provided graciously by Digital Draw Network) and sponsored by SOS Technical; it worked out quite well.  Our topic this evening was .NET Serialization.  We addressed serialization from various angles, experimenting with Binary, SOAP, XML, and custom serialization.  In addition we explored creating a custom IFormatter and even touched on versioning of serialized objects.

All in all it was a lot of fun and we had a good time.  I wish I could have done better, but then again, I always do.

Oh, and as an aside, our gathering at Denny's afterwards is getting bigger and better every time.  We had many people show up and it was a blast.  I really look forward to the 'after event' event.

Thursday, February 08, 2007 4:22:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, February 07, 2007

Time has once again flown by!  We're ready to move into February's installment of the Utah .NET User Group on Thursday, February 8th, 2007.

Neumont University, who has been so generous of their time and facilities has a school event that night which occupies both of the rooms we usually meet in. A huge thanks goes out to Digital Draw Network (DDN) for stepping up to the plate and offering space for the User Group to meet :) And - most conveniently - DDN is located in the same office park.

Directions:

When you turn on River Front Parkway off of 10600 South, rather than turning into Neumont, proceed a few buildings down. You'll reach a round-about in the road. Follow that around and turn into the parking log of 10897 South.

Time: 6:00 PM
Date: February 8th, 2007
Place: Digital Draw Network, 10897 South, River Front Parkway, Suite 300

DDN has arranged that the building doors will be open until 6:30. We are very excited about the prospect of meeting there. In fact, due to the multitude of scheduling conflicts for '07, we'll be evaluating the location for future meetings as well. We express our gratitude to DDN for the offer. Additionally, we've arranged to have internet connectivity in the new location (though perhaps not for the first meeting due to the short scheduling notice).

Invite your friends, co-workers, colleagues, enthusiasts, peers, inferiors, and anyone you'd like to attend. The event is free to all.

This month's meeting will focus on .NET Serialization and will be sponsored by SOS Technical.

It'll be a great time! See you there!

Wednesday, February 07, 2007 7:37:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, January 18, 2007

You know...? There are some days where you're with it and have your act together and can speak coherently and formulate your thoughts, and other days where you don't and you stop at green turn arrows while you're driving because you're just fried.  Well, I suppose it happens to everyone once in a while.  It doesn't happen to me all that often, but today (more specifically tonight) was one of those times.

I was giving a presentation to the Utah .NET User Group on SQL Server 2005 Development (SQLCLR, etc) and I just couldn't get my act together and felt completely disorganized.  I suspect the information was fine and the presentation was okay, but I really felt out of sorts.  In a way I feel I did the group a disservice by not being on par with my normal presentations.  While I'd like to offload the blame to something else and make excuses I can't - I must not have been as prepared as I thought and started into it cold, without a plan.  I had actually prepared the presentation quite some time ago and had given it twice to other groups (both of those times went very well and were much more fluid).  Maybe that comfort with the presentation led me not prepare as well as I would have otherwise and I simply started off on the wrong foot.  I can simply say that I wasn't "on my game" and I apologize for that.

Despite all that, in some ways the presentation was successful - the information was imparted and people had good things to say.  Perhaps I'm overly self-critical.

One highlight of the evening was that we had 13 people join us for food/drinks after the meeting (as has been our tradition for about 9 months).  That has been a lot of fun.

I'm looking forward to the coming months - we're trying to put together some different content and meeting formats to mix it up a bit.

Thursday, January 18, 2007 4:45:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback

In the event you didn't get our reminder email and for all those out there that may stumble upon this post before this afternoon:

Last week we had cancelled the Utah .NET User Group meeting due to inclement weather.  We've been able to reschedule it at our regular location (Neumont University) for tonight at 6:00 PM.  We will be meeting on the 2nd floor (rather than on the usual 3rd floor).

Copied (and edited) from the previous post:

The Utah .NET User Group is getting the year started off with a discussion on SQL Server 2005 Development.  Yours truly has the opportunity to drill down and talk about the SQLCLR as well as many of the improvements to the T-SQL programming model in SQL2005.  It should be a good time.

If you're in the neighborhood (meaning the greater Salt Lake City area), please come on down and enjoy a great evening of code, food, friends, and fun.  We're meeting tonight, January 17th, 2007 @ Neumont University @ 6:00 PM.

As we've had the tradition of doing now for almost a year, several of us get together afterwards for a bite.  Please feel free to join us there too!

Date: Thursday, January 18th, 2007
Time:  6:00 PM (arrive early)
Place: Neumont University, Suite 200 (10701 South River Front Parkway, South Jordan, UT)

It'll be loads of fun - bring your friends!

Thursday, January 18, 2007 2:10:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, January 17, 2007

I've been battling a peculiar issue for a couple of days regarding Data Transformation Services (DTS) with SQL Server 2000 and SQL Server 2005.

I wrote a small C# application that utilizes DTS to pull a database from a server down to a client machine.  All in all, the application runs beautifully and the information synchronizes seamlessly.  Occasionally, however an odd error was reported: Unable to connect to source server for Transfer.  This was odd because all of my connections were succeeding.  It was also only on this computer that the error was occurring - I could not duplicate the error on other computers so I began delving into the differences between the machines.

As it turns out, the error has to do with mismatched database versions which I've whittled down to the following matrix:

Client SQL Version Server SQL Version Result
MSDE SQL Server 2000 No Issues
SQL Express SQL Server 2000 No Issues
MSDE SQL Server 2005 FAILS
SQL Express SQL Server 2005 No Issues

As you can tell, the issue arises when the client database engine is not at least as high as the server version.  I'd long suspected that a version mismatch was the root of the problem but hadn't ascertained it until today.  After troubleshooting this for some time to make sure that there wasn't something else I was missing, I ran a trace on the SQL Server to detect whether something was happening there. (I had actually run the same trace before but failed to see the key piece of information).

The DTS transfer objects apparently have baked into them a version check.  When run from an MSDE client it performs the following query:

if (object_id('master.dbo.sp_MSSQLDMO80_version') is not null) exec master.dbo.sp_MSSQLDMO80_version else if
(object_id('master.dbo.sp_MSSQLDMO70_version') is not null) exec master.dbo.sp_MSSQLDMO70_version else select 0

SQL Server will always return a valid result set for the current version (e.g. 2005 returns valid results if the sp_MSSQLDMO90_version sproc is called and 2000 returns correctly if the sp_MSSQLDMO80_version sproc is called), but will throw and exception on any other version's sproc.  The issue is that the MSDE will check for sp_MSSQLDMO80_version on a SQL2005 box which returns an error which ultimately cascades down and cancels the DTS package.

From a SQL Express client, the query is this:

if (object_id('master.dbo.sp_MSSQLDMO90_version') is not null) exec master.dbo.sp_MSSQLDMO90_version else if
(object_id('master.dbo.sp_MSSQLDMO80_version') is not null) exec master.dbo.sp_MSSQLDMO80_version else if
(object_id('master.dbo.sp_MSSQLDMO70_version') is not null) exec master.dbo.sp_MSSQLDMO70_version else select 0

In this case, the query will continue to work against a SQL2005 server or a SQL2000 server.

In other words, SQL is throwing the 'cannot connect' error correctly.  Internally it reports "To connect to this server you must use SQL Server Management Studio or SQL Server management Objects (SMO)".

Solution:

My application must maintain full compatibility with the MSDE -> 2000 environment.  The only solution I could come to was that in order for my application to run against a SQL Server 2005 server the client computer would have to be upgraded to SQL Express.  I hate to have to do that, but I could see no other viable solution.

Are there other thoughts out there?

Wednesday, January 17, 2007 8:50:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, January 10, 2007

The Utah .NET User Group is getting the year started off with a discussion on SQL Server 2005 Development.  Yours truly has the opportunity to drill down and talk about the SQLCLR as well as many of the improvements to the T-SQL programming model in SQL2005.  It should be a good time.

If you're in the neighborhood (meaning the greater Salt Lake City area), please come on down and enjoy a great evening of code, food, friends, and fun.  We're meeting tomorrow, January 11th, 2007 @ Neumont University @ 6:00 PM.

As we've had the tradition of doing now for almost a year, several of us get together afterwards for a bite.  Please feel free to join us there too!

Date: Thursday, January 11th, 2007
Time:  6:00 PM (arrive early)
Place: Neumont University (10701 South River Front Parkway, South Jordan, UT)

It'll be loads of fun - bring your friends!

Wednesday, January 10, 2007 5:58:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, January 01, 2007

It's pretty exciting to get the year started off this way.  I received notification today via email that I was accepted for the second year as a Microsoft MVP.  :)  I hope that more can be accomplished this coming year and the .NET community strenghtened.  Thanks to all those that contribute and make our community rock!

Monday, January 01, 2007 7:04:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, December 15, 2006

Within the context of SQL Server, retrieving the server version is pretty easy, and there is a variety of ways one can go about retrieving the information.

You might consider calling the sp_server_info sproc that I mentioned in a post yesterday with specifying the attribute id of '2':

EXEC sp_server_info 2

Unfortunately, this returns columnar data so unless you do something like I proposed in said post it's largely unhelpful.  Alternatively, and much more simply, you can query the @@version function (which I suspect ultimately defers to the same data that the sp_server_info sproc returns):

SELECT @@version

Still, this data may not be what you're looking for.  It's quite verbose and contains a lot of extraneous information.  You may be after just the version number.  Fortunately, this is quite easily accomplished by querying the serverproperty function.

SELECT serverproperty('ProductVersion')

Incidentally, there is a lot more information you can glean from the server via the serverproperty function (product level, edition, instance name, and much more).

You may have need to write some TSQL that is conditional based on the target SQL Server edition.  For instance, I'm in the process of writing software that must run on both SQL Server 2000 and SQL Server 2005, but where possible I'd like to use SQL Server 2005's capabilities and not necessarily cater to the least common denominator of SQL Server 2000.

The ProductVersion may still be too specific, as it contains minor versions, SP updates, etc.  Essentially, what I want is to ensure that I'm dealing with the correct DB engine by major version # and not take into consideration any minor updates.  The following code illustrates in a very simple manner extracting just the major version from the property.  In this case, the TSQL simply returns a string indicating what was detected, but your code may actually contain logic:

DECLARE @ver nvarchar(128)
SET @ver = CAST(serverproperty('ProductVersion') AS nvarchar)
SET @ver = SUBSTRING(@ver, 1, CHARINDEX('.', @ver) - 1)
IF ( @ver = '8' )
   SELECT 'SQL Server 2000'
ELSE IF ( @ver = '9' )
   SELECT 'SQL Server 2005'
ELSE
   SELECT
'Unsupported SQL Server Version'

Friday, December 15, 2006 3:18:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback

It looks as though Microsoft did in fact release the Visual Studio 2005 Service Pack 1 yesterday.  It's a hefty download (approx 432 MB), but I hope it's worth it.  I imagine it would take a lot to fix many of the 'house-of-cards' issues that VS 2005 has.

Be forewarned, however.  I can't recommend running it on Vista yet.  Within the download instructions it clearly states that 1) the installation on Vista will take a long time (up to an hour just to verify digital signatures) and 2) there's an additional update to SP1 for Vista that is currently in Beta.

Enjoy :)

Friday, December 15, 2006 2:52:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, December 13, 2006

...
"The time has come," the coder said,
"To talk of many things:
Of loops - and blocks - and heaps and stacks -
Of variables - and strings -
And why you see AND, OR, 'n NOT
And whether the NIC blocks pings.
...

          - Adapted from Lewis Carroll's Jabberwocky

Tomorrow (December 14th, 2006) we'll have the December installment of the Utah .NET User Group.  We're very excited to have Robert Green (more recent/updated website/blog?) visiting us from out of town.  He'll be talking about "Developing Office 2007 Solutions with Visual Studio Tools for Office 2nd Edition" (VSTO SE).

Come one and all - it should be a great event!

Time: 6:00 PM
Date:
December 14th, 2006
Place: Neumont University (10701 South River Front Parkway, South Jordan, Utah)

See you there!

Wednesday, December 13, 2006 4:20:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, November 30, 2006

Along with Scott Golightly and Pat Wright, I had the opportunity today to particpate in a SQL Server 2005 all-day training at our local Microsoft offices.  The day was broken into three parts: Pat covered SQL Server Management, Scott discussed Business Intelligence, and I tackled Development.

While I can't speak for the others (as I was not able to attend their presentations unfortunately), my presentation went very well - I even threw in some off-the-cuff demonstrations (as I am want to do) of returning tabular information from a .NET stored procedure :)  We covered a variety of topics, most notably the SQLCLR, TSQL enhancements, and ADO.NET 2.0 along with a smattering of data encryption.  All in all I'm fairly happy, though I can always look back and nitpick some things that I should have done better.

I had a great time and I believe those in attendance did as well.  I look forward to the next one.

Thursday, November 30, 2006 11:09:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, November 29, 2006

I was going though some old code that I wrote a few years ago and stumbled upon a little nugget that might prove to be useful.  Back in my VB 2.0-6.0 days there was an event on a Form called QueryUnload.  This handy little event proved to be very useful because not only could it be cancelled (effectively stopping the form from unloaded), but it also provided some information as to how the form was closing (e.g. whether the user clicked the 'X', code closed it, or the OS was shutting down).

Unfortunately nothing exists in the .NET Framework out of the box that provides this functionality.  I had this need a long time back and drummed up some code that may prove to be useful out there, so here it is.

Essentially, I have a base form class (appropriately named FormBase) which exposes an UnloadMode property that you can query in the _OnClosing event (note that the OnClosing method is sealed so you need to add the handler rather that override the OnClosing method directly).  The UnloadMode property indicates how the form is closing down.

Here's the code:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace Devstone.Demo.Forms {

#if ( DEBUG )
   public class FormBase : Form {
#else
   public abstract class FormBase : Form {
#endif

      public enum FormUnloadMode : int {
         None,
         FormControlMenu,
         Code,
         OperatingSystemShutDown
      }

      private bool _closeButton = false;
      protected FormUnloadMode ulMode = FormUnloadMode.None;

      public FormUnloadMode UnloadMode {
         get { return ulMode; }
      }

      protected sealed override void OnClosing(CancelEventArgs e) {
         // set the flag back to false so as to not prevent the WM_CLOSE message
         // from changing the UnloadMode

         _closeButton = false;

         // ensure that the event will be properly raised in the derived classes
         base.OnClosing(e);

         // reset the UnloadMode flag
         ulMode = FormUnloadMode.None;
      }

      protected override void WndProc(ref Message m) {
         
const int WM_CLOSE = 0x0010;
         const int WM_SYSCOMMAND = 0x0112;
         const int WM_ENDSESSION = 0x0016;
         const int SC_CLOSE = 0xF060;

         switch ( m.Msg ) {
            case WM_CLOSE:
               if ( !_closeButton ) ulMode = FormUnloadMode.Code;
               break;

            case WM_SYSCOMMAND:
               if ( m.WParam.ToInt32() == SC_CLOSE ) {
                  _closeButton = true;
                  ulMode = FormUnloadMode.FormControlMenu;
               }
               break;

            case WM_ENDSESSION:
               ulMode = FormUnloadMode.OperatingSystemShutDown;
               break;
         }

         base.WndProc(ref m);
      }

   }

}

I hope this proves helpful in some small way.  In terms of future enhancements, I may go ahead and implement a QueryUnload method on this to hearken back to the older programming model (which is perhaps a bit more clear in some instances).  If I do, I'll be sure to update the blog accordingly.

Wednesday, November 29, 2006 6:18:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, November 12, 2006

I've been a little quiet on the blogging front lately, but it's not without good reason.  I've been working feverishly with the rest of my team at Experlogix, churning out the next version of our awesome web-based product configurator: Version 4.6.

Despite the small number revision (over 4.5), 4.6 represents a major step forward on so many different levels.  I am very excited about this next version.  We've added some fantastic functionality and substantial performance improvements - and there's more coming in the next versions as well :)

Now that it's code complete I can rest a little easier.  In fact, this week I'll be in Redmond, Washington on campus at Microsoft to assist in a special project.  I'm very much looking forward to interacting with the CRM team (of which I am acquainted with several members).  It should be a great week.

Sunday, November 12, 2006 1:26:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, November 08, 2006

The Utah .NET User Group's meeting for November 2006 has been postponed 6 days to Wednesday, November 15th.  Neumont University, our long-time generous sponsor, has a quarterly Career Night on our regularly scheduled night, but they have allowed us to come in on a different evening. :)

This month we'll be pleased to hear from Justin Long on the topic of Reflection.  This will undoubtedly prove to be a great meeting as Reflection plays such an important role in the life of a .NET developer.

So don't forget: 6:00 PM on Wednesday, November 15th, 2006 @ Neumont University (10701 South River Front Parkway, South Jordan, Utah).

Be there or be sq..like me - in Redmond on the Microsoft campus.  I wish I could join the group, I'm sure everyone will have a great time.

NOTE: If you haven't already done it, go to the new website (which has been upgraded to CS 2.1 SP1) and register there.  Going forward, all activity will take place on that website.  Forums, blogs, photo galleries, etc are forthcoming.

Wednesday, November 08, 2006 6:39:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Go and feel the goodness. To quote from that page:

"The Microsoft .NET Framework 3.0 is the new managed code programming model for Windows®. It combines the power of the .NET Framework version 2.0 with new technologies for building applications that have visually compelling user experiences, seamless communication across technology boundaries, and the ability to support a wide range of business processes. These new technologies are Windows Presentation Foundation, Windows Communication Foundation, Windows Workflow Foundation, and Windows CardSpace. The .NET Framework 3.0 is included as part of the Windows Vista™ operating system."

Wednesday, November 08, 2006 1:13:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, November 01, 2006

The dreaded "External component has thrown an exception" has a tendency to be quite unfriendly.  To assist in its unfriendliness, it doesn't report any of the goings-on that caused the error in the first place.

Examining the stack track may point you in the right direction but it usually doesn't really pin it down.

For instance, you may have an ASP.NET user control (.ascx) that you've created, but attempting to host it on a page and rendering it may result in the NSFE (Not-So-Friendly-Exception) without any indication as to what may have gone wrong.  Your exception stack may indicate something to the effect that the error occurred in the TemplateParser.GetParserCacheItemInternal method, preceded by BaseCompiler.ThrowIfCompilerErrors.  Still, not too enticing.

Such was the situation presented to me today by an application on which I was working.

Generally speaking the "External component has thrown an exception" error is usually indicative of an unresolvable error on your .aspx or .ascx page.  A missing function reference, for instance, where a control has a method name wired up to an event but the method has been since removed from the code behind.

In my case I had a custom server control that I had created embedded on my .ascx and I had double, triple, fourple checked that the correct .dll was on the server.

After some dabbling and experimenting, I discovered that it wasn't the presence of the server control that was bombing the site (because it would render), but rather the presence of some particular properties.  All of these properties have something in common - their data type: Color.

I thought maybe it was some odd color deserialization or type conversion but I ruled that one out pretty quickly.  This was working on 3-4 other test environments, why was it failing here?

As it turns out, the answer dawned on me after about 15 minutes of banging my head.  Due to the nature of this particular product, and how it's being hosted, I had removed all assemblies in the web.config's <compilation /> node and added back in the ones I had explicitly needed heretofore.  The assembly in which the Color type is defined (System.Drawing), however, was not among them.  Simply adding it back in fixed the problem.

<compilation defaultLanguage="c#" debug="false">
   <assemblies>
      <clear />
      <add ... />
      <add assembly="System.Drawing, Version=..." />
   </assemblies>
</compilation>

Word to the wise, if you're getting the NSFE "External component has thrown an exception" and you've double-checked that the page is indeed valid, verify your references!

Wednesday, November 01, 2006 9:47:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 25, 2006

I had the distinct privilege of presenting on Refactoring at the Utah County .NET User Group tonight.  We had a great turnout and I'm fairly pleased with the presentation as a whole - I feel it went quite smoothly.  In past months the group had met at UVSC, but they're now in the process of moving to another location: NuSkin in Provo.  We met at their office off of EastBay.

The atmosphere was fantastic and there was great feedback and I believe that it worked out being beneficial for all in attendance (myself included).

I gleaned some talking points from an MSDN WebCast on Refactoring (as well as some demonstrations), but I believe it was all for the better.  Additionally, I used the Movie Rentals example from Martin Fowler's book to drive home the points made throughout the talk.

I've made the files (the PPT, the 'before refactoring' code, and 'after refactoring' code) available for download if you'd like to play with them.

Thanks to all that attended and for your feedback.  I look forward to the next time I have the opportunity!

Wednesday, October 25, 2006 2:53:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, October 24, 2006

Please join me tomorrow evening (Wednesday, October 25th, 2006) at 6:00 PM with the Utah County .NET User Group.  I have the privilege of speaking and sharing my thoughts on Refactoring.

In the event that you've been to the UCNUG meetings in the past, be forewarned that the meeting location has moved.  No longer at UVSC, the meetings will now be hosted by NuSkin (which makes the drive a little longer for me) at

NuSkin Network Operations Center (NOC)
1175 South 350 East
Provo, Utah 84606

Note, this is NOT the large building in downtown Provo but is rather in EastBay.  You can get to it by going to the south most Universty Avenue exit in Provo.

Hope to see you there!

Tuesday, October 24, 2006 3:03:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, October 23, 2006

This might be useful to someone out there.  I had had this issue many months ago, but had to resolve it again today because I hadn't jotted down the solution - fortunately it only took about 2 minutes.

I am making use of the FolderBrowserDialog class in the .NET 2.0 Framework.  This class, as it's name implies, provides a graphical interface for browsing and selecting folders.

The issue was that when i made my call to .ShowDialog(), the dialog would appear but it was blank - only the 'Make New Folder', 'OK', and 'Cancel' buttons along with the description were visible, but the folder TreeView was not.  This would have worked from the get-go had a created my project as a Windows Forms project but I didn't; it's a Console application.

To solve the problem, I simply had to add the [STAThread()] to my Main() method - now it works like a charm.

[STAThread()]
public static void Main() {
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);
   Application.Run(new TestForm());
}

// ...then on the TestForm class

private void btnBrowseForFolder_Click(object sender, EventArgs e) {
   string folderPath = browseForFolder();
   // do something with the result
}

private string browseForFolder() {
   using ( FolderBrowserDialog dlg = new FolderBrowserDialog() ) {
      dlg.Description = "Select a folder";
      dlg.ShowNewFolderButton = true;
      dlg.RootFolder = Environment.SpecialFolder.MyComputer;
      return ( DialogResult.OK == dlg.ShowDialog(this) )
         ? dlg.SelectedPath
         : null;
   }
}

Monday, October 23, 2006 5:50:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [8]  |  Trackback
 Sunday, October 22, 2006

I was reminded at the Code Camp that I hadn't posted a little application that I wrote to facilitate the creation and testing of regular expressions.  There are many tools out there that are downloadable that provide this functionality, but I found it easier to talk about regular expressions from an instructional standpoint with my own tool and demonstrations.

I therefore set out to create such an application and it's freely downloadable here.  Essentially, the program allows you to create regular expressions and test them against some text/file of your choosing.  You can also save off a collection of regular expressions to file so that you can create your own library of expressions.  If you attended the User Group meeting you'll find that I've enhanced the UI slightly to make it more useful and instructional.

NOTE: The download is the source code and requires the .NET Framework 2.0 to work properly.

Enjoy!

Sunday, October 22, 2006 8:29:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

For those interested, I've cleaned up and posted the code that I created that Utah Code Camp 2006 as a download right off of the blog.  Feel free to get it and enjoy!  I had a good time creating it.

So there you go!

Sunday, October 22, 2006 3:29:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

At the code camp yesterday I was approached with a PInvoke question on how to handle a cross-unmanaged/managed function callback.  Back in my Win32 days (which is now some years on the past of doing native Win32 programming on a daily basis) this was common place, but I've not had this need in the .NET world - though the need does exist, as demonstrated by this gentleman's circumstances.

I took the task of figuring it out right there and quickly came up with this example that illustrates the technique that I took.

This example uses the frequently illustrated Win32 function EnumChildWindows to enumerate the child windows of a given window (e.g. controls on a form).  Sure, you can accomplish this by simply enumerating the .Controls collection on the form, but that's not what we're illustrating here - we want to do it via Win32 and a function callback.

The EnumChildWindows function accepts as a parameter a function pointer which will be called-back by the Win32 function once for each window found.  Once the callback returns FALSE (i.e. zero) the enumeration stops.

Setting up this function in managed code is trivial:

[SuppressUnmanagedCodeSecurity()]
private static class NativeMethods {
   internal delegate int EnumChildProc(IntPtr hWnd, IntPtr lParam);

   [DllImport("user32.dll")]
   internal static extern int EnumChildWindows(
         IntPtr hWnd,
         [MarshalAs(UnmanagedType.FunctionPtr)] EnumChildProc fn,
         IntPtr lParam);
}

Notice that in order to make the callback work I have to decorate the function callback parameter with the [MarshalAsAttribute] designating that as a FunctionPtr.

To test this functionality I created a simple form, added some controls (including a ListBox and Button).  I then named the button btnEnum and the ListBox lstOutput.  Then I wrote a little code in the Click event handler to call the unmanaged function, providing it with a callback function (via my delegate instance).  The callback function resolves the hWnd (window handle) of the window to the control and outputs the control name to the list box as follows:

private void btnEnum_Click(object sender, EventArgs e) {
   lstOutput.Items.Clear();
   NativeMethods.EnumChildWindows(this.Handle, enumChildren, IntPtr.Zero);
}

private int enumChildren(IntPtr hWnd, IntPtr lParam) {
   Control ctrl = Control.FromChildHandle(hWnd);
   lstOutput.Items.Add(ctrl.Name);
   return 1;   // indicate that we want to continue enumerating until we reach the end
}

That's about it...there's not much to it :)

Sunday, October 22, 2006 2:53:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 11, 2006

I would be remiss were I to not post this announcement:

Join us on Saturday, October 21st for Utah Code Camp!

The local .NET Users Group and SQL Server Users Group is conducting a "Code Camp" for local software programmers next month at Neumont University.  The code camp follows the Code Camp manifesto that it is for the community by the community and always free.  We are looking forward to excellent sessions on lots of different topics.

The event is scheduled from 8:00 AM to 5:00 PM. The conference is free, however we request that you please register at www.msutahevents.com and enroll for the event so that we can get as accurate a head count as possible.

We will have sessions for both Developers and DBA's. We will also have a Sponsors area with lots of giveaways!

If you would like to speak or are interested in speaking, please email Pat Wright at pat_wright@sqlpass.org - visit www.msutahevents.com for more information.

Please register and join us for what should be an exciting and fun day!

Wednesday, October 11, 2006 1:43:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 04, 2006

The PropertyGrid control that is provided with Visual Studio, while not the most powerful of controls, provides some pretty awesome functionality.  In a nutshell it permits you to bind an object to it via a .SelectedObject property.  The PropertyGrid will then, via reflection, discover properties on your object and display them for your viewing/editing pleasure.  In doing so, you can provide users of your applications with a UX (user experience) similar to what you would receive within Visual Studio's design time.

Doing this binding is trivial and quite enjoyable actually.

The PropertyGrid also allows you to further extend the design experience with UITypeEditor-derived types.  These types can provide custom dropdowns or popup dialogs that give you the ability to offer the user a rich experience in setting property values.  Often times these are used when a property value is more complex than a simple value (e.g. collection management, graphical color selections, etc).  These, too, are a lot of fun and quite easy to create.

If a property on your bound object is another non-simple type (such as a Customer), the PropertyGrid won't know how to represent the data and will simply call .ToString() on the property, thereby returning the class name.  This is usually not the desired behavior.  Perhaps you want to display some more palatable text or a particular property of the object.  This can easily be accomplished through a mechanism known as a TypeConverter.

A TypeConverter-based class provides a consistent manner of converting an object of one type to another.  Via the TypeConverterAttribute class, you can designate the TypeConverter-type to use to represent your property as in the following example.  Suppose that a Customer object is a property on some other object (such as an Order) that is bound to the PropertyGrid.

namespace Devstone.Samples.Objects {

   public sealed class Order {
      private Customer _customer;

      public Customer Customer {
         get { return _customer; }
         set { _customer = value; }
      }
   }


   [TypeConverter(typeof(CustomerConverter)]
   public sealed class Customer {
      //... additional code here
   }


   internal sealed class CustomerConverter : TypeConverter {
      public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
         if ( destinationType == typeof(string) && value is Customer ) {
            // NOTE: we could simply cast value to Customer and return a string consisting
            // of property values if we chose to do so.
            // for simplicity sake, however, we'll return just some basic text.

            return "(Customer)";
         }
         else {
            return base.ConvertTo(context, culture, value, destinationType);
         }
      }
   }

}

Now, rather than 'Devstone.Samples.Objects.Customer' showing up as the value for the Customer property, we have the nicer '(Customer)' value.

This particular example is very straightforward and simple.  Tomorrow, I'll follow up with a more complex and robust example that takes the PropertyGrid and extends it further by dynamically creating properties on the fly that represent actual properties on the constituent objects.  This gives us the ability to view and edit hierarchical information directly within the PropertyGrid. :)  It's cool stuff.

Wednesday, October 04, 2006 6:13:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Tuesday, October 03, 2006

This is making the rounds lately and it seems to have many developers up in arms - and semi-understandably so.  Somasegar recently announced that while Visual Studio version 2005 (with SP1) would be supported on Windows Vista, Visual Studio 2002/2003 would NOT.  When I first read that it took me aback.  What?  How could they not support them?  I use both VS 2003 and VS 2005 on a daily basis and am planning on moving to Vista promptly.

First of all, let me be clear about this (and this seems to be something of a point of confusion):  Visual Studio 2002/2003 (the products) will not be supported on Windows Vista.  This does not mean that .NET 1.1 applications will not be supported - they WILL.  Even then, that doesn't mean that VS 2002/2003 won't work on Vista...they just won't be supported.

As you're no doubt aware, the Microsoft Windows team has done a tremendous amount of work around security and the locking down of the operating system for the Vista release, many of the changes impact the way that applications (particularly debuggers) work on the OS.

Scott Guthrie has the following to say (taken from Dennis van der Stelt's blog) :

The big technical challenge is with enabling scenarios like advanced debugging. Debuggers are incredibly invasive in a process, and so changes in how an OS handles memory layout can have big impacts on it. Vista did a lot of work in this release to tighten security and lock down process/memory usage - which is what is affecting both the VS debugger, as well as every other debugger out there. Since the VS debugger is particularly rich (multi-language, managed/native interop, COM + Jscript integration, etc) - it will need additional work to fully support all scenarios on Vista. That is also the reason we are releasing a special servicing release after VS 2005 SP1 specific to Vista - to make sure everything (and especially debugging and profiling) work in all scenarios. It is actually several man-months of work (we've had a team working on this for quite awhile). Note that the .NET 1.1 (and ASP.NET 1.1) is fully supported at runtime on Vista. VS 2003 will mostly work on Vista. What we are saying, though, is that there will be some scenarios where VS 2003 doesn't work (or work well) on Vista - hence the reason it isn't a supported scenario. Instead, we recommend using a VPC/VM image for VS 2003 development to ensure 100% compat. Hope this helps - even if the answer isn't entirely what we'd all like it to be, Scott

I've been considering making my primary development environment on a VPC anyway - maybe this is the way I'll have to go in the Vista timeframe.

Tuesday, October 03, 2006 3:54:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, September 24, 2006

In doing a little ASP.NET 2.0 databinding to a DropDownList control I was presented with a nice little error:

"'[ddlcontrolname]' has a SelectedValue with is invalid because it does not exist in the list of items. Parameter name: value"

Well, this is occurring on a postback for one DropDownList on a requery of a dependent DropDownList.  In other words, DDL01 performs an autopostback which forces DDL02 to requery its list.  The error is occurring because the SelectedValue that was originally bound to DDL02 is no longer in the set of data now associated with it.

You might try, as I did, to work around this by setting the SelectedValue property to null or string.Empty prior to rebinding, but that didn't seem to work for me.

There is a very easy solution to this dilemma, however.  In my case, all I had to do was clear the list, then set SelectedValue to null, then rebind as the following method does:

private void bindToList(DropDownList ddl, DataSet ds, string textFieldName, string valueFieldName) {
   // clear out any previous selection to avoid the ugly IndexOutOfRangeException
   ddl.Items.Clear();
   ddl.SelectedValue = null;

   // perform databinding as normal
   ddl.DataSource = ds;
   ddl.DataTextField = textFieldName;
   ddl.DataValueField = valueFieldName;
   ddl.DataBind();
}

Sunday, September 24, 2006 3:59:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, September 22, 2006

I recently created a small WebSite project in Visual Studio 2005, in part, to simply reaquire a feel for it.  In generally, my programming style and thinking falls more in line with the Web Application Projects which are akin to creating web projects in Visual Studio 2003.  The scope of this project is very small so I figured that it would be good to experiment with the WebSite project on a small scale...it seems to fit nicely.

That said, however, I have an ASPNETDB.mdf database that maintains membership for the website with a few canned users.  One thing that I do like in VS2005 is the "ASP.NET Configuration" option which launches a Cassini website for the administration of users, roles, permissions, etc that are maintained in the ASPNETDB.mdf database.

Once the database is moved over to the server, however, you lose the ability to run this management website...or so it may seem at first.

In fact, you have all of the management tools as part of the .NET 2.0 installation, but you have to wire it up.  So, if you want the nice ASP.NET Web Site Administration Tool on your server simply follow these steps:

  1. Create a new virtual directory that references %WINDIR%\Microsoft.NET\Framework\v2.0.50727\ASP.NETWebAdminFiles.  I called it ASP.NETWebAdminFiles.
  2. Make sure that the appopriate version of ASP.NET is used.  In this case, it should be .NET 2.0.
  3. On the directory security tab, disable anonymous access.
  4. Set the default document to default.aspx.

Then, to properly manage your website, you need to specify a couple of query string parameters: applicationPhysicalPath and applicationUrl.

For instance:

http://localhost/ASP.NETWebAdminFiles/default.aspx?applicationPhysicalPath=C:\TestWebsite\&applicationUrl=/

For each website, I set up a simple shortcut that provides quick access to the administration console.

Friday, September 22, 2006 10:27:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [12]  |  Trackback

Ok, maybe it's just that it's 4:30 AM and I need to get to sleep, but this is something that has bugged me for sometime, and it's just coming to a head right now.

I just don't get how VS2005's Publish Web Site option is all that helpful, especially on a second time around.  Let me explain.

I really like how simple it is to 'push' a website up to a server.  Simply enable FrontPage Extensions on the web site in IIS and viola, you can publish from VS2005.  Slick.  If you're publishing for the first time, it's all smooth and it pushes the entire website (content, databases, etc) as prescribed in your project in VS.

I would argue, however, that that's usually not the last time the website is touched.  Tweaks need to be made, new logic introduced, alternative site navigations added, etc.  In short, there's maintenance.  When you go to publish a second time around it will warn you with a message indicating that "Existing files in the destination location will be deleted.  Continue?".  While appreciate the warning, I think it's completely out of line.  The warning is, in fact, telling you the truth.  When you publish, ALL files, folders, etc in your target folder will be purged.  I think this is a bad thing in several ways.

In an effort to combat this work around this egregious behavior by excluding files from your project in an attempt to not upload them; this will, in fact, work - the files will not get uploaded, but they will disappear from the server all the same.

But the one thing that's really irking me right now is how it handles databases.

Suppose you're creating a website that uses SQL Server Express and have the customary ASPNETDB.mdf in the App_Data folder to maintain membership, profiles, roles, personalization settings, etc.  There's not a convenient mechanism to publish your website from Visual Studio because doing so will effectively destroy the target database, replacing it with the one from your development environment.

So what's the deal?  Why does it a) not give the option to skip the uploading of databases b) have to delete every stinkin' file?

Friday, September 22, 2006 9:46:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, September 07, 2006

By way of announcement, I thought I'd pass this along.  I'm very excited about it and plan on participating.

Utah Code Camp
October 21st 2006
Neumont University
Salt Lake City, UT
The local .NET Users Group and SQL Server Users Group is conducting a "Code Camp" for local software programmers next month at Neumont University.  The code camp follows the Code camp manifesto that it is for the community by the community and always free.  We are looking forward to excellent sessions on lots of different topics. 
 
The Saturday, October 21st event is scheduled from 8:00 AM to 5:00 PM.
The conference is free please register at
www.msutahevents.com.  We will have sessions for both Developers and DBA's. 
We will have a Sponsors area with lots of giveaways! 
If you would like to speak or are interested in speaking Please email Pat Wright at
pat_wright@sqlpass.org.  Or visit www.msutahevents.com  for more
information.

Thursday, September 07, 2006 5:59:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Over the past several months (years) I've given a series of presentations on the topic of Threading in .NET.  These presentations have been to various .NET User Groups, at company 'brown-bag events', or even over Live Meeting.  They have been a real treat for me as I love the topic and it's near and dear to my heart.  I came across this free e-Book (also available as a PDF) that I would highly recommend to anyone wanting to dig in and learn more about Threading in .NET.  The e-Book is written using the C# language, but that shouldn't be a hindrance to anyone wanting to understand the inner workings of the topic.

Enjoy!

Thursday, September 07, 2006 9:56:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, September 06, 2006

All in all, I must say that I am indeed a fan of the Web Application Projects (WAP) in VS 2005.  If you're familiar with web development in the VS 2003/.NET 1.1 world, then this provides a much more comfortable and familiar experience than the Web Site Projects (WSP) in VS 2005 out of the box.  I won't belabor the benefits of using them here as they have been covered extensively online.

I had created a WAP several months ago and came back to recently to update it and work on enhancing the functionality.  By way of tendencies, I like to design such that each class is an internal class (that is, not publically exposed) with internal constructors, methods, properties, etc.  As such, when I make a class public my intentions are clearer and the scope of the class is well defined.  Well, such a model doesn't work too well with WSP due to the myriad of assemblies created.  Today, however, in testing my application I was presented with an error message indicating that a class was “inaccessible due to its protection level”.  This warning indicates to me that I'm trying to use a class out of its defined scope (in this case - internal).

As it turns out my .aspx code-behind IS in the same project as the internal class, but the WAP was exhibiting behaviors reminiscent of the WSP.  In fact, the project would compile and I'd get .dlls generated in my \bin folder, but at runtime the .aspx pages were getting compiled into their own dlls.

The easy way to fix this (and I didn't know this wasn't here before) is to do the following:

  1. Right-click on the WAP in the solution explorer
  2. Select “Convert to Web Application”

That fixed up whatever was wrong with the project and now it works like a charm.

Wednesday, September 06, 2006 2:33:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, September 05, 2006
Borlard just released their Turbo Explorer for Windows.  I've been anxious to try out this free tool for a while now (ever since it was announced).  It enables you to compile Delphi, C++, Delphi.NET, and C#.  It seems pretty cool.  We'll see how it compares to Visual Studio 2005.  While I LOVE VS.NET, the 2005 version is something of a stack of cards.  I'd like to see this give it a run for its money.  Maybe that'll be the kick the pants that Microsoft needs to release some updates.
Tuesday, September 05, 2006 9:53:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, August 30, 2006

Jeffrey Palermo has assembled a great list of productivity features inherent in JetBrain's ReSharper (R#) that make it one of my all-time favorite tools.  I have a hard time remembering the myriad of keyboard shortcuts, but he mentions several that I use on a daily basis that make programming a joy.

It's a great reference for users of the tool and a great list of enticements for those not using it that want a boost to their programming environment.

Wednesday, August 30, 2006 9:20:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, August 22, 2006

I made a few updates to the Regex Assembly Builder tool that I posted a few days ago.  Most notably are the following updates:

  • NEW: Added a 48x48 256 color icon.
  • FIX: Opening Strong Name Key file returns access denied if the file is readonly.
  • FIX: Unable to specify assembly name identifier with period delimiters.
  • NEW: Application now strong named.  This is a breaking change.  All assemblies generated heretofore with the utility will not work with this application because of an embedded attribute.  This will not be an issue moving forward, but my apologies for the mistake.
  • NEW: Added ability to specify output path and to change the target directory the generated assembly.
  • UPD: Set font on the Zoom/Text Editor to be 'Consolas, 9pt'.  If you don't have Consolas, get it - you won't regret it - hands down one of the greatest monospaced fonts out there.
  • UPD: Set anchor on RegexEditor control to support resizing of TextBox controls.
  • UPD: Added application configuration file to support binding redirects when application is updated.

All in all, these updates were added smoothly.  Let me know if you have any issues with the update.

Feel free to download it here.

Coming features include:

  • Namespace view in TreeView to allow for a more natural navigation.
  • Alphabetical listing of regular expressions in TreeView.
  • (possible) Regular expression editor to allow for the creation of the regular expressions directly in the tool.  I may integrate with another tool if anyone has suggestions.

What features would you like to see in it?

Tuesday, August 22, 2006 8:54:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, August 18, 2006

Regular expressions provide a powerful means to harness the capabilities of text.  Deeply ingrained in the heart of .NET is a vast and powerful regular expression toolkit found in the System.Text.RegularExpressions namespace.  This is a well-known fact.  Many developers, myself included have been using them for years and years because they provide unparallelled control over interpreting, parsing, extracting, replacing, and manipulating strings.

I'm not going to bore anyone by further explaining the benefits of regular expressions nor will I go into explaining how they are constructed and how they work.  Every single developer, IMHO, should have on his desk the fantastic book “Mastering Regular Expressions” by Jeffrey Friedl.  It is a MUST read.  It's a MUST read many times.  I absolutely love it.  Alright, enough evangelism.

When creating regular expressions in .NET you have the opportunity to establish how the regular expression will be used to process your text by a RegexOptions enumeration.  By default, the regular expressions that you create are interpreted and evaluated at runtime.  There is nothing particularly wrong with that.  If your regex is used infrequently then that can be perfectly adequate.  However, suppose your regular expression needs to be utilized much more frequently.  It is quite possible that your application's performance may begin to degrade because of such frequent interpretation of the regex.

One of the RegexOptions enumeration values is Compiled.  When you apply the Compiled option to the regular expression you are telling the .NET runtime that you don't want the regex interpreted, but rather that you want it to be compiled.  Ultimately, this will usually have the benefit of being more efficient.  However, there is a cost.  The compilation happens at runtime.  When the execution path hit the regex flagged for compilation the .NET runtime will dynamically generate an assembly, load it, and execute it.  So there's an initial hit.

This might be fine and dandy...but it's going to happen once for every compiled regular expression.  In other words, if you have 25 compiled regular expressions in your code, you'll end up with 25 dynamically generated DLLs in memory.  That doesn't sound good to me.

The wonderful thing is you can take this hit up front entirely and generate a single assembly that contains as many pre-compiled regular expressions as you desire.  This is accomplished through code - there's no built-in IDE support.  The added benefit of a pre-compiled regex assembly is that you can fine tune your regular expressions out-of-band with your application.  You don't have to recompile your application(s) to get the updated regular expressions.  Instead, you reference your regex library and you have access to them.

This magic is accomplished via the Regex.CompileToAssembly() method.

Over the past couple of days I created a little application that I call the “Regular Expression Assembly Builder”.  This tool provides you with the ability to gather your regular expressions, organize them, and ultimately compile them into a DLL.  Essentially, with this tool you can:

  • Open and edit existing assemblies containing regular expressions (via Reflection).
  • Create brand new assemblies.
  • Organize your regular expressions into hierarchical namespaces.
  • Strongly name (even Delay Sign) your resulting assembly with a key file.
  • Apply several attributes to your target assembly.
  • ...and much more

Here are a few screen shots of the application in action:

  

The application plus source can be downloaded directly here or from the link on the main blog page.

Enjoy!

NOTE: I didn't know a tool like this existed already, but when I was just about done with the project I stumbled upon a similar, yet distinct, project that you might also be interested in.  It looks like Brian Delahunty took the initiative to create this well before I did.  Good job!

Friday, August 18, 2006 4:56:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Thursday, August 17, 2006

Microsoft released Visual Studio 2003 SP1 today.  If you're a daily user of VS 2003, as I am, you'll want to get this update.  I'm now waiting for Microsoft to release a Visual Studio 2005 service pack; VS2005 is much more of a house of cards.  That said, there are many quirks in VS 2003 that this fixes.

Thursday, August 17, 2006 4:17:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, August 16, 2006

I had the opportunity today to present over Live Meeting to a group of programmers all over the country for First Data Corporation on the topic of Threading in .NET.  While I use Live Meeting in some capacity almost every day (multiple times), I have never actually given a presentation over one.  As I tend to do, I had very few slides and almost all “on-the-fly” demos.  Until now, pretty much all of the presentations I've given have been live and in in-person.  It sure makes it easier to see reactions and demeanor.  Online presentations, are by their very nature, are more impersonal and without an audience present it's a little trickier.

Despite the unfamiliarity of the territory, I feel the presentation (though a little rushed due to time constraints) went pretty well and was well received.  I look forward to the opportunity of doing it again in the future.

Wednesday, August 16, 2006 9:46:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, August 10, 2006

This month, the monthly Utah .NET User Group meeting will be one week late (Thursday, August 17th, 2006).  These summer months have been a challenge with respect to scheduling and our speaker isn't in town today, but will be next week.

To quote from our email reminder that went out:

Matt Smith is a Senior Consultant with Microsoft Consulting Services in Salt Lake City and will be speaking to us on Domain Specific Languages (DSL) and DSL Tools. This is surely to be a very exciting and informative session as Matt has worked intimately with these tools for the past few years.

You definitely will NOT want to miss this awesome session. So show up, invite your friends, colleagues, parents, and anyone you know - it'll surely be a great time.

          Time: 6:00 PM
         
Date:
Thursday, August 17th, 2006
         
Place: Neumont, University (10701 South River Front Parkway, South Jordan, Utah)

See you at there!

This should be a fun meeting!

Thursday, August 10, 2006 3:12:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, July 26, 2006

I'm an avid fan of ReSharper by JetBrains (as many of you are very aware).  It wasn't until today, however, that I discovered that the team at JetBrains has an official JetBrains .NET Tools Blog where the various .NET products are discussed.  Fortunately, I'm not too far behind as the blog was only started on June 1, 2006.

What a great idea.  I'm subscribed!

Wednesday, July 26, 2006 8:14:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, July 12, 2006

We're very excited to have Aaron Skonnard back to the Utah .NET User Group for a second month of Biztalk.  This time, the focus is on Biztalk Orchestrations, whereas last month, he targeted an overview of Biztalk and Messaging, etc.

Therefore, you are encouraged to come and participate on July 13th, 2006 at 6:00 PM at Neumont University.  It'll be great fun.  Bring your buddies, find your friends, persuade your pals, convince your comrades and companions, venid con vuestros vecinos, and let's gather as a gang of geeks! - or is it gaggle of geeks :) ?

It'll be a blast.

Aaron's a great speaker - you won't want to miss it.

(Don't forget to join us afterwards to hang out, chill, and have a great time!)

Wednesday, July 12, 2006 5:01:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, July 10, 2006

I updated my download control a bit tonight - fixing (hopefully) a bug in the download process.  I've had many people download files from the site and not a single person has sent me the feedback that it wasn't working though all other indications are that it's failing.  For instance, a user attempts to download a file multiple times in a few minute span, etc.  It was working fine in my test environment but apparently it wasn't working correctly from the actual server.

When you request a download you get an email with a confirmation number and a direct link back to the download.  Well, I had an HTTP handler set to receive the request that consists of the download identifier along with the confirmation code.  However a bug (I'm presuming it's in IE) misinterprets the HTTP Headers in the response and it attempts to save the file with the useless name of the CONFIRMATIONCODE.dspx rather than the appropriate name (.dspx is the extension I use to identify my 'Devstone Server Page' handler).

My code resembles the following, where info is a class instance that identifies the requested download:

HttpResponse res = context.Response;
res.Cache.SetCacheability(HttpCacheability.Private);
res.ContentType = "application/download";
res.AppendHeader("Content-Disposition", "attachment; filename=" + info.FileName);
res.WriteFile(info.GetDirectServerPath());

Can anyone see anything blatently wrong with this code?  I've had it work many times (even occasionally from the production site), but for the most part it doesn't seem to work from a direct link - however, it does work with I perform a Response.Redirect() to the url.  I'll look into the differences to see what might be happening.

As a temporary patch to the problem, when you attempt to download a file, you're asked to provide your confirmation number.

I apologize for the inconvenience.

Monday, July 10, 2006 4:08:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, July 06, 2006

Well, my quiz from about 3 weeks ago didn't really elicit the response I was hoping.  However, for the benefit of those that have been wondering, I've decided to post my results.  As you may recall, the quiz was a seemingly simple one: to determine if a directory is empty (e.g. that it has no contents: files or folders).

Almost alarmingly, there is nothing built into the .NET Framework for such an operation.  One solution (which was proposed in the preceding post) is to evaluate the results of the Directory.GetDirectories() and Directory.GetFiles() methods.  Doing this, however, forces the runtime to enumerate all of the contents of the folder only to be discarded once we see determine that there were indeed files and folders.  There's no way (that I've seen) to call a Directory.IsEmpty() method or even a Directory.GetDirectoryCount() or Directory.GetFileCount() (though those might also force enumeration internally if they existed).

I have a solution that I propose as being better than the contrived example from the original post, though it is a little lengthier.  I haven't run any stringent analysis on its performance though I have to believe that it's faster than the method that relies on enumeration.  I also imagine that its performance will be substantially better over a network.

This solution relies on some built-into-the-Windows-kernel API calls to walk the contents of a directory (for there isn't any functionality there either to see if a directory is empty).  As soon as a file or folder is found it quits and indicates that the folder is non-empty.

private static bool isDirectoryEmpty(string directory) {
   IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
   NativeMethods.WIN32_FIND_DATA findData;
   IntPtr handle = IntPtr.Zero;
  
   try {
      handle = NativeMethods.FindFirstFile(@"
\\?\" + Path.Combine(directory, "*"), out findData);
      if ( INVALID_HANDLE_VALUE == handle ) {
         // call GetLastError() here to be explicit on the reason of the failure
         // for now, simply assume that the directory is not empty.

         return false;
      }
  
      // spin on the directory until a file/directory other than the current (.) or parent (..) is found
      // if something is found, quit immediately - we've determined that the folder is not empty
      do {
         if ( "." != findData.cFileName && ".." != findData.cFileName ) return false;
      } while ( NativeMethods.FindNextFile(handle, out findData) );
     
      // no file or directory was found, so return that it is indeed empty.
      return true;
   }
   finally {
      if ( IntPtr.Zero != handle )
         NativeMethods.FindClose(handle);
   }
}

This code makes use of a NativeMethods class which I define below.  It contains the system API calls that are used to walk the directory contents.

internal static class NativeMethods {
   public const int MAX_PATH = 260;
   public const int MAX_ALTERNATE = 14;

   [StructLayout(LayoutKind.Sequential)]
   public struct FILETIME {
      public uint dwLowDateTime;
      public uint dwHighDateTime;
   };

   [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
   public struct WIN32_FIND_DATA {
      public FileAttributes dwFileAttributes;
      public FILETIME ftCreationTime;
      public FILETIME ftLastAccessTime;
      public FILETIME ftLastWriteTime;
      public int nFileSizeHigh;
      public int nFileSizeLow;
      public int dwReserved0;
      public int dwReserved1;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)]
      public string cFileName;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)]
      public string cAlternate;
   }

   [DllImport("kernel32", CharSet=CharSet.Unicode)]
   public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

   [DllImport("kernel32", CharSet=CharSet.Unicode)]
   public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

   [DllImport("kernel32")]
   public static extern bool FindClose(IntPtr hFindFile);
}

NOTE:  Please let me know if I overlooked something obvious.  I've not done stringent analysis with this code but wanted to put it out there as a proposed solution for determining that a directory is empty in an efficient manner.

Thoughts?

Thursday, July 06, 2006 7:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, June 21, 2006

I was performing some routine debugging today and suddenly received a very strange error; one that I had not heretofore received:

Error while trying to run project: Could not load file or assembly 'TestApplication' or one of its dependencies. The module was expected to contain an assembly manifest.

When I attempted to run the application from Explorer I was greeted with the message:

C:\SomeBuildDirectory\TESTAP~2.EXE
The NTVDM CPU has encountered an illegal instruction.
CS:0e1b IP:0173 OP:65 63 74 69 6f Choose 'Close' to terminate the application.

Needless to day, I was a bit disconcerted.  I started to dig into to see what the problem was.

The very first thing I did was Google it.  The search yielded a page that seemed to fit the bill exactly.  A user by the name of PCarrier had experienced a very similar problem.  He had developed an application in C# and was getting the same result.  As it turns out, he had a virus.  Well, that was a possibility here, but I doubted it at first.  However, I decided to see for myself what was going on.

The next thing I did was investigate my Post-Build actions - this solution has several projects each of which has its own custom Post-Build action.  Nothing seemed out of place.

I then investigated the directory system.  I noticed was that in my \obj directory, the output .exe file was the proper 13K but in the \bin directory it was 6K.  The file was being truncated!

Therefore, I opened up FileMon and started monitoring for changes to that file and sure enough I saw some very erratic behavior.  It looked strange.  Several accesses to the file in the \obj folder were correct and they allocated the file properly but when it came time to write it to the \bin folder it looked like there was some ADS (Alternate Data Stream) stuff going on which was truncating the file to it's 6K size.

I started to suspect virus so I installed the AV software (yes, I didn't have any AV software running at this juncture) and ran a scan.  Nothing turned up.  I then ran the RootkitRevealer to see if anything cropped up there - nothing out of the ordinary.  Well, that was putting my mind at ease, but I was still facing the same problem.

Strangely, after doing the AV scan and Rootkit detection the ADS activity ceased.

I decided to retrace my steps to see if anything was amiss and started again to investigate the Post-Build actions and then (finally!) something stood out at me.  I had edited the macro to copy a shared configuration file and rather than copy the file to the target directory, I had mistakenly copied the configuration file to the target path (which includes the output .exe name)

The command I had was

copy $(SolutionDir)SharedConfiguration.config $(TargetPath)

And it should have been

copy $(SolutionDir)SharedConfiguration.config $(TargetDir)

Sometimes the little things get you.  I'm thankful that it was such a simple thing and that viruses didn't enter the picture, but I kick myself that I didn't suspect that the .exe file wasn't a valid one to begin with.  I should have opened the .exe in Notepad2 and the problem would have been clear from the start.

...but I can now rest at ease.

Wednesday, June 21, 2006 6:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Wednesday, June 14, 2006

Here's an interesting one.

I was futzing around today with the various classes in the System.IO namespace that deal with the file system (e.g. File, Directory, Path, FileInfo, etc).  Is it just me or does it seem like an omission to not have something like an .IsEmpty property?  Granted, this functionality doesn't exist even in the Windows API as such, but still...the .NET framework is all about making the task of developing software easier, more accessible.

Sure, there are a few strategies that one could employ to determine if a folder is indeed empty (none of which I like).  They usually involve getting the list of files and subdirectories and determining if there are none.  I don't really like these approaches because they involve getting the full list of files and folders first just to compare the count to zero (0).  For example:

private bool isDirectoryEmpty(string path) {
   string[] subDirs = Directory.GetDirectories(path);
   if ( 0 == subDirs.Length ) {
      string[] files = Directory.GetFiles(path);
      return ( 0 == files.Length );
   }
   return false;
}

Suppose the directory contained hundreds or thousands of files and/or directories?  A bunch of cycles are wasted simply enumerating all of the files only to discard the results.  Is there a better way using the .NET framework?

I propose there is and I have a better solution, but I'd like your thoughts and ideas.  I'll post my answer in another post.  (I'm hoping there's something built-in that I'm simply overlooking, but I'm not holding out for it)

(BTW: I really think you should be able to have code that resembles the following and be done with it.  I guess I could propose it to the .NET team (which I'll do).  If nothing else, we could wait until .NET 3.0 when we have extension methods at our disposal.)

if ( Directory.IsEmpty(@"C:\SomePath") ) {
   // happily proceed knowing that it was empty at the moment we called the method
   // ...but might not be at this moment :)
}

Wednesday, June 14, 2006 8:54:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, June 09, 2006

I was writing code all day yesterday, reorganizing some projects, refactoring code, etc, etc.  Lots of work - something I would be very loathe to have to repeat.  A lot of the code I had written over the last few weeks and hadn't yet checked into source control because it was still undergoing testing and analysis.

Upon shutting down Visual Studio it crashed.  More accurately, an Add-In crashed, bringing down VS.NET with it.  I really thought nothing of it...that is, until today when I opened my project.  Some of the key files I had been working on yesterday  were missing.  A little perturbed, I selected the 'Show All Files' option.  There they were so I reincorporated then into the project and I figured all was well.  I investigated the files and saw they were empty except for the basic, empty class definition - no code!  I started to sweat, but I figured that if it didn't save those files, I could recreate the refactorings from the files from which I extracted the code originally.  No dice!  I went back to the old files and saw that all the code had been deleted.  In other words, it saved my changed to the original files, but died before saving my changes to the new refactorings.  All of the code was gone.

Now I was a little more than sweating, you know that feeling when you're watching a scary movie and you just know that someone is about to jump out, but you don't know when or where?  Well, I was going through that.

My saving grace was that I hadn't recompiled (nor would I have been able to with the compiler errors it would have introduced).  I could open the library that I had built yesterday in Reflector and see all my source code.  Phew!  Granted, I've lost my XML comments (which were plentiful and abundant), but that's nothing compared to the code I would have lost.  I simply need to reincorporate the code, rename the variables, and I should be back in business.

Friday, June 09, 2006 5:59:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, June 07, 2006

We have the distinct honor this month to welcome Aaron Skonnard to the Utah .NET User Group.  We were all too happy to bump the meeting back a week (to the 15th) to accommodate his hectic schedule.  We hope this doesn't put undue contraints on everyone's schedules and that we have some great attendance.  It looks to be a fantastic meeting.

Aaron will be talking about Biztalk 2006 - something to which he's been devoting an inordinate amount of time :).  We invite all to attend.  Enjoy the meeting, the pizza, the comaraderie!

Time: 6:00 PM
Date: Thursday, June 15th, 2006
Place: Neumont University (10701 South River Front Parkway, South Jordan, Utah) (click for a map)

Wednesday, June 07, 2006 8:08:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, May 26, 2006

I had the privilege today of conducting a “Brownbag Session” over lunch at SelectHealth today.  The topic was Visual C#.  For the most part the team is new to .NET (and C# for that matter), though some in attendance did have some limited exposure.  We hit on several topics mostly revolving around language and syntax, but we talked about classes, partial classes, properties, generics, iterators, anonymous methods, delegates, and quite a bit more.

There's only so much you can do in a 1 hr block, but I think it went over pretty well and I hope they were happy with the results.  I was.  Thanks for the opportunity to present - as always, it's a great pleasure!

Friday, May 26, 2006 8:19:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, May 22, 2006

I'm developing a web service in ASP.NET 1.1 through which I need to transfer files.  There are a myriad ways in which I could accomplish this, but rather than roll my own, which I am sometimes inclined to do, I wanted to use some built-in functionality.  This functionality can be found in WSE 2.0 and DIME (Direct Internet Message Encapsulation) - thanks Fabio for reminding me.  Believe me, it pains me that I cannot use WSE 3.0 and MTOM with .NET 2.0, but such is not possible with this project - at least not yet.  But transferring the files would be orders of magnitude easier via MTOM, but alas, here we are.

Shortly after I set up the web service and tested it locally, I wanted to ensure that it would work remotely.  The machine to which I have it deployed is running on a Virtual PC running Windows Server 2003.  I could see the nice ASP.NET web service description page navigating via my browser to the .ASMX file, but when I attempted to access it via code, I was first presented with the following error message:

System.Net.WebException: The request failed with HTTP status 401: Unauthorized.
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
...

Oh, that's right, I need to include some credentials to access this particular web service.  Therefore, I added the following snippet of code below my client proxy declaration:

ServiceWse svc = new ServiceWse();
svc.Credentials = CredentialCache.DefaultCredentials;

Once I did that and ran my test harness I was greeted with a slightly more inocuous error:

Microsoft.Web.Services2.Security.SecurityFault: An error was discovered processing the <Security> header ---> System.Exception: Creation time in the timestamp can not be in the future.
--- End of inner exception stack trace ---
at Microsoft.Web.Services2.Security.Utility.Timestamp.CheckValid()
at Microsoft.Web.Services2.Security.Utility.Timestamp.LoadXml(XmlElement element)
at Microsoft.Web.Services2.Security.Utility.Timestamp..ctor(XmlElement element)
at Microsoft.Web.Services2.Security.Security.LoadXml(XmlElement element)
at Microsoft.Web.Services2.Security.SecurityInputFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services2.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services2.InputStream.GetRawContent()
at Microsoft.Web.Services2.InputStream.get_Length()
at System.Xml.XmlScanner..ctor(TextReader reader, XmlNameTable ntable)
at System.Xml.XmlTextReader..ctor(String url, TextReader input, XmlNameTable nt)
at System.Xml.XmlTextReader..ctor(TextReader input)
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
...

As it turns out, WSE will utilize a timestamp to verify security tokens between the SOAP sender and receiver and if the two computers' clocks vary by more that the default tolerance of five minutes, it will fail.

One solution would be to synchronize the client to the server, but that's a pain and may not properly work in a global scale which would be required by my particular application.

This baffled me for a few minutes because the time on my local PC and the time on my VPC were the same; that is, they appeared the same.  I quickly saw that the VPC's time, though the same with respect to hrs, minutes, and seconds is relative to Pacific Time whereas my laptop is on Mountain Time.  So they ultimately varied by 60 minutes, well over the default tolerance.

The solution is to alter the web.config file on the server and the client application's configuration file to include an override to the tolerance.  I set it to allow for up to a day (24 hrs) in difference to accommodate for any discrepancy that may arise.

<configuration>
   <configSections>
      <section name="microsoft.web.services2" type="Microsoft.Web.Services2.Configuration.WebServicesConfiguration, Microsoft.Web.Services2, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   </configSections>
   <microsoft.web.services2>
      <security>
         <timeToleranceInSeconds>86400</timeToleranceInSeconds>
      </security>
   </microsoft.web.services2>
</configuration>

I'm actually very happy that I had this situation arise now rather than after I had deployed because I now remember that I have to test for time zone differences.

Monday, May 22, 2006 9:29:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback

Resharper 2.0 was released by JetBrains today!  I have been anxiously awaiting this release since November of last year at the debut of VS 2005.  Being an avid fan of Resharper 1.0 - 1.5.1 for VS 2003, I was very excited about the prospect of getting similar yet advanced functionality in both VS 2003 and VS 2005.  Well, today (finally) the wait is over.

I've since downloaded them (both versions) and am in the process of installing.  The VS 2003 installation went smoothly and without a hitch, but the VS 2005 is taking a LOOOONG time during the application of some VS.NET hotfixes.  Anyone else have similar issues?  It's not like the sitting there idly, though the installer does indeed look stalled - it's not providing any feedback whatsoever.  I'm using FileMon and Process Explorer to watch the installation and it is indeed working, it's just taking forever and the UI doesn't give any feedback.

Monday, May 22, 2006 4:33:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, May 16, 2006

If you're creating a Web Service and you run across an error that resembles this:

Server Error in '/MyVirtualDirectory' Application.

Parser Error
...
Parser Error Message: Could not create type 'xxx.MyWebService'.

Don't worry, it's not that difficult to fix.

First of all, make sure your website is indeed within a Virtual Directory running the proper version of ASP.NET.  This can be ascertained in IIS.

Barring that, make sure you have deployed your website's DLLs into the proper \bin directory.  I ran into this error because my virtual directory is actually housed within another virtual directory/site that had it's own assembly references and I had removed them in my web.config like this:

<compilation defaultLanguage="c#" debug="false">
   <assemblies>
      <clear />
   </assemblies>
</compilation>

Once you do that, you effectively lose reference to your own DLLs.  Simply add yours back, along with the others you need (Web Services will require System.Xml and System.Web.Services).

Your web.config file may resemble this for ASP.NET 1.1:

<compilation defaultLanguage="c#" debug="false">
   <assemblies>
      <clear />
      <add assembly="System.Xml, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null" />
      <add assembly="System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, Custom=null" />
      <add assembly="My web service dll here" />
   </assemblies>
</compilation>

 

Tuesday, May 16, 2006 3:11:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Wednesday, May 10, 2006

I have the opportunity later on tonight (now that it's past midnight I can say that) of presenting at the Utah .NET User Group.  This presentation will be the third in a series focused on component development.  The preceding two talks have focused on control development, specifically Windows Forms Controls and ASP.NET controls.

This go-around, however, we'll be talking about Windows Services and the SCM.

If you're in the neighborhood, please come on by and enjoy free pizza and a fun presentation.  Hope to see you there!

Date: 05/11/2006
Time: 6:00 PM
Place: Neumont University, Suite 300 (10701 South River Front Parkway, South Jordan, Utah)

Wednesday, May 10, 2006 5:27:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, May 08, 2006

Microsoft released today the Visual Studio 2005 Web Application Projects for Visual Studio 2005 (obviously).  I've been running on the betas and RC1 of the product since it came out and I've just upgraded to the release.  If you like the VS 2003 / ASP 1.x programming model but would like to take advantage of the 2.0 Framework and the coolness of Master/Content pages and much more, check it out!

http://msdn.microsoft.com/asp.net/reference/infrastructure/wap/default.aspx
Monday, May 08, 2006 12:47:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, September 21, 2005

[EDIT: 07/10/2006 - Fixed stupid spelling error]

I created a very simply control the last night that might come in handy to someone.  If you've ever had the need to have a series of images cycle at some interval on your webpage then this might be useful.  The script to create such functionality on your site is quite trivial (though perhaps it gets slightly more redundant and complicated if you need multiple, distinct, rotating images on your page simultaneously, each with a varying interval between switching images).  However, I wanted to have an ASP.NET control that I could just drop on a page, set up its sequence of images, have it work, and call it good.

Well, I've created a control called ImageRotator that provides this functionality.  Simply add the control library (dll) your website's \bin directory, create the little markup needed, and you're up and running.  The .zip file contains the full source (which really isn't much at all) and the 'release-build' bits for both .NET 1.1 and 2.0, along with a readme tutorial on how to use it.

By way of example, in order to set it up on your page, all you have to do is this:

<%@ Register tagPrefix="devstone" namespace="Devstone.Web.WebControls" Assembly="ImageRotator" %>

<devstone:imagerotator id=imgRotator runat=server interval=3000>
   <devstone:imageref imageurl="~/images/image001.jpg" />
   <devstone:imageref imageurl="~/images/image002.jpg" />
   <devstone:imageref imageurl="~/images/image003.jpg" />
</devstone:imagerotator>

Pretty simple, but all it does is help solve a simple problem.

You can download the .zip file here.

Wednesday, September 21, 2005 8:36:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [23]  |  Trackback
 Saturday, February 19, 2005

[Updated 06/22/2006 - fixed typo in code]

Lately, I've been debugging an issue in an application that I wrote that was a bit frustrating, though until today I've pretty much ignored the error.  I decided to bite the bullet and dig in and try to figure out the problem.

I have an application that creates some datagram (UDP) endpoints using the Socket class found within the System.Net.Sockets namespace.  As it turned out, there were times during my application's execution path in which the sockets would suddenly stop responding, though the port was still open (this could be ascertained via netstat or TCPView).  To make the situation more perplexing, I had another application that used the exact same objects and it didn't exhibit the same behaviors - it continued to run as expected.

So I dug in a bit and within about 30 seconds figured out the problem - at least what was causing the problem:  the point at which the application always had this problem of stopping to respond it would always send a datagram packet to localhost (127.0.0.1) to determine if it could find a particular listener/host.  Well, if I had a listener at localhost there was never a problem, but if no socket was bound to that endpoint my UDP socket would throw a SocketException during the EndReceiveFrom().

I put together some tests and found that the WinSock error being thrown was WSAECONNRESET; in other words the socket was forcibly closed by the remote host.  This was disconcerting for a little while, but I found that if I simply called BeginReceiveFrom() again, it would be up and running - but this wasn't necessarily the right fix.

Microsoft made some changes to their WinSock implementation between Windows NT 4.0 and Windows 2000.  One of these was that the Recvfrom() function would return WSAECONNRESET instead of continuing to block or time out if the sendto() function resulted in an “ICMP port unreachable”.  In other words, if you sent a datagram to a listening port all was cosher and behaved normally, however, if you attempted to send a datagram to an endpoint without a listening application, a pending/blocking call to Recvfrom() would throw.  Previously, my SocketException handling code would simply return without attempting to re-listen, assuming that if the error were received the socket was closing down.

It is possible, fortunately, to achieve Windows NT 4.0 behavior rather than having the socket return an error result.  This is accomplished by calling the WSAIoctl() function in Ws2_32.dll.  Well, if you're writing code using the .NET framework, it's not necessary to dip down into P/Invoke to call the function, but rather you can use the IOControl() method on the Socket class.  The key lies in passing in the control code of SIO_UDP_CONNRESET and a value of False (0) to turn off the 'new', Windows 2000 behavior - thus restoring the expected Windows NT 4.0 behavior.

const uint SIO_UDP_CONNRESET = 0x9800000C;

It's interesting, and I feel unfortunate, that the IOControl() function accepts control codes of type Int32.  Many of the control codes are UInt32 values.  So instead of taking the default value for SIO_UDP_CONNRESET and passing that in (which we can't because it's out of the range for an Int32), we must take the same value and convert it to a signed value.

// 0x9800000C == 2440136844 (uint) == -1744830452 (int) == 0x9800000C
const int SIO_UDP_CONNRESET = -1744830452;
byte[] inValue = new byte[] { 0, 0, 0, 0 };     // == false
byte[] outValue = new byte[] { 0, 0, 0, 0 };    // initialize to 0
_socket.IOControl(SIO_UDP_CONNRESET, inValue, outValue);

Simply make that call during the initialization of the socket and the WSAECONNRESET error will not be raised when a datagram is sent to a closed/invalid end point.

Happy coding!

Saturday, February 19, 2005 5:22:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Thursday, December 02, 2004

This hit me pretty good today - it took about an hour and a half to solve.  The whole time I spent almost pulling my hair out...though I did have a glass of apple juice which whet the appetite nicely.

Ever since I removed myself from my local Administrators group, I've been wanting to test a particular website that I developed (and have been developing for some time).  The website uses impersonation, thereby allowing me to effectively run as a different user.  I didn't want, even for testing/development purposes, to have direct access to databases or other objects.  Rather I've wanted to write the software in an environment more like the one into which it will be deployed.

Well, because I removed myself from the Administrators group, I have had to run ASP.NET as a user other than ASPNET in order to debug the websites.  Of course, this was not a problem at all.  I simply encrypted my credentials via aspnet_setreg.exe and updated the machine.config file's to point to the registry as the source for its credentials.  All has been great - until today, well, yesterday, but that's a different story all together.

Upon attempting to run this website, I was greeted with the not-so-friendly-but-somewhat-informative message of “Access Denied to 'C:\...\Index.aspx'.  Failed to start monitoring file changes.” Therefore, I attempted to run the project from within Visual Studio, to see if I could further debug the problem and isolate any issues.  I got a different message all together: “Configuration Error.  Unable to load c:\windows\...\machine.config.  Either a required impersonation level was not provided, or the provided impersonation level is invalid.”

This seemed a simple, permissions settings.  I proceeded, therefore, to grant my impersonated user rights to the folders and files in question.  No dice.  Same message.  Hmmm, this was getting interesting - and a tad frustrating.

I tried granting the user Full Control at the root C:\ level to see if that took care of things - a big fat nope!

Interestingly, if I set the impersonated user to be my domain administrator it still didn't work.  It ONLY worked under my account.  Then I started to think - I probably don't have rights to impersonate.  When I was in the Administrators group I had that right by membership, but now that I'm a lowly User, that privilege had been stripped.

The solution?  Well, I opened my Local Security Policy (running it as the administrator), expanded Local Policies\User Rights Assignment and navigated to the 'Impersonate a client after authentication' policy setting.  Sure enough only three accounts by default were granted this right:  Administrators, ASPNET, and SERVICE.  I simply had to add my account (because that's the account the ASP worker process is running as) to the list.  Then I opened the command prompt and ran gpupdate /force to ensure that all policy changes took effect, then ran iisreset.

Now it works and I'm ready to ride off into the sunset and retire - at least for the evening.

Thursday, December 02, 2004 6:12:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [14]  |  Trackback
 Thursday, July 22, 2004

You ever have the need to validate whether the user entered a valid IP address?  Regular expressions come in really handy here.  However, if you do some searching online for a good regex to accomplish this, you'll undoubtedly come across some regular expressions that are pretty lousy or only half-baked.

When validating an IP address, we only want to accept values between 0.0.0.0 and 255.255.255.255.  I wrote the following function which seems to do the trick nicely:

[UPDATE: 09/28/2006]

As mentioned by a few of the readers, my initial regular expression was shortsighted and I overlooked a subset of numbers.  I've updated my solution to use a suggestion by AleXX below, which correctly validates an ip address from 1.0.0.0 to 255.255.255.255.

Thanks for the observations, guys, and the corrections.  I'll be more thorough in the future.

public bool IsValidIPAddress(string ipAddr) {
  
string pattern = @"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$";
  
Regex reg = new Regex(pattern, RegexOptions.Singleline | RegexOptions.ExplicitCapture);
  
return reg.IsMatch(ipAddr);
}

Thursday, July 22, 2004 7:06:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [12]  |  Trackback