Error: Class Not Registered (0x80040154) when Querying FTP Runtime State

I had a great question from a customer earlier today, and I thought that it was worth blogging about. The problem that he was running into was that he was seeing the following error when he was trying to query the runtime state for the FTP service in an application that he was writing:

Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))

He was using Visual Basic, and his code looked okay to me, so for the moment I was stumped.

I'm more of a C# guy, and I remembered that I had written the following blog many years ago:

Viewing current FTP7 sessions using C#

I copied the code from that blog into a new Visual Studio project, and I got the same error that he was seeing when I ran my code - this had me a little more confused. Have you ever said to yourself, "Darn - I know that worked the other day...?" ;-]

I knew that there is more than one way to access the runtime state, so I rewrote my sample application using two different approaches:

Method #1:

AppHostAdminManager objAdminManager = new AppHostAdminManager();
IAppHostElement objSitesElement =
  objAdminManager.GetAdminSection("system.applicationHost/sites",
  "MACHINE/WEBROOT/APPHOST");
uint intSiteCount = objSitesElement.Collection.Count;
for (int intSite = 0; intSite < intSiteCount; ++intSite)
{
    IAppHostElement objFtpSite = objSitesElement.Collection[intSite];
    Console.WriteLine("Name: " + objFtpSite.Properties["name"].StringValue);
    IAppHostElement objFtpSiteElement = objFtpSite.ChildElements["ftpServer"];
    IAppHostPropertyCollection objProperties = objFtpSiteElement.Properties;
    try
    {
        IAppHostProperty objState = objProperties["state"];
        string ftpState = objState.StringValue;
        Console.WriteLine("State: " + ftpState);
    }
    catch (System.Exception ex)
    {
        Console.WriteLine("\r\nError: {0}", ex.Message);
    }
}

Method #2:

ServerManager manager = new ServerManager();
foreach (Site site in manager.Sites)
{
    Console.WriteLine("Name: " + site.Name);
    ConfigurationElement ftpServer = site.GetChildElement("ftpServer");
    try
    {
        foreach (ConfigurationAttribute attrib in ftpServer.Attributes)
        {
            Console.WriteLine(attrib.Name + ": " + attrib.Value);
        }
    }
    catch (System.Exception ex)
    {
        Console.WriteLine("\r\nError: {0}", ex.Message);
    }
}

Both of these methods returned the same COM error, so this was getting weird for me. Hmm...

The FTP runtime state is exposed through a COM interface, and that is implemented in a DLL that is named "ftpconfigext.dll". That file should be registered when you install IIS, and I re-registered it on my system just for good measure, but that didn't resolve the issue.

I had a brief conversation with one of my coworkers, Eok Kim, about the error that I was seeing. He also suggested re-registering the DLL, but something else that he said about searching the registry for the InprocServer32 entry made me wonder if the whole problem was related to the bitness of my application.

To make a long story short - that was the whole problem.

Both the customer and I were creating 32-bit .NET applications, and the COM interface for the FTP runtime state is implemented in a 64-bit-only DLL. Once we both changed our projects to compile for 64-bit platforms, we were both able to get the code to run. (Coincidentally, all I had was a 32-bit system when I wrote my original blog, so I probably would have run into this sooner if I had owned a 64-bit system way back then. ;-])


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

Using URL Rewrite with QDIG

One of the applications that I like to use on my websites it the Quick Digital Image Gallery (QDIG), which is a simple PHP-based image gallery that has just enough features to be really useful without a lot of work on my part to get it working. (Simple is always better - ;-].) Here's a screenshot of QDIG in action with some Bing photos:

(Click to enlarge photo.)

The trouble is, QDIG creates some really heinous query string lines; see the URL line in the following screenshot for an example:

(Click to enlarge photo.)

I don't know about you, but in today's SEO-friendly world, I hate long and convoluted query strings. Which brings me to one of my favorite subjects: URL Rewrite for IIS

If you've been around IIS for a while, you probably already know that there are a lot of great things that you can do with the IIS URL Rewrite module, and one of the things that URL Rewrite is great at is cleaning up complex query strings into something that's a little more intuitive.

It would take way to long to describe all of the steps to create the following rules with the URL Rewrite interface, so I'll just include the contents of my web.config file for my QDIG directory - which is a physical folder called "QDIG" that is under the root of my website:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- Rewrite the inbound URLs into the correct query string. -->
        <rule name="RewriteInboundQdigURLs"  stopProcessing="true">
          <match url="Qif/(.*)/Qiv/(.*)/Qis/(.*)/Qwd/(.*)" />
          <conditions>
            <add input="{REQUEST_FILENAME}"  matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}"  matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite"  url="/QDIG/?Qif={R:1}&amp;Qiv={R:2}&amp;Qis={R:3}&amp;Qwd={R:4}"  appendQueryString="false" />
        </rule>
      </rules>
      <outboundRules>
        <!-- Rewrite the outbound URLs into user-friendly URLs. -->
        <rule name="RewriteOutboundQdigURLs"  preCondition="ResponseIsHTML"  enabled="true">
          <match filterByTags="A, Img, Link"  pattern="^(.*)\?Qwd=([^=&amp;]+)&amp;(?:amp;)?Qif=([^=&amp;]+)&amp;(?:amp;)?Qiv=([^=&amp;]+)&amp;(?:amp;)?Qis=([^=&amp;]+)(.*)" />
          <action type="Rewrite"  value="/QDIG/Qif/{R:3}/Qiv/{R:4}/Qis/{R:5}/Qwd/{R:2}" />
        </rule>
        <!-- Rewrite the outbound relative QDIG URLs for the correct path. -->
        <rule name="RewriteOutboundRelativeQdigFileURLs"  preCondition="ResponseIsHTML"  enabled="true">
          <match filterByTags="Img"  pattern="^\.\/qdig-files/(.*)$" />
          <action type="Rewrite"  value="/QDIG/qdig-files/{R:1}" />
        </rule>
        <!-- Rewrite the outbound relative file URLs for the correct path. -->
        <rule name="RewriteOutboundRelativeFileURLs"  preCondition="ResponseIsHTML"  enabled="true">
          <match filterByTags="Img"  pattern="^\.\/(.*)$" />
          <action type="Rewrite"  value="/QDIG/{R:1}" />
        </rule>
        <preConditions>
          <!-- Define a precondition so the outbound rules only apply to HTML responses. -->
          <preCondition name="ResponseIsHTML">
            <add input="{RESPONSE_CONTENT_TYPE}"  pattern="^text/html" />
          </preCondition>
        </preConditions>
      </outboundRules>
    </rewrite>
  </system.webServer>
