PrevInst

Description

One of the most frequently asked questions online goes along the lines of:

Easy! Lookup the Command$() function in the help files that came with VB. Not long after doing this, many folks figure out that they can rather simply associate a given file extension with their application, by making just a few registry entries. When files with that extension are subsequently double-clicked, Windows passes their name(s) to the associated application. So the inevitable follow-up question becomes:

This is where the problem first becomes obvious. Windows fires off the requests one by one, starting a new instance of the application for each file to be opened. Somehow, you need to send the second and subsequent filenames over the first instance of your application, then quit the extraneous instance.

Almost a decade ago, I wrote up a method (see below) that involves subclassing and using the WM_COPYDATA message to pass data between instances of an application. This method still works, of course, and is especially useful in situations where an ongoing "conversation" needs to occur, or you don't have particular file extensions associated with your application. But WM_COPYDATA is extremely "heavy" for the most common, simple cases.

If you have associated one or more file extensions with your application, Windows offers a native method to accomplish your goal. While many view DDE as a relic that's outlived its usefulness (indeed, VS.NET doesn't even support it!), this is exactly the mechanism Windows uses to pass instructions to applications when their self-defined action verbs (open, print, etc.) are selected within the file system.

Defining Association Verbs

The easiest way to associate an extension, and define corresponding verbs, for an application is through use of a REG file. For example:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.xyz]
@="XYZfile"

[HKEY_CLASSES_ROOT\XYZfile]
@="Association Test File (XYZ)"

[HKEY_CLASSES_ROOT\XYZfile\Shell]

[HKEY_CLASSES_ROOT\XYZfile\Shell\Open]
@="&Open"

[HKEY_CLASSES_ROOT\XYZfile\Shell\Open\command]
@="\"C:\\Code\\Samples\\PrevInst\\DDE\\PrevInstDDE.exe\" \"%1\""

[HKEY_CLASSES_ROOT\XYZfile\Shell\Open\ddeexec]
@="[OPEN(%1)]"

[HKEY_CLASSES_ROOT\XYZfile\Shell\Open\ddeexec\application]
@="PrevInstDDE"

[HKEY_CLASSES_ROOT\XYZfile\Shell\Open\ddeexec\topic]
@="system"

There's lots of documentation out there what each of these entries means, so I'll just touch on a few in the specific context of this sample. Of course, the above entries could (arguably should) be made with API registry calls, rather than relying on a user to Merge a REG file.

ddeexec
This is the actual command that's passed to your application, where %1 is replaced with the filename the user had selected. You can use multiple commands, each enclosed within square brackets, to cause a specific sequence to be sent.
ddeexec\application
The basename for your application - the part that comes before the ".exe". If you use the same name as your project, Windows will initiate the DDE conversation with instances running under the IDE as well.
ddeexec\topic
Corresponds to what VB calls a LinkTopic. You must assign this same value to a form or control that's always available while your application is running.

Listening For Your Cue

Once all the proper registry entries have been made, setting up your application to work together with Windows really couldn't be simpler. It really comes down to just these few lines of code in your main form:

Color-coded with vbMarkUp - try it today!
Private Sub Form_LinkExecute(CmdStr As String, Cancel As Integer)
   Debug.Print CmdStr
End Sub

Private Sub Form_Load()
   ' LinkMode must be set at design time!
   'Me.LinkMode = 1 - Source

   ' LinkTopic must match that stored in registry!
   Me.LinkTopic = "system"   ' (Arbitrary)

   ' Just for kicks!
   If Len(Command$) Then
      MsgBox Command$, vbInformation, "Command$()"
   End If
End Sub

You'll find that, when the user double-clicks one of your data files, you no longer get anything directly on the command line if an instance of your application is already running. If the user selects multiple data files, and chooses one of your defined verbs, you will only get one file on the command line, while both this initial and all subsequent files will be passed via DDE.

Download this sample below, and play around a bit. It's so simple, the patterns should become immediately apparent.

Published

This sample, or the one from which it originally derived, was published (or at least peripherally mentioned) in the following article(s):

APIs Usage

This sample uses the following API calls:

Module Library Function
FPrevInst.frm kernel32


user32



lstrlen
lstrlen
RtlMoveMemory
FindWindow
IsIconic
SendMessage
SetForegroundWindow
ShowWindow
MPrevInst.bas kernel32

user32







lstrlen
RtlMoveMemory
CallWindowProc
FindWindow
GetWindowLong
IsIconic
SendMessage
SetForegroundWindow
SetProp
SetWindowLong
ShowWindow

Don't see what you're looking for? Here's a complete API cross-reference.

Download

Download PrevInst.zip   Please, enjoy and learn from this sample. Include its code within your own projects, if you wish. But, in order to insure only the most recent code is available to all, I ask that you don't share the sample by any form of mass distribution.

Download PrevInst.zip, 52Kb, Last Updated: Tuesday, February 21, 2006

See Also

The following resources may also be of interest: