Thursday, August 26, 2004

In an ongoing effort to keep Visual Basic .NET within the mainstream, Microsoft is making some additions to the language that will definitely keep it viable moving forward.  I'm excited with this new direction.  Read on!

Thursday, August 26, 2004 7:09:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback

For all those “Star Wars“-philes out there, those that are dying to have Star Wars be a reality (aside from the prequel debacles), look no further.  It appears that the Death Star has been found near Yavin...er, Saturn.

http://www.engadget.com/entry/1884557355723102/

Thursday, August 26, 2004 6:50:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, August 25, 2004

I got my first chance to experiment with and quickly get frustrated with InstallShield X this past week.  I've never taken the time to sit down and write an installer, so it was all new to me.  To it's credit, InstallShield is a very powerful package and I am quite pleased with it.  My frustration stems from the steep learning curve.  I knew what I wanted to do, but couldn't figure out how to do it.  Trying to navigate the help is a daunting task.  Why don't companies write help that's helpful?  I had a very hard time figuring out how to accomplish what seems like a trivial task.

I started out creating a Basic MSI project and that got me off the ground and well immersed in the InstallShield world.  However, I quickly found that I was limited with what I could do.  I needed more power.  I therefore resorted to an InstallScript installer.  As soon as the new project opened up I felt more at ease...now this was stuff I could understand!

Ok, long story short (sorry!), I was creating an installer for a web component.  This component gets installed within a existing virtual directory on the user's machine.  I needed to enumerate the websites and virtual directories.  I quickly came to a screeching halt however when I discovered that InstallShield has no mechanism that supports enumeration.  Sure, there's built in support for a few looping constructs (e.g. for..endfor, repeat..until, while..endwhile) but no foreach support.  No way to take a collection and walk the contents.

Fortunately, the all-seeing, all-anticipating wizards at InstallShield foresaw the need for extensibility and allow you to import and dynamically load Win32 dlls and call native functions.  I therefore began the arduous task of creating a C++ dll that provided entrypoints that emulate the foreach loop to which we've become so accustomed in C# and VB.  I received some inspiration from some articles and things I found online - here's my C++ code:

#include "stdafx.h"
#include <windows.h>
#include <comdef.h>
#include <atlbase.h>

#define CHECKHRESULT( expr ) if ( FAILED(expr) ) return expr;

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
   return TRUE;
}

long __stdcall ForEachStart(VARIANT *pCol, VARIANT *pVar)
{
   if ( VT_DISPATCH != pCol->vt || 0 == pCol->pdispVal ) return E_INVALIDARG;

   try
   {
      CComVariant     result;
      DISPPARAMS      disparams = {0};
      UINT            argErr;
      pCol->pdispVal->Invoke(DISPID_NEWENUM, IID_NULL, 0, DISPATCH_PROPERTYGET, &disparams, &result, NULL, &argErr);

      return result.Detach(pVar);
   }
   catch(...)
   {
      return E_UNEXPECTED;
   }
}

long __stdcall ForEachNext(VARIANT *pEnum, VARIANT *pVar)
{
   try
   {
      if ( !pEnum ) return E_NOINTERFACE;
      if ( pEnum->vt != VT_UNKNOWN || pEnum->punkVal == NULL ) return E_INVALIDARG;

      CComQIPtr<IEnumVARIANT> pEnumVariant = pEnum->punkVal;

      ULONG         celtFetched;
      CComVariant   v;

      if ( S_FALSE == pEnumVariant->Next(1, &v, &celtFetched) ) return E_FAIL;
      if ( VT_DISPATCH != v.vt ) CHECKHRESULT(v.ChangeType(VT_DISPATCH));

      return v.Detach(pVar);
   }
   catch(...)
   {
      return E_UNEXPECTED;
   }
}

