Scott Hanselman

Vista Reliability and my Tolerance for Pain

September 19, 2006 Comment on this post [31] Posted in Reviews
Sponsored By

VistareliabilityUPDATE: My bluescreens, while they appear to be in Win32k.sys, are actually in ATI's ati2evxx.exe. It's apparently the most common blue screen in RC1. Those naughty device drviers! Fortunately, it's been fixed in the latest ATI driver coming soon to a Windows Update near you. If this BSOD stops with this new build then my reliability numbers will go up greatly! Things are looking up.

Crap crap crap. My totally-stable-for-four-years machine is now blue screening daily under Vista. This is getting irritating.

Disclaimer: I'm pissed, so I'm blogging in frustration. I'm still stoked about Vista, I'm just not stoked about the new computer I'm going to have to buy if I can't get this one to work.

Notice my declining stability in the Reliability and Performance Monitor in the image at right? This isn't looking good and it's only been 10 days. Sigh. That's what I get for being an early adopter of RC1 Beta4.

This is very frustrating.  My guts tells me that this is a race condition in the menu handler of Win32k.sys, but that was supposed to have been fixed year ago.

I don't like being afraid of my own machine.

Stuff doesn't work anymore that I love:

  • FinePrint - The printing subsystem has totally changed. Perhaps I need to reinstall?
  • Canon PIXMA MP500 - I'm unable to share this printer to any other machines in the house.
  • 200 GIG Firewire - My WDC 200GIG Firewire just disappears. sbp2port.sys says, huh? Then the drive is gone for hours. Then, later, it'll show up.
  • 200 GIG USB - My Maxtor 200GIG USB plugs into my main USB2 Root Hub and Vista proclaims "Your Unknown Devices is ready to be used." It works plugged into my Dell Monitor (which is recognized as "Generic PnP Monitor" by the way.
  • Video - Flash Videos don't play. Well, that's not true, they start, play 5 seconds, and then stop, never to start again. Windows Media Player (WMV, MPG, AVI) videos only play on the Primary Monitor. On the secondary monitor they play at 5fps. Same with Media Center. It's totally unusable on the second monitor.
  • Nero - Nero doesn't work, but to Vista's credit, it did warn me ahead of time, so I uninstalled it.
  • IE7 - It keep getting into a state where the Menu Bar is showing, and I can't click in the address bar. I click in the bar and it flashes like it WANTS to select itself, then the Menu Bar flashes, then I'm left with focus on the page. I'm unable to navigate unless I Ctrl-O to get to a new page.
  • IOMEGA REV Drive - Looks like a regular CDR and the system tries to format it like one and chaos ensues. The drive - my primary backup media - is useless under Vista so far. Here's a screenshot about how much Iomega cares. Even kinder support from their "Gurus" on their forums. Here's what their moderator said: "We currently do not have any released drivers for Windows Vista, It is still unclear if we will need to release new drivers when Vista is finished. At this time we do not have any support for beta versions of Windows Vista. However apon official release of Vista you can expect any driver updates assuming they are required."
    Hm, this milk is bad. Maybe it'll be better later!
    Norevdriveonvista
  • Virtual PC - There's a known problem with the Network Drivers for VPC under Vista, but the supposed solution of just reinstalling those drivers from the Network Control Panel succeeds, then Virtual PC doesn't work anyway.
  • Windows Defender - Literally 75% of the items in my startup are marked "Not yet classified" and I have no way in the Defender UI to give it the equivalent of the "chill" command.
  • Windows Security - The security control panel insists that I have no Anti-Virus software even though eTrust has been running on my system for 4 years and was recognized under XP.
  • Aero - Programs here and there will cause a scary, heart-stopping black screen and monitor flicker as the entire system switches into Basic Mode because one program can't handle Aero (I smell a Windows Theming Denial of Service Attack here somewhere) and then gets stuck in Basic Mode long after the offending app has left.
  • Diskeeper - The greatest defragmenter ever just disappears every once in a while. Poof. Not sure what it was working on at the time, or if that file was important.
  • Windows Backup/Image Backup - I started getting concerned, so I tried to backup my stuff. First I did a Windows Image Backup and that ended up on I:\WindowsImageBackup\scottpc\Backup 2006-09-18 082147. Then I wanted to just backup some music files. Not only couldn't I select just those directories, but the system insists on backing up my C: Drive again, except this time it's to I:\SCOTTPC\Backup Set 2006-09-18 215943. Note the directory is one up now. Apparently I can't have both file backups and image backups on the same drive without this confusion. One other interesting note, the system teases me with the .VHD extension it puts on the Image Backups, but I suspect it's not a Virtual PC Hard Drive.
    Here's a fun little screenshot as the backup fails:
    Randomvistaerror1
  • TrueCrypt - Kind of works. You have to right click on the actual EXE itself (not the Start Menu Link) and "Run as Administrator." Not sure why the User Access Control just requires another click, rather than entering one's password again.
  • Microsoft Intellitype Keyboard and Mouse - Um, wha? You'd think that support for every Microsoft Keyboard and Mouse would be built in, but nope. Both these had to go before I installed. For some reason now my USB keyboard doesn't work in the text-based Vista pre-boot screens though it worked in the Windows XP pre-boot (F8) screens.

Why doesn't the team just come to my house and hang out? Am I just really, seriously, using computers in a way that isn't appropriate or is not supported? Should pave this machine? I really don't think so.

Eh, it's beta. What can you do? Vista, why has thou forsaken me?


Feel free to WinDBG.exe around if you like that kind of thing:

File Attachment: vistabugcheckwindbganalysis1.txt (5 KB)

File Attachment: Mini091806-01.dmp (136 KB) (it's NOT a zip, just change the extension to .dmp)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Preventing Dialogs on the Server-Side in ASP.NET or Trace.Fail considered Harmful

September 19, 2006 Comment on this post [4] Posted in ASP.NET
Sponsored By

Dialogs on the server-side? What is this nonsense of which you speak? Are you insane? Sadly no.

Back in the day, if you ran a VB6 COM DLL under Classic ASP, or as I like to call it "Poopy-SP," and you forgot to set "Unattended Execution" and "Retain in Memory" you could get yourself into a pickle if an error in the application caused a dialogbox or messagebox to pop up on the Server-side, just waiting for some poor schmuck to click "OK."

Recently I got an email that a high-powered blogger was seeing Dialog Boxes on his hosted instance of DasBlog while running under ASP.NET 2.0. These dialogs would hang the thread they were on, and hang IIS until they were dismissed. Shocked he and I were to say the least as "that ain't supposed to happen."

I suggested that he disable JIT Debugging in his registry. He tried it and indicated that he was getting a full stack trace inside the message box. Since the JIT Debugging Dialog doesn't look like that, I figured that wasn't the problem.

I spoke to some folks at MSFT who said that if there were unhandled exceptions being thrown from DasBlog, how they'd be presented would be controlled via the legacyUnhandledExceptionPolicy setting in his web.config like this (we don't have any unhandled exceptions, BTW, there's 'global' exception handlers in two places):

<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="true" />
  </runtime>
</configuration>

This tells the CLR to act more like .NET 1.1 in this respect as the behavior for unhandled exceptions has changed in 2.0 (MSDN Mag Article here and MSDN Topic here).

This "solution" smelled fishy to me, didn't seem like a good idea, and ultimately didn't solve his problem.

He then indicated that he was seeing errors in our SharedBasePageErrorHandler, which made sense to me as we handle all Page-level exceptions there.

private void SharedBasePageErrorHandler(object sender, EventArgs e )
{
    Exception pageException = Server.GetLastError();
    try
    {
        loggingService.AddEvent(
            new EventDataItem(EventCodes.Error,
            pageException.ToString().Replace("\n","<br>"),
            this.Page.Request.Url.ToString()));
        System.Diagnostics.Trace.Fail(this.Context.Error.Message);
    }
    catch
    {
    }
}

Seems pretty straightforward, and his stuff WAS getting logged indicating that the LoggingService.AddEvent was working fine.

What about this System.Diagnostics.Trace.Fail call, though? Seems harmless, but it's the only other thing in there and it would explain where the stack trace was coming from - it's inside of this.Context.Error.Message.

There was a problem back in the day with Trace.Fail or Debug.Fail locking up the ASP.NET Worker Process.

If you reflector into the implementation of the DefaultTraceListener in the .NET Framework you'll see a call to AssertWrapper.ShowAssert (that should only happen if AssertUIEnabled is true and UI Permissions are available.) That must be the case in our blogger friend's situation.

public override void Fail(string message, string detailMessage)
{
      string text1;
      StackTrace trace1 = new StackTrace(true);
      int num1 = 0;
      bool flag1 = DefaultTraceListener.UiPermission;
      try
      {
            text1 = this.StackTraceToString(trace1, num1, trace1.FrameCount - 1);
      }
      catch
      {
            text1 = "";
      }
      this.WriteAssert(text1, message, detailMessage);
      if (this.AssertUiEnabled && flag1)
      {
            AssertWrapper.ShowAssert(text1, trace1.GetFrame(num1), message, detailMessage);
      }
}

Assuming those things are true and ShowAssert does get called, interally the AssertWrapper.ShowAssert calls SafeNativeMethods.MessageBox only if the user is in an interactive mode. It's possible that MB_SERVICE_NOTIFICATION isn't being passed into the MessageBox indicating that it's a non-interactive service who is throwing the box.

Setting this option in the web.config (or machine.config) works around the issue by not even getting into AssertWrapper.

<configuration>
   <system.diagnostics>
      <assert assertuienabled="false" logfilename="c:\log.txt"/>
   </system.diagnostics>
</configuration>

This looks like a bug in ASP.NET 2.0, but we'll see.

The workarounds are:

  • Don't use Trace.Fail()

or

  • Set AssertUIEnabled="false" in your web.config.

The strange part about this is why it's seen on some systems and not others.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Accessing Private and Authenticated Feeds - Why it's important

September 19, 2006 Comment on this post [8] Posted in Corillian | Web Services
Sponsored By

Niall Kennedy blogs about accessing private feeds, but doesn't mention that IE7 and Office 2007 doesn't support it.

Niall says:

HTTP Authentication works with most desktop aggregators but runs into trouble with most online aggregators which rely on a common feed store based on feed and/or link URIs.

He's close. The Common Feed Store in the new Microsoft RSS Platform is an offline store that can't handle authenticated feeds. Since Microsoft is heading towards (headed?) dropping a huge ball here, it'll really depend on Bloglines, My.Yahoo and Live.com to get it right and support secure, authenticated feeds.

Dare posts about Niall's post and has an interesting comment:

"At the end of the day, can Bank of America trust that RSS Bandit or Bloglines is doing a good job of adequately protecting the feed from spyware or malicious hackers?"

Of course they can't, just as BofA can't control that I might use any old HTTP stack to talk to their regular website. Angle brackets over HTTP are what they are. RSS just makes them more regular and a little easier to parse. It's true, a man-in-the-middle attack or trojan that targets offline aggregators would have a field day; but they would with any client and nearly any protocol.

Dare adds:

More importantly, even if they certify these applications in some way how can they verify that the applications are the ones accessing the feed? Niall mentions white listing user agents but those are trivial to spoof. With Web-based readers, one can whitelist their IP range but there isn't a good way to verify that the desktop application accessing your web server is really who the user agent string says it is.

I would propose within the context of banking, keying off Dare's comment, that OFX and RSS are arguably the same thing with RSS just being more presentation focused. OFX being pulled into Microsoft Money and Yodlee is no different from RSS being pulled into RSS Bandit or Bloglines.

What's more interesting a question to ask is, how can we integrate CardSpace-style trust - real trust - between a client and server over the wide open Internet while still allowing for the unattended retrieval of data? Multi-factor authentication just isn't possible given the RSS model at any point other than the initial subscription. We'll have to include an InfoCard (read: client-side cert) token within an HTTP POST (or long GET) request for an RSS Feed. That's at least 12-18 months away from adoption by the masses - and that's assuming that VISA gives free InfoCards away to everyone. It'll take someone with the power of VISA or AMEX to become a (free) Security Token Service (STS); adoption by Verisign who will charge us $14.95 will be a non-starter. But this is all future talk.

I say this: IE7 and Office 2007 not supporting Basic or Digest Authentication out of the box for accessing secure feeds will negatively affect adoption of RSS more than any other failing of the spec since its inception. It will slow adoption down at every level; it will make it harder for Financial Institutions to justify it and it will flummox internal Enterprises who don't have completely NTLM/AD infrastructure.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Sun's Java JRE switches Vista Aero into Basic UI Scheme

September 18, 2006 Comment on this post [2] Posted in Musings
Sponsored By

If you're running Windows Vista RC1 and you notice that the nice Glassy Aero UI keeps switching into "Basic Mode" every time you visit a website with some Java on the page, you should upgrade to the Beta of the Java Runtime Environment (JRE) 6 Beta 2 (second link on the page) as it supports staying in Aero mode while running Java Applets.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Coding4Fun Hardware Boneyard - Using the CueCat with .NET

September 16, 2006 Comment on this post [7] Posted in Coding4Fun | Programming
Sponsored By

In an ongoing Mardi Gras celebration of the soon-to-happen Coding4Fun Redesign, I got permission from the rockin' awesome Dan Fernandez to published a Coding4Fun article here on Hanselman.com as an exclusive. This article will be republished on the new redesigned site very soon, along with a "Coding with the FingerPrint Reader" article and possible a fabulous super-secret IR/Robot/PowerShell article.

Dig up your CueCat and enjoy.


Coding4Fun Hardware Boneyard - Using the CueCat with .NET

Summary: In this rogue installment of "Some Assembly Required" column, Scott Hanselman borrows Travis Illig's CueCat BarCode scanner and creates a plugin for Windows Live Writer than lets him blog more easily about books he's reading. We decode the bar code info, change UPCs into ISBNs, call Amazon's Web Service via REST and integrate with Windows Live Writer all in one article. Whew!

The CueCat BarCode Scanner

180px-Cuecat1Here's what Wikipedia has to say about the CueCat BarCode scanner:

The :CueCat is a cat-themed handheld barcode reader developed in the late 1990s by the now-defunct DigitalConvergence Corporation, which connected to computers using the PS/2 keyboard port and later USB. The :CueCat enabled users to link to an Internet URL by scanning a barcode appearing in an article, catalog or on some other printed matter. In this way a user could be directed to a web page containing related information. The system that supported this functionality is no longer in operation.

Ah, nothing like the smell of obsolete hardware to get me thinking. You can get CueCats all over, there are millions of them, including on ebay. I asked around and my buddy Travis Illig loaned me his. It's ripe for hacking.

Decoding the CueCat Output

The CueCat speaks a funky encoded format that it "types" as if it were a keyboard. For all intents, it IS a keyboard as the PS2 version we're using is installed in series with your existing (if you have one) keyboard.

For example, my copy of Neil Gaiman's excellent "Stardust" produces this output:

.C3nZC3nZC3nZE3r0Chr3CNnY.cGf2.ENr7C3n1C3PWD3rYCxzYChnZ.

Which can be broken up into a series of tokens, some are one character and some are two. The periods are delimiters, so we'll toss them for now.

 C3 n Z C3 n Z C3 n Z E3 r 0 Ch r 3 CN n Y EN r 7 C3 n 1 C3 P W D3 r Y Cx z Y Ch n Z

The first part is the serial number of CueCat itself, which freaked a log of privacy folks out in the past. The scanned barcode is actually the last part after the third . delimiter:

EN r 7 C3 n 1 C3 P W D3 r Y Cx z Y Ch n Z

Each of these tokens is a key to a lookup table. The encoding is pretty lame, and very arbitrary, but it confounded folks for a few hours at least. There's dozens of implementations of this decoding to be found on the Web, although none in .NET, which is where we come in. Here's a resource page that includes several implementations in C, Perl and Python, http://www.beau.lib.la.us/~jmorris/linux/cuecat/ but it hasn't been updated in at least six years. There's also some very old but interesting discussion here http://cexx.org/cuecat.htm.

The lookup table looks like this (a C# example):

static Decoder()

{

    decodingTable = new Dictionary<string, int>[3];

    decodingTable[0] = new Dictionary<string, int>();

    decodingTable[1] = new Dictionary<string, int>();

    decodingTable[2] = new Dictionary<string, int>();

 

    decodingTable[0]["Z"] = 0;

    decodingTable[0]["Y"] = 1;

    decodingTable[0]["X"] = 2;

    decodingTable[0]["W"] = 3;

    decodingTable[0]["3"] = 4;

    decodingTable[0]["2"] = 5;

    decodingTable[0]["1"] = 6;

    decodingTable[0]["0"] = 7;

    decodingTable[0]["7"] = 8;

    decodingTable[0]["6"] = 9;

 

    decodingTable[1]["C3"] = 0;

    decodingTable[1]["CN"] = 1;

    decodingTable[1]["Cx"] = 2;

    decodingTable[1]["Ch"] = 3;

    decodingTable[1]["D3"] = 4;

    decodingTable[1]["DN"] = 5;

    decodingTable[1]["Dx"] = 6;

    decodingTable[1]["Dh"] = 7;

    decodingTable[1]["E3"] = 8;

    decodingTable[1]["EN"] = 9;

 

    decodingTable[2]["n"] = 0;

    decodingTable[2]["j"] = 1;

    decodingTable[2]["f"] = 2;

    decodingTable[2]["b"] = 3;

    decodingTable[2]["D"] = 4;

    decodingTable[2]["z"] = 5;

    decodingTable[2]["v"] = 6;

    decodingTable[2]["r"] = 7;

    decodingTable[2]["T"] = 8;

    decodingTable[2]["P"] = 9;

}

 

private static Dictionary<string, int>[] decodingTable;

See how some tokens are two chars and some are one? We'll run through the tokens testing to see if they are in each dictionary and creating the result. For my Gaiman book, we end up with:

978006093471251300

Now what do we do with it?

UPCs and ISBN

Universal Product Codes or UPCs are different from ISBNs. This page at the Book-Scanning Project gave me the algorithm I needed to convert a UPC code into an ISBN that I could use to talk to Amazon.com. Most books have a bar code with 13 digits starting with "978." Turns out that the first 3 digits of a UPC are the country of origin, but "978" is "Bookland." Apparently the UPC number for a book is called the "Bookland EAN."

Here's the Javascript taken from http://isbn.nu.

if (indexisbn.indexOf("978") == 0) {
   isbn = isbn.substr(3,9);
   var xsum = 0;
   var add = 0;
   var i = 0;
   for (i = 0; i < 9; i++) {
        add = isbn.substr(i,1);
        xsum += (10 - i) * add;
   }
   xsum %= 11;
   xsum = 11 - xsum;
   if (xsum == 10) { xsum = "X"; }
   if (xsum == 11) { xsum = "0"; }
   isbn += xsum;
}

Now, converted into C#

public static string ConvertUPCtoISBN(string code)

{

    if (code.StartsWith("978") == false)

    {

        throw new ArgumentOutOfRangeException("UPCs that might be books have length 13 and start with 978");

    }

    code = code.Substring(3,9);

    int xsum = 0;

    for (int i = 0; i < 9; i++)

    {

        xsum += (10 - i) * int.Parse(code[i].ToString());

    }

 

    xsum %= 11;

    xsum = 11 - xsum;

 

    String val = xsum.ToString();

 

    switch (xsum)

    {

        case 10: val = "X"; break;

        case 11: val = "0"; break;

    }

 

    code += val;

    return code; //Now an ISBN

 }

So, my Gaiman UPC of 978006093471251300 turns into an ISBN of 0060934719. This ISBN can be used to talk to Amazon. Note that the source code to this article includes both C# and VB.

Calling Amazon's REST Web Service

Amazon.com has a rich Web Services platform that includes formal SOAP-based Web Services, but for our project, their HTTP GET-based system will work nicely. I visited the Amazon Web Services Home Page and applied for a free Amazon Web Service Developer Key. You'll need to do the same if you want to get the code to work. Amazon has some create documentation on their site that makes this really easy.

I'll make an HTTP GET call to Amazon like this (broken up for readability):

http://webservices.amazon.com/onca/xml?
Service=AWSECommerceService&
AWSAccessKeyId=YOURAWSKEYHERE&
Operation=ItemLookup&
ItemId=0060934719&
IdType=ASIN

This HTTP GET will result in an XML document as seen in this screenshot.

Hanselmancuecataws

Ah! Just the kind of information I was looking for.

A CueCatConsole

Before we integrate with Windows Live Writer, let's make a little test console to make sure this all works together. I put the CueCatDecoder into a small reusable assembly and called it from the Console Application. I also pull the Amazon Web Services key from the configuration file. Note that VS.NET 2005 creates a strongly typed accessor for the "AWSAccessKeyId" automatically.

I've removed the error handling in this listing for clarity.

CueCatDecoder.Product product = null;

product = CueCatDecoder.Decoder.Decode(input);

Console.WriteLine("Type: {0}.", product.Type.ToString());

string amazonType = "ASIN";

string amazonUrl = String.Format("http://webservices.amazon.com/onca/xml
      ?Service=AWSECommerceService&AWSAccessKeyId={0}
      &Operation=ItemLookup&ItemId={1}
      &IdType={2}
      &SearchIndex="
,

            Properties.Settings.Default.AWSAccessKeyId,

            product.ID,

            amazonType);

 

System.Net.WebClient web = new WebClient();

string amazonData = web.DownloadString(amazonUrl);

XmlDocument xmlDocument = new XmlDocument();

XmlNamespaceManager xnm = new XmlNamespaceManager(xmlDocument.NameTable);

xnm.AddNamespace("def", "http://webservices.amazon.com/AWSECommerceService/2005-10-05");

xmlDocument.Load(new System.IO.StringReader(amazonData));

XmlNode itemNode = xmlDocument.SelectSingleNode("/def:ItemLookupResponse/def:Items/def:Item", xnm);

Console.WriteLine(String.Format("FOUND a {0} called {1} by {2}",

        itemNode.SelectSingleNode("//def:ProductGroup", xnm).InnerText,

        itemNode.SelectSingleNode("//def:Title", xnm).InnerText,

        itemNode.SelectSingleNode("//def:Author", xnm).InnerText));

Notice that since the Amazon XML includes a default namespace, we have to let SelectSingleNode know about that namespace by registering a namespace prefix - in this case "def:" - that we use later in our XPath. There are many ways to query XML within the .NET Base Class Library that are faster, but for a small application like this the XmlDocument is a reasonable choice when performance doesn't matter.

Here's the output from my CueCatConsole:

Coding4Fun: CueCatDecoder by Scott Hanselman @ http://www.hanselman.com
Scan item with CueCat now or press Q then Enter to exit
.C3nZC3nZC3nZE3r0Chr3CNnY.cGen.ENr7C3T7D3f0C3vYDW.
Type: ISBN.
FOUND a Book called The Goal: A Process of Ongoing Improvement by Eliyahu M. Goldratt
Scan item with CueCat now or press Q then Enter to exit
.C3nZC3nZC3nZE3r0Chr3CNnY.cGf2.ENr7CNz6C3z6DxvXChzXD3P6.
Type: ISBN.
FOUND a Book called Programming Sudoku (Technology in Action) by Wei-Meng Lee
Scan item with CueCat now or press Q then Enter to exit
.C3nZC3nZC3nZE3r0Chr3CNnY.cGf2.ENr7C3n1C3PWD3rYCxzYChnZ.
Type: ISBN.
FOUND a Book called Stardust by Neil Gaiman
Scan item with CueCat now or press Q then Enter to exit

Fantastic. It works. Let's integrate it with Windows Live Writer, the new Offline Blogging Editor in BETA (at the time of this writing) from http://www.live.com.

Integrating with Windows Live Writer

Windows Live Writer (download) is a new desktop application that folks with blogs can use to write their blog posts offline. I use DasBlog for my blog at http://www.hanselman.com, and since DasBlog supports the standard MetaWebLog API also supported by Live Writer, I was in business right away.

Windows Live Writer also has an SDK that supports "Content Source Plugins" to extend the capabilities of the application and let folks insert new kinds of data. That's exactly what I want to do! I'd like to click "Blog about Book" and have the book title and author appear linked with an image of the book cover. I'd also like have the Amazon.com URL to include my Amazon Associates ID so I could make a few cents if someone clicks my link and buys the book.

Writing a plugin for Windows Live Writer couldn't be easier. The SDK includes step-by-step instructions. I created a class that derived from WindowsLive.Writer.Api.ContentSource and overrode the CreateContent method, returning my new snippet of HTML in the newContent string variable.

Here's the gist of the source, again with error handling removed for clarity. VB is included in the download.

using System;

using System.Collections.Generic;

using System.Windows.Forms;

using WindowsLive.Writer.Api;

using CueCatDecoder;

 

namespace AmazonLiveWriterPlugin

{

    [WriterPlugin("605EEA63-B74B-4e9d-A290-F5E9E8229FC1", "Amazon Links with CueCat",

        ImagePath = "Images.CueCat.png",

        PublisherUrl = "http://www.hanselman.com",

        Description = "Amazon Links with a CueCat.")]

    [InsertableContentSource("Amazon Links")]

    public class Plugin : ContentSource

    {

        const string AMAZONASSOCIATESID = "amazonassociatesid";

        const string AMAZONWEBSERVICESID = "amazonwebservicesid";

 

        public override System.Windows.Forms.DialogResult CreateContent(System.Windows.Forms.IWin32Window dialogOwner, ref string newContent)

        {

            using(InsertForm form = new InsertForm())

            {

                form.AmazonAssociatesID = this.Options[AMAZONASSOCIATESID];

                form.AmazonWebServicesID = this.Options[AMAZONWEBSERVICESID];

                DialogResult result = form.ShowDialog();

                if (result == DialogResult.OK)

                {

                    this.Options[AMAZONASSOCIATESID] = form.AmazonAssociatesID;

                    this.Options[AMAZONWEBSERVICESID] = form.AmazonWebServicesID;

                    Product p = Decoder.Decode(form.CueCatData);

                    AmazonBook book = AmazonBookPopulator.CreateAmazonProduct(p, form.AmazonWebServicesID);

                    string associatesId = form.AmazonAssociatesID.Trim();

                    string builtAmazonUrl = "<a href=\"http://www.amazon.com/exec/obidos/redirect?link_code=as2&path=ASIN/{0}&tag={1}&camp=1789&creative=9325\">{2} by {3}</a><a href=\"http://www.amazon.com/exec/obidos/redirect?link_code=as2&path=ASIN/{0}&tag={1}&camp=1789&creative=9325\"><img border=\"0\" src=\"http://images.amazon.com/images/P/{0}.01._AA_SCMZZZZZZZ_.jpg\"></a><img src=\"http://www.assoc-amazon.com/e/ir?t={1}&l=as2&o=1&a={0}\" width=\"1\" height=\"1\" border=\"0\" alt=\"\" style=\"border:none !important; margin:0px !important;\" />";

                    newContent = string.Format(builtAmazonUrl, book.ID, associatesId, book.Title, book.Author);

                }

                return result;

            }

        }

    }

}

Notice the crazy Amazon.com URL. Most folks with blogs become Amazon Associates to make a little money to pay for hosting (you won't get rich doing this, believe me). The Amazon Associates site includes link builders that create these complex snippets of HTML. I selected the large image, basic HTML option, then inserted String.Format tokens like {0} in the appropriate places.

Installation is simple, just copy Coding4Fun.AmazonLiveWriterPlugin.dll and Coding4Fun.CueCatDecoder.dll to C:\Program Files\Windows Live Writer\Plugins and they will be automatically detected.

Our plugin appears automatically in the Windows Live Writer sidebar. Notice the text and the icon was picked up from the Plugin class attributes. My Amazon Web Services ID and Associates ID are both stored internally by an Options bag managed by Windows Live Writer, making management of preferences very easy for the plugin developer. Once the CueCat data is scanned, we decode it and make the HTTP GET Web Services call using our external CueCat assembly created earlier.

Hanselmancuecatplugin

Conclusion

There's things that could be extended, added, and improved on with this project. Here are some ideas to get you started:

  • Include support for CDs and other products available on Amazon.
  • Add support for more web-based databases with UPC codes.
  • Support different Amazon link styles. 
  • Create a system to print out product tags for a Garage Sale.

Have fun and have no fear when faced with the words - Some Assembly Required!

File Attachment: Hanselman Coding4Fun CueCat.zip (74 KB)


Scott Hanselman is the Chief Architect at the Corillian Corporation, an eFinance enabler. He has thirteen years experience developing software in C, C++, VB, COM, and most recently in VB.NET and C#. Scott is proud to be both a Microsoft RD and Architecture MVP. He is co-author of Professional ASP.NET 2.0 with Bill Evjen, available on BookPool.com and Amazon. His thoughts on the Zen of .NET, Programming and Web Services can be found on his blog at http://www.computerzen.com. He thanks his wife and 9 month old Z for indulging him in these hobbies!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.