Nates Stuff

Perspectives on Computing :: Leave a Comment
Americanish

I am in the middle of a great book right now called The Mother Tongue.  It’s about the origin of the English language.  In the early pages it pokes a little fun at the attempt some companies who have a false mastery of the language.

I seem to buy a lot of these products and they are hilarious!  I’ve been meaning to post this image for a while!  It’s the back of the box of my network cable tester.

Cable Tester Back

My favorite is the “Attention”, “Do not change it on your mind.”  I can’t even fathom what it’s trying to warn me against.  I’ve got a manual for my XO brand radio that is just as bad!  Most of the time you have no clue what it’s trying to tell you.  The Radio, by the way, is not much better.  The English on it is pretty simple, but there are large sections of the user interface that are not translated from Chinese! It’s also a pretty poor MP3 player which sucks because that is the feature I purchased the radio for.

Strong Hashing

Cryptograpny

There are three main tools used in cryptography.  They are hashing, synchronous encryption, and asynchronous encryption.  A very superficial definition could be:

Hashing – Creating a unique value based on blocks of input. Even a minor change in the input, say a value changes by 1 bit, will cause a good hash to change drastically.  Ideally, every bit in the right place is the only way to create this number. Hashing is a one-way function so it is not useful for data hiding, but it is very useful for validating the authenticity of data such as a download or a certificate.  It is used to ensure that a message has not been tampered with.  If even a little change is introduced the unique value becomes very different.  If two different blocks of data are hashed using an algorithm that produces the same resultant value it is called a collision, and the algorithm is considered compromised.  This is because a malicious hacker could change the value of the data and still have the resulting hashed value not change.  When this happens the hash algorithm is useless.  This actually happens a lot!

Synchronous Encryption – This means changing an input block using a key into something unrecognizable by anyone not in possession of that key. This type of encryption is what people most commonly think of when you talk about encryption and has been used since about 1900 BC! Depending on the size of the key and the technique used, It is also the most difficult to break.

Asynchronous Encryption – Two keys are generated that are mathematically linked.  A key that is used to write (public key) and another key to read (private key).  The idea is that a public key can be given to whomever wants to communicate with us.  It doesn’t matter if a hacker captures this key because it doesn’t allow them to read the message that the client sends back.  In the case of SSL/TSL the message that the client sends back is a key to use for synchronous encryption. 

Only the recipient whom has the private key will be able to unlock this message and be able to further communicate with the key that was sent.  The only way to break this type of encryption is to guess the key, but if you have sufficient key lengths (1024 is now standard in the US, for example), then a brute-force attack would take longer than the Universe has to live to guess the key.  Furthermore, because we passed a random set of data (a new synchronous encryption key) a hacker wouldn't know if they successfully cracked the message or not.

This may sound all well and good but SSL/TSL breaks down if the authenticity of the RP (Remote Party / Server) cannot be verified.  That is why we have CA’s, or Certificate Authorities.  The job of a CA is to vouch for the authenticity of the remote party.  This means that IF YOU GET A CERTIFICATE WARNING THIS IS THE ONLY THING PROTECTING YOU FROM A MAN-IN-THE-MIDDLE ATTACK.  If someone relayed communication between you and the remote party the certificate warning is the only thing that will protect you.

Actually, all three of these cryptographic technologies are used in SSL/TSL.  If you have ever gone to an https website and clicked on the “lock” icon, this is what you are likely to see.

Certificate-1 Certificate-2 Certificate-3

The first tab states that the certificate’s purpose is to “Ensure the identity of a remote computer” and has some information about the CA and expiration date.  In the second tab you can see that the hash algorithm is SHA1 and that the public key is an RSA 1024 bit key. Symmetric communication takes over when we pick a key and a symmetric encryption algorithm. The server may reject the algorithm we selected and you would be forced to pick again until you both agreed on which algorithm to use.  Now days it’s likely to be Rijndael AES encryption that is chosen to communicate the rest of your session.

Back to Hashes

Hashes are usually considered the weakest link in this chain of cryptography.  To date the following hash algorithms have been compromised: HAVAL, MD2, MD4, MD5, PANAMA, RadioGatun, RIPEMD, SHA-0, SHA-1, and Tiger.  Many of these algorithms are not completely compromised, bur rather practically compromised.  We know that we can break them but it’s unfeasible. That is to say that generating a collision for a message is likely to be more difficult than it’s worth.  The most common algorithms in use today are MD5 and SHA1, both of which have recently been compromised.

In 1996 a flaw was found in the design of MD5 and in 2007 it was broken completely and it was demonstrated that a pair of files could be created which have the same hashed value and they were able to fake SSL certificate validity.  This caused the DHS (Department of Homeland Security) to issue a statement indicating that the MD5 function should be considered cryptographically broken and that it is unsuitable for further use.  This is no small thing as we have an insatiable addiction for MD5!  We use it everywhere!  Everywhere else we use SHA-1 which has also been cracked.  The DHS suggests SHA-2 but since it is algorithmically similar to SHA-1 there is a good chance it won’t be effective for very long. 

So how do we make hashes stronger?

I don’t believe that we are all of a sudden going to invent a new generation of hashing algorithms that are significantly stronger.  In fact, if anything we’re getting better and better at cracking these hash functions so they have shorter and shorter lifespan.  I think what we will have to do is become more inventive in our use of them.

Hash functions work a lot like block ciphers (synchronous encryption), that is they work on blocks of data at a time.  If you are going to fool a hash function then you either need to make the tampered block result in the same hashed value or you need to “balance” out the message in a later block.  Blocks are typically 512 bytes of data.  If there is a remainder at the end of the message it will be hashed with a partially-empty block. 

Blocks

One obvious thing you can do to protect yourself is to supply more than one type of hash for any given set of data.  So, for example, if you download a file from the Internet you sometimes see a few different hashes.  You may fool one hash, but you’re not going to fool two hashes with the same collision, especially if the two hashing algorithms are unrelated.

While using two hashes seems a reasonable approach there are some down sides.  First, it’s twice the work for your computer hashing the same bits using two different hash functions. Secondly, it is not possible in every scenario where a single hash must be used.  One example of this is the use of HMAC authentication.  Such authentication works kind of like this:

Client   Server
User “nzaugg” wishes to authenticate

------>

 
 

<------

Okay, here is a very large and unique phrase.
Hash(Password+Phrase)

------>

Yep, that’s what I get when I Hash(Password + Phrase)

We can’t really send more than one hash as a response and even if we did, it wouldn't help any.

While playing with a pair of hacked files I had an idea which could still use MD5 but stagger the hashes so the blocks align differently, then hash those hashes together.  It sounds pretty home-brew, but let me explain.

Offset Hashing

Lets say that I have that same hash as before but I have compromised data in block #1 (red line). If we hash that data again with different data before it and at a different place in the block it will yield a different result. In order for this to work though, we need actual data in the offset blocks (in blue).  We can simply take the last 256 bits from the end of the file for the beginning and the first 256 bits for the end. We could also fill it with A..Z, etc. It doesn’t matter too much but should be convention.  This is important because if we left the blocks on the beginning and the end empty then we could not detect tampering on those two blocks using MD5 as it’s somewhat position independent.  At least it didn’t work when I tried it.  It has more to do with what values preceeded it and therefore produced the same value if the previous values were all zero.

Offset Blocks

And using no new hashing algorithms we can now detect the hash tampered data. It’s still about twice as expensive as a 1-pass hash but overall hashing is fairly inexpensive and modern computers more that compensate.  It may be possible to calculate a collision once, but is impossible to calculate the collision for one hash pass and have it work for the other.

It was a little troubling to me that unless the blue squares were filled with some data I was unable to detect the changed data. So I thought why not offset hash with two different hash algorithms and then use a 3rd to hash together the results of the other two hashes.  I could use MD5 for pass 1, SHA-1 for pass 2, and SHA-2 to combine the two passes into a single hash.

Offset Blocks Different Hash Functions

The result is an unbeatable hash!  It doesn’t matter that both SHA-1 and MD5 have been broken, no one has ever broken the pair used together and offset hashing makes that task even more impossible especially considering that the two algorithms chosen are very different algorithms. The third algorithm doesn’t need to be different than one of the other two chosen, it essentially just helps make subtle differences in the hashes stand out.

By the way, BitTorrent downloads rely heavily on hashing functions for both data verification (did I hear you correctly) but also for authenticity.  There are programs out there (links below) that can change the file and get it to generate the same hash.  When you download stuff off of BitTorrents you can never know what you are downloading does not contain a virus.  Even if they were to use my staggered diff hash idea the virus could have been there before the torrent file was even created.

Links
C Sharp Query: A replacement for SQL Server Full Text Search

SearchIcon

A short time ago I was working on a project using SQL Server Full Text Search.  This was my first real deep exposure to the engine and it worked pretty well.  However, for the project I was working on there were some very serious problems that I was never able to overcome.  Try as I might to tweak SQL Server Full Text Search I was never able to tweak it as much as I needed. 

Here are some of the problems that I faced while playing with the Full Text Engine:

  1. SQL Server Full Text Engine 2005 (FTE) had some scalability issues.  I didn’t so much need it to index hundreds of millions of rows (although it needed to be able to do a lot of records) but mostly I need a lot of queries per second.  It seemed to only be able to run on a single processor at at time.  If someone did a ridiculously huge query everyone else on that server would come to a halt!  On a box with 8 logical processors this was not acceptable!
  2. FTE has not capability for compound words (i.e. the term ‘Riverboat’ and ‘River Boat’).  Sure, you could put such pairs in the Thesaurus as expansions, right?  Well, I will get to that.
  3. FTE black boxed the word breaker.  You might think that this is not a big deal but you would be wrong!  FTE considered the ampersand ‘&’ a word breaker when I needed it to not be.  For example, in FTE if you did a search for ‘R&B’ the ampersand would break that into the words ‘R’ and ‘B’.  Both of such are in the noise words list by default. Therefore, the terms ‘R&B’ and ‘AT&T’, etc, were optimized out of the query by design.  Creating your own word breaker is possible but very difficult and not recommended.  Also, I needed an underscore ‘_’ to break words and it did not.
  4. The ranking information that came back from FTE was not very good.  This is because the ranking could not be used to compare two different queries and also because the ranking data was not very good.  The numbers were not very evenly distributed. IE, I might have the top 30 rows with a rank of “34”, and the rest had a rank of “20”.  The numbers are also arbitrary and meaningless.
  5. The ‘FREETEXTTABLE’ term is useless!  It will only UNION sets rather than INTERSECT them.  This means that a search for ‘Sax’ could return 1,254 rows while the term ‘Tenor Sax’ would return 2,259 rows.  Every term you add will increase the size of the result rather than decrease it. We had to use the ‘CONTAINSTABLE’ search term but that led to problems with any compound word in the thesaurus and looked awful! Something like:
    (“Sax” OR “Sax*” OR FORMSOF(“Sax”) OR THESAURUS(“Sax”))
    That is for a one term word.  Each word would need it’s own set of criteria.
  6. FTE was kind of slow on the larger queries.  Returning a set of over 1,000 seemed to be quite a chore!
  7. In order to add things to the thesaurus you had to change the XML file and restart the service.  You may even have to rebuild the index – I don’t remember.
  8. Every few days or so the searches would slow down a lot!  In order to get it speed back up we had to shut down the FTE service and the SQL Server service and rebuild the index. We hoped to write a script to do this chore every so often but for some reason the script that Management Studio generated didn't seem to work the same way.

That's all well and good, but what did I gain by writing my own version?

  1. Most of the code is reentrant and the code that is not uses highly efficient reader/writer locking to make sure the index doesn’t change out from underneath you!  This means that I can fully utilize all logical processors on the machine and large queries will not interfere with smaller ones.
  2. It also means that the index can be rebuilt while the old index is still in use.  Once the build is complete they can be quickly and easily swapped.
  3. I was able to create ranking that yielded very nice even distribution between every result in the set. (more on this below)
  4. I was able to pull word breaking terms from a file. There is also one file per language. These files are also used to break the search term that was used to create the index. Because it uses the exact same word breaker on both the search terms and the index data we got much better search matching!  Terms like ‘R&B’ were indexed and searched for in the same way. Noise words would be dropped the same way as well. It made working with compound words possible.
  5. This search is much faster than SQL Server FTE.  All my queries returned in less than 100ms, usually much less!
  6. Ranking could be customized. (more on this below)
  7. I was able to create a ‘FREETEXTTABLE’ style query that reduced the number of results as the search term became more specific.
  8. I could change the thesaurus on the fly.
  9. I was able to manage compound words much more effectively.
  10. I got to implement my own custom Binary Search which was pretty fun!

Before you go tearing off to download my version of search and rip out SQL Server FTE, there are some tradeoffs to using my system.  Most notably I keep my index in memory where I don’t believe FTE does that.  In the case of this project it only resulted in ~16MiB of memory usage per language.  We had 32GiB of memory on that system so that isn’t such a big deal even with the pretty deep set of data indexed. But if you had 100 million or more then this may not be a good solution for you. 

CSharpQuery is also slower at creating the index.  It seems like the index creation for FTE is almost instantaneous where it takes about 10 seconds per language to build mine.  Again, not a big deal in most cases but could be problematic for very large data sets. Lastly, FTE is able to provide the data right to you in T-SQL and is easy to setup and consume.  CSharpQuery will merely give you a list of indexes and ranking info and you are expected to get the data into SQL Server yourself.  Although, this does mean that it can be used outside of SQL Server which is a plus!

The real genius in the CSharpQuery code is the ranking.  As mentioned above the ranking in FTE has two fatal flaws.  It doesn’t allow the rankings to be used outside of the result set and the ranking values is pretty scant.  In a typical result set of 100 rows, there would only be maybe 5-10 distinct numbers for ranking data.  This means that there are large sections that appear unsorted or unranked! This is a pretty difficult problem to come up with a ranking algorithm that is both universal in it’s application and provides a ranking so unique and precise as to almost be synonymous with a hash. 

This is accomplished by using these four filters:

  • Word Proximity – How close are the search terms to each other?
  • Multiple Occurrence – How many times does the search term appear in the indexed phrase?
  • Low Phrase Index – Are the search terms found near the beginning of the phrase, perhaps in a title?
  • Word Matching – Did we find the exact words or did we use the thesaurus or front word matching to find this row?

The ranking of results is the most complex part of the code. It is also the most process intensive. Each row is ranked independently based on the 4 different filters. Each filter ranks the row between 0 and 1. To produce the final rank, each filter result is weighted as some filters are better at finding what you are looking for than others. Based on this setup some of the less discretionary filters can be used to break ties. This results in a very nice even distribution between the entire result set. The final ranking number is also a number between 0 and 1.

Another great feature of CSharpQuery is the tweaked thesaurus for compound words.  Lets say that you were looking for a song titled “Riverboat Shuffle”. In FTE if we were to simply do a thesaurus lookup on “Riverboat” we’ll also get “River Boat”.  This means it will return all results with river and boat but not necessarily together.  In my version of the thesaurus the exact same results are returned from the queries “Riverboat Shuffle” or “River Boat Shuffle”.

NOTE: The thesaurus that comes with this download is not necessarly a great one.  Basically I used a dictionary to find compound words but discovered that there are a lot of words that to a computer look like a compound word but are actually not.  Such an example is “monkey” was found from “mon” and “key” but “mon key” is not the same as “monkey”.  It is your responsibility to clean up the thesaurus.  If you would like you can also send it back to me when your done so everyone else can benefit.

Building an Index

In yet another instance of “It’s free for a reason”, CSharpQuery doesn’t come with any kind of tool to build your index for you.  Instead you have to make a small program to build the index.  This can also be advantageous.  For example, in my C# code I combine different rows from various track tables. This way even though the composer is part of a different table, typing their name in will show all of the tracks for that composer.

public static void UpdateCSharpFullTextSearchIndex(
    IDataStore con, int langId, CultureInfo cultureInfo, 
    string cSharpQueryIndexDirectory) {
    
    // Quicksearch SQL
    string quicksearchSql = @"
        SELECT 
            t.TrackID,
            p1.Text + ' ' + -- TrackName
            p2.Text + ' ' + -- TrackDescription
            p3.Text + ' ' + -- AlbumName
            p4.Text + ' ' + -- LibraryName
            ar.ArtistName + ' ' +
            t.Publisher as IndexText
        FROM Track t 
        INNER JOIN Album a 
            ON t.AlbumID = a.AlbumID
        INNER JOIN RecordLabel r 
            ON a.RecordLabelID = r.RecordLabelID
        INNER JOIN Artist ar 
            ON t.ArtistID = ar.ArtistID

        INNER JOIN Phrase p1 
            ON t.Title_Dict = p1.DictionaryID 
            AND p1.LanguageID=@LangID
        INNER JOIN Phrase p2 
            ON t.Description_Dict = p2.DictionaryID 
            AND p2.LanguageID=@LangID
        INNER JOIN Phrase p3 
            ON a.AlbumName_Dict = p3.DictionaryID 
            AND p3.LanguageID=@LangID
        INNER JOIN Phrase p4 
            ON r.RecordLabelName_Dict = p4.DictionaryID 
            AND p4.LanguageID=@LangID";

    SqlConnection conn = Con(con).Connection as SqlConnection;
    try {
        conn.Open();
        SqlCommand cmd = new SqlCommand(quicksearchSql, conn);
        cmd.CommandType = System.Data.CommandType.Text;
        cmd.Parameters.AddWithValue("@LangID", langId);
        SqlDataReader rdr = cmd.ExecuteReader();

        SQLServerIndexCreator creater = new SQLServerIndexCreator();
        
        // Quicksearch Index
        creater.CreateIndex(
            "QuickSearch", // The name of the index
            cSharpQueryIndexDirectory, // Index Dir
            rdr, // An open Data Reader
            cultureInfo, // The culture info for this index
            "TrackID", // The [Key] (int) column of the index
            "IndexText"); // The [Value] column of the index
        rdr.Close();
    } finally {
        if (conn != null && 
            conn.State == System.Data.ConnectionState.Open)
            conn.Close();
    }
}

As you can see in this index creation example, we concatenate the track name, track description, album name, and library name for the quick search index.  This will then create the .index file like “Index_QuickSearch.en-US.index”.  You will notice the file is in the format of “Index_[index name].[culture code].index”.  It is important to have a few things in place before you try this.  The following files should exist by default:

  • Invalid Chars.txt – Contains invalid chars like [tab]~{}[CR][LF], etc.
  • Noisewords.global.txt – Contains words that are not useful to index like [a-z], “and”, “the”, etc.
  • Substitutions.global.txt – Contains a list of substitutions you wish to make, this is usually used to indicate what symbols break words and which ones do not.  For example: “:= “ means that we’re going to substitute the “:” sign for a blank space.
  • Thesaurus.global.xml – The thesaurus contains synonyms and compound words.  NOTE: if you use the compound word functionality, the compound term must come second.
  • WhiteSpace.global.txt  – This file tells the work breaker which chars are whitespace so those can be safely used to split the word terms.

These files all work together to help create a better index.  You will also notice that the convention is “.global.txt” for these files.  That is because for each culture you will want to specialize these files for these languages.  So you can have the file “WhiteSpace.en.txt” and “WhiteSpace.en-us.txt” etc. The global lists are merged with the list for a specific language so they are global for all languages.

Congratulations, you now have an index!  Now for the easy part – Using the index.  A typical full text query will look something like this:

public static List<Track> QuickSearch(
    IDataStore con, 
    string csharpQueryIndexLocation, 
    string searchCriteria, 
    int recordLabelID, 
    int pageSize, 
    int pageNumber, 
    SortCriteriaType sort, 
    Language uiCulture, 
    User user, 
    out int rowCount) {
    
    rowCount = 0;
    int? labelID = 
        (recordLabelID > 0) ? recordLabelID : (int?)null;

    FreeTextQuery.DatabasePath = csharpQueryIndexLocation;
    CultureInfo ci = 
        uiCulture.LanguageID == 1 ? 
        CultureInfo.InvariantCulture : 
        new CultureInfo(uiCulture.CultureCode);

    List<QueryResult> trackResults = 
        FreeTextQuery.SearchFreeTextQuery(
        "QuickSearch",    // The Index Name 
        ci,               // The culture info
        searchCriteria);  // The search term

    string trackIDxml = FormatResultsAsXml(trackResults);


    // Do the search
    int? nRowCount = 0;
    var searchResults = Con(con).QuickSearch2(
        trackIDxml, 
        uiCulture.LanguageID, 
        pageSize, 
        pageNumber, 
        labelID, 
        user.UserID, 
        (int)sort, 
        ref nRowCount);
    
    rowCount = nRowCount ?? 0;

    return searchResults;
}

The code above simply calls FreeTextQuery.DatabasePath to set the location of the text index.  You only need to set this path once since it is static but I set it for every call, just in case.  Next I call FreeTextQuery.SearchFreeTextQuery to perform the search.  This returns a list of QueryResult which gives you back the [Key] that you specified when creating the index, the rank (presorted), and the locations of the words in the original [Value] that it matched to.  This is very handy if you wanted to do something like highlight search terms especially if you want to also highlight words that matched via the thesaurus.

After I get my results I call a function called FormatResutlsAsXml so I can get an XML string to send to SQL server and join these keys to the actual track information.  My implementation of FormatResultsAsXml actually uses a StringBuilder because I found this to be a little quicker at creating an XML string.

private static string FormatResultsAsXml(
List<QueryResult> phraseResults) {
if (phraseResults == null || phraseResults.Count == 0) return null; StringBuilder sb = new StringBuilder(); sb.AppendLine("<root>"); foreach (QueryResult r in phraseResults) { sb.AppendLine("<result>"); sb.AppendLine("<key>" + r.Key + "</key>"); sb.AppendLine("<rank>" + r.Rank + "</rank>"); sb.AppendLine("</result>"); } sb.AppendLine("</root>"); return sb.ToString(); }

All we have to do now is pass this into our stored proc.  Now, at about this point I’m sure you are thinking something along the lines of I’m using SQL Server 2005, why not use the XmlDocument rather than pass this value in as text.  The answer to that question is simple – For some very strange reason the new XML capability in SQL 2005 is VERY SLOW! It is unusably slow for sets of data larger than just 500 nodes!

Here is a snippet of my T-SQL code:

ALTER PROCEDURE [dbo].[QuickSearch]
(
    @TracksKeysXml text
    ,@LanguageID int
    ,@PageSize int
    ,@PageNumber int
    ,@RecordLabelID int = NULL
    ,@UserID int
    ,@SortID int = NULL 
    ,@RowCount int OUTPUT
) AS
BEGIN

    DECLARE @Tracks TABLE 
    ( 
        TrackID int primary key, 
        [Rank] real 
    )

    IF ( @TracksKeysXml IS NOT NULL )
    BEGIN
        DECLARE @xmlDocTracks int
        EXEC sp_xml_preparedocument 
            @xmlDocTracks OUTPUT, 
            @TracksKeysXml

        /* xml input: 
        <root>
            <result>
                <key>123456</key>
                <rank>0.75245</rank>
            </result>
        </root>*/
                
        INSERT INTO @Tracks
            SELECT * FROM 
            OPENXML(@xmlDocTracks, '/root/result', 2) 
            WITH ( [key] int 'key[1]', [rank] real 'rank[1]')
            
        EXEC sp_xml_removedocument @xmlDocTracks
    END

...

It’s as easy as that!  Feel free to leave a comment if you have any questions.

This software is distributed under the terms of the Microsoft Public License (Ms-PL). Under the terms of this license, and in addition to it, you may:

  • Use this code royalty free in either open source or for profit software.
  • You may not remove the copyright notices from any of the files.
  • You may not charge any 3rd party for the use of this code.
  • You may alter the code, but must not distribute the source once it has been altered.
  • You should give the author, Nathan Zaugg, credit for code whenever possible.
  • The code is provided to you as-is without any warranty, implicit or explicit.

I would also appreciate it if you left a comment if you found the code to be useful.

You may download the source from here: http://interactiveasp.net/media/p/1124.aspx

UPATE: This project is now being maintained on CodePlex.  See http://csharpquery.codeplex.com/ to get the latest code.

Microsoft Public License (Ms-PL)
Microsoft Public License (Ms-PL) This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. Definitions The terms "reproduce," "reproduction," "derivative...
Apple Tax?

MacVsPC

We have all seen the TV commercials of Mac and PC standing there while the Mac makes some funny remark about how unreliable PC is or how much more fun Mac is.  We all got a good laugh and I even started recording them before realizing that they were available online.  I also think that Microsoft had a good laugh, at first.  Or at least until it became clear that this one-sided add campaign was really starting to hurt their reputation. 

Microsoft, perhaps wanting to fire back started a series of TV commercials that were horribly unfunny!  Those were followed by vague “I AM PC” commercials that weren’t meant to be funny but didn’t have much substance either.  Now Microsoft has started the “Buy anything you want for $1,000 or $1,500 – BINGO! The commercials, in my opinion, are very effective at dispelling the myth that Mac’s are better than PC’s.  This has been dubbed the “Mac Tax”.

They aren’t convincing everyone though.  It was a comment from Redmond Report newsletter that got me fired up!

MAILBAG: THE MAC TAX THAT ISN'T, MORE

Microsoft has been talking up the so-called "Mac tax" to dissuade
people from moving to Apple. Marc thinks it's a little disingenuous to
call it that:

"For what it is worth, the 'Mac Tax' is not real! If you want, you
can configure a Dell with specifications virtually identical to any
Macintosh in the Apple product line and come up with very nearly
identical pricing. The catch, of course, is that an Apple Macintosh
is severely overpowered to meet the needs of most folks. Most folks
can meet their computing needs with a $500 to $800 Dell, or they can
go overboard and spend $1,000 and get a 'fully loaded' Dell that will
last them a good five years. Or, they can buy a 'bottom-of-the-line'
MacBook.

The truth is that if Apple could sell as many computers as Dell or
HP, they could afford to sell low-end $500 computers, but because
they don't sell a large enough number of computers to tolerate the
extremely narrow profit margins Dell and HP get on those $500
systems, Apple simply cannot afford to do so. Dell and HP 'take a
loss' on those entry-level systems but they make it up on very high
volumes and the occasional sale of $1,000-plus systems. All of
Apple's systems must be $1,000-plus systems for them to stay
in business."
-Marc

Well Marc, that sounds like a challenge!  I completely disagree with the argument that you can configure a Dell with specifications virtually identical to any Mac and come up with nearly identical pricing.

Mac vs. PC Challenge

So what does Apple have to offer?

White MacBook

MacBook

MacBook Air

MacBook Pro

MacBook Pro

Price

2.0GHz, 120GB $999.00

2.0GHz, 160GB $1,299.00

2.4GHz, 250GB $1,599.00

1.6GHz, 120GB $1,799.00

1.86GHz, 128GB SSD $2,499.00

2.4GHz, 250GB $1,999.00

2.66GHz, 320GB $2,499.00

2.66GHz, 320GB $2,799.00

Display

13.3-inch (viewable) glossy widescreen

1280 x 800 pixels

13.3-inch (viewable) LED-backlit glossy widescreen

1280 x 800 pixels

13.3-inch (viewable) LED-backlit glossy widescreen

1280 x 800 pixels

15.4-inch (viewable) LED-backlit glossy widescreen

1440 x 900 pixels

17-inch (viewable) high-resolution LED-backlit glossy widescreen

1920 x 1200 pixels

Option: Antiglare display

Processor

Intel Core 2 Duo

1066MHz frontside bus

3MB shared L2 cache

Intel Core 2 Duo

1066MHz frontside bus

3MB shared L2 cache

Intel Core 2 Duo

1066MHz frontside bus

6MB shared L2 cache

Intel Core 2 Duo

1066MHz frontside bus

3MB or 6MB shared L2 cache

Option: 2.93GHz

Intel Core 2 Duo

1066MHz frontside bus

6MB shared L2 cache

Option: 2.93GHz

Memory

2GB (two 1GB) of 667MHz DDR2 SDRAM

Option: Up to 4GB DDR2

2GB (two 1GB) of 1066MHz DDR3 SDRAM

Option: Up to 4GB DDR3

2GB of 1066MHz DDR3 SDRAM (onboard)

2GB (two 1GB) or 4GB (two 2GB) of 1066MHz DDR3 SDRAM

Option: Up to 4GB DDR3

4GB (two 2GB) of 1066MHz DDR3 SDRAM

Option: Up to 8GB DDR3

Hard drive1

120GB Serial ATA, 5400 rpm

Option: Up to 320GB hard drive

160GB or 250GB Serial ATA, 5400 rpm

Option: Up to 320GB hard drive or 128GB solid-state drive

120GB Serial ATA, 4200 rpm or 128GB solid-state drive

250GB or 320GB Serial ATA, 5400 rpm

Option: Up to 320GB hard drive at 7200 rpm or 128GB solid-state drive

320GB Serial ATA, 5400 rpm

Option: 320GB hard drive at 7200 rpm, 128GB or 256GB solid state drive

Battery3

Up to 4.5 hours of wireless productivity

Up to 5 hours of wireless productivity

Up to 4.5 hours of wireless productivity

Up to 5 hours of wireless productivity

Up to 8 hours of wireless productivity2

Graphics

NVIDIA GeForce 9400M with 256MB of shared DDR2 SDRAM

NVIDIA GeForce 9400M with 256MB of shared DDR3 SDRAM

NVIDIA GeForce 9400M with 256MB of shared DDR3 SDRAM

NVIDIA GeForce 9400M and 9600M GT with 256MB or 512MB of GDDR3 memory

NVIDIA GeForce 9400M and 9600M GT with 512MB of GDDR3 memory

Enclosure

Polycarbonate

Precision aluminum unibody

Size (H x W x D)

1.08 x 12.78 x 8.92 inches

2.75 x 32.5 x 22.7 cm

0.95 x 12.78 x 8.94 inches

2.41 x 32.5 x 22.7 cm

0.16 to 0.76 x 12.8 x 8.94 inches

0.4 to 1.94 x 32.5 x 22.7 cm

0.95 x 14.35 x 9.82 inches

2.41 x 36.4 x 24.9 cm

0.98 x 15.47 x 10.51 inches

2.50 x 39.3 x 26.7 cm

Weight4

5.0 pounds

2.27 kg

4.5 pounds

2.04 kg

3.0 pounds

1.36 kg

5.5 pounds

2.49 kg

6.6 pounds

2.99 kg

What does HP have to offer?

The computer that compares with the entry level 13” white MacBook computer:

Well, this comparison isn’t really apples to apples.  After all I couldn't find anything with a 13” screen (either smaller or bigger).  Also, I took upgrades that I think most people would take.  Here is what I came up with:

  • HP G60t Laptop running Windows Vista Home Premium x64
  • Intel(R) Core(TM)2 Duo Processor T6400 (2.0GHz) (=)
  • 3GB DDR2 (+)
  • 250GB 5400RPM SATA (+)
  • 256MB NVIDIA GeForce 9200M GE( )=
  • 16.0" diagonal High Definition HP Brightview Display (1366x768) (+)
  • Free HP DESKJET D4360 PRINTER (with mail-in rebate) (+)
  • Cost: $708.99 with $100 instant rebate.  Savings: $290.01; I did get some other free upgrades, but because they always have this kind of thing I keep them. Here is the link (for as long as it lasts)

The (-) indicates that the chosen component was less than it’s apple counterpart. The (=) indicates identical hardware.  The (+) indicates superior hardware. 

DECISION: I got a VASTLY superior PC from HP for almost $300 less!  The HP notebook even looks nicer than the white apple book.  Hands down winner here.

Because my previous configuration beat the next level up MacBook, I’ll move on to the MacBook Air using Dell’s website:

  • Dell XPS M1330 running Windows Vista Home Premium x64
  • Intel® Core™ 2 Duo T9300 (2.5GHz/800Mhz FSB/6MB cache) (+ & –)
  • 3GB Shared Dual Channel DDR2 SDRAM at 667MHz (+ & –)
  • Ultra Performance: 128GB Solid State Drive (=)
  • 128MB NVIDIA® GeForce™ 8400M GS (–)
  • 13.3" UltraSharpTM  WXGA (1280 x 800) display with TrueLifeTM  (available with 2.0 MP camera) (=)
  • Weight 3.97 lbs; Size 31.8 x 2.31x 23.8 cm (–)
  • Cost: $1,444.  Savings: $355 / $1,055. 

This wasn’t as quite as good as comparison as I hoped.  I needed a Dell Laptop that did the Solid Sate drive (the whole reason for MacBook Air).  The processor is faster than both configurations of MacBook but with a slower BUS speed.  In mind this makes them a wash but depending on what you are doing it could make a difference one way or another although it’s not likely to. 

The other was the RAM.  I couldn't configure this computer with anything less than 3GB which is 50% more RAM, but again, it’s slower RAM so there could be some performance considerations.  Most of the time, though, the quantity of RAM beats out the speed of the RAM.  It’s still many times quicker than virtual memory residing on a disk drive. The graphics card available for the PC was not as good as the one that comes with the MacBook Air. The XPS system came in a bit chunkier in both weight and size.

DECISION: If you are in the market for a PC that is powerful and small / light and cost is not much of a consideration then the MacBook is a real contender.  I might have done better if I would have gone with Acer who is known for their small computers but I still rather doubt that I’d get something as small and powerful.  The MacBook Air is the winner for performance and size – that is if you can overlook the price tag.

The next comparison is the MacBook Pro series laptops.  While shopping at HP here is what I got and how it compares:

  • HP HDX 18t with Windows Vista Ultimate with Service Pack 1 (64-bit)
  • Intel(R) Core(TM)2 Duo Processor T9550 (2.66 GHz) (+ & =)
  • 4GB DDR2 System Memory (2 Dimm) (-)
  • 320GB 7200RPM SATA Hard Drive (=)
  • 512MB NVIDIA GeForce 9600M GT (=)
  • 18.4" diagonal High Definition HP Ultra BrightView Infinity Display (1920x1080p) (+)
  • Blu-Ray ROM with SuperMulti DVD+/-R/RW Double Layer
  • HP Integrated HDTV Hybrid Tuner (+)
  • Cost: $1,606.99 Savings: $392.01 / $892.01 / $1,192.01 Again, here is the link so you can see for yourself.

I didn’t bother comparing all three MacBook Pro’s separately, the system I built beat even the high end MacBook Pro.  This HP system is every bit as good as the MacBook or even better!  What are the differences?  Again, I had a hard time coming up with comparable memory from the manufacturer. If I wanted that high end stuff, I can still get it on the cheap from New Egg.  I even got some freebies that I didn’t expect like a bigger monitor, Blu-Ray DVD-RW combo and an HDTV tuner integrated into the system. 

DECISION: The HP notebook is a clear winner!  If I had the $1,606.99 I would buy the PC right now! PC’s are a great deal!

CAVEOTS: I wasn’t able to weigh in on all aspects of the laptop such as battery life which I have no data from HP or Dell (mostly because it varies greatly between configurations).  I also make the assumption that the overall quality of the computer is equivalent.  This is probably a true assumption – all of the PC’s had very high ratings from their customers.  I also didn’t investigate warranty models very carefully so that is left to the buyer to evaluate. 

Summary

I wouldn't recommend to my parents, grand parents, co-workers, friends, or peers that they choose a Mac over a PC.  A Mac is not a better computer by virtue of being made from Apple.  You are paying more for a name brand just like anything else.  If the software you need to run is Windows based then buy a PC, if it’s Mac based then buy a Mac.  Simply running Windows in some kind of VM software is not going to give you a very good end-user experience.  The “Mac Tax” is real!

Windows XP Mode and Windows Virtual PC

windows-logo-small

I have been playing with Windows 7 in my VM machine for a few weeks now and have found it to be amazingly fast and lean!  So what will keep people from making the leap from XP to Windows 7? Application and hardware compatibility of course!  Windows has such a large following now days in large part because of it’s commitment to compatibility between versions of windows.  Compatibility is one of the major reasons Windows Vista has been so marginalized. 

Since Microsoft is not providing a direct upgrade experience from XP to Windows 7, and because Windows 7 is based on Vista technology, it can be quite a sale to get people to convert from Windows XP to Windows 7.  For those in this camp there is some good news.  Last week the Windows Blog team posted a very provocative solution called Windows XP Mode for Virtual PC

Based on the information provided it looks as though they have developed “unity” like features for Microsoft Virtual PC.  This allows a program running under a virtual machine to be moved onto the hosts desktop.  It will look like it is running on the host machine when it is really running under the virtual machine. It can be so transparent that the only way you can tell on the example in the blog post is because of the tool tip on the shortcut.  VMware has had this technology for a little while but my experience with it is kind of mixed.  It is really cool to see your app running on say a Mac, but the user experience is not even as good as running the application in the virtual machine directly.  Still, this technology has real promise.  Citrix, for example, bases their business on this type of technology.  Microsoft’s Terminal Services even has support for application virtualization.

For users who only have one or two incompatible applications keeping them from upgrading, this should be a big help!  That in conjunction with the great product Windows 7 is shaping up to be I can see people migrating en masse

WindowsVPC7_2_23127862 
taken from the original blog post.

Links:
Creating a Silverlight 3 Out Of Browser Application

Silverlight Logo Small

Silverlight 2 is great! Silverlight 3 is AWESOME! My first experiences with the beta framework and tools have been overwhelmingly positive.  In this post I’ll go over my experience creating an application using some of the new features and I’ll show how easy it is to make your Silverlight application available offline.  To get started you’ll need the tools. Download the Silverlight Tools for Visual Studio 2008 SP1 from the official Silverlight website.  It is also recommended that you download and install the Silverlight Toolkit from the same site.