Then, all I had to do was add the new dll to the installer support files and then in my InstallScript load it up.  Here's my InstallScript that I wrote to enumerate the websites on the current machine.  This is not a complete example as my custom dialog loads up websites and filters virtual directories based on website selection, but this should be enough to whet your appetite.  If you want my full code, just let me know and I'd be happy to share. (Of course I'd expect full credit if you use it in your own stuff ;-)

#define ERROR_SUCCESS   0

prototype loadForEach();
prototype unloadForEach();
prototype ForEach.ForEachStart(byref OBJECT, byref VARIANT);
prototype ForEach.ForEachNext(byref VARIANT, byref OBJECT);
prototype LIST getWebsiteList();

function loadForEach()
   STRING dllPath;
begin
   dllPath = SUPPORTDIR ^ "ForEach.dll";

   if ( 0 != UseDLL(dllPath) ) then
      MessageBox("Failed to load dependency file; cancelling installation.", SEVERE);
      abort;
   endif;
end;

function unloadForEach()
   STRING dllPath;
begin
   dllPath = SUPPORTDIR ^ "ForEach.dll";
   UnUseDLL(dllPath);
end;


function LIST getWebsiteList()
   OBJECT w3Svc, site;
   VARIANT varEnum;
   LIST ret;
begin
   // create the list to be returned
   ret = ListCreate(STRINGLIST);
 
   loadForEach();
   set w3Svc = CoGetObject("IIS://localhost/W3SVC", "");
   if ( IsObject(w3Svc) ) then
      ForEachStart(w3Svc, varEnum);
      while ( ERROR_SUCCESS = ForEachNext(varEnum, site) )
         if ( "IIsWebServer" = site.Class ) then
            ListAddString(ret, site.ServerComment, AFTER);
         endif;
      endwhile;
      set varEnum = NOTHING;
   endif;
   unloadForEach();
   return ret;
end;

All in all I'm pretty pleased with this code, but it did take some time to get used to it; it's kind of a mix between VB and C.

My biggest question is this: How can a tool as powerful and widely used as InstallShield make it to version 10 (I'm assuming that's what the 'X' means) and not have any support for enumeration?  I'm glad they have support for extensibility and native Win32 calls, but c'mon, this is ridiculous!

Wednesday, August 25, 2004 7:19:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback

I tasked myself the other day to generate a list of websites and their contained virtual directories on my local machine.  This data could be used in a variety of circumstances (from installers to administrative tools).  Anyway, this is the .vbs that I came up with as a test harness:

Call DisplayTree(GetObject("IIS://LOCALHOST/W3SVC"), 0, "")

Sub DisplayTree(obj, depth, path)
   If ( "IIsWebServer" = obj.Class ) Then
      WScript.Echo Space(depth * 3) & path & obj.Name & " - " & obj.ServerComment & " (" & obj.Class & ")"
   ElseIf ( "IIsWebVirtualDir" = obj.Class OR "IIsWebService" = obj.Class ) Then
      WScript.Echo Space(depth * 3) & path & obj.Name & " (" & obj.Class & ")"
   End If

   Select Case obj.Class
      Case "IIsWebService", "IIsWebServer", "IIsWebVirtualDir", "IIsWebDirectory"
         Dim currObj
         For Each currObj in obj
            Call DisplayTree(currObj, depth + 1, path + obj.Name + "/")
         Next currObj
   End Select
End Sub

Simply copy the text and paste it into a new .vbs file (e.g. EnumVirtualDir.vbs) and run it from the command prompt: cscript EnumVirtualDir.vbs

Kinda handy!

Wednesday, August 25, 2004 6:51:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I'm a component developer.  Always have been.  I have a passion for creating reusable units of code - especially in the form of controls.  Back in the day, I was an avid ActiveX control developer and if I say so, some of my controls have been very successful and well received...particularly a dropdown ActiveX calendar control (more on that another day).

ASP.NET developers are (or should be) familiar with two types of controls: UserControl and WebControl.

UserControls are in essence HTML with code-behind that are referenced via a URL on the containing page.  They can be loaded dynamicaly (via Page.LoadControl(”...ascx”)).  A UserControl provides visual HTML reuse.  Perhaps some good examples of when to use a UserControl would be a custom link, a data entry form, or a link/menu bar.  I am probably not alone when I say that I think they're really cool.

WebControls, on the other hand, are quite a bit more complicated - but more powerful as well.  Unlike the UserControl, the WebControl doesn't have a nice, pretty HTML front-end.  All of the rendering of the control happens server-side in the WebControl's Render() method.  To create a WebControl basically all you need to do is derive from WebControl and override the Render() method and output your custom HTML as in the following example:

namespace Devstone.Web.TestControls {

   public sealed class MyWebControl : WebControl {
      protected override void Render(HtmlTextWriter output) {
         output.Write(“Custom control text“);
      }
   }

}

Then in order to use this control on a page you do the following on the .aspx page:

<%@ Register tagPrefix=“devstone“ Namespace=“Devstone.Web.TestControls“ Assembly=“Devstone.Web.TestControls“ %>
<body>
   <devstone:MyWebControl runat=server />
</body>

That's pretty slick...but you can do oh so much more than this.

If your control has properties (as most in fact do), you can reference the controls as child Xml elements to the tag on the aspx page or as attributes.  Personally, I prefer the attributes approach.  However, suppose you want to have a control that has child elements that you want to be child controls - not properties.  The trick is to add the ParseChildrenAttribute to your WebControl with a value of false.  When you do this, each element will be interpreted by a built-in parser and handed off to a control builder class that you designate with the ControlBuilderAttribute.  Upon parsing the element, ASP.NET will call into your WebControl's AddParsedSubObject() method, passing in the new control instance.

A word of caution, however.  The ASP.NET parser is kinda stupid (or maybe it's brilliant and I can't fathom it's logic).  It will call AddParsedSubObject(), passing in LiteralControl instances for each line between the child elements and you probably want to ignore those, only looking for your own custom tags.  Here's the updated control now:

[ParseChildren(false)]
[ControlBuilder(typeof(MyWebControlBuilder))]
public sealed class MyWebControl : WebControl {
   protected override void AddParsedSubObject(object obj) {
      // ignore all child controls except the ones we care about
      MyCompanyLink link = obj as MyCompanyLink;
      if ( null != link )
         this.Controls.Add(link);
   }

   protected override void Render(HtmlTextWriter output) {
      // render all child controls
      // this is kinda brain-dead, but it gets the point across
      foreach ( Control ctl in this.Controls )
         ctl.RenderControl(output);

      output.Write(“Custom control text“);
   }
}

internal sealed class MyWebControlBuilder : ControlBuilder {
   public override Type GetChildControlType(string tagName, IDictionary attributes) {
      switch ( tagName.ToUpper() ) {
         case “COMPANYLINK“:
            return typeof(MyCompanyLink);
         default:
            return null;
      }
   }
}

At this point, all I have to do is define the MyCompanyLink control, derive from a WebControl and override it's Render() method.  The .aspx page might look like this:

<devstone:MyWebControl runat=server>
   <companyLink />
</devstone:MyWebControl>

Granted, this is pretty simplistic.  What if we're creating a control that has more than one level of nested children controls?  An example of this might be a menu or a toolbar control.  Well, what we've talked about so far applies, but it's not enough.  By default the ASP.NET parser will only parse down one level for child controls.  If you go to the trouble of creating a child control such as a toolbar and add buttons, the buttons will be interpreted as properties of the toolbar.  Ok, so you can add the ParseChildrenAttribute to the child control, but it's ControlBuilder will never be invoked.

After a bit of digging I found the solution and it's pretty simple:  Add the PersistChildrenAttribute to the root object.

[ParseChildren(false)]
[PersistChildren(true)]
[ControlBuilder(typeof(MyWebControlBuilder))]
public sealed class MyWebControl : WebControl { ... }

Now, if you had, say a toolbar child control you could do the following to create a custom button arrangement:

<devstone:MyWebControl runat=server>
   <toolBar>
      <cut />
      <copy />
      <paste />
   </toolBar>
</devstone:MyWebControl>

I am, in fact, in the process of creating a control that does just this and when it's ready I'll post it up here for public consumption.  Enjoy!

Wednesday, August 25, 2004 6:32:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Tuesday, August 24, 2004

Every developer needs his (or her) bag of tricks, techniques, and strategies.  This is a trick that I've used for ages, but today I really needed it so I thought I'd share my thoughts.

If you've ever had the opportunity (or [dis]pleasure) of having to debug Javascript in Visual Studio you've probably had your share of frustrations.  Sure, there are documents out there that identify all of the settings you need to make to ensure proper debugging of client-side script, but I've not found them 100% reliable.  Sometimes, you can establish breakpoints in your code and step through it in a debugging session, while other times you cannot.  At this time I'm not going to go through all the nuances of the debugger, but instead point out a cool set of tips.

The one technique that I've never seen fail is the use of the debugger statement in your client-side Javascript.  The first thing you have to do is enable client-side debugging by unchecking the 'Disable script debugging' option in Internet Explorer.  Next, add the debugger; statement anywhere within your Javascript code.

If you run your ASP.NET application from within VS.NET, the debugger; statement will act as a breakpoint.  Otherwise, when IE hits the debugger; statement it will prompt you for a debugger to use (preferrably VS.NET) when the statement is hit at runtime.

Very cool!

Tuesday, August 24, 2004 12:46:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, August 18, 2004

Perhaps for my own benefit, I post the following.  Writing down (or typing) something usually helps me remember things, so hopefully this will serve as a reminder going forward.  I do this once in a blue moon, and everytime I have to go hunt it down and then for some oddball reason, I promptly forget it, promising myself I won't.

Every once in a while, Internet Explorer (6.0 in my case) seems to forget to display controls (such as buttons and dropdowns) using the current Windows theme - that is, with the nice sleek edges, rounded corners, and mouseover behavior.  I haven't nailed down what it is that is causing this behavior; I don't have any CSS styles that would otherwise alter the border-style of the elements or anything.  However, there is a META tag that we can use to force the issue.

By simply adding the following tag to the <head> section of the page you can ensure that themes will be (or will NOT be) supported on the page:

<meta http-equiv=”MSThemeCompatible" content="yes">

Wednesday, August 18, 2004 5:02:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, August 14, 2004

Talk about frustrating!  Over the past several weeks I've been working on a web application that exhibited very interesting behaviors.

After compiling it, I would then browse to my virtual directory and the very first time it ran I would get errors reported.  Then I would refresh the page and it would work - for a bit.  Then I would get the same errors reported.  I'd refresh a second time and from that point on it ran flawlessly.  Interestingly, these problems did not exist on a coworker's machines.  However, if I ran the application from Visual Studio .NET, it ran fine from the get go.

Needless to say, I started to dive it and try to figure it out.  I robustified my error logging mechanisms in my application so that more descriptive messages would be written to the event log.  In short order, this led me to see that when from IE my ASP.NET Session and Application were recycling.  This is why I was getting the errors...I was loading objects up into Session at the start and then, all of the sudden, the values were gone.  I found it interesting that when run from VS.NET, the session objects would remain even after the Session recycled!

I did a little digging and found a couple of articles (article1, article2) on support.microsoft.com that offered some hope.  Well, after lots (and lots) of experimentation, I found that the articles offered the correct solution.  My virus scanner was affecting the session.  After I performed a rebuild (and a subsequent deploy), the OS marked those files as modified which recycled the process (ok, I was expecting that one).  Then, the anti-virus software would scan the files, affecting them again, again recycling the process.  The solution offered by the MS articles (and others) indicated to turn off AV scans on my \WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files folder.  That didn't seem to fix the problem.  However, I turned off scanning on my \Dev folder and all was hunky dory!

Sometimes, what seem to be the simplest of issues...are!  Once you know what the solution is.  For the longest time I thought it was a threading issue, but my robustified error logging eliminated that possibility :)

Saturday, August 14, 2004 11:52:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, August 11, 2004

Here's a little technique that I created for reading values out of an application configuration file; be it an app.exe.config file or a web.config file, the strategy works the same.  While I am very appreciative of the ease that as .NET developers we have of getting up off the ground and reading values from an application configuration file (along the lines of what we did in unmanaged code with GetPrivateProfileString() for .ini files) I am frequently burdened by having to deal with the standard

<appSettings>
   <add key=“blah“ value=“blah, blah, blah“ />
</appSettings>

There's really nothing inherently wrong with this.  It's fast.  It's easy.  It's convenient.  It's easily readable.  It's easily retrievable:

string val = ConfigurationSettings.AppSettings[”blah”];

For the vast majority of applications this works out just fine.  That said, I'll point out that I'm kinda particular when it comes to my software - anyone who works with me would undoubtedly concur.  I'm a perfectionist when it comes to writing my code - sometimes to a fault.

The <add key=... /> tag just isn't all that descriptive and it makes your code cluttered with calls to ConfigurationSettings, breaking down the OO model to some extent.  If you want the OO feel, then this just doesn't cut it.  Sure, you can create a wrapper class that as it's properties are called, delegates the call on to the ConfigurationSettings object, but that, too, is clumsy as well as laborious to create.

This is where configuration sections enter the picture.  The .NET framework has carefully (and conveniently) provided a set of objects and interfaces that we can use to control the loading of configuration values.  Now, rather than the <add key=”blah” value=”abc” /> tag, we can use proper XML and say <blah>abc</blah>.  All we have to do is just a bit of wireup and we'll call it good.  In order to properly use custom configuration sections we have to make changes to the .config file as well as our application.

In order to create a custom section within the .config file, we must create what is known as a Configuration Section Handler.  A class that implements the IConfigurationSectionHandler interface will suffice.  This interface has a single method called Create() which is called by the .NET Framework when it's time to load the custom section.  It's up to us to parse the Xml contained within our custom node...but there's a cooler, fancier way than simply parsing the Xml as we'll see here shortly.

First, let's declare our custom configuration section and associate our IConfigurationSectionHandler to it:

<configuration>
   <configSections>
      <sectionGroup name="devstone">
         <section name="user" type="MyApp.MySectionHandler, MyApp" />
      </sectionGroup>
   </configSections>
</configuration>

The Xml code above declares that within the .config file there will be a group named devstone and within that group will be found a section (XmlNode) called user.  When loaded, the .NET Framework will automatically create an instance of the MyApp.MySectionHandler class found in the MyApp assembly.  The MySectionHandler class implements the IConfigurationSectionHandler interface.

Ok, this is pretty standard stuff, but let's get down to the code that actually reads the section and show some cool techniques.  The section handler code is all found within the IConfigurationSectionHandler.Create() method.  Reading the Xml from the file will be easy.  So easy, in fact, that we won't do it at all - directly.  In fact, we'll rely on the .NET Framework to do it for us.  The trick is to create an object that serializes out to the same Xml structure found in our configuration section.  But, how will the Framework know which object to create in order to deserialize our Xml section properly?  We'll embed the type name in the Xml node.

By doing this we can create a generic section handler that will simply read this type name from the node and deserialize the Xml to the designated object.  Here's the code for the new IConfigurationSectionHandler class:

public sealed class MySectionHandler : IConfigurationSectionHandler {

   object IConfigurationSectionHandler.Create(object parent, object configContext, XmlNode section) {
      // inspired by code by Marc Focas
      //
http://www.thecodeproject.com/csharp/ConfigSectionHandler.asp
     
      XPathNavigator nav = section.CreateNavigator();
      string objType = (string)nav.Evaluate("string(@type)");
      XmlSerializer ser = new XmlSerializer(Type.GetType(objType));
      return ser.Deserialize(new XmlNodeReader(section));
   }
  
}

That's all there is to it.  Now let's check out the custom section and its associated class.

<configuration>
   <configSections>...</configSections>
  
   <devstone>
      <user type="MyApp.UserSettings, MyApp">
         <name>Aaron</name>
         <title>Supreme Overlord</title>
      <user>
   </devstone>
</configuration>

The class that drives this is the Employee class found within the MyApp application:

[XmlRoot("user")]
public sealed class UserSettings {

   private string _name;
   private string _title;
  
   public Employee() {
      /* a public, parameterless constructor is required for serialization to work properly */
   }
  
   [XmlElement("name")]
   public string Name {
      get { return _name; }
      set { _name = value; }
   }
  
   [XmlElement("title")]
   public string Title {
      get { return _title; }
      set { _title = value; }
   }
  
}

Note the usage of the XmlRootAttribute and the XmlElementAttribute.  They are not strictly necessary, but it's important in this instance to use them because of the case.  Were I not to put the attributes on, the Xml would need to match case- and spelling-wise the properties and the class name.  I prefer camelCased Xml so I explicitly put the attributes on while maintaining the PascalCasing for my code.

Well, this is pretty slick!  Now comes the trick of loading the configuration section at runtime.  In order to achieve this, we can use the ConfigurationSettings.GetConfig() method.  But where do we perform the call?  And more importantly, where do we store the deserialized object?  Do we make it a global object?  Do we recreate it anytime we need access to a property?

I believe this is an excellent example of when a singleton object should be used.  At any given time we only have one set of settings for the application.  To achieve this, we'll modify the UserSettings class that we just created a bit:

[XmlRoot("user")]
public sealed class UserSettings {
   private static UserSettings _user = null;
  
   public static UserSettings Current {
      get {
         if ( null == _user ) {

            lock ( typeof(UserSettings) ) {
               if ( null == _user ) _user = ConfigurationSettings.GetConfig("devstone/user") as UserSettings;
            }
         }
         return _user;
      }
   }
  
   //...the rest of the code still goes here
}

Now, the section will be loaded the first time the UserSettings class is accessed.  The call to GetConfig("devstone/user") instructs the .NET Framework to load the devstone section group at the user section.  It will, in turn, and by virtue of the <configSections /> Xml block, load our MySectionHandler class which inspects the Xml, thereby deserializing our UserSettings class.  Now in order to retrieve the user name in the .config file we make the following call in our code:

string name = UserSettings.Current.Name;

That's a lot more attractive and much more object-oriented than:

string name = ConfigurationSettings.AppSettings["Name"];

A very powerful usage of this model is that of creating components.  It's a powerful paradigm to be able to drop your own company-specific sections in a .config file and have the classes necessary to read them.  This way, you won't have to go mucking up someone else's .config file and potentially clashing with their already extant names.  It's provides a much more professional feel to your application.

Enjoy!

Wednesday, August 11, 2004 4:29:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, August 06, 2004

I ran into something today that had me stumped for a little bit.  I'm developing a website that streams images down to the client.  These images originate from a SQL Server database and are context sensitive as well as user-definable.  I don't yet have my image caching implemented, but that doesn't really play into this discussion.

I have set up an HttpHandler mapped to my custom extension (.dsix) which gets intercepted by aspnet_isapi to be processed (can't wait for Whidbey in which we'll get handlers out of the box for the .asix extension - active server image files :-)  My HttpHandler resolves some HttpContext sensitive details about the request and then proceeds to acquire the image bits from the database.

Now before anyone jumps down my throat, these images are very small (6k-8k) and yes, I'm storing them in memory on the server.  I'm not concerned about this as a matter of fact because 1) my site has very few users (5-15) and 2) there is only 1 image per user in memory.  Should the site get to be more demanding I'll refactor and reevaluate the storage.

