personal comments edit

I think getting feedback on your software is valuable. It’s nice to know people are using what you put out there and it’s interesting to see what they think.

Usually.

The thing I’m sort of stewing over is something I’ll call, for the sake of discussion, “unjustified negative feedback.” Let me dive into an example.

I have a free, open-source add-on for Firefox that makes it easier to manage the list of sites to which you allow Windows pass-through authentication. It’s called Integrated Authentication for Firefox. The description is as follows:

Most people don’t realize it, but Firefox will do integrated authentication like NTLM (Windows pass-through) just like Internet Explorer. Some people solve the issue by going around Firefox and hosting IE right in Firefox. The other way to do it is to keep Firefox as the rendering engine and tell Firefox it’s OK to use integrated authentication with a given site.

The problem is that managing the list of sites you allow Firefox to pass-through authenticate with is not straightforward and involves manually manipulating configuration settings.

This add-on makes it easier to manage this list, allowing you to stick with Firefox but still use pass-through authentication like Windows/NTLM or Kerberos.

NOTE: This add-on does not actually DO the authentication. Firefox itself already has built-in integrated authentication, it’s just not obvious how to get it to work. This add-on makes it easy to configure Firefox to use its already-existing features, but it does not do the authentication proper.

The problem this add-on solves is in that second paragraph: “managing the list of sites… is not straightforward and involves manually manipulating configuration settings.” If you’re a power user (and not everyone out there is), you can hit the “about:config” page in Firefox and tweak the “network.negotiate-auth.trusted-uris,” “network.negotiate-auth.delegation-uris,” and “network.automatic-ntlm-auth.trusted-uris” settings in the appropriate format and accomplish the same thing. But it’s not pretty and it’s not really a first-class interface.

The point is, there’s a certain audience for this software. Most people seem to like it because they didn’t know about those buried settings and this makes it nice and easy to deal with. And then there are folks who leave reviews like this:

Disappointing. http or https is required before each entry when it really isn’t required when set manually. You can’t paste in a ton of coma separated items to add them quickly. This isn’t any better than going to about:config unfortunately.

That got me a one-star. Or this one:

This is just a GUI for the “network.automatic-ntlm-auth.trusted-uris” value of the “about:config” page… if anything, it makes adding the URL’s more difficult, since you can’t add them comma-separated or without “http” prefix. For this to be actually useful a content menu like “Add this URL” or automatic/wildcard addition of an HTTP Auth protected pages (not a good idea from security perspective) would need to be added; as of right now this could very well be replaced with a link to above configuration value.

That one was two stars.

Notice any common theme? “This isn’t any better than going to about:config…” “This is just a GUI for the … value of the ‘about:config’ page…” I feel like the folks giving the bad reviews didn’t actually read what the plugin did. Of course it’s not any better than going to “about:config” – that’s all the plugin does.

The folks leaving the reviews were obviously not the target audience for the plugin. If you’re comfortable messing around in “about:config” then just do it. But I don’t have a ton of ratings over there, so just a few bad reviews take my overall average down pretty easily.

I use my plugin as an example, but this sort of thing is pretty common in software feedback. Here’s a one-star review from a recent Free App of the Day in the Amazon AppStore:

I haved played a lot of drawing apps for my kindle fire but this one is the ultimate worst it needs to be User friendly and I hate it so take that people who made this app you just got pwned lookout swaggar alert……. so yea u guys suk

That’s not even worth the electrons it’s printed on. Maybe you didn’t like the app… but you sort of got what you paid for it (nothing), and I can’t imagine it was worth only one star when there are actual thoughtful reviews that give it much higher ratings.

And that’s what I’m talking about: unjustified negative feedback.

I feel like there’s always that guy who has to say, “I know this is a screen shot app, but how come it doesn’t make coffee, too? ONE STAR!” It’s like there needs to be a screener before people leave feedback. “Did you actually use the software? Did you read the description of what the software was intended to do? Did it do what it said?”

I know there’s not really any way to solve it because there are always going to be trolls, people who use the software who aren’t the target for it, and folks just generally having a bad day who will take it out on the developer.

At the same time, I’m not entirely sure the people leaving the feedback, especially for free/open source software, realize there’s probably only one or two people working on it… in their spare time… for no money… and maybe something more constructive would be beneficial.

Maybe this is what Jeff Atwood was talking about when he said, “90% of all community feedback is crap.” I wish we could reduce that percentage. It’d be a far more motivating experience to build better products.

Note: I think there’s a strong (inversely-proportional) correlation between “valuable feedback” and “community breadth.” The wider the audience for a product, the larger the percentage of crap feedback. The user base for Firefox is huge and there’s a wide range of skill levels, so you get a higher crap percentage. The user base for Android apps in the Amazon AppStore is huge and, again, there’s a wide range of skill levels, so, again, high percentage of crap. On the other hand, I find that slightly smaller, more focused communities like the user base for CodeRush/DXCore plugins, has a vastly lower percentage of crap feedback. Everyone seems willing to work together to make the ecosystem better. Something to think about.

net, vs, gists comments edit

As of CodeRush/Refactor/DXCore 11.2, DXCore supports installation of plugins using Visual Studio Extensibility (VSIX) just like any other extension you might find in the gallery. This is beyond cool for a couple of reasons.

First, I’ve been [slowly] working on a couple of different ways to create some nature of “plugin gallery” for DXCore extensions. There are so many community plugins, plus things like CR_Documentor and CR_CodeTweet that aren’t on the community plugin site, that it’s really hard to know what’s out there. This would allow DXCore plugins to use the standard Visual Studio Gallery mechanism for browsing.

Second, once you install a DXCore plugin, there’s no real “auto-update”mechanism. You have to go check to see if there’s a new release and manually do installation yourself. With VSIX and the gallery, you can get notified of new versions of the plugins via the standard Extension Manager in Visual Studio. Auto-update: solved!

I decided my pilot project would be to get a VSIX installer attached to CR_Documentor. If you have a DXCore plugin, you can use these same steps to get a VSIX installer for your plugin. If you’re starting a new plugin, you can check out this great article from Alex Skorkin on how to start a fresh plugin with the provided DXCore/VSIX plugin template.

TWO IMPORTANT NOTES before you begin:

  • I converted a C#/.csproj plugin so the steps I took here assume you’re using C#, too.If you’re using VB, you may have to do some different/additional steps that I’m unaware of.
  • You will lose support for Visual Studio versions before 2010 because you’ll need to update the target .NET framework. If you can’t afford to lose that support for your plugin, stop now.

First, install the prerequisites. You’ll need…

Optional: Create a new, empty DXCore VSIX standard plugin project for reference. You won’t actually be doing any coding in here, but having a populated skeleton plugin really helps if you need to grab some code copy/paste style or check to see how something is set up. This was pretty key for me to figure out what I needed to do to add VSIX to my plugin – a skeleton plugin and a diff tool. Once you’ve created it, build it once and close it. You’re done with it unless you need to go refer to something or troubleshoot it.

Open up your plugin project. We’re going to make a few modifications to the project properties.

  • Switch the target framework to .NET 4.0. You need to do this because VSIX only supports .NET 4.0. It does mean you’ll be giving up support in your plugin for versions of Visual Studio before 2010.
    Set the target framework to .NET 4.0

  • Switch your build output paths to bin\Debug\ and bin\Release\ for the Debug and Release build configurations, respectively. Most plugin projects have the build output set so the plugin will build right into your Community Plugins folder. This makes it easy to debug. Once you switch to VSIX, you debug in a different way, so you don’t want the plugin going into the Community Plugins folder anymore. You do need to have a build output location, though, so switch it back to the standard bin\Debug\ or bin\Release\ location.
  • Remove any post-build copy/deployment tasks. In my plugins, rather than change the build output paths, I use post-build copy tasks to copy the plugin into my Community Plugins folder. Again, you don’t want to auto-deploy like this because there’s a different mechanism for VSIX, so remove any of these steps from the project.

That’s all for now with the project properties. We’ll come back to that in a little bit once we’ve made a few more modifications.

Add some assembly references to your project if you haven’t got them already:

  • System.Core
  • System.ComponentModel.Composition

You’ll need these so the MEF portion of the VSIX installer will work.

Open your plugin project file in a text editor. It’s time to tweak a few things by hand.

We need to add some top-level global properties to the project. In the top-level PropertyGroup node, which appears just below the root Project node, you need to:

  • Add a ProjectTypeGuids node that has the VSIX project GUIDs in it.
  • Add the VSIX properties that help direct the VSIX build.
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<GeneratePkgDefFile>false</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
<CopyOutputSymbolsToOutputDirectory>true</CopyOutputSymbolsToOutputDirectory>

My top-level PropertyGroup node in CR_Documentor now looks like this. I’ve made the additions bold.

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>10.0.20506</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <ProjectGuid>{9229512A-C004-46FD-8CEE-D096C883E827}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>CR_Documentor</RootNamespace>
    <RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <GeneratePkgDefFile>false</GeneratePkgDefFile>
    <IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
    <IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer>
    <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
    <CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
    <CopyOutputSymbolsToOutputDirectory>true</CopyOutputSymbolsToOutputDirectory>
  </PropertyGroup>
</Project>

These properties are really important to getting the project to build successfully. For example, early on I forgot to add the GeneratePkgDefFile property over and set it to false. For quite some time I got an odd error that I couldn’t figure out and stopped the build from finishing:
CreatePkgDef : error : No Visual Studio registration attribute found in this assembly.

Adding the property fixed the build. I was able to find my error by comparing my .csproj to the empty/skeleton VSIX plugin .csproj I had created. That’s why I mentioned that as an optional step at the top – it’s good to have something working to compare against.

Now add the build targets that allow VSIX to compile. At the bottom of your project file, locate the following line:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

Just below that, add this line to reference the VS SDK targets:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" />

If there’s any additional project cleanup you want to do, now’s a decent time. For me, CR_Documentor has been around for a while so there was a lot of stuff referring to old versions of .NET and/or old versions of Visual Studio, all of which has persisted over the course of several project upgrades. I can’t give you any guidance on this and you do it all at your own risk. You aren’t required to do any additional cleanup to get the VSIX stuff to work; I just mention it since you’re already neck-deep in .csproj hacking.

Save your changes and close the text editor. You’re done manually tweaking the project. If you didn’t previously close the project in Visual Studio, you’ll now be prompted to load the changed project. Go ahead and do that.

Add a VSIX Plugin Extension class to your plugin. This is the little “shim” that signals DXCore to load your plugin from VSIX. Super easy and not even really any code.

  • Add a new class to your project. I called mine “VsixPluginExtension”because I’m all about naming. :)
  • Set the class to implement DevExpress.CodeRush.Common.IVsixPluginExtension. This is a marker interface; no code to write or implement for it.
  • Add a System.ComponentModel.Composition.ExportAttribute to the class so it exports IVsixPluginExtension.

That’s it. Here’s a copy/paste version you can grab and just change the namespace, even:

using System.ComponentModel.Composition;
using DevExpress.CodeRush.Common;

namespace YourNamespaceHere
{
  [Export(typeof(IVsixPluginExtension))]
  public class VsixPluginExtension : IVsixPluginExtension
  {
  }
}

Finally, add a VSIX manifest to the project. The VSIX manifest is a little XML file that describes what’s in the VSIX package. Unfortunately, there’s no template for this file type so you either need to copy one in from a different project (another reason having that empty/skeleton VSIX plugin project is handy) or you need to manually create the file yourself.

  • Add an XML file to the project called source.extension.vsixmanifest and set the “Build Action” on it to “None.” If you copy it in from the skeleton project, you’re done.
  • Add some empty/placeholder manifest data. You can edit the data later in a nice designer in Visual Studio, but to get you going you have to have something in it. Here’s the content from a skeleton VSIX plugin if you want to copy/paste.
<?xml version="1.0" encoding="utf-8"?>
<Vsix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
  <Identifier Id="MyDemoVsixPlugin">
    <Name>MyDemoVsixPlugin</Name>
    <Author>MyCompany</Author>
    <Version>1.0</Version>
    <Description xml:space="preserve">Empty DXCore VSIX plugin project.</Description>
    <Locale>1033</Locale>
    <SupportedProducts>
      <VisualStudio Version="10.0">
        <Edition>Pro</Edition>
      </VisualStudio>
    </SupportedProducts>
    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
  </Identifier>
  <References />
  <Content>
    <MefComponent>|%CurrentProject%|</MefComponent>
  </Content>
</Vsix>

Obviously those values won’t be right for your project. Your project isn’t called “MyDemoVsixPlugin” or whatever. We’ll fix that soon.

Open the project properties again. It’s time to set up the debugging environment.

The way VSIX debugging works for DXCore plugins is that, rather than deploy your plugin to the Community Plugins folder, Visual Studio can automatically deploy your built VSIX package (which includes your plugin) into an “experimental” version of Visual Studio. This “experimental” version is sort of like running under a “test user profile” so it won’t have all of the fancy stuff you’ve installed into your VS – it’ll be pretty bare bones. In fact, the first time you run it, you’ll be prompted to set up some default things like your window layout settings and whatnot, just like the first time you ran Visual Studio. Don’t be fooled by the “experimental” thing – it’s real, full VS. It’s just the user profile that’s considered “experimental.”

An interesting side note is that you can actually see where VS is deploying your plugin for debugging if you look in %USERPROFILE%\AppData\Local\Microsoft\VisualStudio\10.0Exp\Extensions

which is something like C:\Users\yourusername\AppData\Local\Microsoft\VisualStudio\10.0Exp\Extensions

If you run into issues where you have to manually remove your extension from the experimental instance, you can delete it from that folder.

On the Debug tab, set the “Start external program” value to start Visual Studio (devenv.exe). Under “Command line arguments,” put /rootsuffix Exp to instruct Visual Studio to use the “experimental” instance of Visual Studio.

Set Visual Studio to run an Experimental instance for debugging.

On the VSIX tab (which will now appear because your project type was set via the ProjectTypeGuids node you added earlier), for all build configurations, select the “Create VSIX Container during build” and “Deploy VSIX content to experimental instance for debugging” options.

Set the VSIX container to build and deploy

Close the project properties. You’re done with that.

Finally, do some minimum update to the manifest to tailor the package for your plugin.

  • Double-click the source.extension.vsixmanifest file you added earlier. This will open it up in a nice designer.
  • At a minimum, change the following properties:
    • ID: This is the “unique ID” for your package. Usually it’s the name of your plugin, but you may or may not want that. Just as long as you don’t change it later, you’ll be fine.
    • Product Name: This is the name of your plugin. You’ll see it appear in the Extension Manager in Visual Studio.
    • Version: This is the version of your plugin. You probably want this to match the version of your plugin assembly.

Save your changes and hit F5 to build and start a debugging session. Once the experimental version of Visual Studio starts up, check to see that your plugin is available. You can even go into the Visual Studio Extension Manager (Tools –> Extension Manager…) to see your plugin listed as an extension.

Troubleshoot your build. If you are getting warnings or errors, now’s the time to fix them. I had several warnings about different things in CR_Documentor because I updated the .NET target framework from 2.0 to 4.0 and a lot has changed. Unfortunately, I can’t give you much guidance on this part. If you’ve followed the instructions up to now, you should have all of the pieces in place to get this building and debugging properly. If you’re getting odd VSIX errors…

  • Go back and make sure you got ALL of the properties in place in your project.
  • Verify you have the right version of the Visual Studio SDK installed for the version of Visual Studio you have installed. If you have VS 2010 SP1, the regular VS 2010 SDK won’t work – you need VS 2010 SP1 SDK.
  • Compare your plugin project to an empty/skeleton VSIX DXCore plugin project. Pop the files open in a diff tool and see what the differences are. This was key in my troubleshooting efforts.

Once you’ve verified the deployment is working, it’s time to do a little fine-tuning on the manifest.

The manifest file is what determines how your plugin appears in the Visual Studio Gallery and in the Extension Manager. You can put as much or as little effort into this as you want – the minimum values you need are the ones I mentioned earlier. If you want your plugin to look professional, though, and be discoverable to everyone, a little more work is required.

Open the manifest in the Visual Studio designer. A [somewhat terse] explanation of the main values you see at the top of the designer is on MSDN. I’ll tell you how I set up CR_Documentor and you can make the appropriate changes for your project.

  • Author: The name of the person/group responsible for the plugin. Since mine’s open source, I put “CR_Documentor Contributors” as the author.
  • Description: A short text description explaining what the plugin does. This can be used to search for plugins, so having keywords in the description can help people locate your plugin.
  • Supported VS Editions: Open this and select all of the standard VS 2010 editions as well as all Express editions. Since DXCore can run on any of these, you want your plugin for DXCore to also be discoverable by people with any of these versions.

    Note that you may start getting a build warning once you select the Express Editions of Visual Studio:
    source.extension.vsixmanifest : warning : VSIX targets Express Versions of Visual Studio but VSIX contains non-template content.

    From what I can tell, this warning is erroneous and can be ignored.

    On the other hand,if you choose the Express Editions and see this warning, you won’t be able to upload your VSIX to the Visual Studio Extension Gallery. The Gallery auto-checks the uploaded VSIX and rejects any VSIX with non-template content targeted to VS Express SKUs.

    Select all of the VS editions
  • License Terms: This is a small text file that will contain license information for your plugin. You’ll see this information when you install the plugin. I called mine “license.txt” because it’s easy.
    License content shows up in the installer
  • Icon: A 32x32 image (png/bmp/jpg/ico) that will appear in the Extension Manager and the Gallery next to your plugin’s name and description.
  • Preview Image: A 200x200 image (png/bmp/jpg/ico) that will appear in the Extension Manager and the Gallery showing a screen shot of your plugin.
  • More Info URL: A URL people can click from within the Extension Manager to read more about your plugin. I pointed mine to the CR_Documentor home page.
  • Getting Started Guide: A URL people can click from within the Extension Manager to learn how to get working with your plugin. I pointed mine to the CR_Documentor installation and usage wiki page.

Here’s what my manifest looks like, fully populated (click to enlarge):

A fully populated VSIX manifest

Any files you put in the VSIX package (the license, the icon, the preview image) need to be put in the same folder as the .csproj and the source.extension.vsixmanifest. I tried to put them at a different level in the project (solution level) and it caused all sorts of headache. Don’t buck the system, just stick them at the project level and life will be great.

Save your changes, rebuild, and debug your plugin. Now when you look in the Extension Manager, you’ll see your plugin along with the other VS extensions and it’ll look totally professional. (Click to enlarge)

Your plugin will show up in the Extension Manager.

I’ll be releasing CR_Documentor via VSIX soon. I have a few more things to tidy up since I’ve updated to .NET 4.0. In the meantime, if you want to see the working source, you can see it in Subversion in the Google Code repository.

net, gists, aspnet, csharp comments edit

When validating input in a web forms application, you need to validate data types on the client and server side because you’re working with text boxes and server controls. When you move to MVC, the client-side validation is still an interesting problem to solve, but the server-side validation all happens as a by-product of model binding.

The DefaultModelBinder has some special built-in provisions to handle data type parsing errors and automatically convert those into standardized model state errors.

If you’re writing a custom model binder and you want to participate in this…

  • Get the value to parse from the value provider. If there is no value to parse, return null and you’re done.
  • Create a ModelState object and set the Value property to the value you’re about to parse.
  • Add the ModelState object to the incoming ModelBindingContext. - Attempt to parse the value. If it’s successful, great. Return the properly parsed value and you’re done.
  • If you can’t parse the value…
    • Add a FormatException to the ModelState object.
    • Return null.

