Background/Overview
This article takes the original programming idea from the Wave File Editor Control and
extends the application’s functionality into a client/server application set.
The WaveEdit app can now be used as a stand-alone program or as a server, controlled by
client applications written in VC++ or VB.
Making the transition from stand-alone application to client/server has become easier
due to MFC’s built-in automation support (faster/easier development at the expense of
increasingly bloated code).
If you look at the original Wave Editor Control code, the functionality for graphing is
encapsulated within a CWnd derived object – all very nice and modular and easy to move
around, but within the context of MFC automation, maybe too unnecessarily complicated.
So to simplify, I took the functionality out of the WaveEditCtrl class and put it in the
server’s OnPaint handler.
IDispatch, Type Libraries and Stuff
To enable communication between applications (Automation), COM provides the IDispatch
interface. IDispatch permits COM objects to expose functions, data and events to the
outside world, allowing other applications to control these programmable objects. Although
the IDispatch interface can be queried at runtime so that a client can know which data and
functions are supported by the object, this requires creation of an instance of the object
and may not be what we want in terms of flexibility. This is where the Type Library comes
in and acts as a sort of #import library for COM objects.
Using type libraries within our automated applications provides us with information about
the interfaces supported and the methods that each interface supports – regardless of
whether our COM object has been created or not.
Basic Recipe
I found it easiest to create the code by following a cookbook recipe, starting from
scratch – two individual AppWizard generated SDI applications with automation support
added. With initial automation support you get an automation skeleton consisting of
initialization code for the application’s InitInstance() function, a dispatch map within
the document class and a type library. Adapting the WaveEditCtrl code into the server
proved not too difficult – basically, moving the initial caret positioning into the view’s
OnInitialUpdate().
Programming Flow
Initialization – The server application exposes its functionality to
the outside world through its COM enabled IWaveEdit interface in the form of various
methods that are called from the client application.
When the client launches, it creates an object of the IWaveEdit interface, looks for it’s
CLSID in the registry and creates the IDispatch object. This gives the client access to
the server’s methods providing the ability to launch the server with the client.
Because the default nature of an automation server is to be invisible – we need to call
three methods when launching the client, ShowApp(), ShowWindow() and SetCursor(). These
methods allow the server to launch in an identical manner to it’s stand-alone counterpart.
These three methods respectively allow the server’s main window and view to become visible
as well as placing the blinking cursor in it’s initial position within the client area.
Interaction – When the client Play button is clicked, it’s timer
starts the stopwatch in the client’s StatusBar and calls the server method – SetPlay()
which in turn starts it’s own timer and sends the cursor on it’s merry way.
Similar server methods are called for the client’s Pause and Stop buttons.
So when does the client’s stopwatch know when the server has finished playing
it’s wave file?
The IsWaveEnd() method of course! – The client continually calls this method when it’s
timer is running to check the status.If the caret reaches the full width of the server’s
client area it sets the IWaveEdit boolean property – result. Once result returns true, the
client timer simply kills itself.
void CWaveEditClientView::OnTimer(UINT nIDEvent) { ... m_waveEditObject.IsWaveEnd(); if (m_waveEditObject.result){ KillTimer(1); m_bPlaying = FALSE; } ... }
Definitly not the most complex COM-based app around, but should be of interest if you
wish to automate some of your own code ideas.
BTW, sorry about the On/Off buttons – they might indicate to you that a COM object is
being created through a class factory, or an IUnknown interface pointer is being released
– I assure you that they simply show and hide the server application!
The screen redraw of the server app is annoying when clicking the client play button. But
remember, in this simplified code we just draw random lines – in an audio application you
would reload the same samples from the same wave file.