July 2008 Blog Posts

How Do You Find Interface Implementation Overrides Via Reflection?

I'm working on some custom FxCop rules and one that I want to do is to catch people who try to call Dispose() on objects deriving from System.ServiceModel.ClientBase<T> because they didn't implement IDisposable in a safe manner.

So you have ClientBase<T> which looks, in a very abbreviated fashion, like this:

public abstract class ClientBase<TChannel> : ICommunicationObject,
          IDisposable where TChannel : class
{
  // Other stuff... and then
  void System.IDisposable.Dispose()
  {
    this.Close();
  }
}

Later, I might have a class that derives from that. Maybe a special type of client, and I might implement my own safe IDisposable version:

public class CustomClient : ClientBase<IMyService>,
          IDisposable where TChannel : class
{
  // Other stuff... and then
  void System.IDisposable.Dispose()
  {
    try
    {
      this.Close();
    }
    catch
    {
      this.Abort();
    }
  }
}

Try not to get hung up on the hokey implementation there, just stick with me - you have a sort of "overridden" Dispose() call. The thing is, if I put my CustomClient in a using statement, it's the "overridden" Dispose() that executes, not the one in ClientBase<T>.

I want my FxCop rule to catch people who put something deriving from ClientBase<T> in a using block, but if you've got an override like in the CustomClient class there, I want it to let it go.

How do you detect that?

I've been all over the System.Reflection namespace and I can't find anything. If you do Type.GetInterfaces() or Type.GetInterface() it shows that you implemented IDisposable either way because it gets all of the interfaces you implement all the way through the inheritance chain. Type.GetInterfaceMap() only returns the base implementation - the one from ClientBase<T> - in all cases. It ignores the derived class's "override." The only thing I can figure out that seems to work, but feels really bad, is this:

public static bool OverridesDispose(Type runtimeType)
{
  // For brevity, we're assuming the incoming Type isn't null and
  // implements IDisposable. I've omitted those checks here.
  MethodInfo info = runtimeType.GetMethod(
    "Dispose",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
    null,
    new Type[] { },
    null);
  if (info == null)
  {
    info = runtimeType.GetMethod(
      "System.IDisposable.Dispose",
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
      null,
      new Type[] { },
      null);
  }
  if (info == null)
  {
    return false;
  }

  Type declaringType = info.DeclaringType;
  if (
    declaringType.IsGenericType &&
    declaringType.GetGenericTypeDefinition() == typeof(ClientBase<>)
  )
  {
    return false;
  }
  return true;
}

See what I'm doing? I basically query for an implicit interface implementation, then if that's not found, I get the explicit interface implementation. If neither are found, I figure there's no override. If one is found, then I ask what the declaring type of the method is, and if it's the ClientBase<T> type, it's not overridden, otherwise it is.

But the code smell! Ugh!

Am I missing some easier way to do it?

Birthday 32

Last week was #32 for me. I had a great time the weekend before last, checking out The Dark Knight and hitting the driving range.

On the day of my birthday my parents came over and brought dinner - Mom made this super-tasty Mexican meal. I ate far too much of that and had some cheesecake when it was all done. They also got to visit the new kittens, who are starting to get big but no less wild. Jenn got me a red and black Nintendo DS with Guitar Hero: On Tour. My parents got me a case for all that and Mario Kart DS.

This past weekend, we went to the Washington County Fair on both Friday night and Saturday. I rode one of the rides called "El Niño" with Jenn and our friends Angela and K. It was pretty crazy and probably one of the better traveling fair rides I've been on. We also saw The Pirate's Parrot show and it was great as usual, even though I got a pretty decent sunburn out of it. I hope they come back again next year.

In the end, it was a pretty great birthday. It's going to be a while before I reach my next power of 2, so I'm going to have to savor this one.

Typemock Isolator Open Source Licenses

