Porting Word2MediaWikiPlus to VB.NET: Part 1

[This series of articles starts off with a prologue here.]

Initial Setup

First up, I’d downloaded the source code from here.  When I downloaded it the browser would only let me choose between htm, mht and txt format (I chose txt).  Then, figuring that my work would be easiest if opened the file in Visual Studio 2005 Team edition (with the VSTO SE addition), I needed to figure out what file extension I should assign to ensure Visual Studio recognized it for the kind of code that it is (and get as much of the Visual Studio Intellisense auto-completion and auto-colouring as possible).

I tried .BAS and .VBA, but neither of those extensions had any associated icons, and when I opened the .VBA, every line was displayed entirely in black.

Then, in exploring the filesystem to re-open the file, I noticed I’d created a C#–based project [since my VS2005 install is oriented to C# — having forced myself to write an app in it to prove to myself I wasn’t forever tainted by my earlier VB.NET experiences] instead of VB.NET, so I deleted that one and tried again (File > New > Project > Other languages > Visual Basic > Office > 2003 Add-ins > Word Add-in).  I chose Word 2003 because (a) that’s what I’m currently using, (b) that’s the platform I have a little experience with, and (c) it’s what most folks around the world would be using — at least, much moreso than Word 2007.  I chose to name the solution Word2MediaWiki++, to honour the fine work upon which I’m building.

[I’ve thought about whether this will become just one Project in a larger Solution, and I assume at some point there’ll be a need to rename either the Solution or this first Project to distinguish between this single piece of functionality and the overall Word-to-MediaWiki functionality.  It’s quite likely in fact, but I don’t want to overthink this or over-engineering the proceedings too badly – I have to remind myself that just getting the same functionality in VSTO add-in form is my primary goal.]

Quick Aside

I’ve done some reading about VBA to VSTO conversions in the past (while I was still at Microsoft), and I’d actually developed another Word 2003 add-in — from scratch — as a way to provide a platform for re-implementing a very comprehensive VBA macro-based application we were using.  So I already had some idea that this was possible, and that there are many resources (from Microsoft* and elsewhere) to help in making both the high-level transition and in converting many of the low-level VBA constructs.

A simple search on Google for “vba to vsto” comes up with 828,000 results, and many of them on the first page or two are good, first-person lessons.  However, the articles/white papers/blog posts I’d previously read that had given me the confidence I’m building on now include these:

Licensing

Oh, and before I actually go do any work on this code, I should double-check that the original author hasn’t put any restrictions on it (copyright, restrictive license, “all rights reserved”)…

… So according to the SourceForge home for W2MWP, this is licensed under the GPL.  It looks like I’m in the clear, so long as I publish this back out under GPL too – wouldn’t want to run afoul of the GPL police now would we?

 

Join us again – same Bat time, same Bat channel!

[*Footnote: My only gripe about Microsoft’s VSTO SE add-in work is that they’re so freakin’ focused on Office 2007 development that I have to do some serious detective work – either trolling through old VBA or pre-add-in documentation and hoping the classes are similar, or winding through the “everything is wonderful” Office 2007 documentation and hoping the stuff I need hasn’t been reinvented for the wondrous new world of Office 12.]

From VBA to VSTO: porting Word2MediaWikiPlus to VB.NET

I’ve gotten religion about Wikis a while back, and recently I’ve had an incentive to look into the world of conversion applications.  I’m looking into the applications that are available to convert from one format (e.g. Office documents) to MediaWiki format (i.e. the engine behind the venerable Wikipedia).

I went wandering through the Wikipedia Tools pages and when I found the Office-oriented tools, I was surprised that many of them were implemented as VBA macros.  One in particular caught my eye: the one labeled Word2MediaWikiPlus.  It’s a single VBA macro, with a small number of functions, and it looks just ripe for creating a VSTO add-in.  Further, as compared to the other Word macros, it seems like it’s a superset of the others, and that the others preceded this and/or have since been abandoned.

I’m really getting into the community mindset, and I figure that not only will some of my colleagues appreciate having this kind of functionality, but that there’d be many folks on the ‘net who’d probably use something like this as well.  And once a basic CommandBar framework was put in place, and the source code available for re-use, then anyone who’d like to add their own functionality to a VSTO add-in for Office should be able to leverage the basic framework I could establish.

Sounds simple eh?  Just a little parsing through the object model, some digging through VBA-to-VB.NET conversions, and a little refresher on Office.CommandBar and its brethren – and voila!  One VSTO add-in that implements the same functionality as you see in the original VBA macro.  [Famous last words.]

No, I’m not quite that naive – I suspect it’ll take a good deal more than that before I’m through.  With that in mind, I’m considering a novel approach to this project: as I’m going through each stage of the conversion, I’ll blog about my experiences – the dead-end code paths I pursue, the inconsistencies in the Word object model, the under-documented features of the original source code, and all the places I find anything useful that keeps this moving towards completion.

Wish me luck, and I’ll keep you posted on my progress!

Troubleshooting RunOnce entries: Part One

I’ve been investigating the root cause for a critical issue
affecting my CacheMyWork app (for those of you paying attention, it has come up in the past in this column). Ever since I received my (heavily-managed) corporate laptop at work, I’ve been unable to get Windows XP to launch any of the entries that CacheMyWork populates in RunOnce.

Here’s what I knew up front

  • On other Windows XP and Windows Vista systems, the same version of CacheMyWork will result in RunOnce entries that all launch at next logon
  • On the failing system, the entries are still being properly populated into the Registry – after running the app, I’ve checked and the entries under RunOnce are there as expected and appear to be well-formatted
  • The Event Log (System and Application) doesn’t log any records that refer even peripherally to RunOnce, let alone that there are any problems or what might be causing them
  • The entries haven’t disappeared as late as just before I perform a logoff (i.e. they’re not being deleted during my pre-reboot session).

Here’s what I tried

UserEnv logging
  • I added HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\UserEnvDebugLevel = 30002 (hex).
  • This is able to show that the processes I’m observing are firing up correctly, but there is nothing in the log that contains “runonce” or the names of the missing processes, and I haven’t spotted any entries in the log that point me to any problems with the RunOnce processing.
ProcMon boot-time logging
  • I’ve got over 3.3 million records to scan through, so while I haven’t found anything really damning, I may never be 100% sure there wasn’t something useful.
  • After a lot of analysis, I found a few interesting entries in the ProcMon logs:
Process Request Path Data
mcshield.exe RegQueryValue HKLM\SOFTWARE\Network Associates\TVD\Shared Components\On Access Scanner\BehaviourBlocking\FileBlockRuleName_2 Prevent Outlook from launching anything from the Temp folder
mcshield.exe RegQueryValue HKLM\SOFTWARE\Network Associates\TVD\Shared Components\On Access Scanner\BehaviourBlocking\FileBlockRuleName_10 Prevent access to suspicious startup items (.exe)
mcshield.exe RegQueryValue HKLM\SOFTWARE\Network Associates\TVD\Shared Components\On Access Scanner\BehaviourBlocking\FileBlockWildcard_10 **\startup\**\*.exe
BESClient.exe RegOpenKey HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce Query Value
Explorer.exe RegEnumValue HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce Index: 0, Length: 220
waatservice.exe RegOpenKey HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce Desired Access: Maximum Allowed
Windows Auditing

I finally got the bright idea to put a SACL (audit entry) on the HKCU\…\RunOnce registry key (auditing any of the Successful “Full Control” access attempts for the Everyone special ID). After rebooting, I finally got a hit on the HKCU\…\RunOnce key:

Event Log data

First log entry

Second log entry

Third log entry

Category Object Access Object Access Object Access
Event ID 560 567 567
User (my logon ID) (my logon ID) (my logon ID)

And here are the interesting bits of Description data for each:

Description field

First log entry

Second log entry

Third log entry

Title Object Open: Object Access Attempt: Object Access Attempt:
Object Type Key Key Key
Object Name \REGISTRY\USER\S-1-5-21-725345543-602162358-527237240-793951\Software\Microsoft\ Windows\CurrentVersion\RunOnce [n/a] [n/a]
Image File Name C:\WINDOWS\explorer.exe C:\WINDOWS\explorer.exe C:\WINDOWS\explorer.exe
Accesses

DELETE
READ_CONTROL
WRITE_DAC
WRITE_OWNER
Query key value
Set key value
Create sub-key
Enumerate sub-keys
Notify about changes to keys
Create Link

[n/a] [n/a]
Access Mask [n/a] Query key value Set key value

Not that I’ve ever looked this deep into RunOnce behaviour (nor can I find any documentation to confirm), but this seems like the expected behaviour for Windows. Except for the fact that something is preventing the RunOnce commands from executing, of course.

