Monthly Archives: April 2008

Phantom Header Changes in Word

0
Filed under Office, Troubleshooting

For today’s blog entry, I’m going to delve into a bit of Word esoterica that, if you’re into this sort of thing, you might find fascinating, and if you’re not, well, you have been warned<g>.

If a document is changed in Word, you’d expect to get a prompt when you close the document, wouldn’t you?

Well, that’s apparently just expecting too much, my friend.

And if you were to load a document programmatically and simply iterate through it, without changing anything, you’d expect that the document wouldn’t actually change, wouldn’t you?

Well, that’s also… oh, you get the picture.

It appears to be possible in Word, without programming, to create a header that doesn’t actually exist, or rather, that exists but is completely empty.

If you then iterate through the headers of the document, retrieving the Range of each, the act of simply “retrieving” the range, not actually modifying it, will cause the header to be modified, inserting a paragraph mark where there was none before.

This can effectively shift the entire document down, since, in the original document there was no “paragraph” in the header, but now there is.

Even more interesting, this modification isn’t logged internally by Word, so if you were to close the document, you get no prompt that a change has been made, and from what I can tell, the change isn’t actually saved, although it’s very visible on-screen.

You can test this by creating a document with a null header, then, adding this VBA code and running it:

Sub Test()
   Dim sct As Section
   Dim r As Range
   Dim hdr As HeaderFooter

   For Each sct In Me.Sections
      For Each hdr In sct.Headers
         Set r = hdr.Range
      Next
   Next
End Sub

How do you create a document with a null header? To be perfectly honest, neither I nor a Microsoft Tech could figure that one out. Please let me know if you can.

But what I did discover was that the situation yielded something you could detect.

Essentially, the problem is this:

Each SECTION object in Word has a PAGESETUP object associated with it.

And each PAGESETUP object has an associated HEADERDISTANCE and TOPMARGIN property. It also has FOOTERDISTANCE and BOTTOMMARGIN properties, but I’ll only deal with headers here. Footers are handled in exactly the same way.

In a document’s section, if the HEADERDISTANCE is ever >= the TOPMARGIN, any changes in the size of the header have the potential for “spilling out” over the limit of the margin and pushing the content of the section body down.

I say potential, because if the header doesn’t contain anything, or if what it contains is not enough to push the bottom of the header past the TOPMARGIN, everything will stay put and all will be well.

In any case, right before you save the document, you simply need to iterate through all the headers in all the sections of the document and test if they’re empty. If they are, you force the HeaderDistance back to half of the TopMargin.

Section.PageSetup.HeaderDistance = Section.PageSetup.TopMargin / 2

Why half? It essentially doesn’t matter what it is, as long as it’s less than the TopMargin.

But what makes up an “empty” header?

Once you get the Range of the header

set Range = Header.Range

Make sure it’s not a linked header

If Range.LinkToPrevious then {it's linked so don't bother with it}

and then check the character and word counts

If Range.Characters.Count > 1 and Range.Words.Count > 0 and Range.Characters.Count > Range.Words.Count Then
   {this is NOT an empty header}
End If

If it’s empty, adjust the HeaderDistance as noted above.

Fun stuff.

Fixing System.String, part 2

0
Filed under Uncategorized

I talked about fixed the IsNullOrEmpty method on the string object here.

But another aspect of the string object that has irritated me for a while is the Length property.

Obviously, the Length property returns the length of the current instance of a string variable.

However, because it’s an instance property, it can only be invoked against variables that have actually already been initialized to some value.

If you try and run this code, you’ll get an Object Not Set error when you execute the second line:

dim s as string
debug.print s.Length

Now, anybody that’s coded in VB intuitively knows that an unitialized string has a length of 0, and such a request should certainly  not result in throwing an error. It doesn’t matter one whit whether this is technically incorrect or not. I my book, those issues are for the compiler to work out, NOT the programmer.

Since the string class is an intrinsic type and we really can’t inherit from it or extend it in any traditional way, is there any way to correct this gaff?

With VB 2008, there is.

I blogged a bit about extension methods in the previous mentioned post and that’s what I’ll use to fix the Length property as well.

Unfortunately, as I also mentioned in my previous post, Length is a property  and not a method, so you can’t create an extension method for it directly, which is a shame.

However, long time VB programmers don’t think to use “Length” anyway, you always retrieved the length of  string via LEN(stringname), and LEN is not a member of the string object.

So, just add the following to a module to your application:

Imports System.Runtime.CompilerServices

Module StringExtensions

    ''' <summary>
    ''' Returns length of the string or 0 if the string is empty or has never been assigned (is nothing)
    ''' </summary>
    ''' <param name="aString"></param>
    ''' <returns></returns>
    <Extension()> _
    Public Function Len(ByVal aString As String) As Integer
        Return Microsoft.VisualBasic.String.Len(aString)
    End Function
End Module

and you’ll have a LEN function available directly from any string variable that will work regardless of whether the variable is initialized or not.

Also, note the <summary> comment. This will be displayed by Intellisense in the IDE, which can be quite handy.

image

And finally, you can’t use Return Len(aString) in the function, because doing so would be treated as a recursive call to the Len Extension method itself! Thus, I fully qualify the call to the Microsoft.VisualBasic.Strings LEN function.

All of this begets the obvious next question, “Why not just use Len(stringvar)“?

To which, I have no good answer.

Maybe it’s just a .NET thing<g>

My Entries on the List of Things to Hate about VB.Net

0
Filed under Rants, VB Feng Shui

First, I should say I’m a pretty ardent supporter of VB, even VB.net. Heck, that should be obvious from the name of this blog<g>

But, as I’m getting more and more into the “.net” of VB, I’m finding more and more things to dislike about it.

There’s plenty already written on this subject and maybe I’m a tad late to the party, but I haven’t seen these particular things mentioned before, so here’s my additions to the topic.

  • Sluggish IDE. Good god. I’ve got a dual core Core2 rig with ultrafast video and 4gb of ram, and the VS2008 IDE feels like trudging through a Valdez-soaked beach compared to working with VB6. I understand this isn’t specifically a VB problem. The IDE applies to all .NET languages. But that doesn’t change the fact that if you program in VB, you use the IDE, and it’s a dog.
  • Intellisense. I like Intellisense, the concept, but there’s just something about the VB.NET implementation that is so much more “in the way” that VB6′s. It feels a bit like that brother-in-law that you can’t get off the couch soon enough…
  • MDI style IDE. While there are definitely some improvements to the UI of the IDE, and just maybe I’m a relic, but I MUCH prefer the VB6 (and before) “SDI” floating windows style IDE to this monolithic, MDI style environment. To make good use of it, you have to maximize it on-screen, which hides everything else onscreen. Granted, a nice dual or triple monitor setup minimizes that impact, but still, I just find it much more cumbersome and “in the way” than the old environment.
  • Uninitialized strings variables. This one just floors me. Take this code:
        dim s as string
        debug.print len(s)
        debug.print s.length
    The len(s) line works like you’d expect (you get 0), but the s.length line fails with an error! What the hell? An uninitialized object? This is like a car manufacturer coming out with the new year model and swapping the gas and brake pedals. I don’t care whether they told me about it before or not, it’s just plain stupid. Why on earth have two functions that do exactly the same thing but one works as any longtime VB programmer would expect and one does not? And no, “That’s just the CLR/it’s necessary for C#/Blah Blah” just doesn’t cut it.

As I read through many of the posts, I have to admit I’ve started to wonder myself as to whether this is all some low-level ultra-subtle manipulations on the part of MS to nudge people further and further away from VB. Yes, it starts to sound like conspiracy theory, but surely, all this couldn’t be completely unintentional, could it?

Even so, there are ways around some of this. And I still much prefer VB, the language, to anything even resembling C and it’s ilk.

Still, if no one complains, things won’t change (or they’ll get worse<sigh>).

Fixing System.String

0
Filed under .NET, Languages, Rants, VB Feng Shui

String manipulation is a very different beast in VB.NET than in VB6. In fact, I’d wager that it’s the one area that trips up programmers new to VB.NET more than any other aspect of the language.

I’ve talked about some annoying aspects of System.String before, but this go round, I’m going to concentrate on shared methods.

Shared Methods are methods that are invoked via the class type itself, and not via an instance of the class.

For instance, with strings, you have a nicely named IsNullOrEmpty  function that you’d expect would return a true/false indicating whether the string in question is null or empty. Unfortunately, you’d be only half right.

Bring up the Object browser and find the String class, then highlight the IsNullOrEmpty method and you’ll see this:

image

Notice the Shared keyword. That indicates that this method IS NOT an instance method, thus you can’t invoke it via a specific instance of a string; rather, you must invoke it via the String class directly.

So, you can’t do this:

Dim s as string
If s.IsNullOrEmpty Then

But you can do this:

Dim s as string
If String.IsNullOrEmpty(s) Then

Now, it makes perfect sense, from a purely technical perspective, that invoking an instance method on an uninitialized object wouldn’t work anyway, so a method like IsNullOrEmpty wouldn’t make sense to be an instance method, since attempting to invoke it via a variable that hadn’t actually already been initialized would result in an “Object not Set” error.

However, this is VB, not some ivory tower exercise in theoretical language design. If I’m going to invoke a method like IsNullOrEmpty, I’m expect to be able to do so against an instance. Having to invoke it via the String class is just so utterly unintuitive, it defies all reason.

Oddly, the very argument that I note above in favor of using a shared method for IsNullOrEmpty is violated by another string property, Length. Here’s a property that is definitely an instance property, but causes VB code to fail (with the Object Not Set error) when invoked on a variable that hasn’t actually been set to value yet.

Is this just an arbitrary oversight, a design flaw, or an intentional “feature” of the language? I can’t answer that.

But, realistically speaking, I can say that it’s utterly frustrating to have elements of a language, such as these, behave so drastically different from one version to another. It doesn’t matter that the syntax is different (x.Length vs Len(x), for instance), there is an expectation there that simply is no longer met and does nothing but confuse.

Fortunately, with VB 2008, there is a relatively trivial way to correct these problems, and likely a host of other similar issues.

It’s called “extension methods”.

To create an IsNullOrEmpty that works like any reasonable person would expect it too, just put this in a Utility module somewhere in your project:

Imports System.Runtime.CompilerServices

Module StringExtensions

    ''' <summary>
    ''' Returns True if the current string instance is nothing or a null string
    ''' </summary>
    ''' <param name="aString"></param>
    ''' <returns></returns>
    <extension ()> _
    Public Function IsNullOrEmpty(ByVal aString As String) As Boolean
        Return String.IsNullOrEmpty(aString)
    End Function
End Module

The Imports System.Runtime.CompilerServices is only used during compilation. You can actually continue to target the .NET runtime v2.0, even if you use this code (however, you still have to compile the code from VS2008, it won’t work in VS2005).

You tag the new version of IsNullOrEmpty with the <extension()> attribute to  mark it as an extension method.

The first parameter of an extension method is required and is an argument of the type of class that you’re extending, in this case the String class.

You can have additional arguments if necessary, but you don’t need any for this method.

This trick takes advantage of the fact that even though the String class already has a method named IsNullOrEmpty, the function signature is not the same as this one (since ours has the implicit first argument). This is effectively an overload and it allows VB to know to call the new method if invoked against an instance, and the old one if invoked against the String class directly (which is exactly what’s being done within the method itself!).

There are several other “shared” methods on the string class that can similarly be extended to more intuitive instance methods in this way, for instance:

  • Compare
  • Concat
  • Format
  • Join

Length could also be added to this list but you can’t quite treat it the same, since it’s a property, and the Extension attribute can’t be applied to properties.

Finally, extension methods can be unbelievably useful for associating functionality with specific classes that you can’t extend in any other way, but, as always, you need to be careful with how much you extend a  class.

For example, it might be tempting to extend the String class with all sorts of path/file/folder manipulation and parsing logic, so that you could do something like this:

dim s as string = "c:\myFolder"
debug.print s.DriveLetter

but doing so could quickly clutter the String object and Intellisense.

As usual with these sorts of techniques, use judiciously.

TurboTax no longer includes a free E-File

0
Filed under Uncategorized

image I realize this is a little late, and almost totally off topic from Visual Basic, but it is a peculiar business decision, if you ask me, which, in a way, has to do with developing VB apps.

Intuit has decided, according to their support staff, that as of this year, if you purchase the CD version or download version of TurboTax (ie, the fat client), you no longer get a single E-FILE for free. You have to pay an extra 17.95 for that privilege.

Now, TurboxTax has always been a pretty good deal in my book, but with the competition in the marketplace now, I can’t see how this is possibly a good idea, other than possibly to drive business to their online offering, which does still offer a free E-File.

Oddly, though, the online version doesn’t have the same “FORMS” mode that the fat client does, thus you can ONLY use their “interview” style format to enter data, which can be cumbersome in many cases. Hence, the two aren’t interchangeable.

I suppose to their credit, they did offer to refund the E-File charge when I complained about it. But by the time their support emailed me back, I’d already printed and mailed my return.

Yet one more reason that FairTax seems more and more like a better idea.

Go there, check it out, and if you agree, and sign the petition.

Fun with the Clipboard

0
Filed under Utilities, VB Feng Shui

Have you ever clipped something to the clipboard and wished you could immediately “do something” with it?

For instance, if I clip a URL, it’d be nice to simply be able to immediately “open” that URL.

Or if a clip a phone number, how about dialing that phone number (intelligently, of course, adding or removing 1+ and dealing with area codes, prefixes and the like). Esp useful for WebEx or Gotomeeting conference calls that require not only dialing a number but dialing the meeting access code as well.

Or how about immediately jumping to Google Maps to highlight a place if you just clipped an address?

I’m building a little open source example utility to do these very things, but was wondering if there wasn’t something else it could do as well (within that general idea) that I’m just not seeing.

Two or three actions just doesn’t seem like enough to warrant a whole utility.

Or is there already a utility to do this?

Any thoughts?

Hack of the Day: Swapping Lines of a 2-line Phone Connection

0
Filed under Hack of the Day, Hardware, Phones

Do you have two phone lines, but you want your modem to use line 2 when it only sees line 1? Or maybe you have a single line phone that you want to use on line 2.

You could go down to Radio Shack and pick up a 2 line phone. Or you could pick up a 2 line phone switch (that lets you switch between line 1 and 2), or even this 2-Line, 3-Way Adapter Jack.

But, that costs money, and if you’ve got a few minutes and a spare phone extension cord, you can probably rewire one in less time than it’d take to run to the store.

First, some background…

Like many people, I have two phone lines into my house, one specifically for my home office, the other, the family phone number.

If you’ve never looked before, a single phone line actually consists of a pair of wires, typically colored RED and GREEN.

If you have two phone lines in your house, (and usually even if you only have one active phone line, you’ll have wiring for two lines), the second line will consist of two wires colored BLACK and YELLOW.

From the front, the plug will usually look like this (note the 4 wire prongs inside the connector socket, the picture’s not great…)

image

But from the back, they’ll usually look like this (note: this is a 2 connector plug, so you can actually plug two phones in simultaneously). Here, you can easily see the RED/GREEN, BLACK/YELLOW pairings.

image

A normal phone will have contacts only for the RED/GREEN wire pair, which means you can only use it with line 1.

BUT, if you simply swap the RED/GREEN and the BLACK/YELLOW pairs, that single line phone can then be used on line 2.

This is trivially easy to do if you have a 2 plug wallplate like the above. In fact, on the wallplate above, the bottom plug has its wires cross-connected to the terminals on the top plug. For instance, note that the GREEN wire from the bottom plug is connected to the terminal with the YELLOW wire on the top plug. The same goes with the RED->BLACK connection, etc.

Then, when you’re ready to wire this back onto the wall, simply connect the RED/GREEN/BLACK/YELLOW wires from the wall to their original terminals on the top plug. You can now plug a single line phone in the TOP plug to connect to line1 or into the bottom plug to connect to line2.

But what if you don’t have this sort of wallplate lying around?

I happened to also have an 2 plug extension cord, the end of which looks like this:

image 

It has all 4 wires on both plugs, so again, the trick is to swap out the wire pairs on one plug, so that one plug is Line2/Line1 and the other is the normal Line1/Line2.

Mine had little tabs that you press in gently to release the two halves of the shell (be careful not to break them off):

image

Open it up, and pull the wire blocks back from the front half of the shell to look something like this:

image

One at a time, gently bend the loose wire prongs forward enough so they can be pushed back through the plastic block, push them through, and swap them with the appropriate other colored wire.

  • BLACK<–>RED
  • YELLOW<–>GREEN

These are thin wires so they bend easily,but they can break off easily too!

Do only one pair at a time to keep from mixing them up.

Also, you only need to swap pairs on ONE of the blocks, just leave the other as is so you can still access line 1 as line 1.

Once you have the wires in their new positions, gently bend the wire prongs back over like they were originally, put the blocks back in the front half of the shell, seat them solidly and then snap the shell halves back together.

Now label the plugs as to which is which:

image

To test, plug a single line phone into the unmodified plug and dial your line 1 number. If it’s busy, you’ll know it’s line 1. If you’ve got phone company voicemail, well, I’ll leave it as an exercise to the reader to determine which line you’re listening to<g>.

Then, do the same for the other plug and you should be calling out on line 2.

Personally, I like modifying the extension cord better, because it’s easier to remove and take it with you or move it about the house if necessary.

Now, why bother with any of this anyway?

Well, I happen to need to constantly connect to web session conference calls (usually via GOTO Meeting), and dialing the phone number, plus the conference access code, was beginning to be a grind.

So, I wrote a little VB.NET utility that monitors the clipboard for specific regular expressions (i.e. a phone number, GOTO Meeting ID or URL, etc) and can then dial the number through the modem and launch the URL all at one pop.

But I needed it to dial out on Line 2 (my office line) and, of course, the modem only sees Line 1.

I’ll be blogging about the utility itself soon, so stay tuned!

The Singing Ringing Tree

0
Filed under Misc

I’m a closet modern art fan, though more often than not, I find myself saying “uuuhhuhhh?” to much of what is deemed “Modern Art.”

Occasionally, though I come across something that’s (at least to me) pretty interesting and actually worthy of the title.

Here’s one of those things, built by a sculptor in Burnley, UK.

Almost completely unrelated but something none-the-less interesting, is the work of Theo Jansen. Here’s his Animaris Rhinocerous.

Makes you want to grab a torch and some pipe, eh?

REG_MULTI_SZ and Windows Installer

0
Filed under Installations

image I have an InstallShield 11.5 based installation, and, at some point, it went south, but in a very subtle way that I did not immediately notice.

For those with a short attention span, I’ll summarize the issue like so:

DO NOT use the “System Search” ability in InstallShield (or the REGLOCATOR table in MSI, for that matter) to retrieve a registry value of the “REG_MULTI_SZ” format.

Doing so appears to cause Windows Installer to internally skip large portions of  Maintenance mode operations, such as Repair, or Remove.

Essentially, the problem was this…

If you open the ARP (Add Remove Programs list) and select a “normal” install, you should see two buttons, Change and Remove.

Clicking Remove will work perfectly fine, regardless of anything I mention here.

Clicking Change, on the other hand, brings up the “Maintenance Mode” screen of an InstallShield installation.

From there, you have 3 radio button choices:

  • Modify
  • Repair
  • Remove

So far so good.

However, in my installation, none of those options actually DID anything anymore. The progress bar would update and go to 100%. You see things apparently happening, but when it all finished, nothing actually had happened.

Now, they ALL worked at one point, but exactly when things broke, there was no real way to tell. This install is >100MB, so I don’t make it a habit of checking in all the various interim builds of the install.

All I did know was that the last released version (which I did have laying around<g>) worked just fine, but the latest version did not.

What’s worse, the last released version was dated some 8 months back, and there had been literally hundreds of changes to the install in the interim, including removed and added files, new scripting, new components, etc.

After working with Macrovision for almost a month and a half, I’d still made absolutely no headway on the problem.

The prospect of reverting back to an 8 month old version and redoing every individual change, along with testing the install to make sure that the maintenance mode would work after each change, was, to the say the least, utterly horrifying.

Macrovision had no thoughts as to even how to debug the issue, much less what might be causing it.

Very early on, I’d already hit on the obvious things.

  1. Generating a full verbose log of both the old, successful install and subsequent uninstall and comparing that to the same log from the new version yielded no clues.
  2. Saving the ISM files (the InstallShield source of an installation project) as XML files and the comparing the old and new also yielded nothing that stood out as a problem.

Long story short, after several days of applying the various changes to the early, working version, I got extremely lucky and stumbled on the cause of the problem.

I’d added several “System Search” items to detect already installed prerequisites and the retrieve specific system information I needed during the installation.

One of these was to retrieve the “InstalledInstances” value from the registry key

HKLM\Software\Microsoft\Microsoft SQL Server

image

This functionality isn’t really InstallShield based, it’s a part of MSI from what I can tell. The definition of the system search element is actually stored in the REGLOCATOR table in the MSI file:

image

So, at least for now, I can’t blame this one on InstallShield.

As it happens, that registry key is a REG_MULTI-SZ value, meaning it can have a single string value, or multiple values, separated by null chars.

And it just so happens that on my test machine, I had two different instances of SQL Server installed, so there just happened to actually be a REG_MULTI_SZ value in that key.

And THAT was the cause of all this headache.

What’s truly ironic about all this is that the installation wasn’t actually using the property that resulted from this System Search anymore. At one time, it checked the listed installed instances of SQL Server for their applicability to our install, but I’m not actually doing that anymore.

Moral of the story? Don’t use the SystemSearch capabilities of an MSI install to retrieve a REG_MULTI-SZ registry value. It’ll work, but then the maintenance mode of your install could very likely fail inexplicably, not to mention what other consequences I haven’t stumbled upon.

Lastly, I did some googling once I’d narrowed things down and found, well, nothing, on the web or MacroVision’s site about it. Maybe what I’m seeing is something specific to my particular machine, maybe not. Given InstallShield’s resistance to being installed twice on different machines with the same license, I’m not willing to jump through those hoops just to test that suspicion.

But maybe this will help someone else caught in those same throes.