A simple skeleton binder that does all that looks like this:

using System;
using System.Web.Mvc;

namespace MyNamespace
{
  public class MyBinder : IModelBinder
  {
    public object BindModel(
      ControllerContext controllerContext,
      ModelBindingContext bindingContext)
    {
      var valueResult = bindingContext.ValueProvider.GetValue(bindingConext.ModelName);
      if(valueResult == null)
      {
        return null;
      }
      var modelState = new ModelState
      {
        Value = valueResult
      };
      bindingContext.ModelState.Add(bindingContext.ModelName, modelState);

      // Try to parse the value.
      object parsedValue = null;
      if(!TryParseValue(valueResult, out parsedValue)
      {
        // If you can't parse it, add a FormatException to the error list.
        modelState.Errors.Add(new FormatException());
      }

      // On success, return the parsed value; on fail, return null.
      return parsedValue;
    }
  }
}

The key part of that is the FormatException. After all is said and done, the DefaultModelBinder goes through the model state errors and finds all of the FormatExceptions that have been added. For each one it finds, it removes the FormatException and replaces it with a standardized message in the format:

The value '{0}' is not valid for {1}.

The {0} parameter is the original value result; the {1} parameter is the display name of the model/property being parsed.

The question then becomes: How do you localize/customize the data type validation message?

The DefaultModelBinder uses resources in the System.Web.Mvc assembly for its default set of error messages. There are two resource IDs to be aware of:

  • PropertyValueInvalid: The message for a value that couldn’t be parsed and resulted in a FormatException. This gets a String.Format call on it where the first parameter is the attempted value and the second parameter is the name of the property. Default value: The value '{0}' is not valid for {1}.
  • PropertyValueRequired: The message for a value that wasn’t available but is required, like a null sent in for an integer. No String.Format on this happens. Default value: A value is required.

If you want to use your own strings, you need to set theDefaultModelBinder.ResourceClassKeystatic property.

DefaultModelBinder.ResourceClassKey = "MyResources";

Once you do that, whenever one of these resources is required, the DefaultModelBinder will use HttpContext.GetGlobalResourceObject using the resource class key you provided and the ID noted earlier. Looking up the PropertyValueInvalid resource would effectively be a call to:

HttpContext.GetGlobalResourceObject("MyResources", "PropertyValueInvalid");

If you don’t have the value in your resources, DefaultModelBinder will fall back and use the default version.

Unfortunately, you can’t change the resource IDs, the string formatting arguments, or anything else… but at least you can change the messages.

windows, gists comments edit

I’m a big fan of the “command prompt here” context menu extensions for Windows Explorer. I use them all the time. Sometimes, though, I need to go the other-way-around.

That is, I’m at a command prompt and I want Windows Explorer open at the current location of my prompt.

explorer %CD%

Pretty simple, but super helpful. I had one of those “man, I’m stupid”moments when I put two-and-two together on this.

I ended up making a little batch file “explore.bat” and stuck it in my path.

@echo off
echo Opening Explorer on %CD%
explorer %CD%

So now it’s just “explore” at the prompt and magic happens. (Yes, I do realize it’s only five characters shorter, but I also get a nice little echo message to tell me what’s going on, plus I don’t have to remember it anymore.)

Note you can get some slightly different functionality if you use some of the command line switches for Windows Explorer, but for me, this works.

General Ramblings comments edit

Mini cupcake
makerIf you happen to have one of these mini cupcake makers, you can put a dollop of cookie dough in each of the cupcake spots and 11 minutes later – cookies.

You may have to “pop” the tops on them at around 8 minutes. They usually bubble a bit.

There was some highly scientific culinary experimentation involved in arriving at that 11 minute point. Earlier and they just fall apart; later and the cookies are pretty hard.

Note we didn’t use liners, we just threw the dough right in.

Oh, and they don’t turn out pretty. But tasty!