Friday, October 05, 2007

The formatting of the code snippets I post from time to time have always bothered me.

They didn't wrap, which is good (wrapping code just annoys the hell out of me), but they trailed off the edge and just appear to get cut off.

If you selected the text, it was all there and could be copied to the clipboard, but... well.... yuck.

Anyway, I finally figured out how to change the CSS such that you get a nice little scrolling window for the snippet.

I should point to Scott Hanselman's blog as the source of the example I found, but I did tweak it a bit.

Here's an example:

Public Function AppVersion$()
   '---- Get the Current Application file's "Full" version info
   Dim p$

   If IsIDE() Then
      '---- just pull from the App values (because the version resource
      '     isn't available
      AppVersion$ = App.Major & "." & App.Minor & ".0." & App.Revision
   Else
      '---- get from the VersionInfo resource, will include the real revision and build numbers
      AppVersion$ = GetFileVersion$(App.Path & "\" & App.EXEName)
   End If
End Function

Since this is purely a CSS thing, it works backwards in all my old posts, and it appears to be (so far) cross browser compatible.

Here's the style (I've enclosed it in a STYLE tag so you can embed it directly in a html file, otherwise, just use the PRE definition).

<style type="text/css">
pre {
	border: 1px solid #e3a83d;
	border-left-width: 2px;
	background-color: #444;
	padding: 1em;
	margin: 2em;
	line-height: 1.2em;
	overflow: auto;
	width: 90%
	font-size: small;
	color: white;
	font-family: consolas, "Courier New", courier, monospace;
}
</style>

Yeah, it's probably pretty minor stuff for all the CSS genii out there, but I'm still surprises with what you can do with a little CSS. Makes me really want to get into the whole WPF thing. 

posted on Friday, October 05, 2007 4:46:43 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [1] • 
Kick it •  Add to del.icio.us •  View blog reactions; 

And in the "Oow, I'm glad I'm not in that department" department.

The latest Woody's Office Watch reports that Excel 2007 can't format 2^16 or 2^16-1 properly, instead deciding to show it as "100,000"

image

Heh. It's not too bad. According to Microsoft, the bug only affects 12 numbers (or is it 2, I'm not sure, but the quote says 12) out of a virtually infinite number space. From Woody's; "They (Microsoft)point out that Excel can use " 9.214*10^18 different floating point numbers " but the bug only affects 12 of that incredibly large number of possibilities".

Oh man. Can I use that one with my next off-by-one mistake? (hey, I don't make 'em much anymore but still...).

posted on Friday, October 05, 2007 4:40:10 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [4] • 
Kick it •  Add to del.icio.us •  View blog reactions; 
 Tuesday, October 02, 2007

Well, through an amazing set of circumstances, including spam filters gone awry, old email addresses that were no longer valid, too many years and too few vacations, somehow my domain name expired without so much as a whimper (that is, until I started getting IM from people and email through different accounts that went something like, "Hey, where's your domain at, dude!?").

Anyway, things are back up now, with all the pointers, DNS entries and hostnames back in place.

Also, I've obtained the domains vbfengshui.com and visualbasicfengshui.com, so they also point here, with all the mail and DNS forwarding goodness that is this series of tubes.

posted on Tuesday, October 02, 2007 10:53:46 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [2] • 
Kick it •  Add to del.icio.us •  View blog reactions; 
 Monday, October 01, 2007

No, I don't do Don Henley impersonations. But I was starting a load of laundry the other day and something struck me. Once I'd recovered and put the broom back where it was supposed to be, I realized something.

Take your standard, run of the mill washing machine. It's essentially a variable speed motor, a few belts and pulleys, a water pump, and big cog timer wheel that sort of looks like the sheet music for a player piano*.

These things will run several times a week, for years on end and only rarely have any issues. Even then, the most likely problems are things like belts wearing out, bearings getting squeaky, or the timer cogs breaking off causing cycles not to start or stop properly. Further, I'd be willing to bet that just about anyone can set one up, plug it it, connect the 2 water hoses and the drain line, and be doing laundry within 30 minutes to an hour, and just about never think about any of that "configuration and setup" again till they move. 

Compare that, then, to the lastest washing machines, with "steam" cleaning, LCD consoles, touchscreens, dozens of "operating modes", delay start timers, and even internet access.

I love gadgets, but seriously, does being internet-enabled or using an LCD touch screen get my clothes any cleaner, or prevent me from actually having to lug my laundry to the utility room? Do they actually save real people any time whatsoever over a 200$ (or cheaper) model with knobs instead of touchscreens and mechanical timers instead of microchips? Is this really progress?

I gotta stop hitting myself with brooms.

* and yes, that's an oversimplification, for all you washing machine enthusiasts out there.

posted on Monday, October 01, 2007 7:09:17 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [2] • 
Kick it •  Add to del.icio.us •  View blog reactions; 

I originally wrote about digital signatures in Office documents way back here, so check there for more information. But I just stumbled across something with Word 2007 and document signing (including signing templates) that had me scratching my head for a minute.

Word, in it's shiny new 2007 skin, now has a nifty little feature to add a signature to a document (or template) right on the...um... what they hell you do call this button?

image

Anyway, under "Prepare", you'll see this:

image

The Add a Digital Signature lets you sign the document right there. Which is great.

Except for one thing.  That signature is not the same as this one:

image

The former actually signs the document, whereas the latter signs the VBA code contained in the document.

If you don't believe me, sign a document using the Prepare menu item, then check the signature using the VBA/Tools/Digital Signature menu item. Then sigh and weep.

So, what does that matter, you ask?

Well, in terms of checking the validity of macro code in a document, from what I can tell so far, the signature on the document isn't checked, only the signature on the VBA code. From a macro/VBA standpoint, signing the document is pretty useless.

I'm still hunting for a way to automate the signing of DOC and DOT files (such as the SIGNTOOL.EXE utility for signing DLL's and EXE's). That would make the whole process much more convenient, not to mention enabling it to be built into a normal build process.

posted on Monday, October 01, 2007 6:54:10 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [2] • 
Kick it •  Add to del.icio.us •  View blog reactions; 
 Tuesday, September 25, 2007

Normally, when you want InstallShield to be able to perform a MAJOR UPGRADE on an install package, you have to change both the "package code" and the "product code", and of course, the product version. However, you'll normally leave the "Upgrade code" the same.

If you do this, InstallShield will install your new "version" as a new product, but it will also detect the existence of the previous version due to the Upgrade code, and force an uninstall of the previous version. I've actually blogged about this before here.

That's exactly what I was expecting when I started testing the latest version of our package (and the first that was intended to support upgrades), and I was a bit surprised and perplexed when it popped up into maintenance mode  of the previous version!

Somehow, I could start the install by dbl clicking on the MSI of the new version, but the maintenance mode of the previous, installed, version would get started?! What the hell?

I started digging through verbose logs, called Macrovision, the works, and nothing seemed to fix it.

Then, I happened to notice mention of a package code in the logs that I didn't recognize. I checked my new version's properties and the package code didn't match what was in the log.

image

Where the heck was it getting this other package code from?

So, I did a search through the project and it turned up here, under the releases screen!

image

Turns out, at some point in the past, this package code had been set, which overrides the package code defined in the Summary Info Stream screen above, when I build this particular release.

Since package codes should normally change with each build, everything was getting screwed up.

I set the "Generate Package Code" option to Yes, and cleared the package code from the releases screen, recompiled and all was well.

I suppose this is an easy mistake to make, but it's one that left me scratching my head for more than just a few minutes.

posted on Tuesday, September 25, 2007 9:22:35 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [0] • 
Kick it •  Add to del.icio.us •  View blog reactions; 
 Friday, September 21, 2007

Over at Scott Hanselman's blog, he just posted an interesting bit about hard resetting your network connection.

Good info, though I can't say I've ever had the need to club it quite that hard.

Anyway, one of the comments struck me as FAR more useful, and something I did not know.

When you're opening a CMD prompt, you can hold down CTRL and SHIFT as your press ENTER to open it as an administrator (after you supply the right password, of course).

This is a very handy little bit if you regularly have to pop onto normal permissioned user's systems in order to load up the newest bits of some accounting package that can't be installed without admin credentials, or if you're insane enough to try developing under Vista with UAC turned on.

It smacks the heck out of:

RUNAS /USER:{machinename}\Administrator cmd

Sadly, it appears to only work under Vista, though.

posted on Friday, September 21, 2007 6:45:53 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [0] • 
Kick it •  Add to del.icio.us •  View blog reactions; 

Ok, nothing to do with Visual Basic, but, you gotta come up for air sometimes!

It's been a while since a video game really wowed me.

I mean, there's lots of slick 1st person shooters out there now and with some of the heavy iron running around now, the graphics can make "Toy Story" almost look like "Dragon' Lair."

Still, nice graphics only go so far. And 1st person shooters are starting to see a little stale.

Then I stumbled into Grid Wars 2. Holy cow. I mean, seriously.

image

World of Stuart summed it up thusly in his review; "THIS is a video game."

Go there for some wicked screenshots (far better than what I've put up here) and a link to download. You can't get it from Marc Incitti's site anymore <sigh>. Apparently, it's a bit too much like "Geometry Wars" for the comfort of its creator. Grab your copy while you can.

I've never played it's progenitor, but Grid Wars 2 definitely brings the frenetic back to video games. Plus, some subtle scoring tricks that WOS discusses in his writeup that make it all the more interesting. And there seems to be no end to the unique powerups that pop (get 150 bad guys, 3 black holes, 4 snakes, with quad cannons, side fire, fast fire and bouncing shots going all at once, and you'll swear your screen is about to catch fire).

And, come to think of it, there is a little bit of VB goodness here, or rather BASIC goodness. It's opensource, and written in BlitzMax, a pseudo-basic, game programming platform form Blitz Research. Plus, the install is unbelievably sweet. A zip file. Just unzip somewhere and run the EXE. T'would be a blessed day that Office trod down that path.  

posted on Friday, September 21, 2007 5:20:56 PM (Central Standard Time, UTC-06:00)   •  # •  Comments [0] • 
Kick it •  Add to del.icio.us •  View blog reactions; 
 Tuesday, September 18, 2007

I've found that many times when debugging an MSI install, it's quite handy to get a full verbose log of what the install is doing.

You can turn on verbose logging from within the installation itself, at least when using InstallShield, but that has several drawbacks.

  • It's implemented as a function of the InstallShield SETUP.EXE front end, so if a user dbl clicks the MSI file directly, it won't log
  • It doesn't apply during the UNINSTALL or the REPAIR operations, which are quite common and can also fail and require logging

To get a full MSI activity log, regardless of the activity, you need to modify a registry key:

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer]

Logging=Voicewarmup

To turn it back off, just delete or blank the Logging value.

The logs will end up as random .LOG filenames in the user's TEMP folder. It's best to clean out your temp folder before running the install so the new LOG files will be easy to find. (or sort by date).

image

But doing all this manually can be a pain.

So here's a little BAT file to take the edge off.

Just save it as MSILogging.bat (somewhere on your path for maximum utility), and then run it like so

MSILogging ON

MSILogging OFF

or just

MSILogging (no parm is the same as ON).

echo off
REM Simple batch to turn verbose MSI Logging on or off
REM command line option is ON or OFF (all lower or all UPPER case)

REM Create the reg file
set file=%temp%\$reg.reg
echo Windows Registry Editor Version 5.00>%file%
echo ;>>%file%
echo [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer]>>%file%
if .%1.==.ON. echo "Logging"="voicewarmup">>%file%
if .%1.==.on. echo "Logging"="voicewarmup">>%file%
if .%1.==.. echo "Logging"="voicewarmup">>%file%
if .%1.==.OFF. echo "Logging"="">>%file%
if .%1.==.off. echo "Logging"="">>%file%

REM for testing purposes
REM type %file%

REM Apply it with Regedit (you'll need to have rights)
REM /s makes it silent
regedit /s %file%

REM Could uncomment this to remove the temp file when done
REM del %file%

 

And no comments about my lame bat file case insensitive batch scripting\:\-\)

Even better, add a line at the end:

copy %temp%\msi*.log .\

and all those log files will end up in your current directory (instead of making you go fishing through Documents and Settings to find them), although this may suit some people more than others.

posted on Tuesday, September 18, 2007 11:32:27 AM (Central Standard Time, UTC-06:00)   •  # •  Comments [0] • 
Kick it •  Add to del.icio.us •  View blog reactions; 
 Sunday, September 16, 2007

Here's another handy bit from my VB6 code shed out back.

If you've worked much with databases in VB6, you know that you almost always end up having to deal with database NULLs at some point.

And, in VB6, the only variable type that can actually deal with nulls is the Variant.

For the most part, my code avoids nulls by coercing them to the most appropriate "null value", either a 0, or a 0 length string, etc, depending on the underlying data type.

However, there are instances where I've needed to compare two values directly from the database, and didn't want to coerce them up front.

That's where this handy routine comes in.

Public Function CompareVariant(Var1 As Variant, Var2 As Variant, Optional CompareMethod As VbCompareMethod = vbBinaryCompare) As Long
   '---- Compare 2 variants that might contain NULL or empty values

   Dim bVal1Null As Boolean
   Dim bVal2Null As Boolean
   Dim bVal1Empty As Boolean
   Dim bVal2Empty As Boolean
   Dim bSameValues As Boolean

   bVal1Null = IsNull(Var1)
   bVal1Empty = IsEmpty(Var1)
   bVal2Null = IsNull(Var2)
   bVal2Empty = IsEmpty(Var2)

   '---- variants are the same if
   '     1) both are null
   '     2) both are empty
   '     3) they are otherwise equal
   If (bVal1Null And bVal2Null) _
      Or (bVal1Empty And bVal2Empty) Then
      CompareVariant = 0
   Else
      '---- you can only check for equal values is if neither of the values is Null or Empty
      If Not (bVal1Null Or bVal1Empty Or bVal2Null Or bVal2Empty) Then
         If CompareMethod = vbTextCompare Then
            CompareVariant = StrComp(CStr(Var1), CStr(Var2), vbTextCompare)
         Else
            If Var1 > Var2 Then
               CompareVariant = 1
            Else
               CompareVariant = (Var1 < Var2)
            End If
         End If
      ElseIf bVal1Null Then
         '---- This is arbitrary, I'm determining that NULL is < empty
         '     this might not be universally appropriate, though
         CompareVariant = -1
      Else
         CompareVariant = 1
      End If
   End If
End Function

Basically, the idea is similar to the built-in VB function StrComp, but it intelligently deals with potentially NULL or empty variants as well.

Are there faster ways to do this? Probably. But I find I need the functionality so infrequently, it hasn't been a priority to optimize.

Still, if you need it, coding this up each time would be a complete pain in the ass (and unfortunately, I've seen that tack taken more than a few times).

If anyone can improve on this, please let me know!

posted on Sunday, September 16, 2007 9:11:51 AM (Central Standard Time, UTC-06:00)   •  # •  Comments [0] • 
Kick it •  Add to del.icio.us •  View blog reactions;