Author Archives: admin

Frets On Fire X From Source Code

0
Filed under Games, Python

poseNOTE: In the process of putting together this post, I discovered both the game PhaseShift, and a fantastic theme for it based on RockBand 3. PhaseShift appears to be everything I was looking for in modifying FoFix and a WHOLE LOT MORE. I pressed on with this post for two main reasons:

  1. It might serve to help anyone else who may be interested in continuing on with more development on FoFix
  2. I had a lot of fun getting everything together and just seeing it work from source.

Still, I highly recommend checking out PhaseShift and the themes that are available for it. There seems to be a lot more work going on with it these days than any of the other rhythm games out there.

If you haven’t played it, Frets on Fire is a RockBand clone from back around 2009. You can read more about the genesis of the game here.

It’s written in Python and open source, so there have been a number of mods made to the original game over the years.

By far the best, in my opinion, is the Frets on Fire X mod, usually called FoFix. Tons of great changes and improvements, plus a fantastic looking theme called ‘RockBand’.

The unfortunate part is the FoFix (and Frets on Fire in general) appear to have fallen by the wayside in terms of people working on them. That’s really too bad, as

it’s a fantastic game which works quite seamlessly.

Still, as good it as it, it could always be better, right?

Features

Really, all I set out to do was add code to the song selection list that would show what instruments were available in each song, without having to actually pick the song and then getting a “This song has no drum part” message. Pretty simple stuff. But this is Python, and open source, and while I’ve worked with open source before, Python was a new beast for me to tangle with.

Getting Organized

If you’re like me, you already have a copy of FoFix installed, it’s just that you run the EXE version of the game, and not the source code version.

If you want to make any changes, you’ll need to be running from the source code itself. That’s what all this is about.

First things first though. This all might not be strictly necessary, but I don’t want to wreck my playable install of the game, so…

  1. Copy the entire FoFix installation to somewhere else, I created a folder called FoFix35 (since 3.121 was the current version).  However, If you have a LOT of songs in your \data\songs folder, don’t copy all of them. There’s no need. Just a few will do. So, at this point, you should have 2 root folders somewhere:
    \FoFix (your original Frets On Fire X installation)
    \FoFix35 (the copy, minus most of the songs)
  2. Create a \FoFix35\src folder if it doesn’t already exist (this is where you’ll want the source files)
  3. Create a \FoFix35\installs folder (this is where I downloaded all the necessary files, see the next section)

The Downloads

Download all these files into the installs folder you created above.

First, the code. You’ll likely want the most recent, active version of FoFix, which, as far as I can tell is 3.121

https://codeload.github.com/fofix/fofix/zip/Release_3.121

Python (Duh, right? You’ll need the 2.6 version)

https://www.python.org/ftp/python/2.6.6/python-2.6.6.msi

PIL (Python Imaging Library, you’ll want 1.1.6, not 1.1.7!)

http://effbot.org/media/downloads/PIL-1.1.6.win32-py2.6.exe

PyAudio

http://people.csail.mit.edu/hubert/pyaudio/packages/pyaudio-0.2.4.py26.exe

PyGame (I used the 1.9.2a version, but if that doesn’t work for you, try installing 1.9.1 over it)

http://www.lfd.uci.edu/~gohlke/pythonlibs/zit7bosa/pygame-1.9.2a0.win32-py2.6.exe

http://pygame.org/ftp/pygame-1.9.1.win32-py2.6.msi

PyOpenGL

http://www.lfd.uci.edu/~gohlke/pythonlibs/zit7bosa/PyOpenGL-3.1.0b2.win32-py2.6.exe

PyOpenGL Accelerator

http://www.lfd.uci.edu/~gohlke/pythonlibs/zit7bosa/PyOpenGL-accelerate-3.1.0b2.win32-py2.6.exe

PyWin32

http://softlayer-dal.dl.sourceforge.net/project/pywin32/pywin32/Build216/pywin32-216.win32-py2.6.exe

The Win32 Dependency Pack

https://fofix.googlecode.com/files/fofix-win32-deppack-20130304.zip

And finally, NumPy (Note: you’ll want this version in particular, older versions didn’t work for me)

http://softlayer-dal.dl.sourceforge.net/project/numpy/NumPy/1.8.1/numpy-1.8.1-win32-superpack-python2.6.exe

 

Installing Everything

First, unzip the Source code zip file into the \src folder you created above. When done, you should have

\FoFix35\src\midi

\FoFix35\src\scripts

\FoFix35\src\win32

plus a bunch of *.py files in the \FoFix35\src folder itself

 

Next, dblclick the Python installation exe and work your way down the list above, till you get to the Win32 dependency pack. Just accept the defaults and you should be able to just install each one after the other.

The dependency pack itself is a zip file that needs to be unpacked in a specific spot.

The zip contains a root folder, ‘deps’. This ‘deps’ folder should be unpacked into:

\FoFix35\src\win32

such that when you’re done, you end up with:

\FoFix35\src\win32\deps

and this deps folder contains ‘bin’, ‘include’ and ‘lib’ folders.

Finally, finish up with the numpy package.

Checking it out

If everything has gone well, you should be able to navigate to

\FoFix35\src\scripts

and run

RunFofFromSources.bat

FoFix should compile and open its normal intro window.

If it doesn’t, be sure to check the log file

\FoFix35\src\fofix.log

This should give you a pretty good indication of what’s gone wrong.

Wrapping Up

If things do go wrong, first check the log I mentioned above.

I ran into several issues while working through this process.

  • I got the wrong versions of some libraries, based on outdated information on the web (Yeah, this post might eventually end up in that same boat!).
  • I started with the wrong version of Python (2.4, you need 2.6).
  • NumPy gave me some issues, in that I didn’t have the right version.
  • The Python Imaging Library I had to back out to an older rev to get functioning

I stuck with the 32bit version of everything. It might be possible to get 64 bit working, but I didn’t go that route.

I’ve played FoFix for almost 4 years now, using 2 Ashley Rock Axe wireless controllers, as well as a Red Octane wireless drum controller. Great fun! And now I can experiment with some Python coding to boot!

Viewing CHM files from Network Drives

0
Filed under Hack of the Day, Troubleshooting

Ok. So this is not a particularly new problem. You get an application, which comes with a help file in the CHM (Compressed Hypertext) format. Works great as long as you don’t try and open it from a networked drive. <sigh>

So, what to do?

Well, Microsoft has a pretty complete suggestion here.

But, it’s registry hacks, it’s not pretty, and, it didn’t work for me on a  Win7 system.

The Help and Manual folks put together a pretty interesting alternative here but this is assuming you’ve got a product you’re installing. Not great for a typical user.

A simpler way

After researching this way more than I should have, I ended up with a simple straightforward solution that works reasonably well.

Just zip the CHM file into a zip file in the same location.

Yes that’s it!

image

Notice in the image that I have zipped the 7Zip CHM file into a zip file. I renamed the file as 7-zip.chm.zip, just to remind me that it contains the CHM file.

To read the file, just double click the ZIP file, which, in modern Explorer windows, will open the ZIP directly, then double click the CHM inside the ZIP itself.

When you do that, Explorer will decompress the CHM file to your temp folder and then open THAT file with the CHM viewer. Because that CHM file is located on your local harddrive, the restrictions in place that prevent viewing networked CHM files, won’t take effect and, presto! CHM viewage without any registry hassles.

 

By the way, the 7Zip command line version is an excellent tool (with a great help file<g>) for doing just this sort of thing, if you happen to like command line tools.

Grab it here.

Merry Christmas

0
Filed under Rants

It’s been a long year.

Merry Christmas to everyone.

Here’s to next year.

Fading two Video Windows in WPF

0
Filed under WPF

imageI was discussing some future features of a commercial jukebox application with the author a few days ago and he lamented about what weak video support the library he had used had for video. In particular, he really wanted to be able to cleanly fade one playing video into another, very much along the lines of cross-fading one audio track into another.

I’d done a little bit of that kind of cross fade for a jukebox plugin I’d written a while back called the NowPlayingScreenSaver, so I thought I might have a go at it.

The result is a simple demo app that illustrates some of the basic WPF animation and media playback techniques quite nicely.

Setting Things Up

Fire up VS2010 and create a new WPF project.

