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 meet