Just got word from the folks over at Typemock - if you have an open source project and you want to use Typemock Isolator, they're going to be coming out with a new (free) license type that allows your open source project to use it. Here's the new licensing structure I've seen:

  • Commercial - $449 (same as today)
  • Personal - $200 (will include one year maintenance)
  • Open Source Project - Free
  • 21-Day Trial - Free

And, of course, discounts for upgrades, etc., on the paid licenses.

This is huge. I've been wanting to add tests to CR_Documentor, but it's almost impossible to do that because of the tight coupling DXCore plugins have with the DXCore engine. For example, I'd love to be able to create a fake language element and unit test the syntax generator... but I don't want to have to write wrapper interfaces and services for everything in DXCore that I interact with. Now I can!

Big side benefit - the folks who are unfamiliar with Isolator can see how open source projects use it and see the benefits. Sweet!

CR_Documentor 2.1.0.0 Released: Fixed the JavaScript Security Warning

I just posted the new version of CR_Documentor, version 2.1.0.0, over at Google Code. This new one hosts a tiny internal web server (instead of manually poking things into the IE DOM) so as long as you can get to localhost, you should be able to see the preview without that nasty warning. I also fixed the focus issue so it shouldn't steal focus when the preview refreshes.

If you upgraded before and are ready for a fix, go get it!

Birthday Weekends - Plural!

Since my birthday falls in the middle of the week this year, we're sort of splitting up the festivities across weekends - some this past weekend, some this weekend.

Friday night I started out by playing some GRAW 2 co-op with my dad and uncle. We normally spend a lot of time struggling through just one of the missions (none of us are super-good and some of the missions really need four or five people), but this time we beat two in one night. My uncle came out with the medal of honor on this one, finishing out the mission after my dad and I had both gotten killed.

Saturday morning I went to the DMV and got my license renewed and actually got out far sooner than I thought I would. In the afternoon we saw The Dark Knight, and it was awesome. Heath Ledger was amazing as the insane Joker and really made him a believeable bad guy, not the cartoonish villain you might envision with the Joker. I hope they make more Batman in this style, and maybe adopt it for future super hero movies. Comic movies don't have to be all cartoonish.

Sunday we went to the driving range with our friends K and Angela to have lunch and hit a bucket of balls. It was a beautiful day out and we had a great time. I'd never hit a golf ball before in my life, so this was a fun intro, even though I didn't do too well. Most of my shots went about 40 or 50 yards, though I had a few that went... a bit shorter than that... and some that darn close to injured people. My furthest went about 140 yards. K put all of us to shame going way past everything we did.

K said we might be sore today, but I'm not doing too bad. My back is a little tight and the inside of my right arm is kind of sore (so I can't really grip things or pick up heavy stuff on the right), but other than that, I'm doing well. I think Jenn is mostly feeling it in the back. Regardless, it was a heck of a fun time and we'll definitely be trying that again.

We stopped by on the way home at Baskin-Robbins, had some ice cream, and finished off the day with a barbecue at our place. Someone was outside cooking something delicious smelling and we couldn't resist - we had to cook our own.

This coming weekend I think we're going to hit the Washington County Fair so we can see the Pirate's Parrot Show and maybe have a funnel cake. Since it's free admission, we may even go Friday night and Saturday. It's a lot of fun and only a few minutes away from home.

Tag Soup

Jeff Atwood posts about the exact thing I thought when I saw the MVC tidal wave coming to overtake web forms. It feels like I'm back in the 90's, doing old-school ASP, because of the tag soup.

Don't get me wrong - I'm all about separation of concerns and such, but every time I look at something like MVC, I see all roads leading to web forms. Maybe different web forms, but web forms nonetheless.

  • Too much tag soup? Package stuff up in helper methods.
  • Oh, wait, the helper methods that generate control structures aren't really flexible enough to allow overridden behaviors or wire things up (like validation). Let's make them instance methods on objects.
  • Hmmm, there's sort of an object lifetime to manage here, and wouldn't it be nice if the packaged widget could be a little smarter about handling view data? Like dynamically populating itself based on data and letting me know if the user changed stuff, because writing that every time is such a pain.
  • Hang on, that's web server controls.