For the mainwindow.xaml, define two mediaelement objects:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <MediaElement Height="210" HorizontalAlignment="Left" Margin="12,12,0,0" Name="media1" VerticalAlignment="Top" Width="316" LoadedBehavior="Manual" />
        <MediaElement Height="210" HorizontalAlignment="Left" Margin="12,12,0,0" Name="media2" VerticalAlignment="Top" Width="316" Opacity="0" LoadedBehavior="Manual" />
    </Grid>
</Window>

Pretty simple stuff. Note that media2 is completely transparent initially (Opacity=0).

For the codebehind in MainWindow.xaml.vb, first, add a few IMPORTS

Imports System.Windows.Media.Animation
Imports System.Windows.Threading

In the MainWindow class, you need to set up a few regional variables to hold the animation storyboards you’ll use, plus a few other bits

Class MainWindow
    Private _StoryBrd1 As Storyboard
    Private _StoryBrd2 As Storyboard

    Private v1up As Boolean = False

    Private WithEvents _tmr As DispatcherTimer

v1up is simply a flag that indicates which mediaelement is currently the “main", upfront” element.

The _tmr is a timer I’ll explain in a bit.

The Loaded Event

When this xaml windows loads, I want to start the media1 element playing, as well as configure a few things.

Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    '---- register media controls so we can animate them
    Me.RegisterName(media1.Name, media1)
    Me.RegisterName(media2.Name, media2)

    '---- position second mediaElement beneath the first
    media2.Source = New Uri("Video2.avi", UriKind.Relative)

    '---- kickstart
    '     video playing
    media1.Source = New Uri("Video1.avi", UriKind.Relative)
    v1up = True
    media1.Play()
End Sub

First, you have to use RegisterName on the MediaElements because you’ll be including them in an animation storyboard.

Next, set the Source properties for each MediaElement to point to a couple of video files. It doesn’t matter what you use, but I picked a couple that were around 20 seconds long for convenience sake. Whichever videos you pick, be sure to include them in the project and set them to COPY IF NEWER so that they will end up in your output folder, where the app can find them when it loads.

image

Set v1up = true to indicate media1 is the “current” video playing and start it up.

How Long’s That Video?

The whole point of this exercise is to fade one video into another near the end of the first video.

That means several things:

  1. You have to know how long the first video is.
  2. You have to know how long we want the fade to last.
  3. With that, you can determine at what point to start fading the current video out, but at the same time start playing and fading the other video in.

The duration you can get with the MediaElement.NaturalDuration.TimeSpan property. However, you can’t actually read that property till the video is loaded.

As a result, you can’t setup the animation until the MediaOpened event has fired:

Private Sub media_MediaOpened(sender As Object, e As System.Windows.RoutedEventArgs) Handles media1.MediaOpened, media2.MediaOpened
    '---- once the media has opened, setup storyboard
    SetupStoryboard(DirectCast(sender, MediaElement))
End Sub

Notice that I’m handling the MediaOpened event for BOTH of the MediaElements here.

Also, for the MediaOpened event to fire, the media must be closed when it’s complete, so we do that in the MediaEnded event:

Private Sub media_MediaEnded(sender As Object, e As System.Windows.RoutedEventArgs) Handles media1.MediaEnded, media2.MediaEnded
    With DirectCast(sender, MediaElement)
        .Close()
    End With
End Sub

Starting that Second Video

At some point, near the end of playing the first video, you need to begin playing the second video. There are a number of ways you could do this. For this demo, I chose a simple DispatcherTimer:

