Microsoft Bob

Just a short, simple blog for Bob to share some tips and tricks.

Be sure to check out my non-technical blog at www.bobsbasement.net.

Month List

Using Classic ASP and URL Rewrite for Dynamic SEO Functionality

I had another interesting situation present itself recently that I thought would make a good blog: how to use Classic ASP with the IIS URL Rewrite module to dynamically generate Robots.txt and Sitemap.xml files.

Overview

Here's the situation: I host a website for one of my family members, and like everyone else on the Internet, he wanted some better SEO rankings. We discussed a few things that he could do to improve his visibility with search engines, and one of the suggestions that I gave him was to keep his Robots.txt and Sitemap.xml files up-to-date. But there was an additional caveat - he uses two separate DNS names for the same website, and that presents a problem for absolute URLs in either of those files. Before anyone points out that it's usually not a good idea to host multiple DNS names on the same content, there are times when this is acceptable; for example, if you are trying to decide which of several DNS names is the best to use, you might want to bind each name to the same IP address and parse your logs to find out which address is getting the most traffic.

In any event, the syntax for both Robots.txt and Sitemap.xml files is pretty easy, so I wrote a couple of simple Classic ASP Robots.asp and Sitemap.asp pages that output the correct syntax and DNS-specific URLs for each domain name, and I wrote some simple URL Rewrite rules that rewrite inbound requests for Robots.txt and Sitemap.xml files to the ASP pages, while blocking direct access to the Classic ASP pages themselves.

All of that being said, there are a couple of quick things that I would like to mention before I get to the code:

  • First of all, I chose Classic ASP for the files because it allows the code to run without having to load any additional framework; I could have used ASP.NET or PHP just as easily, but either of those would require additional overhead that isn't really required.
  • Second, the specific website for which I wrote these specific examples consists of all static content that is updated a few times a month, so I wrote the example to parse the physical directory structure for the website's URLs and specified a weekly interval for search engines to revisit the website. All of these options can easily be changed; for example, I reused this code a little while later for a website where all of the content was created dynamically from a database, and I updated the code in the Sitemap.asp file to create the URLs from the dynamically-generated content. (That's really easy to do, but outside the scope of this blog.)

That being said, let's move on to the actual code.

Creating the Required Files

There are three files that you will need to create for this example:

  1. A Robots.asp file to which URL Rewrite will send requests for Robots.txt
  2. A Sitemap.asp file to which URL Rewrite will send requests for Sitemap.xml
  3. A Web.config file that contains the URL Rewrite rules

Step 1 - Creating the Robots.asp File

You need to save the following code sample as Robots.asp in the root of your website; this page will be executed whenever someone requests the Robots.txt file for your website. This example is very simple: it checks for the requested hostname and uses that to dynamically create the absolute URL for the website's Sitemap.xml file.

<%
    Option Explicit
    On Error Resume Next
    
    Dim strUrlRoot
    Dim strHttpHost
    Dim strUserAgent

    Response.Clear
    Response.Buffer = True
    Response.ContentType = "text/plain"
    Response.CacheControl = "public"

    Response.Write "# Robots.txt" & vbCrLf
    Response.Write "# For more information on this file see:" & vbCrLf
    Response.Write "# http://www.robotstxt.org/" & vbCrLf & vbCrLf

    strHttpHost = LCase(Request.ServerVariables("HTTP_HOST"))
    strUserAgent = LCase(Request.ServerVariables("HTTP_USER_AGENT"))
    strUrlRoot = "http://" & strHttpHost

    Response.Write "# Define the sitemap path" & vbCrLf
    Response.Write "Sitemap: " & strUrlRoot & "/sitemap.xml" & vbCrLf & vbCrLf

    Response.Write "# Make changes for all web spiders" & vbCrLf
    Response.Write "User-agent: *" & vbCrLf
    Response.Write "Allow: /" & vbCrLf
    Response.Write "Disallow: " & vbCrLf
    Response.End
%>

Step 2 - Creating the Sitemap.asp File

The following example file is also pretty simple, and you would save this code as Sitemap.asp in the root of your website. There is a section in the code where it loops through the file system looking for files with the *.html file extension and only creates URLs for those files. If you want other files included in your results, or you want to change the code from static to dynamic content, this is where you would need to update the file accordingly.