Getting Started

After installing the Silverlight tools open Visual Studio and click File –> New –> Project. Click Silverlight in the project types section and select Silverlight Application from the Templates group.

New Silverlight 3 Project

When prompted make sure you check the Host the Silverlight application in a new Web site checkbox and press the OK button.

New Silverlight Application Settings

Once your project comes up we can get started in code.  Open the MainPage.xaml file, open the toolbox and double click TwilightBlueTheme.  This will add some references to our project and update the background of our window.  That is how you use a built-in theme.  Any tool box item that ends with Theme will change the look of your application.

Another great feature of the Silverlight 3 runtime is that we finally have full binding support!  The most useful binding feature is binding the value of one control to another. 

<Grid x:Name="LayoutRoot" Background="White">
    <twilightBlue:TwilightBlueTheme />
    <Button Height="30" Width="150" Content="This is my button Fool" 
            HorizontalAlignment="Left" VerticalAlignment="Top" 
            Margin="5" Click="Button_Click"  />
    <TextBlock Name="tbValue" Margin="50" 
               Text="{Binding ElementName=slider1, Path=Value}" />
    <Slider Name="slider1" Height="80" Width="300" 
            Margin="0 0 0 0" Value="3" />
</Grid>

Now, lets run it.

First Run

You can see that when you move the slider we can see it’s slider position value in the TextBlock we have on our form.  Now that we have our ultra-simple app we want to make it available offline.  We do that by opening the Properties\AppManifest.xml file and add the following:

<Deployment.ApplicationIdentity>
      <ApplicationIdentity 
          ShortName="Nate Test Application" 
          Title="Nate Test App">
          <ApplicationIdentity.Blurb>This is a test silverlight 
                        3 application out of the browser.</ApplicationIdentity.Blurb>
      </ApplicationIdentity>
  </Deployment.ApplicationIdentity>

Now run the application again.  This time right click the Silverlight app and there is a new option to install the application.

Install Silverlight 3 App

You will then get a very simple “install” dialog.  The install process is extremely fast!

Silverlight 3 Application Install 

And now we can launch our application from the start menu.  Here is what it looks like when it is run outside of the browser.

Silverlight 3 Application Running Outside of the browser

To remove the application from your system, just right click to uninstall.

Silverlight 3 Application Uninstall

And that is it!  Silverlight 3 works very well outside of the browser!

 

Links:
Windows 8 – A First Glimpse

Windows 8 Logo

Windows 7 hasn't even been released (Currently RC1) and there is already buzz about Windows 8.  Of course I know that in terms of development Windows 7 is in it’s stabilization phase so naturally the kernel team and perhaps even some others have started on the next generation of Windows software.  I have spent a lot of time on MSDN’s Channel 9 looking at some of the low level design considerations that went into Windows 7 Kernel and there has been some indication that they have already started on the Windows 8 project. 

This week my MCPMag.com news flash talked about Windows 8.  There wasn’t much information to go on other than two job postings for the Windows 8 team and a blog post that included some tantalizing video of possible UI for Windows 8.  This concept design is incredibly cool!  My favorite part is that they completely re-designed Windows Explorer which was in worse need of a makeover than Susan Boyle!

From what I could tell in the short video, some of the new design concepts included:

  • New Windows Explorer Interface- Makes it easier to switch between views (a pain in Vista) and allows you to find the information you need about a file.
  • New Start bar and Taskbar- There is already a new taskbar in Windows 7, but I’m not sure if I really like it.  It’s impossible to tell the difference between a program that is running and a quick launch button.   The new design concept for Windows 8 is obviously based on the Windows 7 design but looked more friendly to use.  My favorite part? You can re-arrange the tabs.  This is really awesome if your OC like I am and expect tabs to be in certain places. The new design focuses on screen real-estate and has a very minimalistic feel to it.  It looks amazing!
  • There is a new feature that will allow a shortcut to launch multiple programs at once.  I don’t think I would ever use this but if I were say a customer service person who had to have 10 apps open all at once it might be nice to have this for after I rebooted the machine.
  • Multiple current users- It looked from the video like you may be able to quickly switch between user sessions & desktops.  You can kind of do this in Windows XP Pro, but it feels more like you are logging out and logging back in as someone else.  This feature kind of looked like I could switch to a new desktop as easily as I can switch between two running applications.
  • The notification area is smaller and sexier!  It is small and looks like most of your nuisance/superfluous notification icons are hidden by default.

Here is the Video of the Windows 8 Concept UI:


Copenhagen User Experience from Copenhagen Concept on Vimeo.

I think Microsoft has the right concept going with this kind of publicity.  While Windows Vista isn’t a great user experience (It’s pretty, but less usable) it has a lot of enhancements under the hood.  Enhancements that make a big difference to a user like me.  They have learned their lesson from Vista and Windows 7 is all about user experience and some minor enhancements under the hood.  There is no information available about what kind of enhancements are in store for Windows 8 other than we do know that it is still based on the Vista / Windows 7 Kernel.  I just hope that Windows 8 has the right mix of usability improvements, kernel / system improvements, and features that create an ROI (Return On Investment) so it’s easier for businesses to justify the upgrade.

Here are some screen shots of Windows 8.  I took them from the video in case you didn’t want to watch that.

Full Desktop

Start Bar

Programs Menu

Explorer

Explorer 2

Task Bar Preview

Notification Area

Scraps

Switch Users Moving Start Bar

Taxed Enough Already (TEA) Party Protests & My Idea for Personal Retirement Accounts

Dollarbill One of the headlines on my news feed came across about anti-tax ‘tea parties’ being held across the U.S. These demonstrations are a form of protest to wasteful government spending.  I was quite disappointed that the article included a blurb that in my opinion threatened the neutrality of the article.  The paragraph stated that this event “[has] been co-opted by the Republican Party.”  It then goes on to try to discredit the official Tax Day Tea Party website by accusing the website owner of being a conservative, as if somehow that is a bad thing.

Deficit Spending and the National Debt

The protests and the demonstrations aside, I do think it's maybe not so much about taxes but more about Government spending and entitlement programs.  People in the forum were quick to note that the 2008 tax year is a President Bush legacy.  Former President Bush really ran up a deficit so he is not guilt free.

Nearly all of the states in the US are prevented from deficit spending by their state constitutions and that means that while states have to make some very difficult decisions in years like 2008 & 2009, they can be very efficient in the recovery years and hopefully build up a reserve of cash for next time.

The Federal Government, on the other hand, can pretty much incur as much debt as it wants.  This is not a good idea and I maintain that the constitution should be amended to prohibit deficit spending.  Think of it this way; lets say that we spend $100 billion deficit for healthcare.  That is a $100 billion loan, right?  Okay, lets be conservative here and say that the US Government gets the amazing rate of 1.5% on that loan.  Lets also assume that it’s going to take us 10 years to pay that loan back, which is very optimistic as we have to stop borrowing before we pay anything back.

This means that for the $100 billion we borrowed to pay that bill we will be paying $897,914,997.95 / month or $10.77 billion / year for the next ten years.  I actually had to write a special calculator to amortize that amount – no online calculator could handle it!  If we continue deficit spending we will in effect be paying more for less as all of the tax revenue has to be applied toward the debt first and social programs last.  Debt is a lot like barnacles on a ship, the more of them you have the more drag you have and it takes more “wind” to move you places.  Get too many and you might as well have stayed on the doc because your not really going anywhere. 

Of course we know that our current proposed deficit is $1.7 trillion which works out to be $15 Billion / month or $183 Billion / year.  Remember, this is just to pay for one year of deficit spending.  Our current national debt is ~$11.17 trillion dollars, as of 4/15/09. That means that we are now paying $100 billion / month or $1.2 trillion / year to make payments on the national debt! That is $3.2 billion / day, $137 million / hour, $2.2 million / min, and $38 thousand per second!  In 2008 the national debt climbed to 73% of GDP (source: Wikipedia).  The $100 Billion we borrowed for our experiment here may seem like a drop in the bucket compared to the national debt but every little bit counts.

This country is literally putting bills on the credit card and it’s only a matter of time before we run out of money.  The longer we wait to fix the problem the more money we end up borrowing and the more painful it is going to be.  Imagine if we had to cut out Medicare, Medicaid, and Social Security AND raise taxes?  This is not impossible and could happen.  The bottom line is that you can not borrow yourself into prosperity! It is completely irresponsible to borrow money to create social entitlement programs!

Personal Retirement Accounts

I don't want Social Security, but I have no choice but to pay it and there is little chance that there will be anything for me to collect when I reach retirement age. Any personal retirement I do save for is taxed so heavily it can't even keep up with inflation!  That is absurd!  I would really like to opt out of Social Security and put that money in my retirement account tax-free, but even if I have to pay it, I still need to be able to save for my retirement tax-free.  Just to keep pace with inflation.

If I were to reform retirement, I would create something called a Personal Retirement Account (PRA).  This would be an alternative to the current Social Security program and would allow people to save for their own retirements tax-free. 

The basis would be a function as such:
( (Inflation Base * 100,000) / (Age / Retirement Age) = Maximum Annual Contribution )

Inflation Base is the percentage of inflation since the bill passed.  If the bill passed in 2009 and inflation for that year was 3.0% then in 2010 the number is 3.0%, then in 2010 if it rose by 2%, the rate is then 5%. 

The idea behind my PRA idea are these simple axioms:

  • The closer to retirement you are the more money you should be allowed to save for retirement.  Conversely, the younger you are the less you need to put away as the money should grow over time and is tax free.
  • Taxes should not be levied on the sums of money being put into these accounts.
  • Taxes should not be levied on the sums of money being withdrawn from these accounts if done so within the bounds of the rules of the accounts.
  • The current rate of $100k / year is a comfortable amount of money to live on.
  • The amount you are allowed to contribute should be adjusted annually for inflation.
  • If you would like to save more money than the maximum annual contribution amount, then taxes should be paid up front (much like an Roth IRA).
  • There needs to be some rules to limit risk to accounts of individuals that are closer to retirement.
  • Participation in a retirement program becomes mandatory once a worker turns 30.  They may either opt for traditional Social Security or Personal Retirement Accounts.  The minimum amount for the Personal Retirement Accounts plan is the current social security withholding amount.
  • You should be able to switch between the two programs.  If you are switching from Social Security then the amount contributed should be put toward the Personal Retirement Account Plan – no interest accrued.  If you switch to Social Security then your account is transferred but you do not get additional benefits and must pay if the value of your account is less than the cumulative Social Security contribution amount.
  • Because these retirement accounts are tax-free, the money should be invested more conservatively to mitigate risk.
  • Retirement Age should be between 55 and 65 years old.
  • It’s in the best interest of our society to have people financially prepared for retirement.  This is why the accounts should always remain tax-free.

Summary

The problem with entitlement programs, such as Social Security, Medicare, Medicaid, etc. is that they are never run efficiently and the completely eliminate peoples ability to choose something that works better for them.  They take away from people the ability to dictate our own futures and supplant it with a poor-at-best replacement.

I lived in the UK for two years where medical care is fully socialized.  I can vouch first hand that while they do not have the same problems as Americans they do envy the quality of American Health Care. Indeed, I believe that the level of health care provided in America is the envy of the world. Rather than fixing problems like ER wait time, cost, etc. nationalization will add to the national deficit and would lower the quality of health care across the board.

Once we socialize medical care we will never be able to get rid of it, and I don't think most people are going to like what they are going to get.  President Obama may not completely nationalize the health care system this go around but he's going to get enough of a foot in the door that the only option for future presidents is going to be full nationalization.

We need to seek to find answers to social problems that are efficient, accountable, optional, sustainable, and above all, responsible.

I am Certifiable!

CrazyFace

It may come as no surprise to those who know me that I have recently confirmed that I am certifiable!  After all, when you have been doing what I do for as long as I have been doing it it’s hard not to be affected.  It happens to a very large number of people in my field and that number continues to grow.

Although I have been certified before, after some exhausting battery of examinations, this online examination is nice as I can quickly determine my status even if there is some kind of doubt – just for my minds sake.

Thanks to Microsoft there is now an online tool to check your IT status as Certifiable!  What, you thought I meant mentally unfit or crazy?  Nope, I’m talking about the uber-geeky Microsoft Certifications.  For some time now they have had the Measure Up practice tests for study preparation, but now there is a new skill-building tool, Are You Certifiable.  Rather than a boring, dry, practice test this new site is a game show! 

This actually makes a lot of sense to me because when I was Microsoft Certification Coordinator for STG I found the best way to get the material to sink-in was to have fun with it.  That usually included some form of study group Jeopardy.  Facts just don’t sink in as well when your mind is not very stimulated and some of the questions can be quite dry.

Here is a screen shot of me capturing the high score for guests.  It will ask you if you are an IT professional or a Developer and gives you questions relevant to your position.  The questions vary greatly and it takes a very well-rounded skill set to do really well, but it’s fun regardless. 

Certification Game - Nate Ahead

My only critique is that it is not practical to practice for a specific certification test you plan to take because you don’t get to pick which topics or banks of questions you get.  It is a lot of fun and has some great “thinking music”!  Leave a comment if you happen to beat my score.

Final Score

I would like to also mention that it is built using Silverlight 2.0 as it is a great example of the power of Silverlight. 

Links
Exception Handling Philosophy

It's time for another episode of Good Idea / Bad Idea:

Good Idea! Catching a specific exception from a suspect numeric conversion and showing a polite input error message.
Bad Idea! Catching all exceptions and "swallowing" the exception.

Okay, so it's not as funny as it is on Animaniacs, but true none-the-less.

Exception Handling in C#

In terms of exception handling I am a big fan of "catch it only if you can do something with it" and/or "catch it only if you mean it".  Putting a try / catch around every method makes debugging very difficult and can leave your application in an unexpected state.  Besides the code is less readable, more complex, and slower performing.

Before I get in to good practices, lets review some bad ones.  The first and most obvious is throwing away exceptions (without regard to what they are).  Example:

// Anti-pattern #1: Throw away the exception
private void button1_Click(object sender, EventArgs e) {
    try {
        AddPerson(new Person() { Name = txtName.Text });
    } catch { }
}

Another example of what not to do is what I call the “catch just because” pattern.  Example:

// Anti-pattern #2: Needless Try / Catch
private void DoStuff(string shipping) {
    try {
        // Call down to middle tier to do the shipping calculation            
        decimal ShippingCost = MyApi.Shipping.GetShippingCost(orderNum);
    } catch (Exception ex) {
        throw ex;
    }
}
//  Needless Try / Catch
private void button2_Click(object sender, EventArgs e) {
    try {
        // CalculateShipping has it's own Try / Catch; see below.
        CalculateShipping();
    } catch (Exception ex) {
        MessageBox.Show(ex.Message);
    }
}

There are multiple problems with the second example.  First, there was no reason for “DoStuff” to catch the exception if all it was going to do was re-throw any exception it encountered.  Second, “DoStuff” is catching any exception rather than limiting the scope to the kinds of exceptions it might expect like “Unknown Zipcode”, “Order not found”, etc.  Next, the “button2_Click” is catching any exception and showing the raw message. 

Rather than showing the exception message which is only part of the clues given in the exception object you should either show a user-friendly message indicating the kind of error that has taken place and save the exception information for later debugging.  Alternatively, you can show ex.ToString() which will show you all information in the exception class.  This is very unimpressive for end-user applications but can be acceptable with internal corporate apps and a simple screen shot will give you the information you need to find and take care of the problem.

Remember, an exception doesn’t necessarily mean that there is a bug in the software.  If you do show a user an “unfriendly” exception message they will immediately think that the software has a bug when the issue could be as simple as the network is down or a database is temporarily unavailable.  I always recommend taking the time to display user-friendly messages and saving the exception information elsewhere.

In the next section I will show some good examples on what "layer" to catch and/or re-throw an exception.

Presentation Layer

Personally I think that any event here should catch any/all expected exceptions and show them in a user-friendly manor.  Notice I said "expected exceptions", this could be pretty vague unless you have an design pattern in place for exceptions in your applications so I'd start with that.  One way to start is to make some "base" exceptions like "DataException", "ValidationException", "InputException", etc.  More specific types of exceptions can be derived from these. 

When do you make a new type of exception?  Well, the exception should have the information needed to solve the problem.  Stack traces and exception information are like clues; leave your self a breadcrumb trail to follow later.  Throwing an ApplicationException because you couldn't connect to the database isn't very helpful. 

It is also acceptable to use pre-defined / framework exception types when appropriate.  For example:

public void DivideMyObjects(MyObjectType o1, MyOtherObjectType o2)
{
    if (o1 == null || o2 == null)
        throw new NullReferenceException(
            "The numerator or the denominator cannot be null!");

    if (o2.Value == 0)
        throw new DivideByZeroException(
            "Denominator may not be zero!");

    if (o1.Value == null)
        throw new ArgumentException(
            "There is no value for the numerator");
    ...
}

You may notice in the code example above that a null reference may occur organically.  I am a strong believer in organic exceptions as good practice. I define an organic exception as an error that is thrown by the Framework. The only exception to that rule is when there isn’t enough information from that exception to track down the problem.  Remember, you need to leave yourself enough clues that you should be able to know what is wrong from the exception information given rather than expecting users to be able to remember what steps led up to the exception.  Few, if any, end users will be able to help you reproduce any particular error.  If this can’t be accomplished by an organic exception then you may want to re-throw that exception with the appropriate information and/or change the type of the exception to be more appropriate for the callers. 

// Presentation Layer Good Example
private void Calculate_Shipping(object sender, EventArgs e) {
    try {
        decimal shippingCost = MyApi.Shipping.CalculateShipping(order);
        txtShippingCost.Text = shippingCost.ToString("C");
    } catch (UnknownZipcode uzipEx) {
        // Specific
        MessageBox.Show(
            "Could not locate shipping information for this zip code!");
        btnSave.Enabled = false;
    } catch (ShippingException shipEx) {
        // Less specific, but still helpful to the user.
        MessageBox.Show("Unable to calculate shipping!"
            + Environment.NewLine +
            shipEx.Message);
        MyLogging.Log(shipEx);
    } catch (Exception ex) {
        MessageBox.Show(
            "An unknown error has occured, Please report this error");
        MyLogging.Log(ex);
        Close();
    }
}

In general you are pretty safe catching exceptions in the presentation layer.  The only real thing to watch out for here is that your control states don’t become invalid because an exception is caught but the event still altered the state.  For example, if a call to “Calculate_Shipping” throws an exception you will want to be careful to invalidate the shipping cost control and disable the “save order” button. 

You will notice in the code above that I did catch generic exception.  The presentation layer is usually okay doing this so long as it is taking adequate measures to protect application integrity.  You will notice that I am closing the order form if I get an exception back that that is unknown.  Of course this will give you plenty of motivation to make sure you know what kinds of exceptions are expected as end users will generally hate having their order forms close.  You may be tempted to leave the form open or take lesser action but it is very important that you keep application integrity.  Otherwise you will spend your life chasing down un-reproducible bugs.