Blocking the Mystery App?

Then I thought of something bizarre: maybe Explorer is checking for RunOnce entries to run during logon, and it isn’t finding any. Is it possible some process has deleted them during boot-up or logon, but before Explorer gets to them?

This flies in the face of my previous theory, that the entries were still there when Windows attempted to execute them, but something was blocking their execution. Now I wondered if the entries are even there to find – whether some earlier component hasn’t already deleted them (to “secure” the system).

If so, the only way to confirm my theory (and catch this component “in the act”) is if the component performs its actions on the Registry AFTER the LSA has initialized and is protecting the contents of the Registry. [It’s been too long since I read Inside Windows NT, so I don’t recall whether access to securable objects is by definition blocked until the LSA is up and ready.]

Hoping this would work, I enabled “Deny” permission for Everyone on the HKCU\…\RunOnce key for both “Set Value” and “Delete” (not knowing which one controls the deletion of Registry values in the key). This also meant that I had to enable Failure “Full Control” auditing for the Everyone group on this key as well.

However, while I’ve firmly confirmed that the Deletion takes place when I remove this Deny ACE, I can’t get Windows to log any information to indicate what process or driver is deleting the registry entries (and thus preventing Windows from executing them). It looks like – beyond what I’ve already found – there’s nothing else for which the LSA is asked to make any access control decisions for the HKCU\…\RunOnce key.

“Run Away!”

That’s all for now – I’m beat and need to regroup. If anyone has any bright ideas on ways to try to dig deeper into this system and figure out what’s missing, I’d love to hear it.

To be continued…

Threat Modeling Google group is now available

I’ve been using Microsoft’s Threat Analysis and Modeling (TAM) tool for about a year now, and I’ve gotten to really love how much easier and user-friendly this tool is than anything else I’ve found so far on the ‘net.  I’ve tried to find anything that was as comprehensive, easy for beginners, flexible and extensible as TAM is (let alone free), and there’s nothing else that even comes close.  Anytime I’m asked now to do any Threat Modeling for a product or technology, the only tool I would seriously consider is TAM.

That said, the more I work with it, I’m finding there are enhancements I’d like to make, or things I’d like to better understand:

  • What are the key steps that I should never skip?
  • What tools are useful for generating additional XSLT Report templates?
  • How does TAM merge overlapping content when importing Attack Libraries?
  • What extensibility classes are available for .NET-friendly developers to add to this tool?
  • What’s a reasonable number of Components or Attacks to include in any one threat model?

 I’ve worked with the TAM team at Microsoft to get some ideas on this, but they’re pretty much working flat-out on the Security Assessments for which they built this tool in the first place.  I’ve scoured their old blog entries (here, here and here) to glean tidbits, but I’d really like to work with more folks who are also using this – share what I’ve learned and get their input and ideas as well.

I’d hoped that Microsoft would have a Community forum for this great tool, but since they don’t, I’ve taken the bull by the horns and created one myself.  You can find it here on the Google Groups site.  Yes, Google.  Horrors!

I’ve tried to use MSN Spaces in the past as a collaboration workspace, but I’ve found Google Groups and Yahoo Groups are both better platforms for this sort of thing.  They give you more control, with less futzing around trying to make things “look right”, and they’re investing significant effort into these platforms.  Frankly, I’m a lazy guy at heart, and it was really freakin’ easy to setup the Google Group.  Sue me.

Call to Action: if you’re using Microsoft’s TAM tool already, or you know someone who’s responsible for things like “Secure Coding”, “Risk Assessments” or “Threat Modeling”, I’d encourage them to check out the Group, post some sample Files, start some Discussions or even just lurk for good ideas!

Debugging persistent Outlook crashing – can only go so far…

I’ve been experiencing a persistent crash in Outlook for months now – often Outlook will crash when I Send an email.  I suspect it’s related to the fact that in the main Outlook form ( Mail/Inbox) the Reply, Reply All and Forward buttons and keyboard shortcuts are inactive.  [They work from the context menu of an individual message, or if I open a message and use the buttons from the Toolbar displayed above the message itself.  Yes, very weird.]  I suspect it’s a result of an unclean uninstall of the Getting Things Done add-in for Outlook – which I used to like, but which has been supplanted by the combination of MindManager and ResultsManager (at least this week).

In any case, I’ve captured multiple .DMP files, but when I try to debug them I get very sketchy results.  I used to think it was because I don’t have access to symbols for the add-ins that I’ve installed for Outlook – which is normally where these kinds of crashes come from.  However, I’ve disabled all the add-ins that are listed in Outlook > Tools > Options > Other > Advanced Options > “Add-in Manager” & “COM Add-Ins”, and I’m still getting the same kind of crashing behaviour.  I’m still getting spotty results, which tells me I don’t even have symbols for Outlook (to map the function offsets that are listed in the dump), and I’ve been beating my head against a wall trying to figure out how to get access to them.

I’m almost positive I’ve got the Microsoft Internet Symbol Server configured correctly, and yet I continue to get errors like this:

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for OUTLLIB.DLL –
OUTLLIB!OlkGetUIlangID+0xd434:

I discovered that you can debug the loading operations with the !sym noisy command.  Once I enabled this, I saw this in my .reload output:

0:000> .reload
…………………………………………………………………………………………………………………………………………………..
SYMSRV:  C:\symbols\OUTLLIB.DLL\4566283D749000\OUTLLIB.DLL not found
SYMSRV:  http://msdl.microsoft.com/download/symbols/OUTLLIB.DLL/4566283D749000/OUTLLIB.DLL not found
DBGENG:  C:\Program Files\Microsoft Office\OFFICE11\OUTLLIB.DLL – Mapped image memory
SYMSRV:  C:\symbols\outllib.pdb\0EAE667B6A73417A9D7DC2E4C81382232\outllib.pdb not found
SYMSRV:  http://msdl.microsoft.com/download/symbols/outllib.pdb/0EAE667B6A73417A9D7DC2E4C81382232/outllib.pdb not found
DBGHELP: outllib.pdb – file not found
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for OUTLLIB.DLL –
DBGHELP: OUTLLIB – export symbols

I wanted to double-check that it wasn’t just a lack of some specific version of OUTLLIB.DLL, so I browsed to http://msdl.microsoft.com/download/symbols/OUTLLIB.DLL/, and received a 404 error.  To make sure there wasn’t some subtle IIS configuration issue, I tested http://msdl.microsoft.com/download/symbols/KERNEL32.DLL (a known good library), which gave me a 403 (Forbidden) error.

That tells me that Microsoft still hasn’t published Office symbols to the Internet – even while they’re trying to push application developers to use Office as a “platform” on which to build enterprise-class applications (VSTO, VSTA).  That’s a really noticeable gap, at least to me.

In any case, this is as good a debug output as I’m able to get:

FOLLOWUP_IP:
OUTLLIB!OlkGetUIlangID+d434
301b7b82 ff506c          call    dword ptr [eax+6Ch]

SYMBOL_STACK_INDEX:  0

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: OUTLLIB

IMAGE_NAME:  OUTLLIB.DLL

DEBUG_FLR_IMAGE_TIMESTAMP:  4566283d

FAULTING_THREAD:  000014d0

DEFAULT_BUCKET_ID:  NULL_CLASS_PTR_DEREFERENCE

PRIMARY_PROBLEM_CLASS:  NULL_CLASS_PTR_DEREFERENCE

BUGCHECK_STR:  APPLICATION_FAULT_NULL_CLASS_PTR_DEREFERENCE

SYMBOL_NAME:  OUTLLIB!OlkGetUIlangID+d434

FAILURE_BUCKET_ID:  APPLICATION_FAULT_NULL_CLASS_PTR_DEREFERENCE_OUTLLIB!OlkGetUIlangID+d434

BUCKET_ID:  APPLICATION_FAULT_NULL_CLASS_PTR_DEREFERENCE_OUTLLIB!OlkGetUIlangID+d434

Unfortunately, Google Groups and the web have nothing helpful to understand what “APPLICATION_FAULT_NULL_CLASS_PTR_DEREFERENCE” even means, let alone how to fix the problem it flags.

If I ever come up with a good answer to this, I’ll be sure to post it.  In the meantime, if anyone has any clues or hints for me on what I could do to narrow this down any further (e.g. any cool or powerful commands that I don’t know about – which probably includes anything beyond .symfix, .reload, k and !analyze -v.

EFS Certificate Configuration Updater tool is released!

After weeks of battling with Visual Studio over some pretty gnarly code issues, I’ve released the first version of a tool that will make IT admins happy the world over (well, okay, only those few sorry IT admins who’ve struggled to make EFS predictable and recoverable for the past seven years).

EFS Certificate Configuration Updater is a .NET 2.0 application that will examine the digital certificates a user has enrolled and will make sure that the user is using a certificate that was issued by a Certificate Authority (CA).

“Yippee,” I hear from the peanut gallery. “So what?”

While this sounds pretty freakin lame to most of the planet’s inhabitants, for those folks who’ve struggled to make EFS work in a large organization, this should come as a great relief.

Here’s the problem: EFS is supposed to make it easy to migrate from one certificate to the next, so that if you start using EFS today but decide later to take advantage of a Certificate Server, then the certs you issue later will replace the ones that were first enrolled. [CIPHER /K specifically tried to implement this.]

Unfortunately, there are some persistent but subtle bugs in EFS that prevent the automatic migration from self-signed EFS certificates to what are termed “version 2” certificates. Why are “version 2” certificates so special? Well, they’re the “holy grail” of easy recovery for encrypted files – they allow an administrator to automatically and centrally archive the private key that is paired with the “version 2” certificate.

So: the EFS Certificate Configuration Updater provides a solution to this problem, by finding a version 2 EFS certificate that the user has enrolled and forcing it to be the active certificate for use by EFS. [Sounds pretty simple eh? Well, there’s plenty of organizations out there that go to a lot of trouble to try to do it themselves.]

Even though this application fills a significant need, it doesn’t (at present, anyway) do everything that might be needed in all scenarios. The additional steps that you might need to cover include:

  • Enrolling a version 2 EFS certificate. [You can automate this with autoenrollment policy and the Windows Server 2003-based CA that is already in place for issuing v2 certificates and Key Archival.]
  • Updating EFS’d files to use the new certificate. [You can automate this by using CIPHER /U, but it’ll take a while if the user has a lot of encrypted files. The good news, however, is that the update only has to re-encrypt the FEK, not re-encrypt the entire file, so it’s much quicker than encrypting the same set of files from scratch.]
  • Ensuring that the user’s EFS certificate doesn’t expire before a new or renewed certificate is enrolled. [This is very easy to accomplish with Autoenrollment policy, but without the use of Autoenrollment, there is a significant risk that when the user’s preferred EFS certificate expires, the EFS component driver could enroll for a self-signed EFS certificate.]
  • Archiving unwanted EFS certificates. [This is different from deleting a digital certificate – which also invalidates the associated private key, which is NOT recommended. This would keep the certificates in the user’s certificate store, and preserve the private key — so that any files encrypted with that old certificate were still accessible. This is hard to do from UI or script, but is a feature I’m hoping to add to the EFS Certificate Configuration Updater in the near future. This is also optional – it just minimizes the chances of a pre-existing EFS certificate being used if the preferred certificate fails for some reason.]
  • Publishing the user’s current EFS certificate to Active Directory. [This is also optional. It is only necessary to make it possible — though still hardly scalable — to use EFS to encrypt files for access by multiple users (see MSDN for more information). This can be automated during Autoenrollment, but some organizations choose to disable publishing a 2nd or subsequent EFS certificate since the EFS component driver may get confused by multiple EFS certificates listed for a single user in Active Directory.]
  • Synchronizing the user’s EFS certificate and private key across all servers where encrypted files must be stored. [This is not needed if you’re merely ensuring that all sensitive data on the user’s notebook/laptop PC is encrypted, so that the loss or theft of that PC doesn’t lead to a data breach. However, if you must also enforce EFS encryption on one or more file servers, the EFS Certificate Configuration Updater will not help at all in this scenario.]

Try it out — Tell your friends (you have friends who’d actually *use* this beast? Man, your friends are almost as lame as mine – no offense) — Let me know what you think (but no flaming doo-doo on my front porch, please). And have a very crypto-friendly day. 😉

Funny Microsoft link of the day/month/year (whatever)

One in a very infrequent and unpredictable series:

http://www.techeblog.com/index.php/tech-gadget/video-zune-commercial-that-got-a-microsoft-employee-fired-starring-steve-ballmer

I’m pretty sure I’ve seen this performance — and if not, it’s damned similar to other occasions when I didn’t know whether to be amused, embarrassed or downright frightened.

Threat Modeling using Microsoft’s TAM tool? Visit the new online forum

I’ve been working with Microsoft’s free Threat Analysis and Modeling Tool for most of the year, and I’ve lamented the fact that there’s no online forum/group where I can share questions, ideas or custom templates with other users of the tool.

Well screw that – now there is just such a group (’cause I just created it).

If you’ve used this tool for developing/documenting your Threat Models, or if you’re considering it, then please feel free to lurk or even participate in this online community.  I’ll be posting what I’ve learned so far in using this tool, making available some reusable templates and reports, and generally giving the new group the care & feeding these things usually require at the onset.

Hope to see you Threat Analysts there!

Shameful: "Hotmail Fails To Deliver Up To 81% Of All Attachment Emails"

Outrageous.  First it took them FOREVER to come through on their promise to deliver 2 GB email accounts in the first place – my wife waited 3 or 4 months for her PAID account to get converted from the time they said it would happen “immediately”, and a year or more for her unpaid accounts. 

And now we see clear, damned-hard-to-refute evidence of how they’re not even providing a reliable mail delivery (let alone storage) service?

Hotmail Fails To Deliver Up To 81% Of All Attachment Emails

I can’t wait to see how they treat the stuff you put in their “reliable online storage services” – I sure ain’t gonna rely on Windows Live to backup my photos or documents.  I mean, how many of them would just “disappear” after six months of inactivity (or even just randomly, when no one’s looking)?

XSLT 1.0 defies the laws of physics – sucks AND blows simultaneously…

Holy crap, whoever came up with XSLT 1.0 must’ve really wanted me to suffer 🙂

I have spent the better part of a couple of weeks fighting with a simple piece of XSLT code to be able to generate some short, organized reports using the Microsoft Threat Analysis and Modelling tool (currently at v2.1.2).

It doesn’t help that the data model used by this tool has made a really poor choice in the way it classifies Threats:

  • The logical model would be to define an XPath like /ThreatModel/Threats/Threat[x], and then use an attribute or sub-element to assign the category of Threat
  • Instead, MS TAM v2.1.2 defines an XPath like this for each threat: /ThreatModel/Threats/$ThreatCategorys/Threats/$ThreatCategory
  • Thus, for a Confidentiality Threat, you get /ThreatModel/Threats/ConfidentialityThreats/Threats/ConfidentialityThreat

It certainly also doesn’t help that Microsoft has fallen behind all the other major XML engine vendors in implementing XSLT 2.0.  This article here indicates that not only did they wait to start any of the work until after the Recommendation was completed, but that they have NO planned ship vehicle or release date (despite the fact that XSLT 2.0 has been in the works for five years).

But really the fundamental problem that I (and about a million other people out there, over the last 7-8 years) am challenged by is the fact that you can’t pass what’s called an RTF (“Result Tree Fragment”) in an XPath 1.0 expression – the XSLT 1.0 standard just doesn’t allow dynamic evaluation of such an expression, AND they didn’t provide any reasonable way to actually get around the problem of RTFs.  It means that all the vendors providing engines to process XSL had to come up with their own extensions to handle this (e.g. [1], [2], [3]), and many people have also come up with creative (but horribly obtuse) ways to get around the problem.

So it goes – I’m stuck with (a) XSLT 1.0 & XPath 1.0 + proprietary “extension functions” [1], [2] in MSXML, because (b) the Microsoft TAM tool only uses the MSXML engine (which is fair – it’s gotta default to something).

What’s REALLY painful is learning that not only did I spend weeks banging my head against a wall learning some very obtuse and shouldn’t-be-necessary coding hacks to what in other languages are fairly trivial problems – but now I discover that it wasn’t even a question of RTFs at all, but rather that XSLT just ends up taking what I think is a reasonably well-thought-out design and dumping it on the floor:

http://groups.google.com/group/microsoft.public.xsl/browse_thread/thread/f3af4340991740e5

Oh, and how did the XSLT overlords solve this problem in XSLT 2.0?  The just eliminated the limitation on RTF in XPath expressions.  Done.  And done. 

Ugh – that’ll teach me to ever get lured into using a [functional?] programming language again.  Back to C# – that seems positively trivial by comparison…

SO: if you happen to have a masochistic streak in you, or you find that you absolutely must use XSLT 1.0 and not either XSLT 2.0 or System.xml.xsl, then (a) you have my sympathies and (b) here are some resources that I recommend you consult sooner than later: