Category Archives: Uncategorized

Dynamically Enabling Access to the VBA Object Model in Office Apps

1
Filed under Uncategorized

Writing addins and macros for Office apps (specifically Word, but this includes Excel and Powerpoint) can be difficult enough as it is.

But when you throw security gotchas into the mix, things can get interesting quickly.

One thing that often hangs people up are the security settings.

There are several different security levels for macros, of course. And there’s also security that controls access to the internal VBA object model.

But what’s the “VBA Object Model”?

Essentially, it’s an object model (not unlike the Word object model that starts at the Word.Application object) that allows macro code (and COM add-in code as well) full access to all the macros written in VBA and stored within the actual document (or template) file.

For instance, you can actually print out all the VBA macro code in a document with a code snippet similar to the following:

For i = 1 to WordDoc.VBProject.VBComponents.Count
   Set vbcode = WordDoc.VBProject.VBComponents(i)

   C = vbcode.CodeModule.CountOfLines

   '---- retrieve the entire code block
   If C > 0 Then
      code$ = vbcode.CodeModule.Lines(1, vbcode.CodeModule.CountOfLines)
      Debug.print code$
   End if
Next

Depending on what you need to do with your code, this can be very very useful.

But…

It doesn’t work at all unless the user checks the “Trust Access to the VBA project object model” on the Macro Security screen (shown here from Word 2007, it’s essentially the same in Word 2000-2003).

image

Now, you might think that, hey, a potentially powerful security hole like that would be something that ONLY a user would be able to change. In other words, the setting wouldn’t be readily available directly through the operating system.

And you’d be right, and wrong.

That setting can be found, quite simply, in the HKEY CURRENT USER registry hive.

For Excel:

image

For PowerPoint:

image

and for Word:

image

However, that’s not the end of the story.

It is true that the setting is a PER USER setting, which means that your application would have to alter this setting for each user that logged onto a machine. But the current user generally has FULL RIGHTS to HKEY CURRENT USER hive, so why put a security setting like this there?

Because it’s not the only place you set it.

There are also keys like these (note the version of Office):

HKEY_LOCAL_MACHINE\Software\Microsoft\Office\12.0\Word\Security
HKEY_LOCAL_MACHINE\Software\Microsoft\Office\12.0\Excel\Security
HKEY_LOCAL_MACHINE\Software\Microsoft\Office\12.0\Powerpoint\Security

Each potentially contains a value named AccessVBOM as well.

If this value is defined and has a value of 0, the value in HKCU is essentially ignored, and the user cannot grant access to the VBOM, even if they manually enter a 1 for the value under the HKCU.

If the HKLM value doesn’t exist at all, or has a value of 1, then the user can set the HKCU value, or change the Trust Access checkbox manually in the application.

And since write access the HKLM is often restricted to local admins, this gives admins a way to lock down this setting on machines, which is a good thing.

Another important point; changing these settings while the Office app is already loaded, will not cause said app to change its behavior dynamically. Put another way, Word, Excel and Powerpoint appear to read these settings once, at load time, and then never read what’s in the registry again until all instances are unloaded and the app is loaded again.

Practically, this means that you can’t change these settings from within a true Office Addin (ie a COM addin or a VBA Macro).

But you CAN change them quite easily from other code. At least, it’s easy to change the HKCU value, and the HKLM value can be changed during your MSI based installation.

Now comes the ethical dilemma.

Your application won’t run (or won’t run in entirety) if access to the VBOM is not granted, and many people would find it difficult to change that setting, even through the UI. But that’s not the kind of setting that you want to change automatically  or surreptitiously without letting the user know you’re doing so. Or is it? Would typical users know what it meant anyway? And most Office macro viruses would be caught via other means now, right?

As a final note, googling “AccessVBOM” turns up plenty of pages describing these keys in various levels of detail, but interestingly, searching the Microsoft.com site comes up with very little on the topic.

Fire and Brimstone! Office 2000, XP, 2003 and 2007 living together!

0
Filed under Uncategorized

This is truly old testament stuff…. Ok, enough with the Ghostbusters….

If you’ve ever had to support multiple Office environments, you’ve probably already discovered the sheer joy and wonderment that is VMWare.

But, if you have to develop against them, sometimes, it’s awfully nice to have the Office version you need right there on your main box.

It’s not terribly common knowledge, but you can actually install various versions of Office side by side (with the sole exception of Outlook, which only tolerates a single version on a machine, for dark and unholy reasons).

However, if you’ve tried, you’ve most likely snagged yourself on a rather irritating burr.

You start Word 2000, say, and you get an MSI (Windows Installer) prompt saying it’s got to repair something. You click OK, and let it do it’s thing. But then, later, when you start Word 2007, you get a similar prompt, this time for the newer version. Back in Word 2000, same prompt again. Grrr.

There’s a few KB articles out there about this annoyance, but nothing that really resolves it completely satisfactorily.

After some experimenting, however, it looks like it is possible to make various Office versions coexist more or less peacefully, without bashing each other over the head with a virtual tire iron every time one or the other fires up.

The key is registration, esp with Word. Each time it starts, Word checks certain registry entries to make sure they point to that version of Word. If not, you get that “repairing” prompt and the long delay while Word resets everything.

This is great for Joe User who accidentally scrambled his registry and just needs Word to work right again, but it sucks for Pauline Power User who has 3 versions of Office on her machine, and switches back and forth between them constantly.

The trick is to prevent Word from reregistering itself when it starts.

Details of how to do just that are in one KB article on the Microsoft site, but they only go so far as to detail how to do this for Word 2007.

The problem is, what if you want DOC files to normally open with Word 2007, but still have Word 2000 on the “Open With” list via the right click menu? I’d imagine this is a pretty typical scenario.

Take the following REG script, save it to a REG file, and dbl click it to merge it into your registry (it shouldn’t require any special privileges because it only deals with the user hive).

Basically, creating the NoReReg value for each version of Word will prevent the reregistration from happening when you start Word.

Windows Registry Editor Version 5.00

;This will keep Word 2007 from reregistering itself each time you run it
[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Options]
"NoReReg"=dword:00000001

;The same trick works for Word 2000
[HKEY_CURRENT_USER\Software\Microsoft\Office\9.0\Word\Options]
"NoReReg"=dword:00000001

;for Word xp
[HKEY_CURRENT_USER\Software\Microsoft\Office\10.0\Word\Options]
"NoReReg"=dword:00000001

;and for Word 2003
[HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Word\Options]
"NoReReg"=dword:00000001

Be sure to have run the version of Word you want to use actively as the LAST THING YOU DO before running this script; ie, if you really want Word 2000 to be the default editor for DOC files, run it, let it register itself fully, and THEN run this script.

Of course, setting the NoRegReg value back to 0 (via REGEDIT) will revert things back to the way they were and allow that specific version of Word to reregister itself.

Furthermore, the newer versions of Word support a “repair” option on the HELP menu that will perform the reregistration as well.

One note; the Word 2000 and 2007 versions I’ve tested explicitly. The XP and 2003 versions I haven’t checked explicitly yet, so YMMV. However, I was utterly surprised that this trick (which was documented for 2007), actually worked with Word 2000.

And finally, neither Excel nor Powerpoint appear to need any of these shenanigans, hence their MIA status in the above script.

Properly reading the EDITTIME structure in an OLE Structured Storage file (ie just about any Office Document)

0
Filed under Uncategorized

There’s lots of articles out there about reading the properties of an OLE Structured Storage file (which is the format used for all Office apps before 2007 and with continued support in Office 2007).

Eduardo Morcillo probably has about the best implementation for VB6 that I’ve come across. Check here for tons of great VB6 code for opening/reading structured storage files, and lots of other very advanced stuff.

.NET likely makes much of this dramatically easier, but I haven’t delved into that yet so I can’t say for sure.

What I do know, however, is that Eduardo’s code doesn’t quite handle a few elements of OLE document properties. One in particular is the EDITTIME property.

EDITTIME normally tracks how long a particular file has been “edited” within a given application, be it Word, Excel, or PowerPoint.

The field itself is stored as a 64bit FILETIME structure, but it’s not actually a FILETIME structure. Instead, the EDITTIME is stored as a direct 64bit number of milliseconds that the document has been edited.

Check out here for all the details, and a good sampling of some C code to convert the EDITTIME element to a number of seconds and back again.

It’s not terribly difficult code, but it’s also virtually undocumented as to the layout of that structure.

I won’t repeat the code here, but I have to give credit to Rolandas Rudomanskis and the Shareaza team for researching the issue and making the code available.

Just kick it

0
Filed under Uncategorized

imageHopefully, this might save someone else from trading in a “dead” machine.

I picked up an AMD based generic box about 6 months ago for my wife. It’s got an ABIT-NF8 mobo, 2 gb ram, and several HD’s, plus a Vista capable graphics card. It worked great for about 3 months till the powersupply went bits up.

So, a trip to Frys, 40$ and all was well, at least for another 3 months.

Then, just a few days ago, it died again. There was definitely power to the motherboard, but the thing wouldn’t power up.

Suspecting the el-cheapo PS I’d picked up, I yanked it and trekked back down to Frys. I’ve got to get one of those PowerSupply tester units (they sell them there but they are perpetually sold out<sigh>).

At any rate, yep, the new PS had gone south too.

Hrmm. So I picked up an decent supply (this time an Antec), come home and wired it all up.

Hit the on button and up she came, for all of about 30 seconds. Grrr…

Lather, rinse, repeat, same thing.

Doesn’t seem like the power supply, but what the heck?

I disconnected everything, all the way down to the fans inside the box, still the same behavior.

But the unit would boot up, it’d just immediately power down. I started suspecting a possible short on the motherboard, but then that’d usually result in an almost instance crowbar.

Without any other ideas, I googled the ABIT NF8 motherboard for any interesting bits.

Lo and behold, I turn up at least three stories of similar behavior.

Basically, with these motherboards, if the CPU fan/heatsink gets jostled just right, it can result in a “bad connection” between the two. The end game is that the MOBO believes the CPU is overheating almost immediately and shuts down to prevent any damage.

Of course the CPU isn’t  overheating but no matter.

I unhitch the heatsink, dab on a little extra thermal grease and reseat it. Power up… Shutdown. Arrrgggghhh!

Ok. Grab the heatsink and shove it down with a good thwack.

image

Power up. Golden. Been running solid for 3 days now.

Sledgehammers aren’t just for railroads anymore.

Getting Consistent Results with Find.Execute in Word

0
Filed under Uncategorized

If you’ve ever written a Word addin, at some point you’ve probably had to use the Find object. It’s quite powerful, allowing you to find (and replace) text based on wildcards, formatting, etc.

However, it can also be an exercise in frustration getting consistent results from Find, especially when “finding” in the same document across several versions of Word (in my case, Word 2000, XP, 2003, and 2007, surely we can ignore ’97 by now<g>).

The problem comes from at least two places that I know of.

The first is that to really execute the find everywhere,  you need to execute it against the range objects of all the storyranges in the document, NOT just the main body.

Fortunately, that’s relatively easy to do:

For each Story in Document.StoryRanges    'Handle the Story here
Next

However, that doesn’t quite get you there. This is because you won’t necessarily enumerate through all the ranges for all the headers and footers in the document if you just use the above loop.

To see the section headers and footers, you have to do something like:

   For Each Section In Document.Sections
      For Each Header In Section.Headers
         'Handle this Header range
      Next
      For Each Footer In Section.Footers
         'Handle this Footer range
      Next
   Next

And finally, simply enumerating through the headers and footers, you may end up with multiple references to the same header/footer. Gah! How? There’s an unobtrusive little Word feature for headers and footers called “Link to Previous”. Here it is lurking in Word 2007.

image

When that’s set, a single header or footer might show up in several different sections, causing you to search the same place twice. Just filter those with the “LinkToPrevious” property set and you’re gold.

The bigger problem is that Word has a way of fouling its internal search state such that when your code executes its find, it simply doesn’t work.

This is a much nastier problem because it’s not consistent. Things will work fine at one point, then fail inexplicably later.

Fortunately, I happened to stumble across an article by MVP Bill Coan that addressed this issue directly. The article is about “Flushing the bad karma from Word’s search“, and how he came up with the solution is nothing short of bayou voodoo, but it’s exactly what I needed to get my search consistent across all Word versions.

Essentially, you perform a dummy search to clear out whatever internal state information Word is holding onto. This allows your real search to execute properly.

The dummy search is simply:

WordDocObject.Content.Find.Execute "^p", MatchWildcards:=False

Even better, it doesn’t appear to negatively impact performance of my search to any perceptible degree.

And finally, even though it looks like this article was written for Word 2000 (and maybe XP), I found that it’s still very much valid for the latest SP releases of 2000, XP, and Word 2003.

The good news is that Word 2007 doesn’t appear to be effected by the bug anymore, but using the dummy search doesn’t have any negative impact here either.

Merry Christmas!

0
Filed under Uncategorized

I rendered a special Apophysis flame just for the occasion.

Sorry it’s shrunken. If you’d like a full render or the flame file, just shoot me an email.

I call it “Snowflakes”

Snowflakes

Have a great holiday and a happy New Year!

When a good application won’t leave (or the brother-in-law app)

0
Filed under Uncategorized

I have an InstallShield-authored MSI installation for an application I work on.

In testing one of the recent interations, I ran into a peculiar problem.

It installed perfectly fine, and after an uninstall, virtually everything was removed properly, except for one particular component.

It didn’t go away.

I turned verbose MSI logging on (see here for details how), and reran the install/uninstall.

For the install, somewhere toward the top of the log, you should see a block that looks like this:

MSI (s) (9C:D8) [16:43:17:376]: Feature: Some Feature; Installed: Absent;   Request: Local;   Action: Local
MSI (s) (9C:D8) [16:43:17:376]: Feature: Some other feature; Installed: Absent;   Request: Local;   Action: Local
MSI (s) (9C:D8) [16:43:17:376]: Component: InstallMSXML6; Installed: Absent;   Request: Null;   Action: Null
MSI (s) (9C:D8) [16:43:17:376]: Component: InstallSQLSMO; Installed: Absent;   Request: Null;   Action: Null

Notice the Installed state is absent and the action is local, meaning that the feature/component doesn’t already exist on the machine and it’s going to be installed locally (the normal installation type).

After an uninstall, the log will contain a similar block, but just reversed:

MSI (s) (9C:B8) [17:00:19:887]: Feature: Some Feature; Installed: Local;   Request: Absent;   Action: Absent
MSI (s) (9C:B8) [17:00:19:887]: Feature: Some Other feature; Installed: Local;   Request: Absent;   Action: Absent
MSI (s) (9C:B8) [17:00:19:887]: Component: InstallMSXML6; Installed: Absent;   Request: Null;   Action: Null
MSI (s) (9C:B8) [17:00:19:887]: Component: InstallSQLSMO; Installed: Absent;   Request: Null;   Action: Null

Note the Installed state of Local but the action is now absent (at least on the two features noted above).

However, my problem component looked like this

MSI (s) (9C:B8) [17:00:19:887]: Component: ProblemComponent; Installed: Local;   Request: Absent;   Action: Null

And what stumped me was, why a null action? The Request was absent, so why wasn’t the action?

I researched the problem a little and got nowhere, then tried tweaking the install several different ways; still no joy.

I finally broke down and put a call into Macrovision. Ended up talking to one of their guys (Thanks, Cary!) and the first thing he asked was, “Search for Disallow” in the log starting at the top and see if there’s anything mentioned.”

Ok. And sure enough, I had several lines like this.

MSI (s) (9C:B8) [17:00:19:626]: Disallowing uninstallation of component: {997FA962-E067-41D1-9396-00A0C90F27F9} since another client exists

After a little GUID spelunking (you can find GUIDs easily in InstallShield by opening the direct editor

image

clicking on the component table and sorting by the component ID)

image

I was able to determine that the problem component, was indeed one of those whose uninstallation was disallowed.

(You might see a number of “Disallowing” lines in your log, often as a result of including Merge modules of standard libraries, like the COM libraries or the VB runtime files, so you have to just ignore them).

So I figured, just search the registry for the GUID in question and remove whatever junk was left over, probably from an aborted install.

No dice. The component information used to track reference counting (which is how MSI knows to “disallow uninstallation since another client exists”) is locked up in a binary registry entry, from what I can tell.

In the end, you’ll generally have two options.

  1. Change the Component GUID. Only do this if you know that component won’t need the same GUID (for upgrading in later releases, for instance).
  2. Pave your test machine or try the install on a different machine.

Not fun, but not too bad.

Application Blocks and Exception Handling

0
Filed under Uncategorized

I have some pretty in-depth experience working with the VB6 exception handling mechanism. I still get an occasional email about my article on Try-Catch exception handling in VB6 in the Sept 2001 Visual Basic Programmer’s journal.

So when I finally got a chance to crack open the Enterprise Application Block and check out the Exception Handling App Block, I was hoping for some pretty nice stuff, or at least something to give me a few ideas.

But what’s there seems pretty underwhelming.

The good news is that most of the stack tracing and related informational-type things that you really had to work very hard at with VB6, are part and parcel to VB.NET. The bad news is that the exception handling application block doesn’t seem to contain much at all to actually make handling exceptions any easier than piling mountains of Try-Catch lines into your source.

Hmm, Try…Catch…Finally, or On Error Goto Handler…Handler: …Resume next

Under the covers, I know they’re very different, but seriously, what I see is just syntactic shuffling.

I’ve always believed that virtually all exception handling in an app should be attributable to individual routines. That’s the way my exception handler for VB6 worked, just without all the syntactic niceties of .NET.

For instance, why shouldn’t I be able to attribute a routine as to how exceptions within it ought to be handled?

<HandleExceptionsByContinuing> _
Public Sub MySub(arg1, arg2, arg3)
    x = 1 / 0
End Sub

In this case, I’ve declared that for this particular method, exceptions ought to be handled by simply continuing execution of the code.

Other cases might raise a specific error, reraise the original error, reraise the original error wrapped in an outer exception, etc. Granted, my example may not be great here.

Most routines are supposed to be so small that having more than one TRY statement would be lame anyway, right? And most of the code I’ve looked at containing Try Catch blocks only contain one in a method anyway.

For those routines where you definitely need to handle specific exception in specific ways, you still have the Try statement to fall back on, but even then, I tend to believe that every exception in an app should be routed through a single handler that can be configured to do certain things, be it emailing the exception, logging it, or (and I’m not recommending this, mind you!) ignoring it altogether.

Littering the workings of your code with boilerplate Try Catch filler strikes me as equally onerous as those comments like:

'Increase the count variable by one
Count = Count + 1

Ugh.

But, just like a well placed comment, when the workings of the code are better explained with inline exception handling, TRY away!

Might be time for my own Enterprise Exception Handling Block…

Steam Punk Laptop

0
Filed under Uncategorized

If you’re not familiar with steampunk, it’s the illegitimate offspring of retro-futurism, a mid-1800’s Sears catalog, The Jetsons, and This Old House.

That god awful Will Smith movie, Wild Wild West, is probably the most, er, public, of examples. Kenneth Branagh surely should have known better.

image

image

But a guy calling himself Datamancer has to have created one of the nicest examples I’ve ever seen, the steampunk laptop, complete with winding key for an off/off switch, brass lions claw feet, and a watch-gears guts under a hardwood framed glass viewing window. According to the site, the thing actually works.

This is just one picture, and it doesn’t really do it justice. Check out his site for lots more, plus some unbelievable steampunk keyboards he sells.

dp-highlighter and Code Snippets

0
Filed under Uncategorized

I’ve seen a very nice effect on a number of blogs where code snippets were formatted and (somewhat) syntax highlighted.

I finally got a chance to do some digging and turned up the very cool javascript package called dp-highlighter.

In a nutshell, you put the dp-highlighter files in a folder on your website, then add the following code toward the bottom of any page that needs code highlighted:

<link type="text/css" rel="stylesheet" href="./dphighlighter/SyntaxHighlighter.css"></link>
<script language="javascript" src="./dphighlighter/shCore.js"></script>
<script language="javascript" src="./dphighlighter/shBrushVB.js"></script>
<script language="javascript" src="./dphighlighter/shBrushXml.js"></script>
<script language="javascript">
    dp.SyntaxHighlighter.ClipboardSwf = 'dphighlighter/clipboard.swf';
    dp.SyntaxHighlighter.HighlightAll('code');
</script>

Make sure you change the shBrushVB.js to whichever language parser you want to use. Also, I put the files in a dphighlighter folder, but you can put them anywhere, really, just be sure to update the paths above.

Finally, for code you want to highlight, just stick it in a <PRE> tag, and add NAME=”code” CLASS=”VB”, like this

<pre name="code" class="vb">

if you got it right, you’ll end up with something like this:

Public Property Let HelpFile(ByVal NewValue$)

   If rbPrologueActivated Then
      rExc.HelpFile = NewValue$
   Else
      VBA.Err.HelpFile = NewValue$
   End If
End Property

There’s lots more documentation and examples at the link above.