Sync to a CD by i6uzYPAt


									Using MCI to Synchronize a Windows-Based Application
with CD Audio
Herman Rodent
Microsoft Developer Network Technology Group

Created: February 10, 1993

Click to open or copy the files for the four sample applications in SINGACD.

This technical article describes a simple technique that uses the Media Control Interface (MCI)
to synchronize events in a Microsoft® Windows™-based application with audio from a CD. The
following topics are covered:
•   MCI message interface
•   Audio CD time formats
•   Owner-draw graphical buttons
•   Extending the idea to other time sources

Four pieces of sample code included with this article create four applications: PLAYACD, a
simple CD controller; SINGACD, a sing-along application that shows the lyrics to a CD as it
plays; TIMEACD, an authoring tool to create the lyric files used by the sing-along application;
and SINGAWAV, an application that demonstrates synchronizing to a waveform.

The Media Control Interface (MCI) can control a number of different types of devices. MCI is
included in Multimedia Extensions to Windows, which comes either as a separate product from
Windows version 3.0 or is included in Microsoft® Windows™ version 3.1. This article looks at
how MCI can be used to synchronize events in a Windows-based application to music playing
from an audio CD. The techniques presented here can also be used to synchronize an
application to any other continuous stream of data that has an MCI driver, such as a waveform,
MIDI, or animation sequence. An example of synchronizing to a waveform is included. If you are
unfamiliar with the Media Control Interface, please refer to the Microsoft Windows version 3.1
Software Development Kit (SDK) Multimedia Programmer's Guide, Chapter 2, "The Media
Control Interface (MCI)," on the Developer Network CD (Product Documentation).

The Media Control Interface

46f53fca-4bff-4f84-abc5-13063b752633.doc        2/8/2012         1
MCI provides a convenient and consistent way of controlling a variety of different device types.
MCI has two major interfaces, a string interface and a message-based interface. The string
interface is the simpler and more direct of the two; you use it by calling the mciSendString
function. The message-based interface is more flexible; you use it by calling the
mciSendCommand function. The string interface is most useful when a device needs to be
controlled from a high-level language like Microsoft Visual Basic™. The message interface,
however, enables simpler management of error return values and so on. The sample code that
accompanies this article uses the message interface. Because the techniques used here are
supported equally well by both the string and message interfaces to MCI, these samples could
have been implemented with the string interface just as well as with the message interface.

What mciSendCommand Does
The mciSendCommand function is an almost direct call to an MCI device driver. The function
takes four arguments: a device ID, the message type, and two optional 32-bit values. The
device ID is used to determine which MCI driver the call is to be directed to. The message type
and 32-bit parameters form the arguments of a message that is then sent to the MCI driver.
This interface has excellent performance and can be expanded to cater to a wide variety of
driver types.

MCI Commands and Data Formats
Many MCI commands are generic to all MCI device types, making it very easy to apply code
that controls one type of device to another. The example presented in this article of extracting
timing information from an audio CD can equally well be used with different audio sources, such
as waveforms or MIDI sequences. Furthermore, it can be used with any data type that has
timing information, such as an animation or a video segment.

Having pointed out the flexibility of MCI, I should also note that most of the examples here use a
timing format that is specific to audio CDs—track, minutes, seconds, frames (TMSF). Most
other timing sources don't have tracks. The section "MCI Time Formats" later in this article
discusses the time formats possible for a variety of devices. It's possible to port the sample
code to a different time format without much trouble. An example of using milliseconds as the
time format is included in the SINGAWAV sample. I chose the TMSF format for the CD
examples for two reasons:
•   The track number information is very helpful when dealing with an audio CD.
•   Requesting the time information in this format allowed me to avoid writing code to extract
    the start time and length of each track and do the conversions between TMSF and some
    other format.

Opening and Closing MCI Devices
MCI devices must be opened before they can be used and closed when they are no longer
required. Opening a device is a simple matter, as the following code illustrates:

      OpenParams.lpstrDeviceType = "cdaudio";
      OpenParams.wDeviceID = 0;
      dwResult = mciSendCommand(0,

46f53fca-4bff-4f84-abc5-13063b752633.doc        2/8/2012        2
                                            MCI_WAIT | MCI_OPEN_SHAREABLE
                                             | MCI_OPEN_TYPE,

In this case, the device is opened with the shareable option bit set, which allows other
applications to open and control the same device. This makes sense in the accompanying
sample applications because one of them controls the CD operation and another simply
interrogates its position—there is no conflict of interest. The open request will fail if the device
doesn't exist, doesn't have a driver, or is already in use and is not shareable.

The device is closed like this:

        dwResult = mciSendCommand(OpenParams.wDeviceID,

MCI Time Formats
MCI supports a variety of different time formats. Some formats are generic to groups of MCI
device types and some are specific to a single device type or a small group of device types.
Table 1 lists the currently defined time formats supported by MCI.

Table 1. MCI Time Formats

Format tag                               Description

MCI_FORMAT_BYTES                         Bytes

MCI_FORMAT_FRAMES                        Frames

MCI_FORMAT_HMS                           Hours, Minutes, Seconds

MCI_FORMAT_MILLISECONDS                  Milliseconds

MCI_FORMAT_MSF                           Minutes, Seconds, Frames

MCI_FORMAT_SAMPLES                       Samples

MCI_FORMAT_TMSF                          Tracks, Minutes, Seconds, Frames

Microsoft Windows version 3.1 Device Driver Kit (DDK) Multimedia Device Adaptation Guide
(Chapter 3, "MCI Device Drivers") suggests that all devices should support
MCI_FORMAT_MILLISECONDS in addition to any format which is conventionally used with that
type of device. In practice, you should find that all devices support the milliseconds format if
they have any concept of time. Table 2 lists the default format for each currently recognized set
of MCI device types.

Table 2. MCI Default Device Formats

46f53fca-4bff-4f84-abc5-13063b752633.doc          2/8/2012         3
Device type                Default format

CDAudio                    MSF

MMMovie                    Frames

Overlay                    (not applicable)

Sequencer                  Specified by the MIDI file element

Videodisk                  HMS for CLV disks, frames for CAV disks

WaveAudio                  Milliseconds

A number of macros are defined in MMSYSTEM.H; these macros allow packed formats such
as TMSF to be unpacked into their components parts. Other macros create the packed forms
from the component parts. For example, MCI_TMSF_TRACK extracts the track component
from a TMSF item, and MCI_MAKE_TMSF builds a TMSF item from the track, minutes,
seconds, and frames components. No support is provided for converting between different time

MCI accepts and returns time information in a format that is set by code. For example, this code
sets the format to TMSF:

      set.dwTimeFormat = MCI_FORMAT_TMSF;
      dwResult = mciSendCommand(OpenParams.wDeviceID,
                                MCI_WAIT | MCI_SET_TIME_FORMAT,
      if (dwResult != 0) {
          return FALSE;

Reading Track Information
If you wanted to convert your application to work completely in milliseconds rather than TMSF,
you could obtain data on the number of tracks, start times, and track lengths by using calls to
mciSendCommand. Chapter 4, "High-Level Audio Services," of the Multimedia Programmer's
Guide in the Windows version 3.1 SDK has a complete example of how track start times and
lengths can be obtained. The code gets the number of tracks on the CD with a call like this:

     mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
     iNumTracks = (int) mciStatusParms.dwReturn;

Then for each track, it gets the start position and length (in the currently selected time format)
with a pair of calls:

46f53fca-4bff-4f84-abc5-13063b752633.doc         2/8/2012         4
     mciStatusParms.dwItem = MCI_STATUS_POSITION;
     mciStatusParms.dwTrack = dwTrack;
                    MCI_STATUS_ITEM | MCI_TRACK,
     dwPosition = mciStatusParms.dwReturn;
     mciStatusParms.dwItem = MCI_STATUS_LENGTH;
                    MCI_STATUS_ITEM | MCI_TRACK,
     dwLength = mciStatusParms.dwReturn;

Note that no error checking is shown in these code fragments.

Getting in Sync
Synchronization is an interesting topic. If you ask most people what it means, they will most
likely tell you that it describes a situation where one event is set up to occur at the same time
some other event occurs. No two things can occur at the same time, however, if one is
responding to the other because there is always a finite response time—the triggered event will
always take some time to detect the occurrence of the triggering event. What's probably most
important is keeping the response time down to an acceptable level. Having got that out of the
way, let's look at how we might have one event triggered by another so as to provide a
reasonable illusion of synchronization.

The SINGACD sample application shows a moving ball above the lyrics of a song. The ball
moves in time with the music and (ideally) stays above the current word in the song. Achieving
this apparent synchronization of the ball with the lyrics is a two-part process. The first part of the
process involves displaying the entire line of text at the right time. The second part concerns
placing the ball over the correct word in the line.

When I first created SINGACD, I used a similar technique to achieve both of these
requirements. I designed a format for the lyric files that would provide information on the start
time of each line and also on the time that the ball should move over each word within the line. It
soon became apparent that, although this made for a simple and accurate player, authoring the
lyric files would be quite a problem and not one I wanted to cope with. As a result, the final
sample application design uses a combination of exact timing information and prediction.

Response Time
In order to keep the response time short and maintain a good illusion of synchronization, we
must be sure to check the CD position frequently. There are two basic ways of doing this: The
application can either perform this task in the idle time of its message processing loop, or it can
set a timer for the rate at which it would like to poll the device.

Using the message loop in an application to do background processing can cause serious
performance problems for other applications. This method does give a very tight response time
however. Using timers means a somewhat slower response time, but in the case of the lyric
player, it's good enough. The SINGACD application uses a timer running with an interval of 300

46f53fca-4bff-4f84-abc5-13063b752633.doc          2/8/2012         5
ms. This probably isn't often enough to give a really good appearance if the words were
individually timed but since the ball position is somewhat vague as it moves along, 300 ms
between movements gives a good illusion of continuous movement and provides reasonable
synchronization with the music. Here's the code used to start the update timer:

      uiTimerId = SetTimer(hwndMain, UPDATETIMER, 300, NULL);

Obviously, in the nonpreemptive world of Windows version 3.1, using the timer method only
works at all if all other applications cooperate by not hogging the CPU.

Timing the Lines
Each line in a lyric file is accompanied by a piece of information that denotes the time that line
should appear. The player monitors the CD position every time it gets a WM_TIMER message
and calls the Update routine. Update finds the text line that begins before that time and ends
after it. It obtains the end time by looking at the start time of the next line. The current CD
position is obtained by this code, which has had the comments removed:

     MCI_STATUS_PARMS status;

     status.dwItem = MCI_STATUS_POSITION;
     if (mciSendCommand(OpenParams.wDeviceID,
                       MCI_WAIT | MCI_STATUS_ITEM,
                       (DWORD)(LPVOID)&status) == 0) {
        dwPosition = status.dwReturn;

The MCI_STATUS command can be used for different things. The dwItem field of the STATUS
structure is initially set to indicate which status item is required. In this case, the position of the
CD is to be returned, so it is set to MCI_STATUS_POSITION. The MCI_STATUS message is
sent to the device using a call to mciSendCommand. If the function is successful, the return
value is extracted from the status structure. Note that the format of the returned position is
determined by an earlier call to set the time format.

Once the correct line has been located, it is formatted and displayed in the client area like this:

               (lpCurrentLine && lpCurrentLine->lpText)
                  ? lpCurrentLine->lpText
                  : (LPSTR)"(no lyrics)");
                 2, uiButtonHeight - tmSysFont.tmHeight - 1,

This places the text at the bottom of the rectangle, leaving room above the text for the ball.

46f53fca-4bff-4f84-abc5-13063b752633.doc          2/8/2012          6
Computing the Ball Position
As mentioned above, it would be possible to include data in the lyric file about when the ball
should appear over each word. This would make for a very nice display and would mean that
the ball trajectory could be curved so that it looked like it was bouncing. Generating the timing
data for this, however, would be very awkward and involve either a lot of listening to each track
and bashing the keyboard at appropriate times or some fascinatingly complex sound analysis
software. Instead, the ball position is simply set to a position along the line proportional to the
time position within that line. The length of the line (in time) is the difference between the time
the next line starts and the time the current line starts. This actually works out well, providing
there isn't a lot of silence (or what I call "hummy stuff") at the end of the line.

A nice refinement of this plan would be to position the ball over a word according to the current
time and the number of words in the line and have the ball move from word to word rather than
continuously over the line.

Sample Applications
Four sample applications are included. This section gives a brief description of each one.

The PLAYACD application is a very simple controller for the MCI CDAudio device. It has a set of
buttons that control playback as follows:

           This button moves the playback position back to the beginning of the current track
           or back to the beginning of the previous track if the current track position is 0:0.

           This button advances the playback position to the beginning of the next track.

           This button moves the playback position back 10 seconds. If held down, it will move
           back ten seconds every second.

           This button advances the playback position by 10 seconds. If held down, it will
           advance the position by 10 seconds every second.

           This button starts playback.

           This button stops playback.

These buttons are implemented as owner-draw buttons. The images for the buttons' up and
down states are contained in a single bitmap:

The draw code for the buttons selects the bitmap into a memory device context (DC) and uses
the ID of the child window to index along the X axis and select the correct set of images and the

46f53fca-4bff-4f84-abc5-13063b752633.doc         2/8/2012         7
current button state is used to determine the Y axis position. A call is then made to BitBlt to
copy the section of the bitmap in the memory DC to the DC supplied in the

The draw code is also used to start and stop the repeat timer that implements the repeated
seek function. Putting this code in a draw routine is a bit hacky, but there is no other clean and
simple way to do this with owner-draw buttons. The code required to draw the button images is
shown here:

 static void DrawButton(HWND hWnd, LPDRAWITEMSTRUCT lpDI)
     RECT rc;
     HDC hDC, hdcBtn;
     int iXBtn, iYBtn;

      hDC = lpDI->hDC;
      rc = lpDI->rcItem;

      switch (lpDI->itemAction) {

      case ODA_DRAWENTIRE:
      case ODA_SELECT:
      case ODA_FOCUS:

            hdcBtn = CreateCompatibleDC(hDC);
            SelectObject(hdcBtn, hbmButtons);

            // Figure out which button image to use.

            switch (lpDI->CtlID) {
            case IDC_BACK:
                iXBtn = 0;

            case IDC_FORWARD:
                iXBtn = BUTTONWIDTH;

            case IDC_SEEKBACK:
                iXBtn = BUTTONWIDTH * 4;

            case IDC_SEEKFORWARD:
                iXBtn = BUTTONWIDTH * 5;

            case IDC_PLAY:
                iXBtn = BUTTONWIDTH * 3;

            case IDC_STOP:

46f53fca-4bff-4f84-abc5-13063b752633.doc        2/8/2012         8
                 iXBtn = BUTTONWIDTH * 2;

            // Figure out the button's state
            // and enable the repeat timer for down buttons.

            if (lpDI->itemState & ODS_SELECTED) {
                iYBtn = BUTTONHEIGHT;

                // Enable the repeat timer.
                uiRepeatCount = 0;
                uiRepeatTimerId = SetTimer(hwndMain,
                                            REPEATTIMER | lpDI->CtlID,
            } else {
                iYBtn = 0;

                 // Stop the repeat timer.
                 if (uiRepeatTimerId) {
                     KillTimer(hwndMain, uiRepeatTimerId);
                     uiRepeatTimerId = 0;

                   0, 0,
                   iXBtn, iYBtn,



Note that no distinction is made among requests to draw the entire button, to select the state, or
to set the focus. No focus rectangle is supported for these buttons because this is only a mouse

With the exception of the buttons, the remainder of the application is very simple.

Click here to run PLAYACD.

SINGACD: A Bouncing Ball Lyric Displayer

46f53fca-4bff-4f84-abc5-13063b752633.doc        2/8/2012        9
The SINGACD application monitors the current CD position and displays lyrics for the current
track. A small red ball follows the position of the word being sung within the written line of the
song. Because the CD is controlled elsewhere, this application is very simple. It opens the
CDAudio device, requests time in TMSF format, and starts a timer. Each time the timer
message arrives, it checks the CD position, finds the current lyric line, and displays it. The ball
position is computed and the ball displayed appropriately.

In order to make the application as visually uncluttered as possible, the File Open menu item
was added to the System menu:

      hMenu = GetSystemMenu(hwndMain, FALSE);
                 MF_ENABLED | MF_STRING,
                 "Open File...");

The ball flickers as it moves over the text because the ball is effectively being erased and then
redrawn many times. This could be fixed by rendering the text and ball image into an off-screen
DC and then bltting the composite image from the off-screen DC to the window in one go. The
ball would then appear to move without flickering.

The largest defect is that no attempt is made to detect if the next line is within the current track
and, since no information is kept as to the length of each track, it is not possible to predict
where the ball should be in the last line of a song. This deficiency is easily overcome at
authoring time by including an empty line at the end of each track, neatly avoiding the need for
me to do any more work on the code.

Click here to run SINGACD.

TIMEACD: An Authoring Tool for Lyric Files
In order to create the timing information in the lyric files, I had to create some sort of authoring
tool. The result is the TIMEACD application, which, while not being madly user-friendly, is
adequate for the job. The section "Authoring a Lyric File" later in this article covers how to use
the tool to create lyric files.

The lyric list uses an owner-draw list box. This makes it easy to present both the time and text
information in one place. The position ball, which shows the current CD position, is drawn at the
left side of the text if the line contains the current song position. A lot of flickering goes on as the
CD plays because the current line is needlessly redrawn quite often.

Click here to run TIMEACD.

SINGAWAV: A Waveform Player

46f53fca-4bff-4f84-abc5-13063b752633.doc          2/8/2012          10
The SINGAWAV sample is a modified version of SINGACD that plays a .WAV (waveform) file.
The .WAV file is built into the application as a resource. It could just as easily have been written
to work with an external file.

The only major change required in the modification is to have all the timing in milliseconds
because there is no concept of track or frames in a .WAV file.

Click here to run SINGAWAV.

Synchronizing to Other MCI Time Sources
Modifying the sample applications to use other MCI devices is relatively simple. The most
obvious change is that a different device needs to be opened instead of the CDAudio device. If
a waveform is to be played, the WaveAudio device needs to be opened. This can be achieved
by explicitly opening the device and then later opening the wave file, or by simply opening the
wave file, which implicitly opens the device. MCI will look up the file extension in the [mci
extensions] section of WIN.INI and use the information there to determine which MCI device to
open. A typical example of the [mci extensions] section of WIN.INI looks like this:

 [mci extensions]

The only difference between opening the device explicitly or letting MCI open it for you is the
time it will take to play a wave file. If you open the device before it is required, you are ensuring
that the driver is loaded and ready, so opening and playing a wave file will not require a long
delay. If you let MCI open the device when you open the file, there will be some delay as the
device driver is loaded and initialized. The following code shows an example of opening a wave
file using only the name of the file:

 OpenParams.lpstrElementName = "BOGUS.WAV";
 OpenParams.wDeviceID = 0;
 dwResult = mciSendCommand(0,
                           MCI_WAIT | MCI_OPEN_ELEMENT,

Note that the MCI_OPEN_ELEMENT flag replaces the MCI_OPEN_TYPE flag that was used to
open the CDAudio device and that we cannot open this device with the shareable option.
Chapter 4 of the Multimedia Programmers Guide in the Windows version 3.1 SDK has a section
entitled "Opening Waveform and MIDI Sequencer Devices," which covers opening MCI devices
in more detail.

The second and slightly more complex change to make in modifying a sample application for a
different device is to alter the time format from TMSF to something else. Changing it to
milliseconds will work for most devices but may not seem very logical to the user—hence the
choice of TMSF for the CD player.

46f53fca-4bff-4f84-abc5-13063b752633.doc         2/8/2012          11
The SINGAWAV sample, a modified version of SINGACD, opens a specific wave and lyric file
and plays them. The FILE.C module used by several of the samples includes a function called
ParseLyricLine, which is used to extract the time and text information of a song line. This
function will parse either TMSF or millisecond formats, so you can read lyric files with time
encoding in either format. Note though that the player must know which format the data is in to
work correctly. The SINGAWAV sample assumes that times are in milliseconds.

Click here to run the SINGAWAV sample.

Authoring a Lyric File
This section describes what you need to do to create a lyric file for your favorite CD.

Type the Text
The first step is to type the text of the CD into a text file. You don't need to do the entire CD;
simply pick a track or two to begin with. You can do other tracks later. (See "Putting Multiple
Lyric Files Together" below.)

As an example, let's say we are going to enter the lyrics for a track on a fictional CD called
"Boring Songs" by Mr. Simple. Use your favorite text editor to create a file with a .TXT
extension, such as BORING.TXT. On the first two lines, enter the name of the CD and the
artist. On subsequent lines, enter the text of the track. You can enter blank lines for spacing;
lines enclosed in square braces ([]) are comment lines that will be displayed but will not show
the moving ball as the song plays. Here is what the contents of BORING.TXT might look like:

 TITLE Boring Songs
 ARTIST Mr. Simple

 [Track 1: The Blah Song]
 Blah, blah, blah de blah
 Blah, blah, blah de blah

 Blah, blah, blah de blah
 Blah, blah, blah de blah

 Blah de blah blah dum de dum

 [Track 2: The Hummy Song]
 Hum hum hummy hum
 Hum de hum hummy hum
 Oh hum hum hum de hum
 Hum hum ho ho hum

As you can see, Mr. Simple is not known for the complexity of his lyrics. They are, however,
ideal material for singing in the shower.

46f53fca-4bff-4f84-abc5-13063b752633.doc          2/8/2012         12
The first lines, the title of the CD and its artist, will be used to construct the caption of the player
when the lyric file is loaded. The line enclosed in brackets will be displayed at the beginning of
each track but will not show the bouncing ball.

Blank lines have been included where there are instrumental bits to the song. The ball position
is calculated as a linear function of the length in time of the current line. Its position depends on
what time it is now and when the next line starts. If there is a lot of time with no lyrics, including
a blank line will help keep the ball synchronized. (Just play with it—you'll see.)

When you have finished typing the text, save the text file and proceed to the next step.

Synchronize to the Song
Having typed the text of a track or two into a text file, run the TIMEACD application and open
your text file. The default extension for the open file dialog is .LYR, so you will have to select the
.TXT option to see a list of .TXT files.

When you have opened the file, the main window should look the way it does in Figure 1.

Figure 1. A text file in TIMEACD, the authoring application

Each line is assigned an initial time value of zero. Now start the CD and listen to the song. As
the lyrics of the song reach a certain line, you select that line in the text file. You can select it
either by clicking it with the mouse or by using the down arrow to advance to that line from the
previous one. As you select the line, the current time from the CD, which is shown at the top of
the client area, is inserted to the left of the text. For best results in the player, you should select
the line immediately before the words are sung. Experiment with some short sections until you
get the hang of it.

The circle to the left of the times in the list shows the line the CD is currently playing. This will
become more useful later when you read back a lyric file to edit it.

46f53fca-4bff-4f84-abc5-13063b752633.doc           2/8/2012          13
When you have finished the section, choose the File Save command. This will create a file of
the same name but with a .LYR extension. Files with the .LYR extension are text files exactly
like the text file you typed except that each line has a time value associated with it. You can edit
.LYR files with a text editor if you wish.

Testing a Lyric File
When you have saved the lyric file (BORING.LYR in our case), you can test it. Run the
SINGACD application and choose the Open File command (from the System menu) to load the
.LYR file created by TIMEACD. Use the player (PLAYACD) to start the CD at the track you
authored, and watch what happens in the PLAYACD window. Errors in your timing will be readily

Correcting Timing Errors
You can correct timing errors by loading a .LYR file into the TIMEACD authoring application. As
the CD plays, the circle will show you where the CD currently is according to the current timing
information in the lyric file. You can click a line at the right time to correct a timing mistake. Use
the player to move back and forth over a section of the lyrics until you feel you have it right.
Then save the file again, and test it using the SINGACD application.

Putting Multiple Lyric Files Together
Files with .LYR extensions may be concatenated using a text editor. It doesn't matter if the
ARTIST and TITLE appear more than once. The last occurrence in the file will be the one that is
used. It is much easier to author individual tracks and then add the resulting .LYR file to a final
compound file than to do the whole thing in one session.

Some Problem Areas
One problem you will come across sooner or later is that MCI is only capable of limited
intelligence when it comes to error reports. For example, if you don't have Microsoft MS-DOS®
CD-ROM Extensions (MSCDEX) loaded and you try to run one of the sample applications, you'll
get a message like this:

Figure 2. Error message generated when running a SINGACD application in the absence

46f53fca-4bff-4f84-abc5-13063b752633.doc          2/8/2012         14
What happened was that MCI tried to load the CDAudio device driver (MCICDA.DRV) and the
driver found that it couldn't initialize because MSCDEX wasn't present. The driver has no way to
report the error back and, unfortunately, doesn't put up a dialog of its own to inform the user of
the problem. If this happens, exit Windows, start MSCDEX, restart Windows, and try again.

46f53fca-4bff-4f84-abc5-13063b752633.doc        2/8/2012        15

To top