Why I Don't Like Macs

I freely admit that I am fiercely loyal where my employer is concerned, but my loyalty pre-dates my employment. I was a big fan of Microsoft long before I went to work for them, which was one of the reasons why I was so thrilled when they offered me a job.

My affection for Microsoft goes back to then they were the "Little Guy" standing up to "Big Bad IBM," and at the time everyone loved Microsoft for that reason. (At that time, Macs were still pretty much toys.) But I became a huge fan of Microsoft when I started working in IT departments in the early to mid-1990s. At the time, the licensing fees for WordPerfect, Lotus 1-2-3, Ashton Tate's dBASE, etc., were astronomical, and our little IT budgets spent more on those licensing fees than we did on hardware, so our PCs were sub-par due to price-gouging. Then Microsoft came along and offered all of Microsoft Office with per-seat licensing that was 50% less than any other single software application, so we suddenly had software for every PC and budget to buy more hardware. This cannot be understated - Microsoft made it possible for us to actually focus on having great computers. To us, Microsoft was the greatest company on the planet.


By way of contrast, let's take a look at what Macs were like. In each place where I worked, we had some Macs, and the experiences were nowhere near similar. First of all, the Macs were hideously over-priced. (And they still are.) When a PC died, the data was nearly-always recoverable, and usually the majority of a PC could be salvaged as well. (It was usually only a single part that failed.) Not so with a Mac - when a Mac died (which was just as often as a PC), the user's data was gone, and we couldn't fix the computer because we couldn't walk into a store and buy over-the-counter parts for a Mac. When a brand-name PC failed, its manufacturer was generally helpful with troubleshooting and repairs, whereas Apple had one answer - send us the Mac and we'll get to it when we can. Seriously. Apple was so unwilling to help their users that we grew tired of even bothering to try. We just boxed up dead Macs and sent them (at our expense) back to Apple and forgot about them until Apple got around to shipping something back to us.

To be perfectly honest, I really tried to like Macs - and I used one for quite a while. I had heard that "Macs are better for [this reason]" or "Macs are better for [that reason]," but in my actual experience most of those claims had little basis in reality (with a few exceptions). Macs simply had a loyal fanbase of apologists who ignored the bad parts of their user experience and evangelized the good parts of their user experience. (Which is pretty much what I do for PCs, right? ;-] ) But after months of using a Mac and wrangling with what I still think is a terrible user interface, coupled with the realization that I could be doing my work considerably faster on a PC, it was my actual use of a Mac that turned me off to Macs in general.


I realize that a lot of time has gone by, and both Apple and their products have gotten better, but years of abuse are not easily forgotten by me. There was a time when Apple could have won me over, but their sub-par products and crappy customer service lost me. (Probably forever.) And make no mistake, for all of the blogosphere regurgitation that Microsoft is a "monopoly," Apple is one of the most-closed and highly-controlled architectures on the planet. What's more, prior to the release of OSX, Macs were a tiny niche, but for the most part they were a social experiment masquerading as a computer company that failed to reach more than 5% of the desktop computer market. In short, Apple was a sinking ship until Steve Jobs returned and Apple saved itself through iPod and iTunes sales. This gave Apple enough capital to abandon their failing computer design and rebuild the Mac as a pretty user interface on top of a UNIX operating system. This was a stroke of genius on someone's part, but you have to admit - when your 15-year-old computer business drives your company to the point of bankruptcy and you have to save your company by selling music players, that's pretty pathetic.

Ultimately, Apple users are a cult, Steve Jobs is their prophet (even though Woz is the real hero), and Apple products have always had half the features at twice the price. And that is why I don't like Macs. ;-]

More Examples of Bad Technical Support

A few years ago I wrote my Why I Won't Buy Another HP Computer blog, wherein I detailed several terrible support experiences that I had to endure with Hewlett Packard's technical support people. In order to show that not everyone has terrible technical support people, I recently wrote my Why I Will Buy Another Dell Computer blog, where I described a great experience that I had with Dell's technical support people. That being said, not everyone can be a good as Dell, so in this blog I will illustrate another bad support example - this time it's from Microsoft's Technical Support.

Here's the situation: I recently purchased a Dell 8700 computer, which came with Windows 8.1 installed. Since I run a full Windows domain on my home network, I would rather run the professional version of Windows 8.1 on my computers, so I purchased a Windows 8.1 Pro Pack from Microsoft in order to upgrade my system. The upgrade process is supposed to be painless; Microsoft sends you a little box with a product key that you use to perform the upgrade.

Well, at least that's the way that it should have worked, but I kept getting an error message when I tried to use the key. So after a few attempts I decided that it was time to contact Microsoft's Technical support to resolve the issue. I figured that it was probably some minor problem with the key, and it would be an easy issue to resolve. With that in mind, I browsed to http://support.microsoft.com and started a support chat session, which I have included in its entirety below:

Answer Desk online chat
Vince P: 5:12:37 PM Hi, thanks for visiting Answer Desk. I'm Vince P.
Welcome to Answer Desk, how may I help you?
You: 5:13:09 PM I just purchased a Windows 8.1 Pro Pack Product key from Microsoft for my Dell 8700 computer, but I get an error message that the key does not work.
Here is the key: nnnnn-nnnnn-nnnnn-nnnnn-nnnn
Vince P: 5:13:43 PM I'll be happy to sort this out for you.
For documentation purposes, may I please have your phone number?
You: 5:14:02 PM nnn-nnn-nnnn
Vince P: 5:14:38 PM Thank you, give me a moment please.
As I understand, you cannot install Windows Media Center using the key that you have, is that correct?
You: 5:17:53 PM Yes, I am trying to upgrade from Windows 8.1 to Windows 8.1 Pro with Media Center
Vince P: 5:18:12 PM First, allow me to set expectations that Answer Desk is a paid support service. We have a couple of paid premium support options should your issue prove complex and require advanced resources. Before we discuss those further, I need to ask some questions to determine if your problem can be handled by our paid support or if it's something really easy that we can fix at no charge today.
I will remotely access your computer to check the root cause of this issue.
[Note: Vince sends me a URL and code to initialize a remote session to my computer using a 3rd-party application.]
You: 5:19:40 PM Why is a remote session necessary?
Vince P: 5:21:19 PM Yes, I need to check the root cause of this issue.
Or I can send you some helpful links if you want.
You: 5:21:52 PM Or you can ask me to check anything for you
What do you need to check?
Vince P: 5:22:38 PM http://windows.microsoft.com/en-US/windows-8/feature-packs
If this link doesn't work, there might be some third party application that are blocking the upgrade.
It is much faster if I remotely access your computer, if it's okay with you.
You: 5:24:34 PM I have gone through the steps in that article, they did not work, which is why I contacted support
Vince P: 5:25:06 PM I need to remotely access your computer.
You: 5:25:11 PM The exact error message is "This key won't work. Check it and try again, or try a different key."
Vince P: 5:25:16 PM Please click on the link and enter the code.
You: 5:25:46 PM Or - you can tell me what I need to check for you and I will give you the answers you need.
Vince P: 5:26:51 PM http://answers.microsoft.com/en-us/windows/forum/windows_8-pictures/upgrade-to-windows-8-media-center/6060f338-900f-437f-a981-c2ae36ec0fd8?page=~pagenum~
I'm sorry, but I have not received a response from you in the last few minutes. If you're busy or pre-occupied, we can continue this chat session when you have more time. If I do not hear from you in the next minute, I will disconnect this session.
It was a real pleasure working with you today. For now, thank you for contacting Microsoft Answer Desk. Again, my name is Vince and you do have a wonderful day.
Your Answer Tech has ended your chat session. Thanks for visiting Answer Desk.

Unbeknownst to "Vince", I worked in Microsoft Technical Support for ten years, so I know the way that the system is supposed to work and how Microsoft's support engineers are supposed to behave. Vince was condescending and extremely uncooperative - he simply wanted to log into my machine, but no one gets to log into my computers except me. I know my way around my computer well enough to answer any questions that Vince might have had, but Vince didn't even try. What's more, when Vince sent me a long support thread to read, he took that as his opportunity to simply end the chat session a few moments later. Very bad behavior, dude.

Unfortunately, Microsoft's chat application crashed after the session had ended, so I wasn't able to provide negative feedback about my support experience, so this blog will have to suffice. If I had a way to contact Vince's boss, I would have no problem pointing out that Vince desperately needs remedial training in basic technical support behavior, and he shouldn't be allowed to work with customers until he's shown that he can talk a customer through a support scenario without a remote session. If he can't do that, then he shouldn't be in technical support.

By the way - just in case someone else runs into this issue - all that I had to do in order to resolve the issue was reboot my computer. Seriously. Despite the error message, apparently Windows had actually accepted the upgrade key, so when I rebooted the computer it upgraded my system to Windows 8.1 Professional. (Go figure.)

Proper Use of Acronyms In Business Communications

I caught some people at work overusing some obscure acronyms in business emails that have considerably more popular uses, so I had to tell them to get used to spelling out phrases at least the first time in order to provide context for everyone else in the conversation. This should be obvious to everyone, but too many people fail to realize that their recipients may have no idea what the sender is talking about based on their individual knowledge.

For example:

  • HTML - This should always mean "HyperText Markup Language," and it should never mean "Happy To Make Lemonade" With that in mind, you should always write "Happy To Make Lemonade (HTML)" when you first use it, and you should probably use it throughout your email. But still, you should consider writing "HyperText Markup Language (HTML)" when you first use it, just to make everything perfectly clear to your readers, and then you can use just the acronym for subsequent references.
  • VS - This could be short for "versus," or it could mean "Visual Studio." Many English-speaking readers will probably be able to determine the correct meaning based on the surrounding text, but in a diverse work environment there is no guarantee that the intended meaning will be perfectly clear to everyone. This means that some recipients will have to re-read what the sender has written in order to verify their understanding, which could have been alleviated by simply using "A versus B" or "Visual Studio (VS)."
  • OMG - This is often used colloquially to mean "Oh My Gosh," but I've seen it used to mean "On Middle Ground." Needless to say, the sentence can have dramatically different meanings depending on how that acronym is understood by the reader. For example: "Right now both parties are having a difficult time finding issues OMG where everyone can agree."

Social media acronyms should not be used in a business context; this includes the following examples:

  • BTW - "By The Way"
  • FWIW - "For What It's Worth"
  • PDQ - "Pretty Darn Quick"
  • SOL - "Sh** Outta Luck"
  • etc.

There are a few possible exceptions which may be commonly-understood business acronyms, but you should still consider your recipients when deciding which of these acronyms you should use and which you should spell out. Here are a few examples:

  • ASAP - "As Soon As Possible"
  • FYI - "For Your Information"
  • FAQ - "Frequently-Asked Questions"
  • Q&A - "Questions and Answers"
  • PS - "Postscript"

There is one simple rule that you should always remember when writing for others:

In business communications, brevity is not always better, and ambiguity will be the death of us all. Winking smile

FTP Clients - Part 14: CuteFTP

For this next installment in my series about FTP clients, I want to take a look at Globalscape's CuteFTP, which is available from the following URL:


CuteFTP is a for-retail product that used to be available in several editions - Lite, Home, and Pro - but at the time of this blog CuteFTP was only available in a single edition which combined all of the features. With that in mind, for this blog post I used CuteFTP 9.0.5.

CuteFTP 9.0 Overview

I should start off with a quick side note: it's kind of embarrassing that it has taken me so long to review CuteFTP, because CuteFTP has been my primary FTP client at one time or other over the past 15 years or so. That being said, it has been a few years since I had last used CuteFTP, so I was curious to see what had changed.

Fig. 1 - The Help/About dialog in CuteFTP 9.0.

To start things off, when you first install CuteFTP 9.0, it opens a traditional explorer-style view with the Site Manager displayed.

Fig. 2 - CuteFTP 9.0's Site Manager.

When you click File -> New, you are presented with a variety of connection options: FTP, FTPS, SFTP, HTTP, etc.

Fig. 3 - Creating a new connection.

When the Site Properties dialog is displayed during the creation of a new site connection, you have many of the options that you would expect, including the ability to change the FTP connection type after-the-fact; e.g. FTP, FTPS, SFTP, etc.

Fig. 4 - Site connection properties.

Once an FTP connection has been established, the CuteFTP connection display is pretty much what you would expect in any graphical FTP client.

Fig. 5 - FTP connection established.

A cool feature for me is that CuteFTP 9.0 supports a COM interface, (which is called the Transfer Engine), so you can automate CuteFTP commands through .NET or a scripting language. What was specifically cool about CuteFTP's scripting interface was the inclusion of several practical samples in the help file that is installed with the application.

Fig. 6 - Scripting CuteFTP.
Fig. 7 - Scripting samples in the CuteFTP help file.

Anyone who has read my blogs in the past knows that I am also a big fan of WebDAV, and an interesting feature of CuteFTP is built-in WebDAV integration. Of course, this functionality is a little redundant if you are using any version of Windows starting from Windows XP and later since WebDAV integration is built-in to the operating system via the WebDAV redirector, (which lets you map drive letters to WebDAV-enabled websites). But still - it's cool that CuteFTP is trying to be an all-encompassing transfer client.

Fig. 8 - Creating a WebDAV connection.

One last cool feature that I should call out in the overview is the integrated HTML editor, which is pretty handy. I could see where this might be useful on a system where you use FTP and you don't want to bother installing a separate editor.

Fig. 9 - CuteFTP's Integrated HTML editor.

Using CuteFTP 9.0 with FTP over SSL (FTPS)

CuteFTP 9.0 has built-in support for FTP over SSL (FTPS), and it supports both Explicit and Implicit FTPS. To specify which type of encryption to use for FTPS, you need to choose the appropriate option from the Protocol type drop-down menu in the Site Properties dialog box for an FTP site.

Fig. 10 - Specifying the FTPS encryption.

I was really happy to discover that I could use CuteFTP 9.0 to configure an FTP connection to drop out of FTPS on either the data channel or command channel once a connection is established. This is a very flexible design, because it allows you to configure FTPS for just your user credentials with no data and no post-login commands, or all commands and no data, or all data and all commands, etc.

Fig. 11 - Specifying additional FTPS options.

Using Using CuteFTP 9.0 with True FTP Hosts

CuteFTP 9.0 does not have built-in support for the HOST command that is specified in RFC 7151, nor does CuteFTP have a first-class way to specify pre-login commands for a connection.

But that being said, I was able find a way to configure CuteFTP 9.0 to send a HOST command for a connection by specifying custom advanced proxy commands. Here are the steps to pull this off:

  1. Bring up the properties dialog for an FTP site in the CuteFTP Site Manager
  2. Click the Options
  3. Choose Use site specific option in the drop-down
  4. Enter your FTP domain name in the Host name field
  5. Click the Advanced button
  6. Specify Custom for the Authentication Type
  7. Enter the following information:

    HOST ftp.example.com
    USER %user%
    PASS %pass%

    Where ftp.example.com is your FTP domain name
  8. Click OK for all of the open dialog boxes
Fig. 12 - Specifying a true FTP hostname via custom proxy settings.

Note: I could not get this workaround to successfully connect with FTPS sessions; I could only get it to work with regular (non-encrypted) FTP sessions.

Using Using CuteFTP 9.0 with Virtual FTP Hosts

CuteFTP 9.0's login settings allow you to specify the virtual host name as part of the user credentials by using syntax like "ftp.example.com|username" or "ftp.example.com\username". So if you don't want to use the workaround that I listed earlier, or you need to use FTPS, you can use virtual FTP hosts with CuteFTP 9.0.

Fig. 13 - Specifying an FTP virtual host.

Scorecard for CuteFTP 9.0

This concludes my quick look at a few of the FTP features that are available with CuteFTP 9.0, and here are the scorecard results:

CuteFTP 9.0.5 Rich Y Y Y Y/N1 Y N/A2
  1. As I mentioned earlier, support for true HOSTs is not built-in, but I provided a workaround that seems to work great for FTP sessions, although I could not get it work work with FTPS sessions.
  2. I could not find a way to extend the functionality of CuteFTP 9.0; but as I said earlier, it provides a COM for scripting/automating FTP functionality.

That wraps things up for today's review of CuteFTP 9.0. Your key take-aways should be: CuteFTP is good FTP client; it has added some great features over the years, and as with most of the FTP clients that I have reviewed, I am sure that I have barely scratched the surface of its potential.

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Why I Will Buy Another Dell Computer

Several years ago I wrote a blog post that was titled Why I Won't Buy Another HP Computer, in which I described (in detail) a series of awful customer support experiences that I had with Hewlett-Packard (HP) when I purchased an HP desktop computer.

I would now like to offer the details of an entirely different customer support experience that I recently had with what is probably my favorite computer manufacturer: Dell.


Why I Like Dell

To start things off, I need to point out that I am a huge fan of Dell computers, and I have been for years. The reason for my admiration is simply this: Dell's computers have always worked for me. I have never had an experience where the hardware in a Dell computer has failed, even in extremely bad conditions. For example, I used to manage the network for a small church which had somewhere around 15 to 20 desktop computers and three Dell PowerEdge servers. During a particularly bad thunderstorm, a nearby lightning strike took out the hard drives on nearly all of the computers, including the servers. Fortunately everything on the network had multiple redundant backups, but as it turns out - I didn't need to use the backups. Only the hard drives were bad - all of the computers survived the damage, and I was able to use a combination of Symantec Ghost and Runtime Software's GetDataBack to restore all of the data on the failed drives to new drives. Despite the wide swath of destruction, all of the computers were up-and-running in less than a weekend.

In addition to having lived through that situation, I have been nothing but impressed with all of the Dell computers that I have owned both personally and professionally, and I have owned a lot. My currently laptop is from Dell, as is my wife's laptop, my son's laptop, my daughters' laptops, my Windows Media Center computer, my tablet PC, and several of my work-related computers. In fact, the only non-Dell computing devices in my house right now are my home-built rackmounted server, my wife's Microsoft Surface, and the HP computer from my earlier blog - which is what led me to my recent experience.

Shopping for a New PC

The single HP computer in my household is several years old, and it was time for me to start thinking about upgrading. I had been doing a little shopping, but nothing serious. Since I am taking some graduate courses at the University of Arizona, I have found myself on the receiving end of spam that various companies throw at college students. (Dear Spammers: if you are reading this, I do not need another credit card, or back-to-school attire, or a summer internship, or student housing, etc.) But one piece of spam caught my eye: Dell had sent me an email advertising a free tablet computer with the purchase of a new computer. Since I was already in the market for a new computer, I thought that I would check out their deals.

I followed the link from the email to Dell's website, where I quickly learned that Dell's offer was for an Android-based tablet. I could care less about an Android device, but I was curious if Dell had a deal for a Windows-based device. With that in mind, I clicked a link to start a chat session with a sales representative. I don't want to give out full names for privacy reasons, so I'll just say that I wound up with a guy whose initials are "N.A." He informed me that Dell did have a deal where I could get a Windows tablet instead of the Android tablet, although it would cost more. That was perfectly acceptable to me, so I said that I was interested, but I had more questions. Our chat session wound up lasting 45 minutes, all of which was entirely due to me because I spent much of the chat session looking at various products on Dell's website and asking N.A. lots of questions about this option or that.

Bad News and Good News

I eventually decided on a deal that I liked, and I gave all of my contact information to N.A. so he could call me to get my credit card information. N.A. sent me the full text of the chat session, and he promised to call me within five minutes. But he never called. I remembered from the chat session that I had mentioned that I might want to wait 24 hours since it was the day before payday, so after 20 minutes I decided that N.A. might have misunderstood what I had meant, and I decided to wait until the next day to see if Dell would call me back.

By the following afternoon I still hadn't heard anything, so I decided to call Dell's 1-800 number to see what the deal was. I was routed through to a sales representative with the initials W.P., and I explained the situation. He did a little checking, and he informed me of two interesting pieces of information: first of all, the Dell deal for a Windows-based tablet had ended the day before, and much worse - apparently N.A. had quoted me the wrong tablet PC anyway.

Giving credit where it was due, W.P. was great throughout the call - I was understandably disappointed at the situation, and I kind of felt like I was being forced into a "Bait-and-Switch" scenario based on mistakes over which I had no control. W.P. checked with his manager, J.H., who said that he would try to contact N.A.'s department to see if they would stand behind their misquoted pricing. With that, W.P. and I ended the call.

I hadn't heard anything by the following afternoon, so I sent an email to J.H. and W.P. to ask what the status was with my situation, and I forwarded N.A.'s email with the original chat session. I also mentioned that I am a big Dell fan, and this situation was not reflecting well on Dell's ability to make a sale. Shortly after I sent out my email, J.H. called me to say that he couldn't contact the right person in N.A.'s department, so he was taking responsibility for the situation on Dell's behalf and was going to honor the deal. Very cool.

Closing Remarks

This entire experience reinforced my appreciation for Dell - not because I wound up getting a good deal, but primarily because a series of people took responsibility for someone else's mistake and worked to make things right.

Ultimately these people's actions made their company look great, and that's why I will buy another Dell computer.


FYI - The computer and the tablet both arrived and they're great. ;-]

How to Merge a Folder of MP4 Files with FFmpeg (Revisted)

I ran into an interesting situation the other day: I had a bunch of H.264 MP4 files which I had created with Handbrake that I needed to combine, and I didn't want to use my normal video editor (Sony Vegas) to perform the merge. I'm a big fan of FFmpeg, so I figured that there was some way to automate the merge without having to use an editor.

I did some searching around the Internet, and I couldn't find anyone who was doing exactly what I was doing, so I wrote my own batch file that combines some tricks that I have used to automate FFmpeg in the past with some ideas that I found through some video hacking forums. Here is the resulting batch file, which will combine all of the MP4 files in a directory into a single MP4 file named "ffmpeg_merge.mp4", which can be renamed to something else:

@echo off

if exist ffmpeg_merge.mp4 del ffmpeg_merge.mp4
if exist ffmpeg_merge.tmp del ffmpeg_merge.tmp
if exist *.ts del *.ts

for /f "usebackq delims=|" %%a in (`dir /on /b *.mp4`) do (
ffmpeg.exe -i "%%a" -c copy -bsf h264_mp4toannexb -f mpegts "%%a.ts"

for /f "usebackq delims=|" %%a in (`dir /b *.ts`) do (
echo file %%a>>ffmpeg_merge.tmp

ffmpeg.exe -f concat -i ffmpeg_merge.tmp -c copy -bsf aac_adtstoasc ffmpeg_merge.mp4

if exist ffmpeg_merge.tmp del ffmpeg_merge.tmp
if exist *.ts del *.ts

The merging process in this batch file is performed in two steps:

  • First, all of the individual MP4 files are remuxed into individual transport streams
  • Second, all of the individual transport streams are remuxed into a merged MP4 file

Here are the URLs for the official documentation on each of the FFmpeg switches and parameters that I used:

By the way, I realize that there may be better ways to do this with FFmpeg, so I am open to suggestions. ;-]

Some Useful and Obscure FTP Configuration Settings

I get a lot of question about various configuration settings for the IIS FTP service, and most of the settings that I discuss with people can be configured through the FTP features in the IIS Manager. That being said, there are some useful configuration settings for the FTP service which I periodical send to people that have no user interface for setting them. With that in mind, I thought I would write a quick blog to point out a few of these obscure settings that I personally use the most-often or I send to other people.

Using Domain Name Syntax as an FTP Virtual Hostname

I use this setting on all of my FTP servers because it seems a little more natural to me. Here's the scenario: the IIS FTP service supports two kinds of hostnames:

  • "Real" FTP hostnames - these are real FTP hostnames that are specified by using the FTP HOST command (as defined in RFC 7151)
  • "Virtual" ftp hostnames - these are FTP hostnames that are specified as part of the FTP USER command

Real FTP hostnames are pretty straight-forward: an FTP client specifies the hostname with a HOST command when a user is connecting to the server. Once the IIS FTP service receives that command, the FTP service routes the FTP session to the correct FTP site.

That being said, the FTP HOST command is still rather new, so only a handful of FTP clients currently support it. Because of that, you can use FTP "virtual" hostnames with the IIS FTP service. By default that syntax uses the "vertical line" or "pipe" character to differentiate between the hostname and user name. For example:

  • "ftp.contoso.com|username"
  • "ftp.fabrikam.com|username"

When you are specifying your FTP credentials in your FTP client, you would enter your username like the preceding examples. While this syntax is valid for both the IIS FTP service and the underlying FTP protocol, it seems a little odd to most users (including me). With that in mind, we added a configuration setting for the FTP service that will allow you to use the more-familiar domain\username syntax like the following examples:

  • "ftp.contoso.com\username"
  • "ftp.fabrikam.com\username"

To enable this feature, use the following steps:

  1. Open a command prompt as an administrator.
  2. Type the following commands:
    cd /d "%SystemRoot%\System32\Inetsrv"
    appcmd.exe set config -section:system.ftpServer/serverRuntime /hostNameSupport.useDomainNameAsHostName:"True" /commit:apphost
    net.exe stop FTPSVC
    net.exe start FTPSVC
  3. Close the command prompt.

More information about this feature is available in the IIS configuration reference at the following URL:

FTP Credential Caching

The FTP service caches user credentials for successfully-authenticated user sessions in order to help improve login performance, and I wrote the following detailed blog about this a couple of years ago:

Credential Caching in FTP 7.0 and FTP 7.5

I don't want to re-post an old blog, but I have sent several people to that blog over the years, so I thought that it was worth mentioning here since it seems to be referenced quite often. The problem that people seem to run into the most is that their old password is still valid for FTP after they have changed it, and this is caused by the FTP service caching their user credentials.

This is especially annoying for me personally when I am working on a development computer where I am creating an authentication provider. Unless I disable credential caching on my development computer, I can never seem to get any work done. To resolve this issue, I disable credential caching for the FTP service by using the following steps:

  1. Open a command prompt as an administrator.
  2. Type the following commands:
    cd /d "%SystemRoot%\System32\Inetsrv"
    appcmd.exe set config -section:system.ftpServer/caching /credentialsCache.enabled:"False" /commit:apphost
    net.exe stop FTPSVC
    net.exe start FTPSVC
  3. Close the command prompt.

The blog which I mentioned earlier goes into more detail about setting a custom timeout interval for credential caching instead of disabling the feature entirely, and all of the settings for FTP credential caching are in the IIS configuration reference at the following URLs:

FTP Client Certificate Authentication

FTP Client Certificate Authentication is an often-overlooked feature of the IIS FTP service, and I think that this is due to two reasons:

  1. There is no user interface to configure the required settings
  2. Configuring the required settings is very difficult

My second reason cannot be understated; I usually have to set up FTP Client Certificate Authentication once or twice a year in order to test various scenarios, and each time I do so I am reminded of just how difficult it can be to get everything right, and equally how easy it is to get something wrong.

Fortunately I took the time a couple of years ago to write a blog which documents everything that it takes to configure the FTP service, and I have used my notes in that blog several times. In complement to my blog on the subject, Vivek Kumbhar wrote an excellent blog series with additional steps to configure your Active Directory for certificate authentication. With that in mind, here are all of the requisite blog posts that you would need to set up this feature:

As I have mentioned before, configuring this feature is not for the faint-of-heart, but it can be very beneficial from a security standpoint.

For more information about the settings that are required for FTP Client Certificate Authentication, see the following articles in the IIS configuration reference:

That wraps it up for today's post. ;-]

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Case Study: Migrating Microsoft’s .NET Community Websites to Microsoft Azure

Have you ever wondered how much work is involved when migrating a traditionally-hosted production website to Microsoft Azure? If so, the following case study might be of interest to you:

Microsoft Azure Migration: Microsoft’s .NET Community Websites
Migrating Microsoft’s ASP.NET and IIS.NET Community Websites to Microsoft Azure

Here's a little background information on this migration case study: last fall Microsoft worked with two of it's hosting partners, Neudesic and Orcsweb, to migrate the www.asp.net and www.iis.net websites from a traditional web hosting scenario (e.g. websites hosted on physical servers) to virtual machines that are hosted in the cloud on Microsoft Azure. Here's what the web farm looked like before the migration:

After the migration, Microsoft had reduced both the hosting costs and the number of servers required by almost 50%.  Here's what the web farm looked like when the migration had been completed:

There are a lot of people who helped make this migration a success - and there are far too many to name here - but I would like to say a special "thanks" to everyone at Neudesic and Orcsweb for making this migration process as painless as possible.

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Simple Utility to Calculate File Hashes

I have to download various files from time-to-time, and it's nice when websites provide checksum hashes so I can validate that the file I just downloaded matches the version on the server. (ON a related note, I wrote a blog several years ago which showed how to create a provider for the IIS FTP service which automatically creates checksum files when files are uploaded to a server; see my Automatically Creating Checksum Files for FTP Uploads blog post for the details.)

In order to calculate hashes for files that I have downloaded, several years ago I wrote a simple command-line application for Windows which uses several of the built-in algorithms in .NET's System.Security.Cryptography. And while I realize that there are probably other tools that provide this same functionality, I have used this little utility for years, and I've had several people ask me for copies. With that in mind, I thought that it might make a nice blog topic if I shared the code with everyone. (Note: It's a really simple sample; the .NET framework does all the real work for this application.)

Without further fanfare, here's the source code. In order to use this code sample, you need to create a new C# project in Visual Studio and choose the Console Application template. When the new project opens, replace the template's code with the following:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Security.Cryptography;

class Hash
    static void Main(string[] args)
        // Verify the correct number of command-line arguments.
        if (args.Length != 2)
            // Show the help message if an incorrect number of arguments was specified.
            byte[] hashValue = null;
            // Verify that the specified file exists.
            if (!File.Exists(args[1]))
                // Show the help message if a non-existent filename was specified.
                    // Create a fileStream for the file.
                    FileStream fileStream = File.OpenRead(args[1]);
                    // Be sure it's positioned to the beginning of the stream.
                    fileStream.Position = 0;
                    // Use the specified hash algorithm.
                    switch (args[0].ToUpper())
                        case "MD5":
                            // Compute the MD5 hash of the fileStream.
                            hashValue = MD5.Create().ComputeHash(fileStream);
                        case "SHA1":
                            // Compute the SHA1 hash of the fileStream.
                            hashValue = SHA1.Create().ComputeHash(fileStream);
                        case "SHA256":
                            // Compute the SHA256 hash of the fileStream.
                            hashValue = SHA256.Create().ComputeHash(fileStream);
                        case "SHA384":
                            // Compute the SHA384 hash of the fileStream.
                            hashValue = SHA384.Create().ComputeHash(fileStream);
                        case "SHA512":
                            // Compute the SHA512 hash of the fileStream.
                            hashValue = SHA512.Create().ComputeHash(fileStream);
                        case "BASE64":
                            // Compute the BASE64 hash of the fileStream.
                            byte[] binaryData = new Byte[fileStream.Length];
                            long bytesRead = fileStream.Read(binaryData, 0, (int)fileStream.Length);
                            if (bytesRead != fileStream.Length)
                                throw new Exception(String.Format("Number of bytes read ({0}) does not match file size ({1}).", bytesRead, fileStream.Length));
                            string base64String = System.Convert.ToBase64String(binaryData, 0, binaryData.Length);
                            Console.WriteLine("File: {0}\r\nBASE64 Hash: {1}", fileStream.Name, base64String);
                            hashValue = null;
                            // Display the help message if an unrecognized hash algorithm was specified.
                    if (hashValue != null)
                        // Write the hash value to the Console.
                        PrintHashData(args[0].ToUpper(), fileStream.Name, hashValue);
                    // Close the file.
                catch (Exception ex)
                    Console.WriteLine("Error: {0}", ex.Message);

    // Display the help message.
    private static void ShowHelp()
    {/>        Console.WriteLine("HASH.exe <hash algorithm> <file name>\n\n" +
            "\tWhere <hash algorithm> is one of the following:\n" +

    // Print the hash data in a readable format.
    private static void PrintHashData(string algorithm, string fileName, byte[] array)
        Console.Write("File: {0}\r\n{1} Hash: ", fileName,algorithm);
        for (int i = 0; i < array.Length; i++)
            Console.Write(String.Format("{0:X2}", array[i]));

When you compile and run the application, you will see following help message when you specify no command-line parameters:

HASH.exe <hash algorithm> <file name> Where <hash algorithm> is one of the following: BASE64 MD5 SHA1 SHA256 SHA384 SHA512

When you specify one of the supported hashing algorithms and a filename, the application will display something like the following example:

C:\>hash.exe SHA1 foobar.zip File: C:\foobar.zip SHA1 Hash: 57686F6120447564652C20426F6220526F636B73

That's all there is to it. As I mentioned earlier, it's a pretty simple sample. ;-]

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

What I Do For A Living

It seems that I have always had a difficult time explaining to people what I do at Microsoft. It's not that I'm unsure about what I do - the details of my job have always been crystal-clear to me and I love what I am doing. It's just that I can't find a way to explain things in a way that doesn't result in blank stares from anyone who isn't a geek. (This problem isn't limited to me, though; my non-technical wife simply responds "I have no idea what he does" when someone asks her what I do for a living.)

Here's a perfect example: when I was a Program Manager on the Internet Information Services (IIS) team, people would often ask me what I did for Microsoft, and I would reply with something like, "I help design and implement the web publishing protocols for Microsoft's web server."

Other Person: [Blank Stare]

I would attempt to remedy the situation by adding, "You know, I design Microsoft's implementation of Internet technologies like the File Transfer Protocol, WebDAV, and the FrontPage Server Extensions."

Other Person: [Blank Stare]

In a sometimes-futile effort to salvage the conversation from complete disaster, I would interject, "You like to use the Internet, right? Well, your computer is on one side of the Internet, and my team helps build the other side of the Internet. That's kind of what I do."

That comment would usually be met with a slight spark of recognition, which was sometimes followed by a half-muttered, "That's nice."

At one time or other during my tenure as a Program Manager on the IIS team I was responsible for a smattering of disparate technologies; things like FTP, WebDAV, FPSE, FastCGI, PHP, URL Rewrite, IIS Express, Log Parser, etc. Most of those technologies garnered little to no interest for the average person, and many of my coworkers found them pretty boring as well. Just the same, I personally found every one of those technologies completely fascinating. (Why else would I spend eight years trying to get just one new command added to FTP?)

A couple of years ago I left the IIS program management team and I joined the writing team which is responsible for documenting Microsoft's ASP.NET framework; and if you have to ask what that means, then you are probably not interested in the answer.

Still, people would ask me what I do for Microsoft, and I would try to explain my job with statements like, "I document the Application Programming Interfaces (or APIs) for Microsoft's ASP.NET."

Other Person: [Blank Stare]

I would try to nudge the conversation along by saying things like, "I help people write web code."

Other Person: [Blank Stare]

Skipping ahead in the conversation, I would usually make a last-ditch attempt by stating, "Let's say you wanted to create a website; if so, you might read something that I wrote in order to help you get started."

Sometimes this remark would illicit a hint of acknowledgment, but usually I just got another blank stare.

This leads me to a few days ago. My wife and I were at dinner, and a waiter asked me what I did for a living. In the back of my mind I started to say something like, "Well, these days I'm documenting a set of APIs that Java programmers will use with Microsoft Azure technologies [blah blah blah]..."

But what actually came out of my mouth was, "I could explain it to you, but I'm pretty sure you wouldn't want me to. Trust me."

I like that answer. I think I'll stick with it in the future. :-)