Imposter Syndrome Reconsidered

thomas

The term “imposter syndrome” is a meme that has captured the imagination of the technology sector – and for good reason; we are insecure people. The term may have made its way into the tech world in an illegitimate manner, however. This illegitimate use of the term is what I want to explore in this post, coupled with the rather obvious conclusion that “insecurity” is the better term in the vast majority of cases, despite its relative un-sexiness.

“Imposter syndrome” was first clinically described in 1978 in my adopted home city Atlanta, Georgia by Pauline Rose Clance and Suzanne Ament Imes in their paper The Impostor Phenomenon in High Achieving Women: Dynamics and Therapeutic Intervention. There are several salient features in the way it was originally understood which distinguish it from the way imposter syndrome is used today by software engineers.

9to5

First, the diagnosis was originally meant to describe the unique situation of professional women in a predominantly male-dominated professional world. In the 70’s we didn’t ask if men felt like phony’s, too. That would have been missing the point.

Second, the phenomenon covered successful women in particular – that is, women whose accomplishments were publicly recognized and not merely a matter of self-esteem. Today when we talk about  imposter syndrome, by contrast (for instance in Scott Hanselman’s famous post I’m a Phony), the point is always that unaccomplished people should not feel like imposters because even successful people like X, Y and Z feel this way. In the original article, obviously, the term could only be applied to X, Y and Z. Other people, male or female, who felt unearned confidence without visible accomplishments, simply were imposters.

chauncy

Third, imposter syndrome was a phenomenon tied to early family dynamics and requiring therapy to overcome. Today, no one says if you have imposter syndrome you should look for professional help to deal with it. Instead, recognizing the condition is meant to be in itself an instantaneous form of self-therapy, because if everyone has imposter syndrome then no one has imposter syndrome.

In 2019 imposter syndrome has moved well beyond its original carefully circumscribed bounds, to the point that 70% of professionals acknowledge feeling like imposters from time to time (this is an often cited statistic I have been completely unable to source). If this large number is meant to make us feel better about our own insecurities, it should nevertheless concern us that the people who run our banks, fill out our taxes, prescribe our medications, perform surgery on us, cook our food, fix our cars and run our government aren’t always sure they know what they are doing. I would question whether this is actually a good thing.

doingitwrong

Imposter syndrome has found a home in technical communities. There are several obvious reasons for this. The sort of people who go into tech tend to be beset by social anxiety. At the same time, the most common communication strategy used in technology is bombastic and overconfident – sometimes described as bro culture. These two things together will cause a frequent sense of inadequacy in the face of often meaningless processes like behavioral interviews, fadish architectural trends and cargo-cult adherence to agile processes. And if agile isn’t working for you, then you are doing it wrong, so you should feel inadequate about that, too.

elizabethholmes

At the same time, the software industry is a young industry with characteristics that make it susceptible to real imposters. It is a complex profession lacking recognized standards of education or certification. After all, even hair dressers have to be licensed. The guy who writes your banking software, on the other hand, doesn’t. For a set of peculiar linguistic social and linguistic reasons, however, we have no easy way for technologists and business people to talk to each other and judge the relative merits of different approaches to writing software. This leaves the unlicensed software developers in a position of needing to police themselves, with mixed success. In philosophy, this is generally known as the crisis of legitimation.

Another problem is that software is very important in our current world. A lot of time and money is determined by whether we make good or poor software solution decisions. In the case of medical software, lives are literally at stake.

fairies

A third characteristic compounded by the crisis of legitimation and the high stakes involved is a tendency to love shiny new technology. Whenever things don’t go well with a project, we gravitate toward unknown and untested new platforms, frameworks and processes to fix our problems. This creates an inverted success strategy where the technology industry prefers mysterious, poorly understood solutions over acquired experience.

All of this leads to lots of tech people normalizing the process of talking about things they do not know anything about. Whereas in established professions, people know not to speak when they have that vertiginous sense that they are out of your depth, in technology, if no one contradicts us we accept this as license to continue bullshitting. Fake it till you make it.

patterson-gibly

Because other people’s money and potentially lives are on the line, however, we should recognize that this is dangerous behavior.

Moreover, the notion that everyone has imposter syndrome fails to recognize that in previous generations, that sense of being out of one’s depth is how professionals discovered their weaknesses in order to move from journeymen to experts in their craft.

In fact, there is a whole genre of literature known as Bildungsroman dedicated to exploring this basic aspect of the transition to adulthood. Novels as diverse as Twain’s Huckleberry Finn, Dickens’s Great Expectations, Jane Austen’s Emma and Hurston’s Their Eyes Were Watching God and Salinger’s Catcher in the Rye cover this difficult journey from naivete and unearned confidence to nuanced understanding and expertise. The most common hallmark of his genre is the recognition that error, misspeaking and insecurity are milestones in our path towards self-mastery. Being insecure is a good thing, not a bad thing, because it drives us to be better.

_108622702_surgeonsphoto

Insecurity is normal and good. This feature of the human condition tends to be masked and smoothed over by terms like imposter syndrome which helps us to ignore these moments of doubt, which is a shame.

For this reason, I prefer that people just say they feel unconfident about an idea or approach. When someone tells me this, I can help them evaluate their idea and potentially refine it so we both learn something.

trump

When someone tells me they have imposter syndrome I don’t really know what I’m supposed to do. They obviously want me to say that everyone has imposter syndrome and so they should believe in themselves. But when the stakes are as high as they are in software engineering, this feels like a cop out. 

If you are feeling like an imposter in tech, maybe that’s something worth exploring more deeply.

A User’s Guide to the terms VR, AR, MR and XR – with a tangent about pork

IMG_0083

Virtual reality, augmented reality, mixed reality and XR (or xR) are terms floating around that seem to describe the same things – but maybe not – and sometimes people get very angry if you use the terms incorrectly (or at least they say they do).

The difficulty is that these terms come from different sources and for different reasons, yet the mind naturally seeks to find order and logic in the world it confronts. A great English historical example is the way Anglo-Saxon words for animals have complementary Norman words for the cooked versions of those beasts: cow and beef (boeuf), pig and pork (porc), sheep and mutton (mouton). It is how the mind deals with a superfluity of words – we try to find a reason to keep them all.

vietnamese-pork-noodle

So as an experiment and a public service, here’s a guide to using these terms in a consistent way. My premise is that these terms are a part of natural language and describe real things rather than marketing terms meant to either boost products or boost personal agendas (such as the desire to be the person who coined a new term). Those constraints actually make it pretty easy to fit all these phrases into a common framework and uses grammar to enforce semantic distinctions:

1) Virtual reality is a noun for a 3D simulated reality that you move through by moving your body. A sense of space is an essential component of VR. VR includes 360 videos as well as immersive 3D games on devices like the Oculus Rift, HTC Vive and Microsoft Immersive headsets.

2) Augmented reality is a noun for an experience that combines digital objects and the real world, typically by overlaying digital content on top of a video of a real world (e.g. Pokémon Go) or by overlaying digital content on top of a transparent display (e.g. HoloLens, Meta, Magic Leap, Daqri).

3) Mixed reality is an adjective that modifies nouns in order to describe both virtual and augmented reality experiences. For instance:

a. A mixed reality headset enables virtual reality to be experienced.

b. The Magic Leap device will let us have mixed reality experiences.

4) xR is an umbrella term for the nouns virtual reality and augmented reality. You use xR generically when you are talking about broad trends or ambiguously when you are talking in a way that includes both VR and AR (for instance, I went to an event about xR where different MR experiences were on display). xR may, optionally, also cover AI and ML (aren’t they the same thing?).

This isn’t necessarily how anyone has consistently used these terms in 2017, but I feel like there is a trend towards these usages. I’m going to try to use them in this way in 2018 and see how it goes.

Atlanta .NET User Group

I will be presenting on “Working with new ASP.NET features in .NET Framework 3.5 Service Pack 1” at the Atlanta .NET User Group on Monday, October 27th.  Magenic will be providing refreshments, as usual.   The meeting will begin at 6:00 PM at Microsoft’s Offices in Alpharetta.  It’s a lot of material to pack into an hour long presentation, but I think I have a few good strategies for working with that.  The presentation will cover Dynamic Data, Entity Framework, Data Services, the Silverlight Media Control, the Ajax browser history feature built into the Script Manager, and Script Combining.  That gives me about 10 minutes per technology.  Whew.

Microsoft Corporation
1125 Sanctuary Pkwy.
Suite 300
Atlanta, GA 30004

Directions to Microsoft

Styling the Validator Callout Extender

The Validator Callout Extender is one of the controls included in the Ajax Control Toolkit.  It allows one to replace the normal appearance of an asp.net validation control with an attractive cutout.  For instance:

This is a required field.

A common problem concerns how to restyle the callout to give it a custom appearance.  The documentation for this control makes it clear that the default CSS class may be overridden in order to change the appearance of the callout. The Callout Extender has a CssClass property which may be set to a custom style.  Unfortunately, the documentation also includes an apparent error.  According to the documentation for the Validation Callout Extender:

If your CssClass does not provide values for any of those then it falls back to the default value.

This turns out not to be true.  Instead, one must override all the nested classes of the Callout Extender in order to get a reasonably good looking callout.  If any of the nested classes are not overridden, once a value has been assigned to the CssClass property, no style is applied to the nested style.

Consequently, in order to do something as simple as setting the back-color of the callout to Blue, the custom style will end up looking like this:

    <style type="text/css">
        .customCalloutStyle div, .customCalloutStyle td
        {
            border: solid 1px Black;
            background-color: Blue;
        }
        .customCalloutStyle .ajax__validatorcallout_popup_table
        {
            display: none;
            border: none;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_popup_table_row
        {
            vertical-align: top;
            height: 100%;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_cell
        {
            width: 20px;
            height: 100%;
            text-align: right;
            vertical-align: top;
            border: none;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_table
        {
            height: 100%;
            border: none;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_table_row
        {
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_arrow_cell
        {
            padding: 8px 0px 0px 0px;
            text-align: right;
            vertical-align: top;
            font-size: 1px;
            border: none;
            background-color: transparent;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_arrow_cell .ajax__validatorcallout_innerdiv
        {
            font-size: 1px;
            position: relative;
            left: 1px;
            border-bottom: none;
            border-right: none;
            border-left: none;
            width: 15px;
            background-color: transparent;
            padding: 0px;
        }
        .customCalloutStyle .ajax__validatorcallout_callout_arrow_cell .ajax__validatorcallout_innerdiv div
        {
            height: 1px;
            overflow: hidden;
            border-top: none;
            border-bottom: none;
            border-right: none;
            padding: 0px;
            margin-left: auto;
        }
        .customCalloutStyle .ajax__validatorcallout_error_message_cell
        {
            font-family: Verdana;
            font-size: 10px;
            padding: 5px;
            border-right: none;
            border-left: none;
            width: 100%;
        }
        .customCalloutStyle .ajax__validatorcallout_icon_cell
        {
            width: 20px;
            padding: 5px;
            border-right: none;
        }
        .customCalloutStyle .ajax__validatorcallout_close_button_cell
        {
            vertical-align: top;
            padding: 0px;
            text-align: right;
            border-left: none;
        }
        .customCalloutStyle .ajax__validatorcallout_close_button_cell .ajax__validatorcallout_innerdiv
        {
            border: none;
            text-align: center;
            width: 10px;
            padding: 2px;
            cursor: pointer;
        }
    </style>

This will give you the following change in appearance:

This is a required field.

In order to play with the nested classes in order to customize the look of the callout, it would be useful to know what the HTML for the Validator Callout actually looks like.  Unfortunately, the callout is generated using client script, making it impossible to simply peek at the source in order to figure out what the markup looks like.  After about an hour of reading through the behavior class for the Callout Extender, I was finally able to come up with this:

        <table id="_popupTable" cellpadding="0" cellspacing="0" border="0" class="customCalloutStyle">
            <tbody>
                <tr class="ajax__validatorcallout_popup_table_row">
                    <td class="ajax__validatorcallout_callout_cell">
                        <table width="200px" cellpadding="0" cellspacing="0" border="0" 
                            class="ajax__validatorcallout_callout_table">
                            <tbody>
                                <tr class="ajax__validatorcallout_callout_table_row">
                                    <td class="ajax__validatorcallout_callout_arrow_cell">
                                        <div class="ajax__validatorcallout_innerdiv">
                                            <div style="width: 14px">
                                            </div>
                                            <div style="width: 13px">
                                            </div>
                                            <div style="width: 12px">
                                            </div>
                                            <div style="width: 11px">
                                            </div>
                                            <div style="width: 10px">
                                            </div>
                                            <div style="width: 9px">
                                            </div>
                                            <div style="width: 8px">
                                            </div>
                                            <div style="width: 7px">
                                            </div>
                                            <div style="width: 6px">
                                            </div>
                                            <div style="width: 5px">
                                            </div>
                                            <div style="width: 4px">
                                            </div>
                                            <div style="width: 3px">
                                            </div>
                                            <div style="width: 2px">
                                            </div>
                                            <div style="width: 1px">
                                            </div>
                                        </div>
                                    </td>
                                    <td class="ajax__validatorcallout_icon_cell">
                                        <img alt="" border="0" src="alert-large.gif" />
                                    </td>
                                    <td class="ajax__validatorcallout_error_message_cell">
                                        This is a required field.
                                    </td>
                                    <td class="ajax__validatorcallout_close_button_cell">
                                        <div class="ajax__validatorcallout_innerdiv">
                                            <img alt="" border="0" src="close.gif" />
                                        </div>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
            </tbody>
        </table>
 

This probably isn’t completely correct, but it’s pretty close.  If you want to try to write your own custom style for the Validator Callout Extender, just copy the markup above, as well as the CSS class above that, into your favorite WYSIWYG editor and have at it.  At a minimum, you should be able to customize the colors either to match your site design (the default background color is Chiffon Yellow, after all), or to indicate different sorts of validation errors.

One question I’ve seen on the ASP.NET Forums concerns whether it is possible to switch the arrow that flies off the left side of the callout to the right.  Unfortunately, the arrow is actually coded into the HTML generated by the Validator Callout, and is not controlled by CSS styles.  It would be nice to see this as a feature of future releases of the Ajax Control Toolkit.

ASP.NET AJAX: Script Globalization without the ScriptManager

bigfoot

I just spent about four hours trying to solve a problem I’ll probably never actually encounter in the wild.  Someone posted on the asp.net forums about script globalization using ASP.NET AJAX.  This can be set up pretty easily by simply using the ScriptManager control and setting its EnableScriptGlobalization property to true.  ScriptManager takes care of importing the necessary scripts and handling all the underlying code required to make things work.

What the seeker on the forums wanted to know, however, was whether this could be accomplished without the ScriptManager.  In theory, all the ASP.NET AJAX framework scripts can be downloaded and used in a non-platform dependent manner.

Certain software problems are ubiquitous, and people tend to fall over themselves blogging and posting about them until Microsoft or some other vendor eventually either fixes the problem and it goes away.  On the other hand, there are problems in the software bestiary that are so rare that working on them is the programming equivalent of doing cryptozoology.

This was a juicy problem of that sort.  And I believe I have a solution. It basically involves redoing some of what the ScriptManager does automatically, but what the hey.

To support globalization in ecmascript, the ScriptManager basically sets a variable called __cultureInfo behind the scenes.  This is then used by various ASP.NET AJAX script methods to provide formatting information.  This is actually explained in the Microsoft documentation for the feature.

Behind the scenes, it seems clear, the ScriptManager is querying the CurrentUICulture of the current thread in order to determine the browser’s preferred language, and passing this to the __cultureInfo variable.  The trick, then, is to determine the format of the culture data passed to __cultureInfo.

Here I had to use reflection to discover that there is an internal ClientCultureInfo type in the Sys.Web.Extensions assembly.  It made sense that this was being serialized and passed to the __cultureInfo variable.  With some trial and error, I finally got the serialization correct.

To make this work, you will need to download the Ajax Framework Library, which is a collection of javascript files.  You will need to import the MicrosoftAjax.js file into your web project, and then reference it in your page, like this:

<script src=”MicrosoftAjax.js” type=”text/javascript”></script>

You will also need to create the ClientCultureInfo class for your project, and make sure that it is serializable.  I tried DataContractJsonSerializer class to do my serializing,  but had problem getting the DateTimeFormatInfo type to serialize correctly, so I finally opted to use the now obsolete JavaScriptSerializer.  Here’s what the class looks like:

    public class ClientCultureInfo

    {

 

        public string name;

        public DateTimeFormatInfo dateTimeFormat;

        public NumberFormatInfo numberFormat;

 

        public ClientCultureInfo(CultureInfo cultureInfo)

        {

            this.name = cultureInfo.Name;

            this.numberFormat = cultureInfo.NumberFormat;

            this.dateTimeFormat = cultureInfo.DateTimeFormat;

        }

 

        public static string SerializedCulture(ClientCultureInfo info)

        {

            JavaScriptSerializer js = new JavaScriptSerializer();

            return js.Serialize(info);

 

        }

 

    }

Now that we have the culture info formatted correctly, we need to be able to pass it to the client variable.  We’ll create a public property in the code-behind that is accessible from the client, and set its value in the Page.Load event handler:

 

        protected void Page_Load(object sender, EventArgs e)

        {

            if (!Page.IsPostBack)

            {

                CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentUICulture;

                ClientCultureInfo o = new ClientCultureInfo(cultureInfo);

                SerializedCulture = ClientCultureInfo.SerializedCulture(o);

            }

 

        }

 

        public string SerializedCulture

        {

            set

            {

                ViewState[“kultur”] = value;

            }

            get

            {

                if (ViewState[“kultur”] == null)

                    return string.Empty;

                else

                    return ViewState[“kultur”].ToString();

            }

        }

Finally, we  need to use this code-behind property to set __cultureInfo.  Just add the following script block to your markup in order to set the current culture:

 

<script type="text/javascript">
    // Get the name field of the CurrentCulture object   
    var __cultureInfo = '<%= this.SerializedCulture %>';
    Sys.CultureInfo.CurrentCulture = Sys.CultureInfo._parse(__cultureInfo);
</script>

 

I added this to the bottom of the page.  To test whether this works, you will need to set the language property of your browser (if you are using IE, like I am).  I set mine to French for testing.  Then append the following script block below the one setting the culture above:

<script type="text/javascript">
    var currentCultureInfoObj = Sys.CultureInfo.CurrentCulture;
    var d = new Date();
    alert("Current culture is " + currentCultureInfoObj.name 
    + " and today is " 
    + d.localeFormat('dddd, dd MMMM yyyy HH:mm:ss'));  
</script> 

 

If you encounter any problems, it is possible that the web.config file is overriding the culture information from the browser.  In that case, make sure the culture is set to “auto” in the config file, like this:

 

    <system.web>

        <globalization uiCulture=auto culture=auto />

    </system.web>

ASP.NET AJAX Server Controls and Extenders

Following the recent release of Visual Studio 2008, I scribed a new tutorial on ASP.NET AJAX Custom Controls using the new IDE.  I originally estimated two weeks for the project, but in order to put in everything I thought needed to be said about writing AJAX-enabled .NET controls, the effort took over six weeks.  Should you have any inclination to see it, you can find it here at www.codeproject.com.

The tutorial is built around the session timeout monitor I originally described at the Imaginative Universal: http://box5921.temp.domains/~imagipi2/SessionExpiredMonitorWithASPNETAJAX.aspx.  Since the code for the tutorial is intended primarily to be instructive, I still plan to write one more revision of the session timeout control, in order to streamline the functionality and try to incorporate the modal popup behavior from the AJAX Control Toolkit, which I will publish in the pages of this blog.

Session Expired Monitor with ASP.NET AJAX

timeout

code download

Sessions are a way of preserving information on a web site between page hits, allowing the programmer to emulate a stateful application when, in fact, web pages are not really stateful.  They are also one of the banes of web development, since sessions eventually timeout when there is no interaction between the user and the web app for a prolonged period of time.  In ASP.NET, this period has a default of 20 minutes, which is really hardly enough time to pick up a donut, refill one’s coffee, and chat with fellow workers before returning to one’s computer.  What this often means is that the user, upon returning to their computer and continuing work after a 20 minute break will find that all of the data entry they have been doing has been lost.  Worse, strange errors will begin to appear in his web browser if the loss of a session is not handled gracefully. 

The most common workaround is to increase the session grace period, called the session timeout.  This is set in your web.config file, and typically looks like this (the timeout period is measured in minutes):

  <system.web>
    <sessionState
      mode="InProc"
      cookieless="false"
      timeout="20"
     />
  </system.web>

A second way of handling this is to add extra code to an app that keeps the session state alive even if the user isn’t doing anything.

A third, and the most common, way is to provide code that redirects a user to a “session expired” page if they try to interact with a web page for which the session has timed-out.  This can be a bit awkward, however, since it is a passive solution that can cause the user some dismay as they hit a submit key only to be taken to a completely unexpected page.

This post deals with an active approach to the same problem.  When the user’s session has expired, it will generate a popup message in the user’s browser window letting him know that he has been inactive for too long.  Additionally it can redirect the browser to a new page with a warning message letting him know what happened.  The user still loses all of his work, of course, but at least this way he knows what happened when he returns to his desk following his coffee break.

This solution uses three tricks.  One is the event model for Master Pages in ASP.NET:  whenever a Content Page is refreshed, its OnLoad event is called,  along with the OnLoad events of the Master Page and any user controls hosted by either the Content Page or the Master Page (the actual order of these events is 1. controls on the Master Page, 2. controls in the Content Page, 3. the Master Page and finally 4. the Content Page).

The second trick is the way ASP.NET Extensions Timer control gets reset.  This is done simply by setting the interval to a new value.  Every time the interval is set to a new value, or even the same value, the countdown on the timer begins again.

The third trick is that the session timeout one sets in the web.config file can be read programmatically simply by querying a property of the Session object.

Putting all of this together, one can build a web user control that simply sits on a Master Page and knows when the user session is ready to expire.  It resets itself to the full timeout period any time a Content Page is refreshed.  when the session expires, the user control can raise an informative message, redirect to another page, or, potentially, simply extend the session timeout (not covered here, but easy to do if you are interested).

A user control to monitor the session timeout is included in the code sample linked at the top of this post.  Here is how you can build your own.

Session Timeout Monitor Recipe:

Ingredients:

  • One Master Page
  • One User Control
  • An Update Panel
  • An ASP.NET Ajax Extensions Timer
  • A Panel control
  • A Button control

1. Create a new User Control in your project. 

2. Drop an Update Panel on the User Control and set its mode property to “Always”.

3. Add an Extensions Timer (not to be confused with the Futures TimerControl) to your project, dropping it in the Update Panel.  Name it TimerTimeout.

Normally, this configuration of the timer control and a conditional update panel is used to refresh a portion of a web page on a regular schedule, for instance in order to create a self-updating clock display.  In this case, however, the timer and update panel are used simply to trigger a notification that the session has expired. 

4. In the User Control’s code behind, add the following lines to the OnLoad event:

        protected void Page_Load(object sender, EventArgs e)
        {
            int milliseconds = 60000;
            TimerTimeout.Interval = Session.Timeout * milliseconds;
        }

This event will be called any time a content page is refreshed.  Whenever this happens, the code inside the event handler resets the AJAX Extensions Timer control to the full session lifespan as set in the web config file, in effect making the timeout for the Timer match the timeout for the session.

Since the Timer control’s Interval property is measured in milliseconds, while the session.Timeout is measured in minutes, a conversion factor of sixty thousand must be used to translate one time period into the other.

To finish this notifier, the Timer’s Tick event needs to be handled.  The Tick event gets called when the Timer’s Interval finally runs out.  In this implementation, the Tick event can either generate a popup message or cause a page redirect.

5. Place a Panel inside the Update Panel.  Set its Visible property to false.

6. Write a simple message inside the Update Panel, such as “Your session has expired.”

7. Drop a Button inside the Panel.  This Button will be used to allow the user to hide the popup message.

8. Add a public property to the User Control called SessionExpiredRedirect:

        private string _sessionExpiredRedirect;

        public string SessionExpiredRedirect
        {
            get { return _sessionExpiredRedirect; }
            set { _sessionExpiredRedirect = value; }
        }

This will be used to set the web page to which the Timer will redirect the user upon session timeout.  If no value is set, a popup message will appear, instead.

9. Handle the Timer’s Tick event:

        protected void TimerTimout_Tick(object sender, EventArgs e)
        {

            if (!string.IsNullOrEmpty(SessionExpiredRedirect))
            {
                if (SessionExpiredRedirect.IndexOf("~")==0)
                    Response.Redirect(
                        VirtualPathUtility.ToAppRelative(
                        SessionExpiredRedirect));
                else
                    Response.Redirect(SessionExpiredRedirect);
            }
            else
                this.PanelTimeout.Visible = true;
        }

This handler checks to see if a value has been set for the SessionExpiredRedirect property.  If not, it makes the Panel control inside the Update Panel visible.

10. To make the Panel control truly popup, set its CssClass property to “timeoutMessage”.  Add the following css style to the page.

<style type="text/css">
.timoutMessage
{
    position:absolute;
    top:100px;
    left:200px;
    background-color:#F5F7F8;
    border-style:groove;
    border-color:Navy;
    padding:15px;
}
</style>

11. Finally, compile this User Control and drag it on to the Master Page.  In design mode, the user control will display a misleading exception message.  Just ignore it.

12. Make sure the Master Page includes an ASP.NET AJAX Script Manager component.

This completes the recipe.  Any Content Page in this project will now automatically include the timeout monitor you have built.  Cookies are optional: this recipe will work with a session managed either with cookies or in cookieless mode.  It will only work if the session mode is InProc.

Garnish with buttered radishes.  Serve at room temperature.

(Code snippets formatted using manoli.net.)

Interop Forms Toolkit 2.0 Tutorial

I originally wrote the prize-winning Interop Forms article below for code project.  The prize, an XBox 360 Elite system, was pretty sweet.  Even sweeter, however, was the nod I received from the Microsoft VB Team here: http://blogs.msdn.com/vbteam/archive/2007/06/01/so-what-does-lt-comclass-gt-actually-do.aspx and here: http://blogs.msdn.com/vbteam/archive/2007/06/04/interopforms-2-0-tip-1-font-property.aspx.

The feedback from the VB Team, along with some help from Mike Dooney, led me along the right path to rework my C# templates a bit (the toolkit comes with only VB.NET templates).  The differences between the VB and C# templates highlight the fact that although the two languages are often believed to do the same thing once code is compiled to IL, this is not really the case, and in fact, when it comes to COM, VB does a much better job.

To be specific, the VB compiler creates additional code in IL to make a VB class’s events and properties visible in VB6, while the C# compiler does not. I had written the original templates with the assumption that the two compilers would write similar IL — because of this faulty assumption, the events thrown in a C# UserControl were never received in VB6. This has been corrected in the updated C# templates by including in C# the extra attributes and interfaces required to make the events visible — interfaces which the VB compiler automatically creates for you in IL.

The C# templates linked below also automatically create these interfaces for you. When you add a new event to your UserControl, you will just need to be sure to add it to the appropriate interface, as well.  The naming convention for the interfaces (one for exposed events, one for exposed properties) is simply the name of the UserControl class preceded by two underscores and one underscore, respectively. It’s a little bit more manual labor than the VB templates require — but not too much more.

From the feedback I received at code project, it is apparent that while the toolkit is a big help to developers still maintaining VB6 applications, the big gain is for FoxPro developers (really a very solid development framework) who can now squeeze a little more out of their interfaces when they need to, thanks to the Toolkit.

 

Diagram

Why use the Interop Toolkit?

A few years ago, the enterprise architects at the company I worked for came up with a central login mechanism for all the company’s applications using web services. They even provided code samples in Java, C# and VB.NET for using their new component. It was intended as a language agnostic solution. When we asked the architects what we should do with the VB6 applications that we were still supporting, the architects were nonplussed. They first provided some esoteric white papers on using SOAP with VB6, then they suggested that we upgrade all of our VB6 apps to .NET, and finally they conceded that VB6 apps simply didn’t have a place in their solution.

Had Interop Forms Toolkit 2.0 been available back then, we could have come up with an integration in under an hour. We would have simply copied the sample code into a new .NET User Control, used the Interop Toolkit to wrap it up as an ActiveX control, and then consumed the control in all of our VB6 apps.

Interop Toolkit 2.0 was just released at the beginning of May. The original Interop Toolkit already allowed VB developers to use .NET Forms in their applications. This is still in Toolkit 2.0, and appears not to have changed much.

What makes Toolkit 2.0 standout is its support for using .NET User Controls as ActiveX controls in VB6.

ActiveXControlHelpers

According to Microsoft, the toolkit is intended as part of a migration strategy for upgrading VB6 applications to .NET piece by piece. I am not sure this is how it is likely to be used, however, or even if it necessarily ought to be used in this way.

Toolkit 2.0 makes most sense as a tool that allows VB6 developers to take advantage of .NET features without being forced onto an upgrade path. Most VB6 applications that are still around obviously meet certain needs very well. Why fix something that isn’t broken?

There are times, however, when you may want to leverage .NET features in your VB6 application. For a long time your only two choices were to upgrade the whole application to .NET, or to forego the nifty new features.

Toolkit 2.0 provides a third option. Simply add the .NET feature you need as a control.

This tutorial will lead you through 1. a mock application that implements the sort of technology we would have used to solve the problem outlined above. It will also cover 2. installing the Interop Toolkit, 3. provide a reference app that gives developers the ability to use real multithreading in their VB6 apps, and finally 4. provide a how-to for integrating XAML files into VB6.

Interop for C# developers

Just as with the previous version, Interop Forms Toolkit 2.0 is geared towards VB.NET developers. The wizard, project templates and item templates that come with the Toolkit only come in VB flavors. This makes a certain amount of sense, since it will mostly be VB developers who will implement these .NET/VB6 integrations. Many developers like to work with both languages, however, and there may be integration scenarios where you need to expose pre-existing C# code to VB6.

For those cases, I’ve written the C# item template and C# project template linked above for Interop User Controls. Simply copy the project template zip file into your project templates folder (the default location is ...\My Documents\Visual Studio 2005\Templates\ProjectTemplates\Visual C#) and the item template zip file into your item templates folder (...\My Documents\Visual Studio 2005\Templates\ItemTemplates\Visual C#). I believe that these templates will only work with Visual Studio 2005, but I havn’t yet tested on older versions of Visual Studio to make sure.

For cases where you need to expose a C# Form, you can use the clever wizard and template written by Leon Langleyben, which I was able to get to work with a bit of tweaking — through no fault of Leon’s, since his add-in was written for the previous version of the Interop Toolkit (refer to the CSXamlEmbeddedForm project in the included CSharp Samples to see what the generated wrapper class should look like in C#).

Installing the Toolkit

Installing the toolkit is fairly straightforward. Navigate to the Toolkit Download Site and, of the three downloads available, run the InteropFormsInstaller.msi file. In most cases, this is all you need to do. When you open the Visual Studio.NET IDE, you should find the new templates, VB6 Interop UserControl and VB6 Interop Form Library, available when you create a new VB.NET project. Under your tools menu, you should also find a new wizard labeled “Generate InteropForm Wrapper Classes”.

If the new wizard does not appear in your tools menu, there may have been a problem installing it. Check Tools | Add-in Manager to make sure that this wizard is selected. If it is present and selected in the Add-in Manager, but still does not appear in your tools menu, you can run the following command in your command line to reset it: Devenv /resetaddin Microsoft.InteropFormTools.InteropFormProxyGenerator.Connect.

Installing the Toolkit on Vista

In order to use the Toolkit on Windows Vista, you will need to download both the msi file as well as the setup file to your harddrive. Then run setup. Running the msi file alone will generate an install error.

To use the C# UserControl templates on Vista, you will need to run Visual Studio as an Administrator. Right-click on the link to your Visual Studio IDE and select the Run as administrator popup menu option. This will let Vista’s UAC feature know that it is alright for the UserControl to write to the registry on build events.

Building a User Control

In this first example, the UserControl will take care of all the processing, and will just sit in the VB6 Form, while the example following it will demonstrate how to pass information between VB6 and VB.NET. Any code snippets will be in VB, though the source code samples linked above include both a VB.NET as well as a C# sample of the control.

In this example we use the Daily Dilbert web service to download a cartoon to our control (you will notice that the Daily Dilbert comes up a lot on CodeProject — for good reason; it happens to be one of the few interesting web services that can actually be used in public tutorials, since it does not require a fee or registration).

Open new project

Begin by creating a new VB6 Interop User Control project using the VB6 Interop UserControl project template. Name the project DailyDilbertControl. By default, a UserControl file is created called InteropUserControl.vb. Since this is the control name that will be displayed in your VB6 control panel, you should rename it to DilbertService.vb, to be more descriptive.

In your new project, you will find the following files: ActiveXControlHelpers.vb, InteropUserControl.bmp, InteropUserControl.manifest. ActiveXControlHelpers.vb, as the name suggests, includes several static helper methods that make the conversion of your UserControl into an ActiveX control possible. There are register and unregister methods, which add details about your control to the registry. There are methods that help convert things like color codes between the .NET scheme and the OLE scheme used in VB6. There is also a method that wires up your events to work with VB6. You can have multiple user controls in your project, but should only have one ActiveXControlHelpers file per project.

The InteropUserControl.bmp is used to display your control in the VB6 Toolbox. I will cover how to customize your ActiveX control image later in this tutorial.

Add a new web reference to the Daily Dilbert web service to your project. To do this, right click on your project and select Add Web Reference… A new dialog will pop up. Enter http://www.esynaps.com/WebServices/DailyDiblert.asmx for the URL and click on Go. Finally, rename the web reference to “DDService” and select Add Reference at the lower right of the dialog. Your Solution Explorer should now look like this.

AddWebReference 

Add a 650 by 215 pixel PictureBox called DilbertPictureBox to your UserControl, as well as a button called RetrieveButton. Create a new RetrieveButton_Click event handler by double clicking on RetrieveButton. Paste in the following code, which will call the web service and retrieve today’s Dilbert strip.

    Private Sub RetrieveButton_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles RetrieveButton.Click
        Dim myDilbert As New DDService.DailyDilbert()
        Dim DilbertMemoryStream As _
        New System.IO.MemoryStream(myDilbert.DailyDilbertImage())
        With Me.DilbertPictureBox
            .Image = Image.FromStream(DilbertMemoryStream)
            .BorderStyle = BorderStyle.Fixed3D
        End With
    End Sub

To finish making your control visible from VB6, just press F5 to preview the control, or simply build it. It will look like this in your Visual Studio UserControl Test Container:

DilbertControlPreview 

And that’s all it takes to build build an ActiveX control in Visual Studio.NET.

Adding an ActiveX Control Image

When you build a new control, InteropUserControl.bmp will be used as the default image for your component in the VB6 Toolbar. You can always use a different image, though. For this project, I want to use this image of Dilbert.

Dilbert

To add it, you first have to add the bitmap file you want to use to your project and set its build property to “content”. Now open the InteropUserControl.rc resource script file with notepad. DO NOT use Visual Studio to do this, as this will mess up your resource script file. InteropUserControl.rc can be found under the DailyDilbertControl project folder. Beneath the default 101 BITMAP InteropUserControl.bmp entry, add an additional entry specifying the custom bitmap you want to use.

changeresourcescript 

Save your resource script file. Now open ActiveXHelpers.vb and find the RegisterControl method. This is where registry entries are created. In the section where the bitmap file is specified, replace the default entry, “101”, with a reference to your own bitmap.

replacebitmap

Now rebuild your control to make sure a new compiled resource file is created. The new image should appear in the VB6 Toolbox rather than the default image.

vb6toolbar

More about adding ActiveX Control Images

You can use a different image for each UserControl in your project, but you will have to modify the ActiveXControlHelpers.vb file a bit to make this work. Alter the RegisterControl method signature to take a second string parameter, and then pass this parameter to the line where the code specifies the resource id of the image.

    Public Sub RegisterControl(ByVal t As Type, ByVal BitmapId As String)

    '...
    
    'ToolBoxBitmap32
    Using bitmapKey As RegistryKey = subkey.CreateSubKey("ToolBoxBitmap32")
    bitmapKey.SetValue("", Assembly.GetExecutingAssembly.Location & ", "  _
    & BitmapId, RegistryValueKind.String)
    End Using
    
    '...
    
    End Sub

Then, in each UserControl, add the BitmapId you want to use for your control to the RegisterControl call.

    <EditorBrowsable(EditorBrowsableState.Never)> _
    <ComRegisterFunction()> _
    Private Shared Sub Register(ByVal t As Type)
        ComRegistration.RegisterControl(t, "102")
    End Sub

Rebuild the entire project once more.

Adding the UserControl to a VB6 project

DailyDilbertComponent 

This is actually the easiest part. Create a new VB6 project. Press CTRL+T to add a new component to your form, and check the DailyDilbertControl library. Press OK. (Vista behaves a bit strangely when you try to add your ActiveX control. It will occassionally throw an error the first time you select OK, then will work normally the second time you do so. Just to be safe, click Apply first just to see if there is an error, and then OK.) Any UserControls in your project will now appear on the VB Toolbar. Simply select the control you want to use and draw it onto your VB6 form. Press F5 to see your .NET UserControl run in a Visual Basic 6 application.

vb6form1 

Adding True Multithreading to VB6

When I was working with VB6 on a regular basis, one of the main impetuses for upgrading to .NET was the ability to implement multithreading. Interop UserControls provide an easy way to add true multithreading to a VB6 application. In a common scenario, you may want the users of your VB app to be able to kick off a process and then continue with their work while the processing occurs in the background. To simulate this scenario, the reference code we are about to build will use a BackgroundWorker control that will perform a time-consuming process in the background while updating a progress bar. In the meantime, the users of the VB6 application that consumes the control can continue with their work.

Create a new VB6 Interop UserControl project called MultithreadedControl. Add a BackgroundWorker control named BackgroundWorker1, a label called LabelWarningMessage and ProgressBar called ProgressBar1. Paste in the following code.

    Public Delegate Sub StartEventHandler(ByVal simpleEventText As String)
    Public Delegate Sub FinishAsyncEventHandler(ByVal asyncEventText As String)

    Public Event StartEvent As StartEventHandler
    Public Event FinishAsyncEvent As FinishAsyncEventHandler

    Public Sub StartProcessing()
        Try
            RaiseEvent StartEvent(".NET process starting")
            Me.BackgroundWorker1.RunWorkerAsync()
        Catch
        End Try
    End Sub

    Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.DoWorkEventArgs) _
    Handles BackgroundWorker1.DoWork
        'wait
        Static prog As Integer = 0
        While (prog < 100)
            System.Threading.Thread.Sleep(50)
            prog = prog + 2
            Me.BackgroundWorker1.ReportProgress(prog)
        End While
        prog = 0
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
    Handles BackgroundWorker1.ProgressChanged
        Me.LabelWarningMessage.ForegroundColor = Color.Red
        Me.LabelWarningMessage.Text = "Working in background..."
        Me.LabelWarningMessage.Visible = True
        Me.ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
    Handles BackgroundWorker1.RunWorkerCompleted
        Me.LabelWarningMessage.Visible = False
        RaiseEvent FinishAsyncEvent("Interop User Control process finished.")
    End Sub
    
    Private Sub BackgroundWorker_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
        Me.ProgressBar1.Value = 0
        Me.LabelWarningMessage.Visible = False
    End Sub

Finally, make sure that the BackgroundWorker events are hooked up to the handlers we’ve written.

backgroundworkerevents 

Also make certain that the BackgroundWorker’s WorkerReportsProgress property is set to true. Build the project.

Open a new VB6 project and add the MultithreadedControl component to your VB6 Form, as you did in the previous example. Also add a multiline TextBox control (Text1), a ListBox (List1), and a CommandButton labeled “Process” (Command1).

In order to receive events from the BackgroundWorker control, you will need to add an additional reference to the control. Click on the menu item Project | References… A reference to MultithreadedControlCtrl will already be checked off from previously adding it as a component. You will now need to also include a reference to the library MultithreadedControl in order to capture events thrown from the .NET Control.

MultithreadedControlReference 

Finally, paste in the following VB6 code. In this code, you declare a new reference to the control, this time decorated with the keyword “WithEvents” in order to expose the control’s events. Wiring up handlers for the events is based only on the names of the procedures, so you have to be careful when typing out the Sub routines that will be used in this way.

Before the underscore, always use the same name you used when you created the second reference (in this case “BackgroundEvents”). Then after the underscore, use the actual event name as it appears in your original .NET control. I’ve seen lots of problems posted to various message boards concerning problems with VB Interop event handling that basically came down to misspelling a handler’s signature — so be careful.

Dim WithEvents BackgroundEvents As MultithreadedControl.BackgroundWorker

Private Sub Command1_Click()
    Me.Text1.Text = ""
    Me.List1.Clear
    Me.List1.AddItem ("Start processing from VB6: " & DateTime.Now)
    Me.BackgroundWorker1.StartProcessing
End Sub

Private Sub Form_Load()
    Set BackgroundEvents = Me.BackgroundWorker1
End Sub

Private Sub BackgroundEvents_StartEvent(ByVal EventText As String)
    Me.List1.AddItem (EventText)
End Sub

Private Sub BackgroundEvents_FinishAsyncEvent(ByVal EventText As String)
    Me.List1.AddItem (EventText)
    Me.List1.AddItem ("Finish processing from VB6:" & DateTime.Now)
End Sub

This reference app basically demonstrates how a .NET BackgroundWorker can be used inside a VB6 application. Even while the processing is occurring, and updating the status bar to let us know how far it has gotten, the end-user can continue typing into the textbox. If you have never programmed in VB6, then this probably seems like a trivial accomplishment.

For those of us who have worked on Visual Basic 6 apps for large portions of our careers, it is a breakthrough.

VB6Multithreaded

Using XAML in VB6

You cannot build a XAML UserControl or a XAML Form and then consume it directly in VB6, unfortunately. You also cannot simply add a XAML Form to a Windows Application project and expose it that way. With a bit of finesse, what you can do is embed a XAML UserControl in a Windows Form and consume that in your VB6 apps. The following walkthrough will show you how.

Create a new VB6 InteropForm Library Project in Visual Studio, and call it XamlEmbeddedForm. Rename the default Windows Form to XamlForm. Now add a second project based on the .NET Framework 3.0 Custom Control Library (WPF) project template and call it XamlUserControl. You can add whatever XAML code you like, at this point. For the reference project, I’ve used the Cube Animation code found in the WPF SDK. Build the XamlUserControl project. In XamlEmbeddedForm, add a reference to the UserControl project. Also add the following four library references: PresentationCore, PresentationFramework, WindowsBase and WindowsFormsIntegration.

NewReferences 

You now will need to add some code to the FormLoad event in order to host the XAML UserControl in your .NET Form. The complete code behind should look like this:

Imports Microsoft.InteropFormTools
Imports System.Windows.Forms.Integration
Imports System.ComponentModel
Imports System.Windows.Forms

<InteropForm()> _
Public Class XamlForm

    Private Sub XamlForm_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) _
        Handles MyBase.Load
        ' Create the ElementHost control for hosting the
        ' WPF UserControl.
        Dim host As New ElementHost()
        host.Dock = DockStyle.Fill

        ' Create the WPF UserControl.
        Dim uc As New XamlUserControl.UserControl1()

        ' Assign the WPF UserControl to the ElementHost
        '  control's Child property.
        host.Child = uc

        ' Add the ElementHost control to the form's
        ' collection of child controls.
        Me.Controls.Add(host)
    End Sub
End Class

Rebuild your solution one more time for good measure. Then go to the Tools menu and select Generate InteropForm Wrapper Classes (if this menu option is missing, refer to the installation instructions above). This will add a new wrapper class to your project that can be exposed to VB6. Rebuild one last time to register your wrapper class in the registry. At this point, your .NET code is complete.

Open a new project in VB6. Open Project | References and check two items to add them to your VB6 project: Microsoft Interop Forms Toolkit Library as well as your .NET project, XAMLEmbeddedForm to add to COM wrapper for your .NET assembly to your VB6 project.

.NET Interop Forms have difficulty knowing when the host VB6 application starts and stops, so some extra code must be added to your VB6 application to handle this. Add the following code snippet to your VB6 Main Form so the .NET code is informed when these events occur.

Public g_InteropToolbox As InteropToolbox

Private Sub Form_Load()
    Set g_InteropToolbox = New InteropToolbox
    g_InteropToolbox.Initialize
    g_InteropToolbox.EventMessenger.RaiseApplicationStartedupEvent
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    g_InteropToolbox.EventMessenger.RaiseApplicationShutdownEvent
End Sub

We are nearly done. To open your .NET Form from VB6, just add a command button to your Main Form and handle its click event with the following code.

Private Sub Command1_Click()
Dim xaml As New XamlEmbeddedForm.XamlForm
    xaml.Show vbModal
End Sub

If everything goes right, when you click the button, you should see an animated cube, written all in XAML.

EmbeddedForm

Conclusion

This tutorial is intended to walk you through the steps needed to create a useful .NET/VB6 integration. It is also intended to give you a sense of the almost limitless possibilities that are open to VB6 developers now that this technology has been made widely available. Half a decade after people were foretelling the doom of VB6 as a development tool, we should come to terms with the idea that VB6 will still be with us for quite a while longer. Interop Toolkit 2.0 ensures that the many years left to VB6 development will be both graceful and productive.

Drinking with the Immortals

drinking_horn


There are various legends about drinking with the Immortals.  They typically involve a wanderer lost in the wilderness who is offered shelter by strange people.  He is brought close to the fire and given beer, or wine, or mead, depending on the provenance of the folktale.  As his clothes dry out, he is regaled by tales of ancient times and slowly comes to realize that his companions are not typical folk, but rather denizens from behind the veil.  He has fallen, through no merit of his own, into the midst of an enchanted world, and his deepest fear is not of the danger that is all around him, but rather that once the enchantment is disspelled, he will never be able to recover it again.


It occurred to me recently that I had such an experience about a year ago.  I was sent by my company to the Microsoft campus in Redmond to spend several days with the ASP.NET Team and other luminaries of the .NET world.


The names will mean nothing to most readers, but I had the opportunity to meet Bertrand LeRoy, Scott Guthrie, Eilon Lipton, and others to discuss the (then new) ASP.NET Ajax.  I had been painfully working through the technology for several months, and so found myself able to almost hold a conversation with these designers and developers.


On the final night of the event all the seminar attendees were taken to a local wine bar and had dinner.  As is my wont, I drank as much free wine as was poured into my glass, and began spinning computer yarns that became more and more disassociated from reality as the night wore on.  I’m sure I became rather boorish at some point, but the Microsoft developers listened politely, and in my own mind, of course, I was making brilliant conversation.


Even to those who know something of the people I was talking to, this might seem like no big deal.  I went drinking with colleagues in the same industry I am in — so what.  But for me, it was as if I were suddenly introduced to the people who make the rain that nourishes my fields and the sunlight that warms my days.  Microsoft software simply appears as if by magic out of Redmond, and like millions of others, day in and day out, I dutifully learn and use the new technologies that come out of the software giant.  To find out that there are actually people who design the various tools I use, and build them, and debug them — this is a bit difficult to conceive.


In A Room of One’s Own, Virginia Woolf reflects on Charles Lamb’s encounter with a dog-eared manuscript of one of Milton’s poems, filled with lines scratched out and re-written, words selected and words discarded:



“Lamb then came to Oxbridge perhaps a hundred years ago. Certainly he wrote an essay-the name escapes me-about the manuscript of one of Milton’s poems which he saw here. It was LYCIDAS perhaps, and Lamb wrote how it shocked him to think it possible that any word in LYCIDAS could have been different from what it is. To think of Milton changing the words in that poem seemed to him a sort of sacrilege.”


My own discovery that the things of this world which I consider most solid and most real — because they are so essential to my daily life — could have been otherwise than they are, was a similar moment of shock, tinged with fear. 


In a moment of anxiety during this sweet symposium, I leaned over to the person immediately to my right and confided in him my strange reflections.  He laughed gently, and dismissed my drunken observations about the contingent nature of reality.  I later found out he was the twenty-three year old developer of the ASP.NET login control, used daily in web applications around the world, when he inquired of me whether I had ever used his control, and what I thought of it.

ASP.NET AJAX 1.0 Released

It was over a year ago that I started working with a product called Microsoft Atlas.  I wanted to use it to build a rich web client for managing licensed commercial music for a cable television studio, based on the expectation that the client must work on multiple platforms (hence a web application) and at the same time provide rich functionality such as our sponsors were used to in their desktop applications (hence Ajax).  The whole time I was building it, I had the expectation that the final release was right around the corner.  In December of 2005, Atlas was rumored to have a release date sometime in the March or April range.  A year, a name change, and a few scope changes later, it has finally arrived.

The product is an example of Microsoft coming late to the party.  Based on a key bit of technology originally developed by Microsoft engineers over seven years ago, the XMLHttpRequest API, alternate vendors like Yahoo, Google and others helped to develop a style of programming called Ajax that allowed web clients to talk to a webserver without a page refresh.  Ajax in turn was adopted by advocates of the term Web 2.0 as one of the hallmarks of the phenomena they wished to tout in an attempt to revitalize interest in the web as a business platform following the disaster that we now all know as The IT Bubble of the 90’s.

Why Microsoft took so long to get around to it is an open question.  Very likely, they were busy getting on the web services band wagon as a way to promote Smart Clients as their technology of choice for integrating the desktop with the web.  While very cool in its own right, it hasn’t really achieved the same mindshare that Ajax has among web developers, and so — better late than never — we now have ASP.NET Ajax to kick around, and it can be downloaded here.

In the meantime, special recognition should be given, I think, to Brent Ashley, who in 2000 came out with something he called JavaScript Remote Scripting, which used javascript to generate dynamic iFrames in order to provide the same functionality that the XMLHttpRequest API does.  In an alternate universe, JSRS could have been the inspiration for Web 2.0.  Brent Ashley still supports his scripts here, a placeholder for his mark on the history of technology.