<%
    Option Explicit
    On Error Resume Next
    
    Response.Clear
    Response.Buffer = True
    Response.AddHeader "Connection", "Keep-Alive"
    Response.CacheControl = "public"
    
    Dim strFolderArray, lngFolderArray
    Dim strUrlRoot, strPhysicalRoot, strFormat
    Dim strUrlRelative, strExt

    Dim objFSO, objFolder, objFile

    strPhysicalRoot = Server.MapPath("/")
    Set objFSO = Server.CreateObject("Scripting.Filesystemobject")
    
    strUrlRoot = "http://" & Request.ServerVariables("HTTP_HOST")
    
    ' Check for XML or TXT format.
    If UCase(Trim(Request("format")))="XML" Then
        strFormat = "XML"
        Response.ContentType = "text/xml"
    Else
        strFormat = "TXT"
        Response.ContentType = "text/plain"
    End If

    ' Add the UTF-8 Byte Order Mark.
    Response.Write Chr(CByte("&hEF"))
    Response.Write Chr(CByte("&hBB"))
    Response.Write Chr(CByte("&hBF"))
    
    If strFormat = "XML" Then
        Response.Write "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
        Response.Write "<urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"">" & vbCrLf
    End if
    
    ' Always output the root of the website.
    Call WriteUrl(strUrlRoot,Now,"weekly",strFormat)

    ' --------------------------------------------------
    ' This following section contains the logic to parse
    ' the directory tree and return URLs based on the
    ' static *.html files that it locates. This is where
    ' you would change the code for dynamic content.
    ' -------------------------------------------------- 
    strFolderArray = GetFolderTree(strPhysicalRoot)

    For lngFolderArray = 1 to UBound(strFolderArray)
        strUrlRelative = Replace(Mid(strFolderArray(lngFolderArray),Len(strPhysicalRoot)+1),"\","/")
        Set objFolder = objFSO.GetFolder(Server.MapPath("." & strUrlRelative))
        For Each objFile in objFolder.Files
            strExt = objFSO.GetExtensionName(objFile.Name)
            If StrComp(strExt,"html",vbTextCompare)=0 Then
                If StrComp(Left(objFile.Name,6),"google",vbTextCompare)<>0 Then
                    Call WriteUrl(strUrlRoot & strUrlRelative & "/" & objFile.Name, objFile.DateLastModified, "weekly", strFormat)
                End If
            End If
        Next
    Next

    ' --------------------------------------------------
    ' End of file system loop.
    ' --------------------------------------------------     
    If strFormat = "XML" Then
        Response.Write "</urlset>"
    End If
    
    Response.End

    ' ======================================================================
    '
    ' Outputs a sitemap URL to the client in XML or TXT format.
    ' 
    ' tmpStrFreq = always|hourly|daily|weekly|monthly|yearly|never 
    ' tmpStrFormat = TXT|XML
    '
    ' ======================================================================

    Sub WriteUrl(tmpStrUrl,tmpLastModified,tmpStrFreq,tmpStrFormat)
        On Error Resume Next
        Dim tmpDate : tmpDate = CDate(tmpLastModified)
        ' Check if the request is for XML or TXT and return the appropriate syntax.
        If tmpStrFormat = "XML" Then
            Response.Write " <url>" & vbCrLf
            Response.Write " <loc>" & Server.HtmlEncode(tmpStrUrl) & "</loc>" & vbCrLf
            Response.Write " <lastmod>" & Year(tmpLastModified) & "-" & Right("0" & Month(tmpLastModified),2) & "-" & Right("0" & Day(tmpLastModified),2) & "</lastmod>" & vbCrLf
            Response.Write " <changefreq>" & tmpStrFreq & "</changefreq>" & vbCrLf
            Response.Write " </url>" & vbCrLf
        Else
            Response.Write tmpStrUrl & vbCrLf
        End If
    End Sub

    ' ======================================================================
    '
    ' Returns a string array of folders under a root path
    '
    ' ======================================================================

    Function GetFolderTree(strBaseFolder)
        Dim tmpFolderCount,tmpBaseCount
        Dim tmpFolders()
        Dim tmpFSO,tmpFolder,tmpSubFolder
        ' Define the initial values for the folder counters.
        tmpFolderCount = 1
        tmpBaseCount = 0
        ' Dimension an array to hold the folder names.
        ReDim tmpFolders(1)
        ' Store the root folder in the array.
        tmpFolders(tmpFolderCount) = strBaseFolder
        ' Create file system object.
        Set tmpFSO = Server.CreateObject("Scripting.Filesystemobject")
        ' Loop while we still have folders to process.
        While tmpFolderCount <> tmpBaseCount
            ' Set up a folder object to a base folder.
            Set tmpFolder = tmpFSO.GetFolder(tmpFolders(tmpBaseCount+1))
              ' Loop through the collection of subfolders for the base folder.
            For Each tmpSubFolder In tmpFolder.SubFolders
                ' Increment the folder count.
                tmpFolderCount = tmpFolderCount + 1
                ' Increase the array size
                ReDim Preserve tmpFolders(tmpFolderCount)
                ' Store the folder name in the array.
                tmpFolders(tmpFolderCount) = tmpSubFolder.Path
            Next
            ' Increment the base folder counter.
            tmpBaseCount = tmpBaseCount + 1
        Wend
        GetFolderTree = tmpFolders
    End Function
%>

Note: There are two helper methods in the preceding example that I should call out:

  • The GetFolderTree() function returns a string array of all the folders that are located under a root folder; you could remove that function if you were generating all of your URLs dynamically.
  • The WriteUrl() function outputs an entry for the sitemap file in either XML or TXT format, depending on the file type that is in use. It also allows you to specify the frequency that the specific URL should be indexed (always, hourly, daily, weekly, monthly, yearly, or never).

Step 3 - Creating the Web.config File

The last step is to add the URL Rewrite rules to the Web.config file in the root of your website. The following example is a complete Web.config file, but you could merge the rules into your existing Web.config file if you have already created one for your website. These rules are pretty simple, they rewrite all inbound requests for Robots.txt to Robots.asp, and they rewrite all requests for Sitemap.xml to Sitemap.asp?format=XML and requests for Sitemap.txt to Sitemap.asp?format=TXT; this allows requests for both the XML-based and text-based sitemaps to work, even though the Robots.txt file contains the path to the XML file. The last part of the URL Rewrite syntax returns HTTP 404 errors if anyone tries to send direct requests for either the Robots.asp or Sitemap.asp files; this isn't absolutely necesary, but I like to mask what I'm doing from prying eyes. (I'm kind of geeky that way.)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rewriteMaps>
        <clear />
        <rewriteMap name="Static URL Rewrites">
          <add key="/robots.txt" value="/robots.asp" />
          <add key="/sitemap.xml" value="/sitemap.asp?format=XML" />
          <add key="/sitemap.txt" value="/sitemap.asp?format=TXT" />
        </rewriteMap>
        <rewriteMap name="Static URL Failures">
          <add key="/robots.asp" value="/" />
          <add key="/sitemap.asp" value="/" />
        </rewriteMap>
      </rewriteMaps>
      <rules>
        <clear />
        <rule name="Static URL Rewrites" patternSyntax="ECMAScript" stopProcessing="true">
          <match url=".*" ignoreCase="true" negate="false" />
          <conditions>
            <add input="{Static URL Rewrites:{REQUEST_URI}}" pattern="(.+)" />
          </conditions>
          <action type="Rewrite" url="{C:1}" appendQueryString="false" redirectType="Temporary" />
        </rule>
        <rule name="Static URL Failures" patternSyntax="ECMAScript" stopProcessing="true">
          <match url=".*" ignoreCase="true" negate="false" />
          <conditions>
            <add input="{Static URL Failures:{REQUEST_URI}}" pattern="(.+)" />
          </conditions>
          <action type="CustomResponse" statusCode="404" subStatusCode="0" />
        </rule>
        <rule name="Prevent rewriting for static files" patternSyntax="Wildcard" stopProcessing="true">
          <match url="*" />
          <conditions>
             <add input="{REQUEST_FILENAME}" matchType="IsFile" />
          </conditions>
          <action type="None" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Summary

That sums it up for this blog; I hope that you get some good ideas from it.

For more information about the syntax in Robots.txt and Sitemap.xml files, see the following URLs:

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
Posted: Dec 31 2012, 08:09 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: IIS | URL Rewrite | SEO | Classic ASP
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Upgrading a Baby Computer

I'd like to take a brief departure from my normal series of IIS-related blogs and talk about something very near and dear to the hearts of many geeks - ripping a computer apart and upgrading its various hardware components just because it's fun. ;-)

Several years ago I bought a Dell Inspiron Mini 1011 Laptop, which is a smallish netbook computer with a 10-inch screen. (Actually, I bought this as an alternate laptop for my wife to use when travelling, since she doesn't like to travel with her full-sized laptop.)  This computer eventually became a "coffee-table laptop" for our house, which houseguests use when they come to visit. Since the netbook computer is so small, our family has affectionately labeled it the "Baby Computer."

Recently my wife and I took a trip to Hawaii, for which I decided to leave my full-size laptop at home, and I brought the Baby Computer instead.  Since I had never needed to rely on the Baby Computer to do anything more than surf the web in the past, I hadn't realized how quickly it was starved for resources whenever I tried to edit photos or write code. (Yes - I actually write code while on vacation... writing code makes me happy.) The Baby Computer shipped with an underwhelming 1GB of RAM, which filled up quickly if I tried to do too many things at once, and it came with a 120GB 5400rpm hard drive. There's nothing that I could do about CPU speed, but as I slogged through the rest of my vacation using the Baby Computer, I resolved to research if the other hardware in this laptop could be expanded.

Figure 1 - Performance Before Upgrading

Once we got home from vacation I did some checking, and I discovered that I could expand the Baby Computer's RAM to 2GB, which isn't much, but it obviously doubled what I had been using, and I decided replace it's original hard drive with a 128GB solid-state drive (SSD). With that in mind, I thought that it would be a worthwhile endeavor to document the upgrade process for someone else who wants to do the same thing with their Dell Inspiron Mini 1011. (Of course, you are undoubtedly voiding your Dell warranty the moment that you open your laptop's case.)

First things first - Dell's support website has some great information about tearing apart your laptop; Dell provides a detailed online Service Manual with all of the requisite instructions for replacing most of the parts in the Dell Mini, and I used it as a guide while I performed my upgrades. That being said, the upgrade process was still a little tricky, and some of the parts were difficult to get to. (Although it seems like Dell may have made upgrades a little easier in later models of my laptop.)

So without further introduction, here are the steps for upgrading the RAM and hard drive in a Dell Inspiron Mini 1011 Laptop.

Step 1 - Remove the Screws from the Back of the Case

This step is pretty easy - there are only a handful of screws to remove.

Figure 2 - Removing the Screws

Step 2 - Remove the Keyboard

It's pretty easy to pop the keyboard out of the case...

Figure 3 - Removing the Keyboard

...although once you have the keyboard loose, you need to flip it over carefully and remove the flat ribbon cable from underneath.

Figure 4 - Detaching the Keyboard Cable

Step 3 - Remove the Palm Rest

This step was a little tricky, and it took me a while to accomplish this task because I had to wedge a thin screwdriver in between the case and the palm rest in order to pry it off. Note that there is a flat ribbon cable that attaches the palm rest to the motherboard that you will need to remove.

Figure 5 - Removing the Palm Rest

With the keyboard and palm rest out of the way, you can remove the hard drive - there's a single screw holding the hard drive mount into the case and four screws that hold the hard drive in its mount.

Figure 6 - Removing the Hard Drive

If you were only replacing the hard drive, you could stop here. Since I was upgrading the RAM, I needed to dig deeper.

Step 4 - Remove the Palm Rest Bracket and Motherboard

Once the hard drive is out of the way, you need to remove the motherboard so you can replace the RAM that is located underneath it. There are a handful of screws above and below the computer that hold the palm rest bracket to the case...

Figure 7 - After Removing the Palm Rest Bracket

...once you remove remove the palm rest bracket, you can flip over the motherboard and replace the RAM.

Figure 8 - Replacing the RAM

Optional Step - Cloning the Hard Drive

Rather than reinstalling the operating system from scratch, I cloned Windows from the original hard drive to the SSD. To do this, I placed both the old hard drive and the new SSD into USB-based SATA drive cradles and I used Symantec Ghost to clone the operating system from drive to drive.

Figure 9 - Both Hard Drives in SATA Cradles
Figure 10 - Cloning the Hard Drive with Ghost

Once the clone was completed, all that was left was to install the new SSD and reassemble the computer.

Figure 11 - Installing the New SSD

Summary

Once I had everything completed and reassembled, Windows booted considerably faster when using the SSD; it now boots in a matter of seconds. (I wish that I had timed the boot sequence before and after the upgrades, but I didn't think of that earlier... darn.) Running the Windows 7 performance assessment showed a measurable increase in hard drive speed, with little to no increase in RAM speed. Of course, since there was no speed increase for CPU or graphics, the overall performance score for my laptop remained the same. That being said, with twice the RAM as before, it should be paging to disk less often, so regular usage should seem a little faster; even when it does need to swap memory to disk it will be faster using the SSD than with its old hard drive.

Figure 12 - Performance After Upgrading

That's all for now - have fun. ;-)

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

Posted: Dec 28 2012, 07:53 by bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Hardware
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Why I Personally Think the Zune Failed

First and foremost - I am not ashamed to admit that I am a card-carrying Zune fanboy. But that being said, as a faithful owner of several Zune devices, I am ashamed of the way that the Zune team at Microsoft so badly botched their product line; the Zune team was so out of touch with their target consumers that it borders on negligence. Here is my totally-biased list of reasons why I personally think the Zune failed.

My Top 10 Reasons Why the Zune Failed

Reason #1 - Microsoft entered the game with TOO LITTLE TOO LATE

There were a smattering of MP3 players on the market by the time that Apple's iPod hit the stores. I still have an RCA Lyra device that kicked butt in its day, but my personal favorites were the Creative Zen devices; you plugged a Zen player into your computer and it showed up like an external hard drive. To add music, you simply dragged & dropped music files anywhere you wanted; the Zen devices used your music files' metadata to sort by albums, genres, artists, etc.

When Apple's iPod hit the stores, its main rise to fame was its end-to-end story from iTunes to iPod, all of which belonged to Apple. Their devices were cool, and their advertising was stellar (as always). Even though they were overpriced, the iPod soon became "the product" that everyone wanted. The iTunes/iPod integration was closed to outsiders, which meant that Apple owned the end-to-end experience, and thereby collected all of the profits from it.

When Microsoft eventually realized that Apple was making enough money off their music/devices sales to save their company - which was formerly close to bankruptcy - they decided to create a device and end-to-end experience for themselves. But when Microsoft tried to do so, they mostly opted for feature parity with iTunes. What was Microsoft thinking? Instead of improving on the iTunes model, they were trying to break into an established market with a product that had little to offer that was above and beyond what consumers could already get.

FAIL.

Reason #2 - The early Zune end-to-end experience was terrible

I bought my wife a Zune for Christmas when they first released. Having owned and used several MP3 players in the past, I thought that it would be a similar experience; let me assure you, it was decidedly not a similar experience. I was so frustrated with the first-generation Zune software that I had boxed up the Zune and was ready to take it back to the store within an hour of trying to get it set up for her. I eventually elected not to do so, and I managed to get it working, but it was a crappy experience that made me apologize to my non-technical wife for burdening her with such a mess.

FAIL. FAIL. FAIL.

Reason #3 - You needed to use the Zune software to put files on the device

Customers wanted to use their Zune devices as external storage, but having to use the Zune software to transfer files to the device prevented that. The prevailing argument was that Zune followed the iTunes/iPod model, but who cares if that's the way that iTunes/iPod worked? Zune customers paid good money for their devices, and they wanted to store files on those. USB flash drives were still pretty pricey at the time, so opening the Zune platform to double as external storage would have been a fantastic selling feature, but that concept escaped the Zune team's leadership because they wanted to force users into having to use their @#$% software in the hopes that they would be tempted to buy more music/videos through Microsoft.

DESIGN FAIL.

[On a related note, the Windows Phone 7 team did not learn from the Zune's failure, and their devices still had the same, stupid Zune software requirement. BRAIN-DEAD FAIL.]

Reason #4 - You couldn't use Windows Media Player with the the Zune

Microsoft already made a killer media player application for Windows that worked with all the third-party MP3 player devices, but when Microsoft introduced their own MP3 player it didn't work with their existing Windows Media Player.

EPIC FAIL.

Reason #5 - Zune Didn't Support Plays For Sure

Microsoft spent a bunch of money cozying up to the music industry and MP3 makers with a program that was entitled Plays For Sure, whereby devices could be certified to play all Windows-based music files, whether they had copy protection on them or not. Even though all of these third-party companies went through the certification process, Microsoft's own player didn't have to; the Zune didn't support Plays for Sure.

SCREW YOUR PARTNERS FAIL.

[On a related note, this probably hastened the demise of WMA as a file format. SHOOT YOURSELF IN THE FOOT FAIL.]

Reason #6 - The Zune Software for Windows Sucked

For a long time - and I mean a really long time - the software that you needed to use with your Zune was next to worthless. It was slow, buggy, and ugly. By the time that the Zune team finally delivered a version of the Zune software that was actually worth installing, the battle for MP3 player supremacy was over and the iPod ruled uncontested.

SCREW YOUR OWN PLATFORM FAIL.

Reason #7 - Dropping the free downloads from the Zune Pass

The Zune pass tried to be the Netflix service of music, and in that sense it was ahead of the curve when it was introduced. Customers paid $14.95 a month, and in exchange they were granted free access to tens of thousands of DRM-based WMA music files - all of which they could download and play on their computers or Zune devices - and customers could play them as long as they kept their Zune pass up-to-date. In addition, customers got to keep 10 free songs a month in DRM-free MP3 format per month.

In September, 2011, some wunderkind in the Zune group decided to take away the 10 free songs and drop the price of the Zune pass to $9.99 per month. This person - whoever they may be - is an idiot. With the incredible amount of free music that is available on the Internet now, the free downloads on the Zune pass was the only feature of the Zune pass that made having a Zune pass worthwhile.

SCREW YOUR CUSTOMERS FAIL.

Reason #8 - Zune Required Users to Buy Music with 'Points'

The rest of the world works with actual money, but the Zune service required customers to use 'points' to buy music or videos, and points did not map directly to dollars and cents. On Amazon or iTunes, music was typically $0.99 per track, but on Zune it was typically 89 points per track. WTF? What the @#$% was a 'point'?

So let's say that you wanted to buy a music file to download; the Zune software would inform you that had to buy points first, which would have some weird exchange rate that didn't make sense. For example, if you bought 400 points for $4.99, that would mean that your 89-point music file actually cost $1.11, which was $0.12 more than Amazon or iTunes. When Microsoft combined their crappy points-based purchasing system with their overpriced music, they created an environment that was a truly horrible customer experience.

WTF FAIL.

Reason #9 - It Took Too Long to Market Zune Overseas

The iPod was dominating media player sales all over the world, but believe it or not - there are people that simply don't like Apple. I constantly saw people all over Europe that were clamoring for a Zune, and Microsoft didn't deliver.

DRAG YOUR FEET FAIL.

Reason #10 - No Zune Software for the Mac

Believe it or not, I saw a lot of Mac users who were asking for Zune software on the Mac. I'm not sure if these people were also iTunes users or not, but I think that the concept of the original "10 free songs" with a Zune pass was appealing to them. Sadly, Microsoft did not deliver - and a whole slew of potential customers were left high-and-dry.

SCREW YOUR POTENTIAL CUSTOMERS FAIL.

In Closing

Despite all this negativism, the Zune team did deliver some great products - I still own several Zunes from the various series of players, although it now feels a lot like owning a Tucker automobile or a Betamax VCR.

Here are some of the coolness factors that Zune had:

  • Wireless Networking - when the first Zunes came out, they were the first players to have wireless networking, which allowed Zune users to share files. I remember at the time that some of my Apple fanboy friends remarked that this was a stupid feature; their usual mantra was: "Who wants networking in a handheld device?" Seriously - I got asked that a lot. Now most every player worth its mettle has Wi-Fi support, as do iPhones, Windows Phones, etc. Zune was well-ahead of the curve.
  • Larger Storage at Cheaper Prices - for many versions of the Zune, you would get the same features in your Zune for a lot less money than a comparable iPod. The Zune didn't catch on, but that certainly wasn't due to price-per-feature.
  • Larger & Better Screens - when the Zune first came out, it's screen was much larger and better than its competitors' screens.
  • HDTV Support - the Zune HD was a great device, and one of the really cool features was output to HDTV from a really tiny device.

In the end, it was very sad for me to see the Zune fail; the Zune was simply a victim of being superior device with inferior product management.

Posted: Dec 22 2012, 22:26 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Zune
Tags:
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

FTP Clients - Part 11: Beyond Compare 3

For this installment in my series about FTP clients, I want to take a look at Beyond Compare 3 from Scooter Software. At its heart, Beyond Compare is a file/folder comparison tool, so it might seem an unlikely candidate for an FTP client, but it has a lot of great FTP features packed into it.

Fig. 1 - The Help/About dialog in Beyond Compare 3.

Note: For this blog I used Beyond Compare version 3.3.5.

Beyond Compare 3 Overview

Like many self-proclaimed computer geeks, over the years I have collected a lot of various utilities that perform specific actions that I need to take care of. Sometimes I discover these tools when Binging my way through the Internet, and other times they come highly recommended from other people. In this specific situation, Beyond Compare falls into the latter category - dozens of people had recommended Beyond Compare to me before I tried it out, and after falling in love with it I have recommended it to dozens of my friends. At the time I was using Microsoft WinDiff to compare files, which is still a great application to do simple comparisons, but Beyond Compare does so much more.

Fig. 2 - The Start New Session screen.
Fig. 3 - Comparing the files within two folders.
Fig. 4 - Comparing the HTML content of two files.

I could go on about Beyond Compare as a comparison tool, but that's really outside the scope of this blog since I am supposed to be talking about FTP features. Needless to say, if you're looking for a good comparison tool, you might want to download the trial edition of Beyond Compare 3 and give it a try.

That being said, let's get back to the business at hand. Beyond Compare 3 has a collection of FTP Profiles, which you can think of as analogous to a site manager in more traditional FTP clients.

Fig. 5 - Opening Beyond Compare 3's FTP Profiles.

Inside the FTP Profiles dialog, you can specify a wealth of connection options for remote FTP sites that you would expect to find in any other FTP client.

Fig. 6 - Specifying FTP connection options.

Once you have established an FTP connection through Beyond Compare 3, you can view your local files and the files in your remote FTP site side-by-side, and then you can perform comparisons, updates, merges, etc.

Fig. 7 - Viewing local and remote files.

Using Beyond Compare 3 with FTP over SSL (FTPS)

Beyond Compare 3 has built-in support for Explicit FTP over SSL (FTPS), which you specify when you are creating the FTP profile for a site.

Fig. 8 - Specifying an Explicit FTPS connection.

Once you have established an Explicit FTPS connection through Beyond Compare 3, the user experience is the same as it is for a standard FTP connection.

Fig. 9 - Comparing files over FTPS.

That being said, at first glance Beyond Compare 3 did not appear to support Implicit FTPS. For me this was not a deal-breaker by any stretch of the imagination since Explicit FTPS is preferred. (Even though Implicit FTPS is supported by IIS7 through IIS8, it is really an outdated protocol.)

10 January 2013 Update: I heard from Craig Peterson at Scooter Software that Beyond Compare 3 does support Implicit FTPS, but it does so implicitly. (No pun intended. ;-]) When you connect using FTP over SSL on port 990, it will automatically use implicit FTPS.

Using Using Beyond Compare 3 with True FTP Hosts

Beyond Compare 3 has built-in support for the HOST command, so you can use true FTP host names when using Beyond Compare 3 to connect to FTP7 and FTP8 sites that are configured with host names. This feature is enabled by default, but if you needed to disable it for some reason, that feature can be accessed on the Connection tab of Beyond Compare 3's FTP Profiles dialog.

Fig. 10 - Specifying support for the FTP HOST command.

Using Using Beyond Compare 3 with Virtual FTP Hosts

Beyond Compare 3'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", but since Beyond Compare 3 allows you to use true FTP hosts this is really a moot point. Just the same, there's nothing to stop you from disabling the HOST command for a connection and specifying an FTP virtual host as part of your username, although I'm not sure why you would want to do that.

Fig. 11 - Specifying a virtual FTP host.

Scorecard for Beyond Compare 3

This concludes our quick look at some of the FTP features that are available with Beyond Compare 3, and here are the scorecard results:

Client
Name
Directory
Browsing
Explicit
FTPS
Implicit
FTPS
Virtual
Hosts
True
HOSTs
Site
Manager
Extensibility
Beyond Compare 3.3.5 Rich Y Y Y Y Y N/A 1
As noted earlier, Beyond Compare 3 supports the FTP HOST command, and is enabled by default for new connections.

1 Note: I could not find anyway to extend the functionality of Beyond Compare 3, but it does have a scripting interface; see their Automating with Scripts and Scripting Reference pages for more details.

So there you have it - Beyond Compare 3 contains many of the features that would make up a great GUI-based FTP client with first-class support for all of the features that I have been examining in detail throughout my blog series about FTP clients. And as I have done with all of my blogs thus far, I included the following disclaimer in all of my preceding posts: there are a great number of additional features that Beyond Compare 3 provides - but once again I only focused on a few specific topic areas that apply to FTP7 and FTP8. For example, one particular feature that I might to experiment with in the future is Beyond Compare 3's support for FTP SSL Client Certificates. But I'll leave that for another day. ;-]

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
Posted: Nov 30 2012, 13:36 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: FTP | SSL
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Using WebMatrix to Take a PHP Class

With the release of WebMatrix 2, I thought that it would be great to take a PHP class and use WebMatrix exclusively for the entire class. Much to my surprise, this proved to be a great experience. Seriously, I did not expect it to go as well as it did. This has nothing to do with WebMatrix, it's just that I've picked up some cynicism over the years where editors are concerned. This pessimistic outlook is largely due to the fact that I've tried a lot of editors based on the recommendations of my fellow geeks, and those have often been bad experiences. Usually they say something like, "Dude, if you're going to write code in <some language> then you have to use the <some editor> application."

Unfortunately, most of these editors fail to live up to their hype, and I am forced to endure trials and tribulations where I loudly exclaim "If I was using <my favorite editor> I would be done by now!" (I periodically accompany those moments with language that is best reserved for a golf course when you've just hit your last Titlelist into the water hazard.)

But those experiences never happened with WebMatrix 2 - not even once; WebMatrix did pretty much everything that I needed it to do, and it did everything really well. As a result, my cynical skepticism quickly gave way to optimistic impression.

I took copious notes about my experiences with WebMatrix throughout the class, and with that in mind, I thought that it would be great to write a blog with my genuinely unbiased thoughts about using WebMatrix exclusively as my PHP authoring platform for the two-month duration of my class. (As a point of trivia, the PHP class that I took was BMIS 410 - Web Enterprise Technologies at Liberty University. Quick shout out to my professor, Michael Hart, who was a great instructor.)

What Went Well

First of all, the intellisense for PHP was quite good - and having the URLs to the PHP.net reference pages in the tooltip help for PHP functions was extremely useful; I spent a lot of time clicking through to the PHP.net website for assistance for one function or other.

Fig. 1 - WebMatrix's Intellisense for PHP.

Using WebMatrix to preview in IE and WP7/iPhone/iPad emulators was great; in my opinion, this experience was much better than the SuperPreview feature of Expression Web.

Fig. 2 - Options for previewing your website.
Fig. 3 - Testing my website in the iPad simulator.

Using the WebMatrix database editor to create tables for my MySQL database was great - in many ways it was much better than using the MySQL Workbench. The biggest drawback in WebMatrix was the inability to create auto-number fields, and I couldn't enter dates in the correct format in the database UI. (That was undoubtedly something that I was doing wrong.) So every once in a while I had to go back to the MySQL Workbench to fix something. That being said, the interface for creating relationships in WebMatrix is great, and much better than using MySQL Workbench.

Fig. 4 - Editing the data in a MySql Database.

FTP publishing is much better in version 2 of WebMatrix. I used an IIS7 web server, so I was able to use FTP7's virtual hosts to publish to a specific site on a shared server. WebMatrix has no FTPS support, so that is something of a loss. (WebMatrix also lacks full WebDAV support, but I've already talked about that in other blogs.)

Fig. 5 - FTP Publish Settings.

This last point might seem trivial, and I realize that a lot of editors have similar features, but the way that WebMatrix keeps track of opening/closing parentheses, brackets, and curly braces saved me more times than I can count.

Fig. 6 - Helping me keep track of what I'm doing.

What Could Have Been Better

Here are the few problems that I encountered with WebMatrix during the course:

This is not a problem that was due to WebMatrix, but everyone once in a while a page would get stuck in the cache and I couldn't see changes that I had made to a page, so I would have to restart IIS Express. I'll have to investigate why that was happening; it could be IIS Express, or it could be the PHP engine - I'm still not sure where the fault lies. Fortunately WebMatrix makes it very easy to restart IIS Express from inside WebMatrix, but still - it was a minor frustration.

WebMatrix only wanted to validate against HTML5, but my class required all assignments to use XHTML 1.0 Transitional DOCTYPE, and that showed up as errors in WebMatrix. Yes - the world is moving to HTML5, but still - that shouldn't cause an error.

Perhaps the biggest feature that WebMatrix lacks is the really cool local and remote side-by-side publishing view that both Expression Web and it's predecessor FrontPage had.

When you have a lot of pages open the WebMatrix tab bar fills up, and it's really difficult to keep track of which pages are open.

It would be nice to tear pages out of the editor like you can do with Internet Explorer and Visual Studio.

I have to mention this last item because it was in my notes, but it's technically not an issue for WebMatrix. One of my personal coding self-annoyances was that I would write the text for a string and then realize that I forgot to put it in quotes; when I would type an opening quote, WebMatrix would try to help me out by adding the closing quote - which would now be outside my string, so I always had to delete one of the quotes. There is an option to turn off that feature; see File->Options->Code in WebMatrix. But that being said, this is a useful feature when I remember to create the quotes before I start typing in a string. So once again, this is really more of a complaint against myself; it's my fault that I sometimes have lousy typing skillz. [sic]

Summary

I should start off by saying that I got an "A" in the course, and I can honestly give WebMatrix some of the credit for that. If I had spent a great deal of time fighting with an editor, I would have had less time to focus on writing PHP code. But in the end, WebMatrix actually made it easier for me to write PHP code.

So in closing, WebMatrix rocks, PHP rocks, and using WebMatrix with PHP definitely rocks.

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

Posted: Nov 29 2012, 10:01 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: PHP | WebMatrix
Tags: ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Programmatically Starting and Stopping FTP Sites in IIS 7 and IIS 8

I was recently contacted by someone who was trying to use Windows Management Instrumentation (WMI) code to stop and restart FTP websites by using code that he had written for IIS 6.0; his code was something similar to the following:

Option Explicit
On Error Resume Next

Dim objWMIService, colItems, objItem

' Attach to the IIS service.
Set objWMIService = GetObject("winmgmts:\root\microsoftiisv2")
' Retrieve the collection of FTP sites.
Set colItems = objWMIService.ExecQuery("Select * from IIsFtpServer")
' Loop through the sites collection.
For Each objItem in colItems
    ' Restart one single website.
    If (objItem.Name = "MSFTPSVC/1") Then
        Err.Clear
        objItem.Stop
        If (Err.Number <> 0) Then WScript.Echo Err.Number
        objItem.Start
        If (Err.Number <> 0) Then WScript.Echo Err.Number
    End If
Next

The problem that the customer was seeing is that this query did not return the list of FTP-based websites for IIS 7.0 or IIS 7.5 (called IIS7 henceforth), although changing the class in the query from IIsFtpServer to IIsWebServer would make the script work with HTTP-based websites those versions of IIS7.

The problem with the customer's code was that he is using WMI to manage IIS7; this relies on our old management APIs that have been deprecated, although part of that model is partially available through the metabase compatibility feature in IIS7. Here's what I mean by "partially": only a portion of the old ADSI/WMI objects are available, and unfortunately FTP is not part of the objects that can be scripted through the metabase compatibility feature in IIS7.

That being said, what the customer wants to do is still possible through scripting in both IIS7 and IIS8, and the following sample shows how to loop through all of the sites, determine which sites have FTP bindings, and then stop/start FTP for each site. To use this script, copy the code into a text editor like Windows Notepad and save it with a name like "RestartAllFtpSites.vbs" to your system, then double-click the file to run it.

' Temporarily disable breaking on runtime errors.
On Error Resume Next

' Create an Admin Manager object.
Set adminManager = CreateObject("Microsoft.ApplicationHost.AdminManager")
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"

' Test for commit path support.
If Err.Number <> 0 Then
    Err.Clear
    ' Create a Writable Admin Manager object.
    Set adminManager = CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
    adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"
    If Err.Number <> 0 Then WScript.Quit
End If

' Resume breaking on runtime errors.
On Error Goto 0

' Retrieve the sites collection.
Set sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
Set sitesCollection = sitesSection.Collection

' Loop through the sites collection.
For siteCount = 0 To CInt(sitesCollection.Count)-1
    isFtpSite = False
    ' Determine if the current site is an FTP site by checking the bindings.
    Set siteElement = sitesCollection(siteCount)
    Set bindingsCollection = siteElement.ChildElements.Item("bindings").Collection
    For bindingsCount = 0 To CInt(bindingsCollection.Count)-1
        Set bindingElement = bindingsCollection(bindingsCount)
        If StrComp(CStr(bindingElement.Properties.Item("protocol").Value),"ftp",vbTextCompare)=0 Then
            isFtpSite = True
            Exit For
        End If
    Next
    ' If it's an FTP site, start and stop the site.
    If isFtpSite = True Then
        Set ftpServerElement = siteElement.ChildElements.Item("ftpServer")
        ' Create an instance of the Stop method.
        Set stopFtpSite = ftpServerElement.Methods.Item("Stop").CreateInstance()
        ' Execute the method to stop the FTP site.
        stopFtpSite.Execute()
        ' Create an instance of the Start method.
        Set startFtpSite = ftpServerElement.Methods.Item("Start").CreateInstance()
        ' Execute the method to start the FTP site.
        startFtpSite.Execute()
    End If
Next

And the following code sample shows how to stop/start a single FTP site. To use this script, copy the code into a text editor like Windows Notepad, rename the site name appropriately for one of your FTP sites, save it with a name like "RestartContosoFtpSite.vbs" to your system, then double-click the file to run it.

' Temporarily disable breaking on runtime errors.
On Error Resume Next

' Create an Admin Manager object.
Set adminManager = CreateObject("Microsoft.ApplicationHost.AdminManager")
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"

' Test for commit path support.
If Err.Number <> 0 Then
    Err.Clear
    ' Create a Writable Admin Manager object.
    Set adminManager = CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
    adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"
    If Err.Number <> 0 Then WScript.Quit
End If

' Resume breaking on runtime errors.
On Error Goto 0

' Retrieve the sites collection.
Set sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
Set sitesCollection = sitesSection.Collection

' Locate a specific site.
siteElementPos = FindElement(sitesCollection, "site", Array("name", "ftp.contoso.com"))
If siteElementPos = -1 Then
    WScript.Echo "Site was not found!"
    WScript.Quit
End If

' Determine if the selected site is an FTP site by checking the bindings.
Set siteElement = sitesCollection(siteElementPos)
Set bindingsCollection = siteElement.ChildElements.Item("bindings").Collection
For bindingsCount = 0 To CInt(bindingsCollection.Count)-1
    Set bindingElement = bindingsCollection(bindingsCount)
    If StrComp(CStr(bindingElement.Properties.Item("protocol").Value),"ftp",vbTextCompare)=0 Then
        isFtpSite = True
        Exit For
    End If
Next

' If it's an FTP site, start and stop the site.
If isFtpSite = True Then
    Set ftpServerElement = siteElement.ChildElements.Item("ftpServer")
    ' Create an instance of the Stop method.
    Set stopFtpSite = ftpServerElement.Methods.Item("Stop").CreateInstance()
    ' Execute the method to stop the FTP site.
    stopFtpSite.Execute()
    ' Create an instance of the Start method.
    Set startFtpSite = ftpServerElement.Methods.Item("Start").CreateInstance()
    ' Execute the method to start the FTP site.
    startFtpSite.Execute()
End If

' Locate and return the index for a specific element in a collection.
Function FindElement(collection, elementTagName, valuesToMatch)
   For i = 0 To CInt(collection.Count) - 1
      Set elem = collection.Item(i)
      If elem.Name = elementTagName Then
         matches = True
         For iVal = 0 To UBound(valuesToMatch) Step 2
            Set prop = elem.GetPropertyByName(valuesToMatch(iVal))
            value = prop.Value
            If Not IsNull(value) Then
               value = CStr(value)
            End If
            If Not value = CStr(valuesToMatch(iVal + 1)) Then
               matches = False
               Exit For
            End If
         Next
         If matches Then
            Exit For
         End If
      End If
   Next
   If matches Then
      FindElement = i
   Else
      FindElement = -1
   End If
End Function

I hope this helps!

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
Posted: Oct 03 2012, 08:57 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , , ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

The New Look for IIS.NET

Following up on today's public release of Microsoft Windows Server 2012 and Internet Information Services 8.0, you'll notice some big changes on the IIS.net website.

Over the past few months, we've been working hard with several partners to roll out a brand-new design for the IIS.net website that resembles more closely the look and feel of our websites for Microsoft Azure, Windows Server 2012, and Visual Studio 2012.

Let us know what you think!

Posted: Sep 04 2012, 17:52 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Windows Server 2012 and IIS 8 are now available!

Microsoft has just released Windows Server 2012! You can find out more about this release on the Official Windows Server 2012 Launch Website (http://www.windows-server-launch.com).

In tandem with the release of Windows Server 2012, the IIS team is happy to announce the general availability of Internet Information Services 8.0 This new version of IIS offers a wealth of new features and improvements, and here are just a few of the enhancements that you can expect in IIS 8.0: Application Initialization, Dynamic IP Address Restrictions, Centralized SSL Certificate Store, CPU Throttling, FTP Logon Attempt Restrictions, Server Name Indication (SNI) Support, Improved SSL and Configuration Scalability, support for Multicore Scaling on NUMA Hardware, and more! Additional information about IIS 8.0 is available in the "What's New in IIS 8.0 for Windows 8?" web page.

If you'd like to try IIS 8.0 for yourself, you can download the evaluation version and start experimenting today!

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

Posted: Sep 04 2012, 03:58 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Video: What's New with Internet Information Services (IIS) 8: Performance, Scalability, and Security Features

The folks in the TechEd group have uploaded the video from my "What's New with Internet Information Services (IIS) 8: Performance, Scalability, and Security Features" presentation to YouTube, so you can view the video online.

You can also download the slides and the WMV/MP4 for my presentation at the following URL:

http://channel9.msdn.com/Events/TechEd/NorthAmerica/2012/WSV332

One quick side note: around 38:55 during the video, I had just asked the audience if anyone had used the IIS Configuration Editor, when a tremendous thunderclap resounded outside - this prompted a great laugh from audience members. After the presentation had ended, a couple people came up and jokingly asked how I had managed to stage that so well.

Smile

Posted: Sep 01 2012, 14:59 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: FTP | IIS | Windows
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Troubleshooting Custom FTP Providers with ETW

I recently received a question from a customer about troubleshooting custom FTP providers, and I recommended using the FTP service's Event Tracing for Windows (ETW) features in order to help troubleshoot the problem. I've helped a lot of customers use this little-known feature of the FTP service, so I thought that it would make a great subject for a quick blog.

By way of explanation, the FTP service in IIS 7.5 and IIS 8.0 allows developers to write their own custom functionality, and over the past several years I have written several walkthroughs and blogs that illustrate how you can create your own custom FTP providers:

That being said, sometimes things go wrong, and when that happens, I use some FTP ETW troubleshooting tricks that I'd like to share.

Setting up FTP ETW Tracing

Several years ago I wrote a blog about FTP and ETW Tracing, where I described how to turn on the FTP service's ETW tracing through a batch file, and then it used Log Parser to render the output in a datagrid for analysis. In the interests of completeness, here is the batch file again:

@echo off

rem======================================================================

echo Verifying that LogParser.exe is in the path...
LogParser -h >nul 2>nul
if errorlevel 1 (
  echo.
  echo Error:
  echo.
  echo   LogParser.exe is was not found. It is required for parsing traces.
  echo.
  echo Recommended actions:
  echo.
  echo   - If LogParser is installed then fix the PATH
  echo     variable to include the LogParser directory
  echo.
  echo   - If LogParser is not installed, then install
  echo     it from the following location:
  echo.
  echo   http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07
  echo.
  goto :EOF
) else (
  echo Done.
  echo.
)

rem======================================================================

echo Starting the ETW session for full FTP tracing...
logman start "ftp" -p "IIS: Ftp Server" 255 5 -ets
echo.
echo Now reproduce your problem.
echo.
echo After you have reproduced your issue, hit any key to close the FTP
echo tracing session. Your trace events will be displayed automatically.
echo.
pause>nul

rem======================================================================

echo.
echo Closing the ETW session for full FTP tracing...
logman stop "ftp" -ets

rem======================================================================

echo.
echo Parsing the results - this may take a long time depending on the size of the trace...
LogParser "select EventTypeName, UserData from ftp.etl" -e 2 -o:DATAGRID -compactModeSep " | " -rtp 20

When you save and run this batch file, it will display something like the following:


C:\FTP_ETW.cmd

Verifying that LogParser.exe is in the path...
Done.

Starting the ETW session for full FTP tracing...
The command completed successfully.

Now reproduce your problem.

After you have reproduced your issue, hit any key to close the FTP tracing session. Your trace events will be displayed automatically.
 

When you see this displayed, you will need to reproduce your problem, and FTP's ETW tracing will record the troubleshooting information.

Once you have reproduced your problem, hit a key to end the ETW session, and you will see the following message displayed:


Closing the ETW session for full FTP tracing...
The command completed successfully.

Parsing the results - this may take a long time depending on the size of the trace...
 

The batch file will eventually call Log Parser to parse the ETW events, and a dialog like the following will be displayed:

Troubleshooting Custom FTP Providers with ETW Tracing

Now that you know how to set up FTP's ETW tracing, let's examine what you should be looking for in the tracing information.In all of the examples in this blog, I am using the XML-based authentication provider that is documented in the How to Use Managed Code (C#) to Create an FTP Authentication Provider using an XML Database walkthrough.

The following illustration highlights several lines that show the FTP service starting its authentication process, loading my custom authentication provider, and ending the authentication process after I have successfully logged in:

This example shows what everything looks like when it works as expected, so now let's look at what happens when something goes wrong.

If I use the same provider, but I enter my username or password incorrectly, I will see the following lines in the trace:

This example informs you that the provider was loaded successfully, but the logon failed. The error code that is returned is 0x8007052E - this hexadecimal 32-bit value can be split into 16-bit values:

  • 8007 - This code informs you that this is a Win32 error.
  • 052E - This code coverts to 1326 in decimal, and if you enter "NET HELPMSG 1326" from a command-prompt, that will tell you that the error was "Logon failure: unknown user name or bad password."

If I continue to use the same provider as earlier, and I delete the XML file that my provider uses, then I will receive the following error:

Once again, this example informs you that the provider was loaded successfully, but an error occurred. In this specific case you see the actual details that the XML file exists, and that is an error that is returned by a throw() statement in the provider. The error code that is returned is 0x80070057 - and once again this hexadecimal 32-bit value can be split into 16-bit values:

  • 8007 - This code informs you that this is a Win32 error.
  • 0057 - This code coverts to 87 in decimal, and if you enter "NET HELPMSG 87" from a command-prompt, that will tell you that the error was "The parameter is incorrect."

If I replace the missing XML file for the provider, but I remove all of the permissions to the file, I get the following error:

As in the previous examples, this informs you that the provider was loaded successfully, but an error occurred. You can't look up the 0x80131500 error code by using "NET HELPMSG" from a command-prompt, but that doesn't matter since the error description informs you of the problem - access to the path where the file is located was denied.

If I enter a bad provider name, I get the following error:

Unlike the previous examples, this informs you that the provider was not loaded successfully. The description for this error informs you that it could not load the provider, and it gives you the assembly information. In addition to the error description, the error code that is returned by the FTP service is 0x80070002 - and once again this hexadecimal 32-bit value can be split into 16-bit values:

  • 8007 - This code informs you that this is a Win32 error.
  • 0002 - This code is obviously 2 in decimal, so if you enter "NET HELPMSG 2" from a command-prompt, that will tell you that the error was "The system cannot find the file specified."

So now let's look at a common perplexing problem:

This example shows the same 0x8007052E error code that we looked at in a previous example, but you'll notice that any reference to the provider is conspicuously absent from the trace - this means that the FTP service made no attempt to load the custom authentication provider. In this specific case, even though I had correctly registered my custom FTP authentication provider on the system, I had not added or enabled the custom authentication provider for my FTP site.

Summary

In this blog I showed you how to troubleshoot several different errors with FTP custom authentication providers by using FTP's ETW features.

As a parting thought, I should point out that the most-common error that I run into when creating my own providers is the last example. Believe it or not, I nearly always miss a step when I am creating a new provider and I forget to add a setting here or there which will cause the FTP service to completely ignore my provider. A perfect example is when I am writing custom home directory providers - I always remember to add the provider to the global list of FTP providers, and I usually remember to add the provider to the list of custom features for my FTP site, but I forget to configure my FTP site to use custom user isolation and my provider is ignored. (Darn, darn, darn...)

;-]

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
Posted: Aug 28 2012, 16:33 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: FTP | Extensibility
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us