Some folks argue they want that "tighter control over the HTML" that the tag soup provides. I, personally, am much less interested in hacking HTML. Let the framework do it for me. I'm more concerned with the business logic anyway.

I've done my share of classic ASP style development in lots of different server side languages, and it always ends up that to get rid of the tag soup, you head towards web forms.

Closed Generic Types Have Their Own Statics

You really have to be careful when you use generic types. Say you have a generic type Foo<T> like this:

public class Foo<T>
{
  public static string StaticValue = "Unset.";
}

Fine, right? Well, what happens if you do this?

Console.WriteLine("Foo<string>: {0}", Foo<string>.StaticValue);
Console.WriteLine("Foo<int>: {0}", Foo<int>.StaticValue);
Foo<string>.StaticValue = "String value.";
Foo<int>.StaticValue = "Integer value.";
Console.WriteLine("Foo<string>: {0}", Foo<string>.StaticValue);
Console.WriteLine("Foo<int>: {0}", Foo<int>.StaticValue);

I bet you can guess what the output is.

Foo<string>: Unset.
Foo<int>: Unset.
Foo<string>: String value.
Foo<int>: Integer value.

That's right - each closed generic type (a generic that has a type parameter provided) has its own set of static values.

Think about how that can bite you - if you have multiple parameters on your generic type, like MyType<T, U, V>, and you have three different types that are used in each of T, U, and V, you end up with 27 sets of static values (it's combinatoric). Possibly not what you're thinking at the time you write the code.

I really can't find any documentation on this. There's a nice CodeProject article about it that shows why this can really work against you if you're trying to make a strongly-typed generic singleton factory, but beyond that, MSDN really only yields up an FxCop rule that's fairly unrelated.

Be careful with your generics and static values!

Simplest Embedded Web Server Ever with HttpListener

While working on solving a CR_Documentor known issue, I realized I needed to have some sort of embedded web server running so I could serve up dynamically generated content. I didn't need a full ASP.NET stack, just something I could pipe a string to and have it serve that up so a hosted IE control would be getting content from a "live server" rather than from a file on disk or from in-memory DOM manipulation... because both of those latter methods cause security warnings to pop up.

I looked at creating a dependency on the ASP.NET development server, WebDev.WebServer.exe, or the assembly that contains the guts of that, WebDev.WebHost.dll, but both of those only get installed in certain configurations of Visual Studio (I think it's when you install the Visual Web Developer portion) and I couldn't really assume everyone had that. Paulo Morgado then pointed me to HttpListener, and let me tell you, that's a pretty sweet solution.

Here's a very simple web server implementation that uses HttpListener. You handle an event, provide some content for the incoming request, and that's what the response content is. It doesn't read files from the filesystem, it doesn't do auth, it doesn't do ASP.NET... it's just the simplest of simple servers, which is exactly what I need for CR_Documentor.

UPDATE 2/2/2009: I found that the super-simple way I had things caused some interesting and unfortunate race conditions which meant things occasionally locked up for no particular reason. As such, I've updated the code sample to use events to handle incoming requests and show the listener running on a separate thread. It's still pretty simple, all things considered, and I have it up and running in CR_Documentor.

using System;
using System.Globalization;
using System.Net;
using System.Threading;

namespace HttpListenerExample
{
  public class WebServer : IDisposable
  {
    public event EventHandler<HttpRequestEventArgs> IncomingRequest = null;

    public enum State
    {
      Stopped,
      Stopping,
      Starting,
      Started
    }

    private Thread _connectionManagerThread = null;
    private bool _disposed = false;
    private HttpListener _listener = null;
    private long _runState = (long)State.Stopped;

    public State RunState
    {
      get
      {
        return (State)Interlocked.Read(ref _runState);
      }
    }

    public virtual Guid UniqueId { get; private set; }

    public virtual Uri Url { get; private set; }

    public WebServer(Uri listenerPrefix)
    {
      if (!HttpListener.IsSupported)
      {
        throw new NotSupportedException("The HttpListener class is not supported on this operating system.");
      }
      if(listenerPrefix == null)
      {
        throw new ArgumentNullException("listenerPrefix");
      }
      this.UniqueId = Guid.NewGuid();
      this._listener = new HttpListener();
      this._listener.Prefixes.Add(listenerPrefix.AbsoluteUri);
    }

    ~WebServer()
    {
      this.Dispose(false);
    }

    private void ConnectionManagerThreadStart()
    {
      Interlocked.Exchange(ref this._runState, (long)State.Starting);
      try
      {
        if (!this._listener.IsListening)
        {
          this._listener.Start();
        }
        if (this._listener.IsListening)
        {
          Interlocked.Exchange(ref this._runState, (long)State.Started);
        }

        try
        {
          while (RunState == State.Started)
          {
            HttpListenerContext context = this._listener.GetContext();
            this.RaiseIncomingRequest(context);
          }
        }
        catch (HttpListenerException)
        {
          // This will occur when the listener gets shut down.
          // Just swallow it and move on.
        }
      }
      finally
      {
        Interlocked.Exchange(ref this._runState, (long)State.Stopped);
      }
    }

    public virtual void Dispose()
    {
      this.Dispose(true);
      GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
      if (this._disposed)
      {
        return;
      }
      if (disposing)
      {
        if (this.RunState != State.Stopped)
        {
          this.Stop();
        }
        if (this._connectionManagerThread != null)
        {
          this._connectionManagerThread.Abort();
          this._connectionManagerThread = null;
        }
      }
      this._disposed = true;
    }

    private void RaiseIncomingRequest(HttpListenerContext context)
    {
      HttpRequestEventArgs e = new HttpRequestEventArgs(context);
      try
      {
        if (this.IncomingRequest != null)
        {
          this.IncomingRequest.BeginInvoke(this, e, null, null);
        }
      }
      catch
      {
        // Swallow the exception and/or log it, but you probably don't want to exit
        // just because an incoming request handler failed.
      }
    }

    public virtual void Start()
    {
      if (this._connectionManagerThread == null || this._connectionManagerThread.ThreadState == ThreadState.Stopped)
      {
        this._connectionManagerThread = new Thread(new ThreadStart(this.ConnectionManagerThreadStart));
        this._connectionManagerThread.Name = String.Format(CultureInfo.InvariantCulture, "ConnectionManager_{0}", this.UniqueId);
      }
      else if (this._connectionManagerThread.ThreadState == ThreadState.Running)
      {
        throw new ThreadStateException("The request handling process is already running.");
      }

      if (this._connectionManagerThread.ThreadState != ThreadState.Unstarted)
      {
        throw new ThreadStateException("The request handling process was not properly initialized so it could not be started.");
      }
      this._connectionManagerThread.Start();

      long waitTime = DateTime.Now.Ticks + TimeSpan.TicksPerSecond * 10;
      while (this.RunState != State.Started)
      {
        Thread.Sleep(100);
        if (DateTime.Now.Ticks > waitTime)
        {
          throw new TimeoutException("Unable to start the request handling process.");
        }
      }
    }

    public virtual void Stop()
    {
      // Setting the runstate to something other than "started" and
      // stopping the listener should abort the AddIncomingRequestToQueue
      // method and allow the ConnectionManagerThreadStart sequence to
      // end, which sets the RunState to Stopped.
      Interlocked.Exchange(ref this._runState, (long)State.Stopping);
      if (this._listener.IsListening)
      {
        this._listener.Stop();
      }
      long waitTime = DateTime.Now.Ticks + TimeSpan.TicksPerSecond * 10;
      while (this.RunState != State.Stopped)
      {
        Thread.Sleep(100);
        if (DateTime.Now.Ticks > waitTime)
        {
          throw new TimeoutException("Unable to stop the web server process.");
        }
      }

      this._connectionManagerThread = null;
    }
  }
  
  public class HttpRequestEventArgs : EventArgs
  {
    public HttpListenerContext RequestContext { get; private set; }

    public HttpRequestEventArgs(HttpListenerContext requestContext)
    {
      this.RequestContext = requestContext;
    }
  }
}

With this simple wrapper, you can new-up a web server instance, start it listening for requests, and handle the IncomingRequest event to serve up the content you want. Dispose the instance and you're done. Here's what it looks like in a simple console program host:

using System;
using System.IO;
using System.Net;
using System.Text;

namespace HttpListenerExample
{
  class Program
  {
    static void Main(string[] args)
    {
      const string ServerTestUrl = "http://localhost:11235/";
      using (WebServer listener = new WebServer(new Uri(ServerTestUrl)))
      {
        listener.IncomingRequest += WebServer_IncomingRequest;
        listener.Start();
        Console.WriteLine("Listener accepting requests: {0}", listener.RunState == WebServer.State.Started);
        Console.WriteLine("Making requests...");
        for(int i = 0; i < 10; i ++)
        {
          HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ServerTestUrl);
          HttpWebResponse response = (HttpWebResponse)request.GetResponse();
          using(Stream responseStream = response.GetResponseStream())
          using(StreamReader responseStreamReader = new StreamReader(responseStream, Encoding.UTF8))
          {
            Console.WriteLine(responseStreamReader.ReadToEnd());
          }
          System.Threading.Thread.Sleep(1000);
        }
        
      }
      Console.WriteLine("Done. Press any key to exit.");
      Console.ReadLine();
    }
    
    public static void WebServer_IncomingRequest(object sender, HttpRequestEventArgs e)
    {
      HttpListenerResponse response = e.RequestContext.Response;
      string content = DateTime.Now.ToLongTimeString();
      byte[] buffer = Encoding.UTF8.GetBytes(content);
      response.StatusCode = (int)HttpStatusCode.OK;
      response.StatusDescription = "OK";
      response.ContentLength64 = buffer.Length;
      response.ContentEncoding = Encoding.UTF8;
      response.OutputStream.Write(buffer, 0, buffer.Length);
      response.OutputStream.Close();
      response.Close();
    }
  }
}

I'm using this approach in CR_Documentor to serve up the content preview and maybe augment it a bit later so I can also serve images from embedded resources and such, making the preview that much richer and more accurate.

CR_Documentor Known Issue: JavaScript Security Warning

There's a known issue with the latest CR_Documentor - sometimes, on an unpredictable basis, it'll start issuing that "active content" JavaScript security warning. It does that because we're updating the document by poking data into the DOM directly. Usually setting your IE security settings to "allow active content to run in files on My Computer" fixes it, but not always.

Unfortunately, it's not really something I can replicate easily, but I know the fix is to, well, stop doing that dynamic poking thing and just serve it up like a regular web server. I have a couple of options:

  1. Create a custom in-proc web server from scratch. I'd have the most control over it and the least dependencies, but it's the most amount of work, too.
  2. Add a dependency to the ASP.NET development server and use that. Basically, just fire up the ASP.NET development server and serve from a temporary filesystem location.

Is it safe to assume most folks have the ASP.NET development server installed with Visual Studio? I could detect if it was installed and issue an error when the user displays the window to tell them they need to have it installed. I'm thinking writing web servers, however tiny, is not my "core competency" so I'd rather use something off the shelf than try to roll everything myself.

UPDATE: Turns out rolling your own is easy with HttpListener. I'm going to try that first.

UPDATE 2: This is currently being worked on and should be fixed in the next version. You can follow the issue progress on the CR_Documentor site.

UPDATE 3: It's fixed! Go get it!

Laser Hair Removal: Treatment 12

Saturday was my 12th treatment on my face, and the last in my second set of six (they sell treatments in blocks of six).

A lot has changed since the first treatment, so since I re-upped for a third set of six, I thought it would be time for a retrospective.

  • It still hurts, but it's nothing like the first time I went. I get MeDioStar on my entire face now, and it's not nearly as bad because the hair is so much thinner. Reading back on that first entry, I totally remember how much apprehension I felt before going back for the second treatment. I don't feel that anymore. (There really is no way you can prepare someone for it, so I think a lot of my reaction was that my pain-related expectations were vastly different than reality. Whattaya gonna do?)
  • I still really like all of the people at the clinic. Everyone from the folks at the front desk to the technicians and the sales people are super nice. They all remember my name, they all make me feel totally welcome, and they're super easy to get along with. I'm glad I chose them.
  • I thought I'd be a closer to done than I am, but then, I was supposed to have done 12 MeDioStar treatments by now and I haven't. I spent a lot of time doing just Dermo Flash to thin things out. It was the right thing to do, but it's taking a while.
  • I really like the results so far. My hair grows differently on my face - I pretty much have to shave against the grain to get a good shave - but I've also not destroyed any sheets, pillowcases, or shirts for quite some time.
  • The spots that are being stubborn are my chin, upper lip, a spot on my right cheek, and a spot on my neck. The rest has done really well. Even the stubborn spots are starting to give way, it's just taking a while.

So I did my thing, got my second set of six, and I'm crusin' along. I should probably take some before and after pix in a couple of weeks to see how well this next set of treatments goes.

CR_Documentor 2.0.0.0 Released: Now With Sandcastle Preview and Open Source Goodness

I know it's bad news to release things on Friday right before the day is out, but I can't hold it in any longer:

I am pleased to announce that after a far-too-long silence, CR_Documentor 2.0.0.0 has been released and is now available for download.

Three major things to report in this release:

  1. All reported bugs have been fixed. If you mailed me or left a comment about a bug, it should be resolved in this release. While there are some known issues, things should render right and it should behave itself reasonably.
  2. Sandcastle "Prototype" preview style is available. You can choose between the classic NDoc preview style or the new Sandcastle "Prototype" style.
  3. The plugin is now open source. I've created a new home for the plugin on Google Code and have released it under the Apache 2.0 license. I'd love to get some help and contributions on it, but even if you're just curious about how it works under the hood, feel free to grab the code.

All the info - FAQ, known issues, etc. - is all on the new CR_Documentor site at Google Code, as is the download. Head on over and check it out!

CR_JoinLines and CR_SortLines Join DXCore Community Plugins

My CR_JoinLines and CR_SortLines plugins for DXCore have been joined up with the DXCore Community Plugins project headed by Rory Becker. Complete source, installation/usage info, etc., has all been put up over there and any further dev on them will be done there.

Hopefully this more public release area will be helpful to folks who not only want to find and use the plugins, but also who want to learn how to write plugins.

The Ultimate Pyro Readiness List

I've been doing various fireworks shows for the past few years and each year I always end up having to recreate the list of things I want to bring along with me to the shoot. Instead of constantly updating and forgetting and such, here's the list of everything you'd want to bring with you if you're shooting a show.

This is pretty exhaustive and will probably need to be adjusted for based on the shoot. For example, sometimes you have to dig trenches so you'll need a shovel... but if you're not digging, no shovel required. Obviously if you've got more than one person, you may need to adjust quantities. Also, sometimes the company you're shooting for will provide some equipment. Here we go...

  • Tools
    • Shovel
    • Rake (like, one of those wide plastic ones for raking leaves)
    • Pocket knife
    • Pliers (if you have a Leatherman tool, use that)
    • Cordless drill with screwdriver bits
    • Hammer
    • Large marker (like a Sharpie)
    • Masking tape
    • Flashlight
    • Continuity tester
  • Clothing
    • Leather work gloves
    • Firefighter turnout or other flame-retardant overcoat
    • Knee pads
    • Sunglasses
    • Hat with brim
    • Foam ear plugs
    • Helmet (motorcycle or firefighter)
  • Sustenance
    • Water
    • Gatorade
    • Energy drinks
    • Dry food - crackers, granola bars, etc.
  • Amenities
    • Cooler (for your food/drinks)
    • Camp chair
    • Camp table
    • Shade shelter (one of those four-metal-legs-and-a-tarp things works great)
    • Moist washcloth in plastic bag
    • Digital camera
    • Book/magazine
  • Health products
    • Excedrin Migraine
    • Ibuprofen
    • Pseudoephedrine (or other decongestant)
    • Sunscreen - SPF45 or better
    • Moist towelettes
    • Bug repellent

Sandcastle Source Released

Sandcastle, the documentation generator for XML doc comments in .NET, has released its source code on CodePlex starting with the May 2008 release. It had gotten booted from CodePlex for not releasing the source, so they released it.

Somehow I don't think releasing the source is going to help anything. It's still crap that you have to run like 10 different commands and have a specially-tailored configuration file to get docs to render and that they leave it up to the community to fill such a huge gap with projects like Sandcastle Help File Builder. I've had to set up Sandcastle in several builds now and I always dread it because it's such a nightmare even with helper programs like that. Sigh.

posted @ Tuesday, July 08, 2008 9:18 AM | Feedback (0) | Filed Under [ .NET ]

Typemock: Isolator 4.3 and a Sneak Peek at Racer

I'm a few days late on this (I was on vacation) but Typemock Isolator 4.3 has been released and has some great new features like Ivonna integration for ASP.NET testing. Check out the blog post on it and then go get it!

While I'm on the subject of Typemock, Roy Osherove has offered up a sneak preview of the new Typemock Racer tool he's working on that will help detect thread deadlock. Very promising, indeed. Check out his site for some great screenshots.

posted @ Tuesday, July 08, 2008 9:12 AM | Feedback (2) | Filed Under [ .NET ]

Hot Poop

In my travels through Walla Walla this weekend, I found a store with possibly the most awesome name ever: Hot Poop.

Hot Poop

I think it's a music store of some nature. Here's the whole store front:

The full Hot Poop store front

I can only imagine people talking about the store. "Hey, man, you wanna check out Hot Poop?" "No, I poked my head into Hot Poop yesterday and didn't see anything I needed." Oh, hellz yeah.

Welcome Kai and Stanley

Welcome Kai and Stanley

We were out running errands today and decided to stop in at the Petco because today was the day the local shelter brings kitties in to show (and hopefully find homes for) and Jenn wanted to see what sorts of kitties were out there.

The result of that trip is that we now have two new 10-week-old kitten brothers we've named Kai and Stanley. They're gray tabbies and very playful. Xev, our six-year-old brown tabby, is still taking some time to get used to them, but otherwise they're really making themselves at home. (Xev doesn't like to play nearly as much as these wild stallions do, so they'll keep each other busy and she'll still be able to participate and have friends so she won't be lonely.)

Tomorrow will be their first full day at home, so this should be interesting.

(Oh, and bonus points if you know why our cats are named Xev, Kai, and Stanley without Googling it.)

Walla Walla Fireworks 2008

Walla Walla Fireworks 2008

The fireworks shoot this year was once again in sunny Walla Walla, Washington.

We left Hillsboro a little after noon on Thursday, July 3 and got up to Walla Walla around 5:00p. We checked in to the Super 8, which was way better than the Travelodge from last year, and had dinner with the rest of the crew at the local Applebee's.

Friday the fourth found us hauling gear off the truck and setting things up around 7:30a. We had another good crew this year, a total of ten of us. The setup went pretty quickly and we checked, double-checked, and re-checked everything as the day went on, just to be sure everything was hooked up safe and ready for action.

At 10:00p the show started. It was electrically fired and Greg was at the helm. I stood by in case any of the shells failed to go off. It's a good thing I did, because we had a couple that the electronic firing didn't catch, so I ran out there with a high-tech solution - a road flare (fusee) attached to a stick - and hand-lit the couple that didn't go off. The show went off great and we had a good crowd cheering us on.

After the show, we loaded the equipment up on the truck and did a first run at cleanup, making sure nothing dangerous was left behind, and headed back to the hotel.

Saturday the fifth we checked out and were back at the field for final cleanup by 9:00a. Cleanup was a couple of hard hours raking and picking up shell casing fragments, then took on the four-hour drive home. (We actually went straight to Jenn's sister's birthday party without stopping at home, so it made for a bit of a long day.)

The weather was about 20 degrees cooler this year and it was way better that way. Thank goodness, too, because I don't think I could have stood it much hotter.

I think next year we're going to see if we can get a show a little closer to home. Walla Walla's a good show, but it being a three-day-affair is a little much for us. It'd be nice to get something local, like the Oak Hills (Beaverton) show. We'll see what happens.

I posted some pictures of the setup, but I didn't get any photos during the show. Actually, I got video of the shoot, and I'm going to see if Greg can cobble it together with his video and produce something really nice. I'll link to it when it's up.

Wall-E

Wall-EJenn and I went with some friends on Sunday to see Wall-E, the latest Pixar piece.

I enjoyed Toy Story and it holds a special place in my heart. I have enjoyed all of the other Pixar movies, too, to varying degrees.

Wall-E is my new favorite. There's just something about cute robots that I can't get over. The characters - most of them robots - were lovable and full of personality, the story was great... I was sucked in from the first minute. Something I thought was cool was that they did so much, particularly at the beginning, without a ton of dialogue. It was all in expressions and actions, and it was amazing.

While it's obviously safe for kids to see, I don't think they'll really get it. I know that my niece was sort of bored by it. My sister-in-law fell asleep (she's fired). Jenn, I, and the friends we went with loved it thoroughly. Definitely worth the full-price admission in my book.

Xbox 360 Sign-in Delay Fix

For the last, oh, two or three weeks, my Xbox 360 has been sluggish when signing me in to Xbox Live. I'd start the console, the logo screen would go by, the dashboard would come up, and I'd have to wait 20-30 seconds before my account would auto sign-in. The real crazy bit was that if I did anything - move to a different blade in the dashboard, manually try to sign in, or whatever - it would mess up the networking so that I'd have to shut the whole console down and reboot in order for it to connect. This weirdness wasn't happening before - I was used to it signing me in within five or six seconds of the dashboard coming up.

I tried all sorts of things - static IP addressing, clearing my settings cache, resetting the network to factory defaults and re-entering everything in manually - and nothing worked. Looking at my network settings, nothing was fighting for the same address, and every other device in the house connected to the network just fine.

My last ditch effort last night: reboot the wireless router and the devices connected to it. Shut down all the stuff connected to the network (laptop, PS3, etc.), reboot the router, and turn everything back on. That fixed it. No more sluggish login. Whatever was gummed up in the works has been flushed out.

Lenovo ThinkPad T60 Undock Issue Resolved

I have a Lenovo/IBM ThinkPad T60 at work with a docking station. For the longest time, I'd get this error when trying to undock:

You cannot eject your computer because one of the devices in the docking station, "Printer Port (LPT1)," cannot be stopped because a program is still accessing it.

For the life of me, I couldn't figure out what the problem was. I don't have any printers hooked up, I hadn't printed anything, I couldn't find anything that was using the port.

There's a KB article on it, and I found forum posts about it, but no real solutions. The way I solved it:

In the BIOS at boot time, there is an option for disallowing access to the printer port from the docking station. I set that. (There is an option to disable the printer port entirely, but I didn't need to do that - I just needed to stop the docking station from accessing it.) A quick save and reboot, and I'm ready to eject from the docking station at any time - no more error.

Note that I don't have any printers locally attached to my machine, so disabling the printer port connection to the docking station didn't hurt me. If you have a local printer attached to your docking station, this may not be the best solution for you.

UPDATE: I am on ThinkPad T60 BIOS version 2.21 and the option is under Config -> Docking Station. Set the "Legacy Devices on Mini Dock" setting to "Disabled" to disallow the docking station access to the parallel port.