One of the most frequently asked questions online goes along the lines of:
- How do I pass information (filenames) to my program?
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:
- How can I make sure all the files are opened by the same instance?
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:
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.