Ok, now to the issue...

I created a SqlDataReader object to read my values from the database and could successfully load the image from the database with the following code:

if ( !dr.IsDBNull(USERSTATE_PORTRAIT) ) {
  
long bufferLength = dr.GetBytes(USERSTATE_PORTRAIT, 0, null
, 0, 0);
  
byte[] buffer = new byte
[bufferLength];
   dr.GetBytes(USERSTATE_PORTRAIT, 0, buffer, 0, (int)bufferLength);

  
using ( MemoryStream ms = new MemoryStream(buffer) ) {
      _portrait = 
Bitmap.FromStream(ms) as Bitmap;
  
}
}

This information was later cached.  When it came time to render the image, I used the following code, where res is a reference to the HttpResponse:

user.Portrait.Save(res.OutputStream, user.Portrait.RawFormat);

I had stored a PNG file in the database.  Upon making the call to Image.Save, I would receive a System.Runtime.InteropServices.ExternalException.  The message was 'A generic error occurred in GDI+'.  Well thanks!  At least is wasn't a specific error!

I did a little digging and found an interesting article in Microsoft support about how Bitmaps need access to their originating stream, file, etc.  It turns out that GDI+ may arbitrarily choose to discard the memory occupied by a bitmap, only to rehydrate it when needed later.  For this reason, the source stream or file gets locked.  Aha! I wish I had understood that behavior several years ago when I really needed it.  Well, as it turns out, I don't still have the MemoryStream from which the image was created just hanging around when it comes time to render the image.  So what I have to do is make a copy of the image, and dispose of the original:

...
using ( MemoryStream ms = new MemoryStream(buffer) ) {
   // see Q814675 - PRB: Bitmap and Image Constructor Dependencies for details
  
Bitmap orig = Bitmap.FromStream(ms) as Bitmap;
   _portrait = getPortraitImage(orig);
   orig.Dispose();
}
...

private Bitmap getPortraitImage(Bitmap original) {
   Bitmap ret = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb);
   // copy the original image into the new one
  
using ( Graphics g = Graphics.FromImage(ret) ) {
      g.DrawImage(original, 0, 0);
   }
   return ret;
}

Now, in reality, my actual code is a little different than what I've shown - I've trimmed this down a bit for simplicity.  What you'd probably want to do that I've not shown is check the color depth of the original image and create an image of the same type rather than defaulting to a 24bit image.  Now the HttpHandler might look something like this:

user.Portrait.Save(res.OutputStream, ImageFormat.Jpeg);

Works like a champ!

Friday, August 06, 2004 5:53:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, August 04, 2004

Have you ever been working on a project and found yourself constantly copying and pasting code between files to maintain consistency (such as comment preambles, disclaimers, (even code snippets! - ok, I'd recommend against that one!) etc).  It can get quite tedious and error-prone, especially if what you're pasting has a bug, misprint, or something that needs to change universally.  If you've ever had occasion to, upon adding a new file to a VS project, go through and rearrange all of the code to fit your standards and add in your comments (and waste hours upon hours doing so), then creating a custom template is for you.  Using the proper jargon: creating a custom project item is for you.

Creating custom templates isn't an entirely trivial endeavor as you may know if you've tried it, but it's not overly complex.  It involves creating (or editing) a .vsz (Wizard) file, a .vsdir file, and customizing your template files.  This process involves navigating several directories and making tiny changes in each one.

The game changes a little if you're trying to add a WebForm to a web project, as I discovered while spending an hour churning over what seemed like it should have worked.  The reason for my frustration is that VS.NET will, while accepting my .aspx template code, generate its own .aspx.cs code.  In fact, it bases it's code on the C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\DesignerTemplates\1033\NewWebFormCode.cs.  I didn't want to go in and edit that file (except for making a few standards-abiding changes) because that would affect every page on every website that I develop, and that's too far-reaching for my tastes.

Here's what I did.  Note that all directories that I reference are found in C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\ as subdirectories.  Also, the example templates, er, project items that I created are for a website called DevTracker.

1)  Navigate to \CSharpProjectItems and create my wizard file called CSharpAddDevTrackerWebFormWiz.vsz.  The contents of this file:

VSWIZARD 7.0
Wizard=VsWizard.VsWizardEngine.7.1
Param="WIZARD_NAME = CSharpAddDevTrackerWebFormWiz"
Param="WIZARD_UI = FALSE"
Param="PROJECT_TYPE = CSPROJ"

The VsWizard.VsWizardEngine.7.1 is the ProgId (yes, back to COM) of the VS.NET-provided IDTWizard implementation - which saves me the trouble of having to create my own wizard - I'll just hijack the plane already in the runway as it gets me off the ground faster.  In fact, that's the recommended practice in this case.

2)  In the \CSharpProjectItems\WebProjectItems folder create a DevstoneWebProjectItems.vsdir file.  The contents of the .vsdir file:

..\CSharpAddDevTrackerWebFormWiz.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|DevTracker WebForm|1|A webform for the Devstone DevTracker Website|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|4534|0|DevTrackerWebForm.aspx

For a description on the format of the .vsdir files, check out this link.  Basically, each line in the file is pipe (|) delimited.  Suffice it to say, the first parameter references the .vsz file we created.  The second param (the GUID) identifies the C# extensibility library (also used later as an identifier for the dll so we can extract the same icon as used for WebForms).  The third parameter is the short description that appears along with the icon.  The fourth parameter is the ordinal position of the icon within the 'Add Project Item' dialog.  Then we have the long description, the GUID, the icon resource identifier, a zero ;-), and the default name to use for the to-be-added file.

3)  Navigate to the \CSharpProjectItems\WebProjectItems\UI folder and create a DevstoneUI.vsdir file.  I chose to do this rather than edit the extant ui.vsdir so I can place all of my custom project items therein and will be able to copy my directory tree over the C# folder and not run the risk of overwriting any files.  It's great that the VS.NET IDE automatically picks up all .vsdir files rather than looking for specific ones!  The contents of the .vsdir file:

..\..\CSharpAddDevTrackerWebFormWiz.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|DevTracker WebForm|1|A webform for the Devstone DevTracker Website|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|4534|0|DevTrackerWebForm.aspx

You'll probably see a lot of similarities to the previously created file in #2.

4)  Within the \VC#Wizards directory, create a subdirectory called CSharpAddDevTrackerWebFormWiz.  This name should match what you entered in the .vsz file in step #1 as the WIZARD_NAME.

5)  It is within this new folder that the script file and templates get created - well, in subfolders.  The easiest thing to do is to copy files that already exist (such as those from the CSharpAddWebFormWiz folder).  Then you should have \Scripts\1033 and \Templates\1033 folders with some files.

6)  Feel free to delete the Templates.inf file from \Templates\1033 - it's not needed.  In fact it will screw you up when overriding VS.NET's default behavior of auto-generating the code-behind files.

7)  Edit your WebForm1.aspx file as necessary (I renamed mine to DevTrackerWebForm1.aspx).

8)  Now it's time to create your code-behind file.  The best thing to do is to copy the NewWebFormCode.cs mentioned above and rename it (e.g. DevTrackerWebForm1.aspx.cs).  At this point you're not done.

9)  The \Scripts\1033\default.js file is the code that drives (sets up) the process of replacing [!output] tags in your source.  In order to override the code-behind behavior we have to edit this file.  That's because the default.js file provided for WebForms is written to handle just one file.  The underlying functionality behind all default.js files is actually found within the script \VC#Wizards\1033\common.js.  Fortunately for me :-D, I didn't have to scour this too much.  I found a great article by Chris Sells that identifies how to do what I've already talked about to some depth.  I took his default.js file and tweaked it to match my needs and I'm now ready to go.

All in all, it's not that bad, but it doesn't seem too well documented and can be a bit disconcerting to a newcomer to the template, dang!, project item arena.

Wednesday, August 04, 2004 7:01:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback