Cheap Tricks: Let's Talk About METADATA TypeLibs

A few years back, articles like this one from 4guys started deprecating the use of #include statements for adovbs.inc and adojavas.inc as the way to incorporate common constants into your projects. The new suggestion was to use a <METADATA> statement -- either within the current page, or inside the application's global.asa -- to add a reference to the desired constants.

Ornery as usual, I resisted the suggestion. Today's article discusses some issues and limitations in the use of METADATA TYPELIB tags, and suggests a few workarounds for diehards who just can't live without them.

First, the Conventional Wisdom

Here's how it's supposed to work. The <METADATA> tag is an HTML comment line with the following general syntax:

<!-- METADATA TYPE="TypeLib"
FILE="explicit operating-system filespec"
UUID="{typelibraryuuid}"
VERSION="majorversionnumber.minorversionnumber"
LCID="localeid" -->

Although the official syntax doesn't support it, for documentation many people -- including some of Microsoft's example code -- add an additional parameter to the tag, the "NAME" parm. It turns out that this parm is ignored by everyone, but it's sometimes useful as instream self-documentation.

<!-- METADATA TYPE="TypeLib"
NAME="some descriptive string"
FILE="explicit operating-system filespec"
UUID="{typelibraryuuid}"
VERSION="majorversionnumber.minorversionnumber"
LCID="localeid" -->

When specifying a TypeLib, you would typically provide either a "FILE" parameter or a UUID parameter, but not both. Here's an example that specifies the FILE parameter:

<!-- METADATA
TYPE="TypeLib"
FILE="C:\Program Files\Common Files\system\ado\msado15.dll"
-->

And here's an example that specifies the UUID parameter:

<!-- METADATA
TYPE="TypeLib"
UUID="{00000205-0000-0010-8000-00AA006D2EA4}"
-->

There's one other thing to bear in mind throughout this discussion. Just like #include directives, METADATA tags are interpreted during the ASP pre-compile step. That is, their internal references are resolved before any ASP code is parsed. This means that you cannot perform any conditional logic at run time, to figure out -- for example -- where some file might exist. One other upshot of this pre-compile issue is that it is not possible to gracefully recover from an invalid TypeLib specification.

... and Problems With Conventional Wisdom

Diligent readers will have spotted the first two problems.

  1. To use the FILE method, you need to know the explicit location of the DLL or TLB, and specify it inside your website at development time. If the file moves elsewhere, you have to modify your source code.
     
  2. To use the UUID method, you need to know the UUID of the DLL, and specify it inside your website at development time. If a new version uses a different UUID, you have to modify your source code.

The good news -- when we're looking at ADO anyway -- is that Microsoft used to enforce some uniformity on the installation process. In general, MDAC installs the ADO libraries inside the directory "Program Files\Common Files\system\ado\", located on the system boot drive. The bad news -- for some of us -- is that we don't necessarily know what drive that is. For example, I have two development servers, and each of them boots from a different drive -- one on "D:" and one on "E:". My code is tested on my partners' servers -- and again, there's no uniformity of boot drives. And finally I distribute and deploy my applications onto my clients' servers, many of which are in shared host environments where I haven't got a clue what the boot drive is -- and in any case it could change at any time, at the whim of a sysadmin.

You might think the answer is some fascist dictate to force everyone to install the operating system and the MDAC libraries on "C:\", but in the real world, that's just not practical. Operating systems get installed as dual-boot configurations, hardware vendors preformat machines with miniscule "C:\" partitions, ISPs move virtual hosts around, seemingly at random... there's lots of reasons you can't rely on finding everything on the C: drive.

Themes and Variations

So let's look at our alternatives. Instead of using the "FILE" format of the METADATA tag, we can use the "UUID" format. The following table lists the UUIDs for several major versions of Microsoft's MDAC technologies:

ADO
2.0 {00000200-0000-0010-8000-00AA006D2EA4} no longer available
2.1 {00000201-0000-0010-8000-00AA006D2EA4} earliest version still around
2.5 {00000205-0000-0010-8000-00AA006D2EA4} came with Windows 2000
2.6 {00000206-0000-0010-8000-00AA006D2EA4} came with SQL Server 2000
2.7 {EF53050B-882E-4776-B643-EDA472E8E3F2} came with Windows XP
ADOR
all {00000300-0000-0010-8000-00AA006D2EA4}  
ADOX
all {00000600-0000-0010-8000-00AA006D2EA4}  

You can download current versions of MDAC -- from 2.1 through 2.7 -- from Microsoft's download center at http://www.microsoft.com/data/download.htm.

Each of the different ADO libraries has its own series of UUID keys, as defined in the Windows registry. The primary libary -- just plain ADO -- contains the definitions for most of the major objects, and each version of the ADO library has its own top-level entry in the series "{0000020X-0000-0010-8000-00AA006D2EA4}", except -- for some bizarre reason -- the new version 2.7 library, which breaks the pattern with UUID "{EF53050B-882E-4776-B643-EDA472E8E3F2}".

The ADO Recordset library (ADOR) is billed as a "lightweight subset" of the ADODB type library, and supports only recordsets. It has a single entry as defined in the Windows registry, with UUID "{00000300-0000-0010-8000-00AA006D2EA4}".

ADO TypeLib Registry Keys on
My XP-Pro Development Desktop

ADOX is the "ADO Extensions for Data Definition Language and Security". ADOX is an extension to the ADO objects and programming model that provides provider-independent objects and methods that support schema and object security definitions for all OLEDB providers regardless of their native syntaxes. This object is represented by the "{00000600-0000-0010-8000-00AA006D2EA4}" UUID key.

The figure to the right shows the ADO-related TypeLib settings in the registry on my XP-Pro development desktop. The machine has been around for quite a while -- it started out in life as Windows NT4, and has seen many versions of ADO come and go. As the figure illustrates, available versions of MDAC range from 1.0 (!!) to the version 2.6 and 2.7 libraries.

There are a few interesting things demonstrated by this listing:

  1. Although the documentation says that MDAC 2.7 is distributed with Windows XP, this WinXP PC doesn't seem to have an explicit {00000207-0000-0010-8000-00AA006D2EA4} entry for ADO 2.7, the way we might have expected. On the other hand, it does have an entry for MDAC 2.7 elsewhere in the registry, under the UUID {EF53050B-882E-4776-B643-EDA472E8E3F2}.
     
    ADO TypeLib Registry Keys on
    NT 4 Server with SQLServer 7.0
  2. On the other other hand, it does have entries for version 2.7 of the ADO Recordset and ADO Extensions libraries. This seems kinda weird, but par for the course on a typical development desktop.

The next figure to the right is a snapshot of the relevant registry keys on one of my test servers, which runs NT4 Server and SQL Server 7.0. The interesting thing to notice about this snapshot is that its highest version entry is MDAC 2.5. This seems to jibe with the fact that the server runs SQL Server 7 -- and the fact that I never bothered to install a newer version of MDAC.

If you ran an ASP program on this server that contained a UUID-style reference to the ADO 2.6 typelib, it would throw an error that looks something like this...

  Active Server Pages error 'ASP 0223'
  TypeLib Not Found
  /foo/foo.asp, line 123
  METADATA tag contains a Type Library specification that does not match any Registry entry.

ADO TypeLib Registry Keys on
NT 4 Server with SQLServer 7.0
After Installing MDAC 2.6 SP1

For grins -- and to see if I could make this error go away -- I downloaded and installed MDAC version 2.6 SP1 from Microsoft's download site onto the NT4 server. After the server was restarted, I rechecked the registry keys, and sure enough (see the listing to the right) the new version 2.6 entries could be found, and the error went away.

The Microsoft web site contains some rather frightening language about the compatibility of SQL Server 7.0 and MDAC 2.7.

Due to issues with clustering, this release is currently NOT supported on SQL Server 7.0 Servers or SQL 6.5 Clustered Servers.

Even though I suspect the warning is actually a stupid typographical error -- I think they intended to refer only to "SQL Server 7.0 Clustered Servers" -- it was enough to keep me from upgrading that server any further. I decided to leave well enough alone in the NT 4 world for now, and move on to the braver, newer world of my Windows 2000 Server.

My Windows 2000 development and production servers are fairly new, and when I originally configured them, they had both Windows 2000 Server and SQL Server 2000 installed.

ADO TypeLib Registry Keys on
W2K Server with SQLServer 2000

So, as the figure to the right shows, these servers have the newer 2.5 and 2.6 versions of the libraries available... although not version 2.7, the "XP" release. In fact, this server even supports backward compatibility with prior versions (2.0, 2.1 and 2.5) of the ADO libraries.

What a Freakin' Can of Worms!

One other bizarre anomaly introduced into this process is the fact that Visual InterDev offers the ability to automatically insert METADATA tags into your projects. From the main menu, select "Project"; from the pulldown select "Project References..." From the resulting dialog scroll to the desired library. If you selected the "ADO 2.7 Library", an entry like this might be added to the "global.asa" file in your project directory:

<!--==Visual InterDev Generated - startspan==-->
<!--METADATA TYPE="TypeLib" NAME="Microsoft ActiveX Data Objects 2.7 Library"
UUID="{EF53050B-882E-4776-B643-EDA472E8E3F2}" VERSION="2.7"-->
<!--==Visual InterDev Generated - endspan==-->

However, remember that the list of available object references was generated from the list of objects installed on your desktop. As soon as you try to run the application on your server, you'll get the ill-fated "ASP 0223" error message mentioned earlier, METADATA tag contains a Type Library specification that does not match any Registry entry.

One possible workaround that lets you avoid some of these version-specific hassles would be to use the UUID of the "ADO Recordset" object instead of the ADO library itself. As you remember, it's just a "lightweight subset" of the complete ADO library, but if all you need is constants, they're all there... it would probably be a good idea to use the ADOR library like this:

<!--METADATA TYPE="TypeLib" UUID="{00000300-0000-0010-8000-00AA006D2EA4}" -->

Are We There Yet?

If by this time you're still convinced you want to use METADATA tags instead of #include files, here's a few things to think about:

  1. You might want to use the "UUID" format to avoid crashes when you run into servers where the OS wasn't installed on drive C:\.
     
  2. You have to keep track of what versions of MDAC are installed on your development desktops and servers, the desktops and servers of your fellow developers, and all of the servers you might want to deploy on. Choose the least common denominator that supports your project requirements.
     
  3. Finally, you might consider adding a METADATA reference to ADOR instead of ADO. If all you need is the standard set of constants, it's probably just as good.

Any Last Words?

Pardon me for expressing an opinion here, but don't you get annoyed at how MS messes stuff like this up? I mean, really... couldn't this have been designed better? For example -- the reason why they designed the registry was so that the operating system could find files without your programs needing to specify explicit file locations... and the reason the registry includes monickers (aka "friendly names") is so that you don't need to know complex multi-digit UUIDs.

How hard would it have been to allow specification of a registered library using its friendly name? For example

<!--METADATA TYPE="TypeLib" FOO="ADODB" -->

Would that have been so hard?

Stupid METADATA Tricks...

Oh, meanwhile, as long as we're in the neighborhood, I thought I'd mention this really cute little anomaly I ran into. Try this at home...

  1. Add a javascript section to your web page.
     
  2. Do that cute little "hide from back-level browsers" trick where you enclose your javascript text inside HTML comment tags.
     
  3. Add the word "METADATA" anywhere inside the script.

Here's an example:

  <script language="javascript"><!--
var foo = "I LOVE METADATA!";
//--></script>
<script language="javascript"><!--
alert (foo);
//--></script>

Know what happens when you execute that snip of script? See the screenshot to the right for a hint.

Know why? Because as soon as IIS sees the word "METADATA" anywhere inside an HTML comment, it suppresses the whole comment. And in this case, that means it suppresses the whole content of the javascript.

    Cool, eh?

    posted @ 2007-11-14 15:27  Max Woods  阅读(722)  评论(0编辑  收藏  举报