</configuration>

Here's the breakdown of what all of the rules do:

  • RewriteInboundQdigURLs - This rule will rewrite inbound user-friendly URLs into the appropriate query string values that QDIG expects. I should point out that I rearrange the parameters from the way that QDIG would normally define them; more specifically, I pass the value Qwd parameter last, and I do this so that the current directory "." does not get ignored by browsers and break the functionality.
  • RewriteOutboundQdigURLs - This rule will rewrite outbound HTML so that all anchor, link, and image tags are in the new format. This is where I actually rearrange the parameters that I mentioned earlier.
  • RewriteOutboundRelativeQdigFileURLs - There are several files that QDIG creates in the "/qdig-files/" folder of your application; when the application paths are rewritten, you need to make sure that the those paths won't just break. For example, once you have a path that is rewritten as http://localhost/QDIG/Qif/foo.jpg/Qiv/name/Qis/M/Qwd/, the relative paths will seem to be offset from that URL space as though it were a physical path; since it isn't, you'd get HTTP 404 errors throughout your application.
  • RewriteOutboundRelativeFileURLs - This rule is related to the previous rule, although this works for the files in your actual gallery. Since the paths are relative, you need to make sure that they will work in the rewritten URL namespace.
  • ResponseIsHTML - This pre-condition verifies if an outbound response is HTML; this is used by the three outbound rules to make sure that URL Rewrite doesn't try to rewrite responses where it's not warranted.

Once you have these rules in place, you get nice user-friendly URLs in QDIG:

(Click to enlarge photo.)

I should also point out that these rules also support changing the style from thumbnails to file names to file numbers, etc.

(Click to enlarge photo.)

All of that being said, there is one thing that these rules do not support - and that's nested folders under my QDIG application. I don't like to use folders under my QDIG folder - I like to use separate folders with the QDIG file in it, because this makes each gallery self-contained and easily transportable. That being said, after I had written the text for this blog, I tried to use a subfolder under my QDIG application and that didn't work. By looking at what was going on, I'm pretty sure that it would be pretty trivial to write some URL Rewrite rules that would accommodate using subfolders, but that's another project for another day. ;-]


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

Bad Christians

I recently saw this video: http://youtu.be/vnJBW49afzg

Let's put this in perspective - a group of self-professed 'Christians' shows up uninvited to a Muslim Arab Festival, where they are confrontational to the Muslims and insulting to the police, and they are carrying signs with slogans like "Repent - your party will burn in the lake of fire (01:14)," "Islam is a religion of Blood & Murder! (03:26)," "Be not believers in drunkards, whoremongers, idolaters, sodomites, fornicators (04:30)," "I am truth and the life - all others are thieves are robbers (04:30)."

No - this was not an act of random violence by a group of Muslim extremists who were targeting an innocent group of Christians; this was a deliberately-staged set of contentious actions by some very unbalanced people who claim to be Christians in order to provoke the very attack that transpired. These otherwise peaceful Arab-Americans were enjoying a day of celebration of their faith and heritage, and these so-called Christians showed up and behaved in an extremely insulting manner, shouting insults with a megaphone and slandering the Muslim faith - these 'Christians' might just as well have been Nazis based on their behavior. The police were 100% correct is telling these fools that they were the cause of the trouble.

Let's reverse things for a moment - let's say that you were a Christian at a peaceful church gathering when a group of atheists showed up to protest by using a megaphone to hurl insults at your religion and they carried signs that proclaimed that Jesus was a drunkard & whoremonger who was the illegitimate son of unwed slut, you would be more than a little offended. You might not personally react with physical violence, but if you had a few thousand Christians in one place when a publicly-outspoken group of atheists or Buddhists or Hindus or Muslims showed up and behaved as provocatively as these 'Christians' did, I can 100% guarantee that someone would eventually lose their cool and start throwing things.

This video is a perfect example of how Christians should NOT behave, and they are certainly not following Christ's example. This is not a question of exercising freedom of speech or freedom of religion - this is a question of exercising good judgment and Christ's love; this group of 'Christians' exhibited neither.

Self-professed 'Christians' like those in this video should be ashamed of themselves.