Middle Layer

Most of your code should end up being middle layer code.  We want the presentation layer to be as light as possible!  The middle layer should act as the API of the application.  Anything that the application “does” this layer should do it either directly or indirectly. In this sense the middle layer needs to be very up front in which exceptions are possible from any given call. 

Take for example a method call in the middle layer that inserts a new customer into the database.  Because you use the database objects you are exposed to any exceptions that are possible from the objects that you use such as SqlExceptions.  You will either need to publish that you are going to allow SqlExceptions to be bubble through this API or you need to catch the expected exceptions from those objects and throw your own brand of exception.

Here is an example of publishing the exceptions that are known to be thrown:

// Middle Layer Exceptions
/// <summary>
/// Calculates shipping for a given order
/// </summary>
/// <param name="order">The order to calculate shipping for</param>
/// <returns>the shipping amount applied to the order</returns>
/// <exception cref="UnknownZipcode">Unknown Zip Code / Zip Not Found</exception>
/// <exception cref="BadWeightException">The weight of the order is zero or unknown</exception>
public decimal CalculateShipping(Order order) {
    ...
}

Known Exceptions

Simply using ndoc XML style comments will give the consumer of the API enough information to make good decisions regarding the use of exception handling from the intellisense.

Another thing you should avoid while designing your middle tier is the use of exceptions as “message handling” in non-exceptional cases.  Remember, the reason you are throwing an exception is because something happened that was catastrophic enough that you were unable to continue with the normal flow of the method.  I admit that there is still a lot of gray area here.  Some of that gray area can be demonstrated in the sample code already shown.  For example, rather than throwing an exception in the “CalculateShipping” method we could refactor so an exception wouldn't be necessary.  Anytime this can be done cleanly I usually opt for these types of changes. 

  1. Refactor to return a ShippingResult class that contains information about the calculation along with the rate.  Such information could include wither it was able to successfully get a shipping rate.
  2. Create a ValidateShippingAddress method that looks up the address before the calculate shipping is run.  It could return true if we know we can get shipping information for that address.
  3. We could pass back a nullable decimal result.  A null return would indicate a failure to retrieve shipping information.  We would want to make sure it was clear in our ndoc comments that this is why the value is nullable.
  4. We could pass pack a zero or negative result.  Again, making sure this behavior is documented.
  5. We could implement the “Get Last Error” pattern; though at this point I’d rather see an exception.

Another question I am often asked is “When is it appropriate to re-throw an exception”.  The answer to that questions is, of course, it depends.  The next few sections will discuss some of these nuances of this question.

Bottom Layer

The bottom layer of an application could be a database layer, a Framework of some sort but the philosophy is very different from the middle layer.  As the bottom layers deal with primitive classes and data and as such can stick pretty well to exception handling ideals.  One of the big differences is that you won’t be using nearly as many user defined exception types.  It is not taboo to use a user defined type in a Framework layer but it’s just likely to be unnecessary.  The difficulty here is the exception message that you are going to pass along.  There could be any number of middle layer calls that call your method so by nature these messages are going to be far more generic.  Generic, however, does not mean it can’t be insightful.  You should include as much information as possible about the exception and the data that caused the exception.  Take the two following exception statements:

  • “Invalid DateTime”
  • “The date ‘4/11/2089’ is an invalid birth date!  The date cannot be in the future!”

The first exception tells us that there is an invalid date time but other than a stack trace gives us no other information.  The second exception is extremely helpful and specific, including as many clues as possible.  The user may have entered the value ‘4/11/89’ and the computer may have just assumed the wrong century. The first error message would cause greater confusion whereas the second, if the user were to see that message, could be helpful.

Summary

Of course there are lots of different ideas and philosophies on exception handling so you need to do what makes sense for your project.  Simply avoiding the two anti-patterns on the top of this blog will improve your code a lot.  MY ONE LAST PEICE OF ADVIECE IS THAT SOMETIMES YOU REALLY WANT TO USE A DEBUG.ASSERT RATHER THAN AN EXCEPTION! The advantage of the assertion is that only debug builds show these messages.  This is good for testing scenarios in which you want to be aware of a certain condition but it’s not necessarily an exception.

Links:
Visual NDepend Blows My Socks Off!

Splash

I’m not easily impressed by even the best of software.  There is usually some little thing that ruins my user experience.  I tell you this so you know that what I say next is something special.  Visual NDepend is AWESOME!  Everything meet or far exceeded my expectations!  If you are not yet running this software, you should be!  This is not one of those ‘nice to have’ pieces of software like you might thing.  This is a must have!  If Microsoft was smart they would do everything they could to package this in with Visual Studio 2010. 

What does it do? It’s hard to answer that question in a paragraph or two.  The basic answer is that it gives you a set of different representations of your software in such a way that you can understand the architecture and complexity.  This can be a huge help before undertaking large refactor efforts.  It also gives you an idea or the quality of the code based off of some metrics.  This quality score can be a really good indicator of wither your code is going to be maintainable and if it’s not it can help you get it there.  Before I go through the quick walkthrough of the software it’s self I’ll just say this – If you’re not paying attention to code metrics and the maintainability of the code from the earliest stages of development your likely not going to develop code that is easy to maintain.  And as you probably know, software maintenance is usually more than 3/4ths the cost of the software lifecycle.  Therefore, if you can get a build process established early and use a tool like NDepend to keep a close watch on some important metrics I can guarantee you will be creating much higher quality software than you would have otherwise.

Installation Experience

This is the best kind of install – you simply unzip the files.  I bet you didn’t think I would do a whole section on install when there is nothing to the install.  I suppose if it were any other product there would be a lot under this section. 

Analyze a Project

The user interface is very intuitive.  I installed integration with Visual Studio 2008 and .NET Reflector with minimal effort and started to analyze my assembly. 

Analyzing

Main Screen

It took a while to analyze but this particular assembly is quite large!  It also pulls in any referenced assemblies so the graph can be complete.  It allows you to specify multiple assemblies just in case you have sets of assemblies in the same project that have no direct relationship.  In this case I had a WCF service that would need to be added separately as it’s technically not a direct dependency.  Once the analysis is complete it’s INFORMATION OVERLOAD!

Analysis

Results

There are six or seven main results to view.  The most obvious is the bubble chart at the top.  This gives you a visual representation of different aspects of the code.  For example it’s currently set to Method level and the metric shown is number of lines of code.  the larger the bubble, the larger the method is in relation to the others.  I can select from Method, Field, Type, Namespace or Assembly.  In addition there are 11 different metrics that you can plot in the bubble chart. 

Bubble Chart

The next thing you notice is the dependency graph.  Unfortunately this project doesn’t have a dependency graph like in my other blog post.  Visual Studio makes it really tough to make circular dependencies but it is possible to create them if not all of your projects are loaded into the same solution.  And as I said in the other post, circular dependencies are very bad!  It’s also good to know what references what.  You might be surprised to see what kind of references get worked in over time.  On a recent project the data layer code kept working it’s way up to the UI even though it didn’t belong there. 

Dependency Graph

The next section is the code metric-centric part. This is my most favorite portion of the results generated and can be a real good indicator of how maintainable a codebase is.  Of course it’s impossible to avoid all complexities in software so you probably can’t have a perfect score with every project but you can use this information to figure out what kinds of habits you have that contribute to un-maintainable code. As you can see from the image below when you select a metric it is highlighted in the bubble chart.  In fact, all of these reports are “active” so anything that is relevant to another will be indicated.  By the way, this application has amazing performance.  I’m pretty sure it’s using WPF under the hood.

Code Metrics 

Methods Too Big

Another really cool feature is the Dependency Matrix.  This takes some practice to use but it helps identify which pieces of which assemblies access pieces of other assemblies.  Again, great for refactoring efforts and a good indication of how an application is put together.  You may have to stair at the image below for a while to figure out what it’s trying to tell you but after a while it makes perfect sense.

Dependency Matrix

Last, but certainly not least is the comprehensive report compiled into HTML.  This is the kind of thing that I’d love to see emailed to me each day, so I hope it’s easy to put this into a build process.  The report is awesome!  My favorite part of the software and I really like this software.  Below is the screen shot of the table of contents that is generated at the top. 

Analysis Report

The only feature I didn’t get a chance to try is code coverage results.  I would have loved to see that in action!  Too bad I don’t have a project with very good code coverage results and I’d be embarrassed to find out anything in detail about my code coverage.  Of course code coverage isn’t everything but it is a useful tool when taken into context and can be made more useful with nDepend.

Overall, I would give this software very high marks!  I can’t wait to make this part of my build process!  It is worth the time and money!

Links:
6 Things I Bet You Didn't Know About Data Binding in WPF

winFX I fell in love with WPF the first time I saw it in action!  WPF is a thing of pure beauty!  Anyway, from the second I saw how the data binding worked I knew that there was going to be a lot to figure out there!  It also seems as though there is no real data binding expert -- or the experts don't blog too much about it.  I created my XAML Cheat Sheet shortly after feeling like I had some kind of grasp on the concept.  It is still a great reference for basic bindings, but when you are real serious about binding you'll have to do better than that.

So I have been working on a project in my spare time called SmartForms 2.0 (more on that later) and I have had to do some very difficult things using bindings.  In my previous experience with WPF I would either create a cleaver type converter and/or just give up and set the value/state of the controls in code (eww!).  This time is different and I have to make data binding work. 

Example #1

Lets say that you need to bind a bool? (nullable boolean) type to a checkbox.

<CheckBox IsChecked="{Binding SomeBooleanField}">Checkbox</CheckBox>

Produces:

DataBound Checkbox

You will notice that the checkbox is in a neither checked nor unchecked state.  Setting IsThreeState="False" doesn't help you as it is false by default. I want the checkebox to be unchecked by default but have no control over the type or the initialization of the type.  Check this out:

<CheckBox IsChecked="{Binding SomeBooleanField, 
TargetNullValue=false}">
Checkbox
</CheckBox>

And now we have normal looking checkboxes!  The best part is that this method doesn't mutate our property.  This was exactly what I was looking for!

DataBound Checkbox Fixed

Example #2

The following XAML is pretty straight forward.  It is simply a text box that we expect to put a city into.

<StackPanel Width="130" Height="40" HorizontalAlignment="Left" 
VerticalAlignment="Top"> <TextBlock>City</TextBlock> <TextBox Text="{Binding SomeTextField}" /> </StackPanel>

Produces:

Databing Textbox No Default

What if the item we are attempting to data bind to is not available.  It's not going to be null, the binding will just fail.  This should work well:

<StackPanel Width="130" Height="40" HorizontalAlignment="Left" 
VerticalAlignment="Top"> <TextBlock>City</TextBlock> <TextBox Text="{Binding SomeTextField,
FallbackValue=UNBOUND}" /> </StackPanel>

Produces:

Databing Textbox Default

Example #3

This is a much more common occurrence.  You have a number that you need to format in a textbox.  This example shows the decimal value with no real format at all.

<StackPanel Width="130" Height="40" HorizontalAlignment="Left" 
VerticalAlignment="Top"> <TextBlock>Cost</TextBlock> <TextBox Text="{Binding SomeDecimalField}" />
</StackPanel>

Produces:

Databing Decimal Non Formatted

There are a few ways to get the format we desire.  The first example uses a static resource:

xmlns:clr="clr-namespace:System;assembly=mscorlib"
<StackPanel Width="130" Height="40" HorizontalAlignment="Left" 
VerticalAlignment="Top"> <StackPanel.Resources> <clr:String x:Key="formatStr">{0:C}</clr:String> </StackPanel.Resources> <TextBlock>Cost</TextBlock> <TextBox Text="{Binding SomeDecimalField,
StringFormat={StaticResource formatStr}}" /> </StackPanel>

You can put the string inline but it looks really funny and for some reason the there has to be a non-whitespace character before the formatting expression which you may not want:

<TextBox Text="{Binding SomeDecimalField, 
StringFormat=Cost: {0:C}}" />

This formats the string to look like "Cost: $1,2398.00".  You can see how that could cause lots of confusion and would be undesirable.  There is a way we can get the binding to work a little better:

<TextBox Text="{Binding SomeDecimalField, 
StringFormat={}{0:C}}" />

OR

<TextBox>
    <TextBox.Text>
        <Binding Path="SomeDecimalField" 
StringFormat="{}{0:C}" /> </TextBox.Text> </TextBox>

The result is a nicely formatted output:

Databing Decimal Formatted

Notice how the binding was applied? This is how you apply MultiBindings which is the next topic.

Example #4

What if you want the value of one or more fields to exist in a single binding?  That is where MultiBindings shine!  Check out this very easy example:

<StackPanel Width="130" Height="40" HorizontalAlignment="Left" 
VerticalAlignment="Top"> <TextBlock>Name</TextBlock> <TextBox> <TextBox.Text> <MultiBinding StringFormat="{}{0}, {1}"> <Binding Path="LastName" /> <Binding Path="FirstName" /> </MultiBinding> </TextBox.Text> </TextBox> </StackPanel>

Results:

Multi Binding

Example #5

Validation can happen at the point of binding. This can be handy if the objects you are binding to have logic built in.  For example, lets say you have a property for birth date.  Obviously the person can not be born in the future!  Here is how we avoid such rotten input!

public DateTime birthdate;
public DateTime BrithDate {
    get { return birthdate; }
    set {
        if (value > DateTime.Today)
            throw new ArgumentException(
"Person can not be born in the future!"); birthdate = value; } }

And the XAML:

<StackPanel Margin="5" Width="130" Height="40" 
HorizontalAlignment="Left"
VerticalAlignment="Top"> <TextBlock>Birthdate</TextBlock> <TextBox Text="{Binding BrithDate, Mode=TwoWay, StringFormat=d, NotifyOnValidationError=true, ValidatesOnExceptions=true}" /> </StackPanel>

Result:

Binding No Error

Binding Error

The error can also be styled if required.  Shown is the default style (nothing custom).

Example #6

The last example is for something called PriorityBinding.  This is poorly named in my opinion as it would be better called CascadingBinding.  The point to PriorityBinding is to name multiple data bindings in order of most desirable to least desirable. This way if the first binding fails, is empty and/or default, another binding can take it's place.  You may have to be a little imaginative to think of such a scenario, but this kind of thing could be useful so I'm glad it's there.  Just for fun, I am using the IsAsync field as well.  This tells WPF to use a thread other than the GUI thread to retrieve this property.  This is important for properties that take a long time to return as it will leave your app unresponsive until the value is bound.  In the case of this example without that attribute the window will not show until the 5 seconds had elapsed.

private string fname;
public string FirstName {
     get {
          Thread.Sleep(5000);
          return fname; 
     }
          set { fname = value; }
}

Xaml:

<StackPanel Margin="5" Width="130" Height="40" 
HorizontalAlignment="Left"
VerticalAlignment="Top"> <TextBlock>Name</TextBlock> <TextBox> <TextBox.Text> <PriorityBinding> <Binding Path="LastNameNonExistant"
IsAsync="True" /> <Binding Path="FirstName"
IsAsync="True" /> </PriorityBinding> </TextBox.Text> </TextBox> </StackPanel>

Result:

Priority Binding

Another neat little thing to know is that the Hierarchy for Binding is Binding -> BindingBase -> MarkupExtension -> Object.  Unlike much of the rest of the .NET Framework you can actually inherit from any of these classes and provide support for your customized binding needs.  That is exactly what I am doing for SmartForms 2.0!  I created a binding class called DataBinding and it looks a little like this:

xmlns:db="clr-namespace:SmartForms2.Binding;assembly=SmartForms2"
<TextBox Name="firstName" Width="175" 
Text="{db:DataBinding Data, DataSource=DB1}" />

My DataBinding class inherits directly from MarkupExtension so all I had to do is implement the abstract method ProvideValue.

Links:
Understanding Lambda Expressions

This post has been sitting in my drafts for months!  Because my friend Phil just posted his post on Lambda Expressions I'll simply link to his post and present the rest of this as supplemental examples.

 // Delegates
// My Blog Engine is having trouble showing these so there may be some extra spaces
public delegate IntMathExpression (int x, int y);
public delegate IntMathExpressionSingle (int x);
public delegate
FloatMathExpression (float x, float y);

// Basic Lamda Expression
IntMathExpression a = (int x, int y) => x * y;
IntMathExpression b = (int alpha, int brovo) => alpha * brovo;
IntMathExpressionSingle c = (int x) => x * x;

// "such that" => 
// Parameters       => function body
// (param1, param2) => param1 + param2

// Lamda Expression using Type Inference
FloatMathExpression d = (x, y) => x + 3.14f * y;
// There is usually no need for custom delegates
Func<float, float, float> e = (x, y) => x * 100.0f + y; // Example using a lamda expression List<int> primes = new List<int>() { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101 }; var p = primes.Where( prime => prime.ToString().EndsWith( "3" ) ); // Deferred Execution foreach (int i in p) { MessageBox.Show(i.ToString()); } // Query Expression Format var p2 = from prime in primes where prime.ToString().EndsWith("3") select prime; List<int> results = p2.ToList(); // Lamda Expressions mixed with Extension Methods List<string> str = new List<string>() { "The Lord of the Rings", "Star Wars, Eposode III", "Ratatouille" }; var p3 = from movie in str where movie.CountVowels() > 5 select movie; List<string> movieResults = p3.ToList();

Posted: Jan 20 2009, 01:20 PM by Nathan Zaugg | with no comments
Filed under: , ,
Booing the Former President on Inauguration Day Shows Very Poor Taste!

There are things that people do that are in poor taste.  Some things that come to mind is scavenging at the dump (yeah, I saw this guy do that one time), yelling at the waitress for onions on your burger,  or leaving the lavatory without property sanitizing your hands.  If you do any of those things you are likely guilty of poor taste. 

Now, I don't really like Bush nor do I like Obama but I do show proper respect to both of these individuals.  Being a Republican without a party I tend to starkly disagree with much of President Obama's platform.  I understand that many people have a lot of pent up emotion and anger toward the decisions the previous administration has made.  Personally I have much of it toward both former President Bush and President Obama, but there is absolutely no way I would ever boo either of them at the inaugural ceremony!  Apparently many of those in attendance choose to boo anytime President Bush and Vice President Cheney where shown on the screen.  Having recently undergone back surgery, Vice President Cheney was in a wheel chair for heavens sakes! 

I have personally never been to an inauguration, but I should think that the kind of patriotic atmosphere that exists would prohibit such disgustingly undignified acts.  To those who choose to boo, you have none of my respect!  I place you below the people who use profane language, below people who put gum under restaurant tables, below people who throw lit cigarettes out the window, and even below people spit on the ground while indoors.  You people have no sense of taste and could never earn respect from anyone who does.

More Posts Next page »