''' <summary>
''' When this timer fires, we're at the fade out of the visible video
''' so load the "other" media element and start it
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub _tmr_Tick(sender As Object, e As System.EventArgs) Handles _tmr.Tick
    _tmr.Stop()

    With If(v1up, media2, media1)
        v1up = Not v1up
        .Play()
    End With
End Sub

Notice that it just determines which mediaelement is the “current playing” element, and switches to the other mediaelement to begin playing it.

Don’t worry about setting up this timer now. You’ll do that in a bit.

The Fade Effect Animation

You’ll notice that the MediaOpened event contains a call to SetupStoryboard, so you need to define that function now.

A StoryBoard in WPF simply describes a sequence of animation steps that will be followed, given a timeline. The full possibilities and features of storyboards and WPF animation are far beyond what I can go into effectively here, but I’ll concentrate on what’s necessary for this effect.

First, the full procedure:

Private Sub SetupStoryboard(Media As MediaElement)
    '---- setup the storyboard object
    Dim StoryBrd = New Storyboard()

    Dim OpacityAnim = New DoubleAnimationUsingKeyFrames
    OpacityAnim.BeginTime = New TimeSpan(0)
    OpacityAnim.Duration = Media.NaturalDuration.TimeSpan
    Dim kf = New SplineDoubleKeyFrame               'keyframe to fade in
    kf.KeyTime = KeyTime.FromPercent(0)
    kf.Value = 0
    OpacityAnim.KeyFrames.Add(kf)
    kf = New SplineDoubleKeyFrame                   'Keyframe at max opacity
    kf.KeyTime = KeyTime.FromPercent(0.2)
    kf.Value = 1
    OpacityAnim.KeyFrames.Add(kf)
    kf = New SplineDoubleKeyFrame
    kf.KeyTime = KeyTime.FromPercent(0.8)           'keyframe to begin fade out
    kf.Value = 1
    OpacityAnim.KeyFrames.Add(kf)
    kf = New SplineDoubleKeyFrame
    kf.KeyTime = KeyTime.FromPercent(1)             'keyframe to be completely faded out
    kf.Value = 0
    OpacityAnim.KeyFrames.Add(kf)

    Storyboard.SetTargetName(OpacityAnim, Media.Name)
    Storyboard.SetTargetProperty(OpacityAnim, New PropertyPath(System.Windows.Controls.Image.OpacityProperty))

    '---- connect it to the storyboard as well
    StoryBrd.Children.Add(OpacityAnim)
    StoryBrd.Begin(Me)

    If v1up Then
        _StoryBrd1 = StoryBrd
    Else
        _StoryBrd2 = StoryBrd
    End If

    '---- set the trigger timer to 80% of duration from now
    _tmr = New DispatcherTimer()
    _tmr.Interval = New TimeSpan(0, 0, 0, 0, Media.NaturalDuration.TimeSpan.TotalMilliseconds * 0.8)
    _tmr.Start()
End Sub

First, create a new StoryBoard object.

Then, create a DoubleAnimationUsingKeyFrames. This is because:

You’ll be animating Opacity, which is a continuous value (as opposed to something like Visibility, which has discreet values). You want the Opacity value to vary smoothly from one point to some other point in the animation sequence. These points are called KeyFrames.

Since the animation will begin immediately, set the BeginTime to 0.

The overall duration of the animation will be the length of the video itself, so use the NaturalDuration property to retrieve that now.

The next, and probably most confusing, part is to define the KeyFrames. To visualize this, imagine that each KeyFrame object represents one distinct point in time. The animation, then, is responsible for changing whatever property you’ll be animating (in this case Opacity), smoothly from one value to another value, between one KeyFrame and the next.

So, define the first KeyFrame as:

Dim kf = New SplineDoubleKeyFrame               'keyframe to fade in
kf.KeyTime = KeyTime.FromPercent(0)
kf.Value = 0
OpacityAnim.KeyFrames.Add(kf)

and the second as:

kf = New SplineDoubleKeyFrame                   'Keyframe at max opacity
kf.KeyTime = KeyTime.FromPercent(0.2)
kf.Value = 1
OpacityAnim.KeyFrames.Add(kf)

And you can see how this means you’ll be changing the Opacity property from a Value of 0 (transparent), to a Value of 1 (opaque), during a timespan starting at the beginning of the video and lasting for 20% (the .2) of the video’s duration.

The other KeyFrames are defined in the same way.

Now, the animation is defined, but you haven’t told WPF what we’re animating, so do that now:

Storyboard.SetTargetName(OpacityAnim, Media.Name)
Storyboard.SetTargetProperty(OpacityAnim, New PropertyPath(System.Windows.Controls.Image.OpacityProperty))

These lines tell WPF that you’ll be applying this animation to a specific MediaElement, and that the animation itself will be effecting the Opacity Property.

StoryBoards can actually contain any number of animations. In our case, there’s only one though. So you need to add the animation to the StoryBoard and then start it running:

StoryBrd.Children.Add(OpacityAnim)
StoryBrd.Begin(Me)

Next, store the StoryBoard you just created in one or the other regional variables (this is because you need to keep the StoryBoard around long enough to allow it to run it’s course).

And finally, since you have the Video duration available at this point, you need to set up the timer to kick off the other video at the 80% mark in this video.

_tmr = New DispatcherTimer()
_tmr.Interval = New TimeSpan(0, 0, 0, 0, Media.NaturalDuration.TimeSpan.TotalMilliseconds * 0.8)
_tmr.Start()

Wrapup

With all that in place, you should be able to run and see one video smoothly fade in, play, then start to fade out as the second video begins to fade in.

Note that as with anything heavily graphical, you may need a fairly powerful PC and graphics card for the fades to be smooth. However, WPF is based on DirectX which is surprisingly capable even on moderate hardware.

What Next?

First, you may want to also animate the Volume property in a similar manner, to fade it in and out along with the video.

Since this is WPF, you can easily apply transforms to squish and fold the video, as well as all sorts of other forms of transitions (fade is just one example).

What can you come up with?

Remote Workers Are Actually More Engaged

0
Filed under Rants, Telecommuting

Interesting article by Scott Edinger over on LifeHacker.

He lists several reasons for this apparent contradiction, and I’ve seen first hand examples of the good and bad in my last 12+ years of on and off remote work.

One element Scott doesn’t touch on is how different remote work experiences can be. I’ve found that often, companies want to support remote workers, but, since they’ve never really done so before, they either can’t or won’t make the mindset changes that come with doing so successfully.

For me, the most successful remote work scenario is when all or virtually all the people involved are working remotely. I call it the Sailor Paradigm. When you’re all in the same boat, you’ll all tend to gravitate to the same mindset and look for ways to make the situation work for the best. And when everyone’s remote, everyone realizes quickly that true communication takes a little extra effort.

On the other hand, when only one or a few people are working remote, everyone that’s stayed in the office can fall back on much easier communication; the water cooler chat, talking shop over lunch, etc. The remote workers get left out and that can often lead to troubles down the road.

Personally, I’ve always felt the ideal solution is one where all the employees (or at least everyone on a team) works remote, but close enough to an office space or to each other to get some face time one a week or so. That’s not always possible, but it does represent the best of both worlds.

Another Contract Down…

0
Filed under Uncategorized

I’ve read that one of the marks of a good programmer is that they’re able to code themselves out of a job. I’m not sure about that, but, I did finish up this contract earlier than expected, so once again, it’s time to get out there and find a new gig.

If you or your company has any needs for an experienced VB guy (VB.net, VB6, or even farther back, but seriously, anyone still use VB3?), .net guy (I can do C#, despite the blog title<g>), SQL guy (I’m particularly proficient in MS-SQL, stored procs, DB design, normalization, index tuning, etc), or even Javascript/jQuery/Knockout/jQueryMobile, etc, give me shout.

Reply here or use the Contact Link at the top to send me a private message.

SED and AWK for the VB Guy

0
Filed under Utilities

Generally speaking, when I need to do a little file manipulation, I usually fire up .net, whip up a little VB.net command line app to do the trick and off I go.

However, a few nights ago, I needed to do some manipulation on a largish (30+meg) xml file. The manipulation itself was fairly simple:

  1. Find a tag in the file
  2. Insert the contents of other files into the target file, right before the tag

However, it was late, and I was feeling a bit lazy, so I googled it.

What I got was almost all the first page results pointing me to SED or AWK.

What’s that?

SED is short of Stream EDitor. Essentially, it’s an app for running a text file through a set of regular expressions and outputting the results.

AWK is short for Aho, Weinberger and Kernighan, the names of the three programmers who originally came up with it. It’s actually a language for processing text. But, any more, it generally refers to the command line application to applying that language to a input file and generating output from it.

Not big on UNIX

Now, I’ve been around long enough to know what SED and AWK are, but I’ve really never actually used them. However, with all these search results pointing that direction, I had to poke around a little more.

You can grab a version of SED for Windows here:

http://gnuwin32.sourceforge.net/packages/sed.htm

And AWK (or GAWK, the gnu version of AWK, get it<g> ) here:

http://gnuwin32.sourceforge.net/packages/gawk.htm

Those pages have tons of excellent resources, as well as examples, all the docs you’d ever want to read, etc.

And these two apps have been around for so long, that, well, a quick Google search will turn up an example of just about anything you’d need to do with them, so I’m not going to muddy up search results any more than to say that they are really handy tools, especially if you know a little bit about regular expressions.

A Windows Observation

However, I would point out one fairly minor nit that I ran into, at least with the above two ports that I tried.

Both work just fine, but SED I found a tad more troublesome to install. The main problem was that it relies on several external DLLs. You can see these dependencies using DependencyWalker:

image

These files need to be in the same folder as the SED.EXE, and they’re all available at the above link. I guess my feeling is that for such a singular tool, these kinds of dependencies should be compiled in. At one point, many many moons ago, it made at least a little sense to reduce your app diskspace requirements by relying on shared dlls and such. But these days, no one cares if an app like this is 150k vs 500k with all the dependencies compiled in.

AWK (or GAWK), on the other hand, has NO dependencies. None. I copied it to my TOOLS folder, which is on my PATH, and viola! Worked right off. Truly an 0-hassle installation.

They both work very similarly, though SED relies mostly on regular expressions, whereas AWK certainly can be used in conjunction with only regular expressions, but also has the full AWK language behind it to boot.

Speed

One note about speed. There’s nothing to note!

Both of these apps were so fast, even against a 33mb input file, that I didn’t even notice they took any time at all. Running them against this file took about the same time as to actually copy the file.

Granted, my needs were simple, and I’m sure more complex expressions would slow things down. But still. That was refreshing.

And that thing I needed it for?

Removing a singular tag from a large XML file automatically:

awk "!/<\/tag\x3E>/" File1.xml >output.xml

Most of the weird look is from:

  • Having to escape the “/” with a “\”
  • Can’t use a “>” in a batch file command line, because it’s interpreted as a “pipe into an output file” command, which I’ve done at the end of the command with “>output.xml”, so I have to escape it as “\x3E”

I suspect I’ll be using it considerably more in my future!

Gotta Love Texas!

0
Filed under Rants

A neighbor down the street picked up a tank a few months back. He’s always driving it around (it’s completely street legal, tagged, plates, etc).

But you almost have to have a spotter riding up top to help tell the driver whether there’s anything in the way or not. That thing’s got some monster blindspots <g>

NeighborsTank

For those inclined, it’s a Swiss Armored Vehicle (I forget the exact name) from the 60’s I believe, but don’t quote me. I know much more about VB than militaria. It’s almost completely restored (yes, I believe even the gun works assuming you can find (or afford) ammo for it.

Not exactly a daily driver, but it’s awesome to see that thing rolling down a suburban street.

BookmarkSave Addin for VB6

76
Filed under VB6

EDIT: I’ve finally gotten around to publishing the source for this addin on CodePlex.

https://bookmarksavevb6.codeplex.com

EDIT 7/25/2012: I’ve just updated the addin (see the ZIP download below) to correct a problem that can occur if you have several projects loaded in a group, and have files of the same name loaded in each project, AND you set bookmarks or breakpoints in those files. In that case, the breakpoints/bookmarks can end up being restored to the wrong file when you load the group of projects. This won’t cause any problems in your code, but your bookmarks and breakpoints won’t be restored properly, and you might receive messages saying that “a breakpoint can’t be set on that line”, or something similar.  Just download the latest version, unzip it and copy the dll over your existing dll.

If first blogged about this addin back here.

Essentially, the idea is to solve a long standing pet-peeve of mine with the VB6 IDE; the fact that it doesn’t save your bookmark or breakpoint locations from one run to the next.

Ugh!

That functionality wouldn’t have taken more than about 30 minutes for someone to implement on the VB team, but, alas, no one did, and I’ve spent far more time than I should have manually restoring breakpoints ever since.

If, like me, you’ve worked in VB.net for any amount of time, and you now find yourself, from time to time, having to load up your trusty copy of VB6 to do some maintenance work, you, almost certainly also like me, sorely lament that missing functionality.

But no more!

After quite a bit of teeth-gnashing, along with some very helpful testing comments from Sam (Thank You!), I think it’s about time to correct that long-suffered oversight!

Download the Addin here:

BookmarkSave Addin ZIP

The Zip file contains one DLL. Just extract it where-ever you want, and run REGSVR32 on it (just like any other COM dll).

NOTE: You will have to have the .net framework 4.0 installed, as this addin is compiled in .net against that version of the framework. Why, you may ask? Well, frankly, VS2010 is much nicer to code in than VB6, and I was actually quite curious whether a VB6 Addin that performed a real, useful, function could be written in VB.net and act, more or less, just like an addin that had been written in VB6. Personally, with this, I think the answer is a pretty deafening “Oh Yeah!”…

DISCLAIMER: As with anything else around these parts, your mileage may vary (the normal “It works on my machine”). I’ve tested it in conjunction with a few of the addins I use (CodeSmart and MZTools, as well as the Mouse Wheel fix, and resource editor) and have had only one minor issue. If CodeSmart is loaded  as well as BookmarkSave, CodeSmart causes the VB6 IDE to crash when you unload it, but only if you ran your application in the VB6 IDE. If this scares you, don’t download or install this addin. I worked around a number of CodeSmart “peculiarities” concerning the way they happen to subclass VB IDE code windows, but so far, the source of this problem eludes me. The good news is that other than the Crash dialog, there doesn’t appear to be any other anomalies (your project saves fine, runs, etc). My suspicion is that during the Addin UNLOAD phase of the VB6 shutdown process, a pointer isn’t being released quite right.

Using the Addin

Well, it can’t get much simpler. Once you’ve registered it, load up VB6 and check the Addins Manager to make sure it’s loaded.

If it is, load up a project of your choice, set a few breakpoints and bookmarks, and close the project.

Reopen it in VB6 and you should see all your breakpoints and bookmarks wondrously reset just like they were before!

There aren’t really any settings or options to speak up.

Bookmarks are saved into an XML file called {projectname}.BM in the same folder as your project’s VBP file.

Breakpoints are saved into a {projectname}.BP file in the same place.

Naturally, you should probably not check those files into version control.

New Hotkeys

Sadly, VB6 doesn’t provide many convenient ways of navigating bookmarks and breakpoints, so this addin adds a few.

Ctrl-K – Toggle a bookmark at the current cursor location

Alt-Left or Right – Navigate to the next or previous bookmark

Alt-Up or Down – Navigate to the next or previous breakpoint

These aren’t configurable at this time.

A few interesting bits

None of this is necessary to actually use the Addin, but I’ll mention it for the curious:

First, I make use of my DLLExport utility to actually export the DLLRegisterServer and DLLUnregisterServer functions, so that the DLL can self register with COM, just like a VB6-authored DLL can. With all the other great COM support in .net, this is a really glaring omission. I wrote about that technique here.

Next, I use my “GenerateLineMap” utility to strip line number info from the PDB file and embed it into the DLL. This information is then used during error handling to annotate any stack trace with real source code line numbers, without actually including the PDB as a separate file. This is a technique I’ve worked on for quite some time, and talked about here, here, and here. Still need to get that article written up.

iGoogle Alternative

3
Filed under Rants, Web

I’ve used iGoogle for ages as a start page, as have many people, so Google’s announcement that they’ll be killing it off late in 2013 came as a bit of a shock.

Ok. No big deal. Surely, there are any number of “Home page” sites out there that can do essentially what iGoogle did.

Well, yes and no.

My first stop was PageFlakes. I’d messed with it several years back, when it was first introduced. But alas, PageFlakes has flaked out and is no more.

MSN has a “home page” type service, but I wasn’t terribly impressed.

Yahoo too. Just really cluttered. I liked iGoogle for it’s simplicity, among other things.

This Might Actually Work

Then I came across a mention of a site called NetVibes. Reading up on it, it looks like it’s more geared toward sys admins that need to keep an eye on statistics about a range of websites, all from a single dashboard. Great stuff, but that aspect costs a few bucks a month.

However, you can sign up for a free account, and set up all sorts of news widgets, RSS feeds, etc, that can populate your home page, just like on iGoogle. It’s got lots of great “ajaxy” stuff like drag and drop positioning, nice configuration GUI’s, a huge theme selection, etc. With the right theme (I chose “World”), it looks almost identical to the clean lines of iGoogle (though it does have an oddly out-of-place top action bar thing going on. Still, not a dealbreaker).

And, I was able to find widgets (or grab RSS feeds) for every single element I had on my iGoogle page.

Plus, it’s really trivial to drop new RSS feeds in place (I do that all the time to watch for specific things for sale on CraigsList).

All in all, a great replacement for iGoogle.

Check it out!