The MCI parser used by mciSendString uses the command tables to tell it how to translate an MCI command string into parameters used by DriverProc. Most of these tables are stored as resources within the device driver. The resource type is RCDATA.
To allow applications to use mciSendString with any new or extended commands that your driver supports, you must create a command table to indicate how to map the string commands to the message commands.
A command table consists of three columns:
The first column contains the commands and flags used in the string form of the command. Each entry in the first column must be terminated with \0 to form a null-terminated string.
The second column is a DWORD and contains the commands and flags used in the message form of the command.
The third column is a WORD and contains the identifiers that tell the MCI parser how to interpret each entry.
The following example from the MMSYSTEM.RC on the DDK distribution disks shows a partial command table:
/*************************************************************/
/* Command Table for the MCI core command set */
/*************************************************************/
core RCDATA
BEGIN
"open\0", MCI_OPEN, 0,MCI_COMMAND_HEAD,
"\0", MCI_INTEGER, 0,MCI_RETURN,
"notify\0", MCI_NOTIFY,MCI_FLAG,
"wait\0", MCI_WAIT,MCI_FLAG,
"type\0", MCI_OPEN_TYPE,MCI_STRING,
"element\0", MCI_OPEN_ELEMENT,MCI_STRING,
"alias\0", MCI_OPEN_ALIAS,MCI_STRING,
"shareable\0", MCI_OPEN_SHAREABLE,MCI_FLAG,
"\0", 0L,MCI_END_COMMAND,
"close\0", MCI_CLOSE, 0,MCI_COMMAND_HEAD,
"notify\0", MCI_NOTIFY,MCI_FLAG,
"wait\0", MCI_WAIT,MCI_FLAG ,
"\0", 0L,MCI_END_COMMAND,
"play\0", MCI_PLAY, 0,MCI_COMMAND_HEAD,
"notify\0", MCI_NOTIFY, MCI_FLAG,
"wait\0", MCI_WAIT, MCI_FLAG ,
"from\0", MCI_FROM,MCI_INTEGER,
"to\0", MCI_TO,MCI_INTEGER,
"\0", 0L,MCI_END_COMMAND,
.
.
.
"capability\0", MCI_GETDEVCAPS, 0,MCI_COMMAND_HEAD,
"\0", MCI_INTEGER, 0,MCI_RETURN,
"notify\0", MCI_NOTIFY,MCI_FLAG,
"wait\0", MCI_WAIT,MCI_FLAG ,
"\0", MCI_GETDEVCAPS_ITEM,MCI_CONSTANT
"can record\0", MCI_GETDEVCAPS_CAN_RECORD,MCI_INTEGER,
"has audio\0", MCI_GETDEVCAPS_HAS_AUDIO,MCI_INTEGER,
"has video\0", MCI_GETDEVCAPS_HAS_VIDEO,MCI_INTEGER,
"uses files\0", MCI_GETDEVCAPS_USES_FILES,MCI_INTEGER,
"compound device\0", MCI_GETDEVCAPS_COMPOUND_DEVICE,MCI_INTEGER,
"device type\0", MCI_GETDEVCAPS_DEVICE_TYPE,MCI_INTEGER,
"can eject\0", MCI_GETDEVCAPS_CAN_EJECT,MCI_INTEGER,
"can play\0", MCI_GETDEVCAPS_CAN_PLAY,MCI_INTEGER,
"can save\0", MCI_GETDEVCAPS_CAN_SAVE,MCI_INTEGER,
"\0", 0L,MCI_END_CONSTANT,
"\0", 0L,MCI_END_COMMAND,
"resume\0", MCI_RESUME, 0,MCI_COMMAND_HEAD,
"notify\0", MCI_NOTIFY,MCI_FLAG,
"wait\0", MCI_WAIT,MCI_FLAG,
"\0", 0L,MCI_END_COMMAND,
"\0", 0L,MCI_END_COMMAND_LIST
END
When you build your command table, ensure that the entries in the second column are DWORDs, and the entries in the third columns are WORDs. If you use any WORDs in the second columns, pad them with a comma and a zero. For example, the MCI_PLAY message in the example is followed by a comma and a zero. The zero is used as a filler to keep the command list properly aligned. Forgetting the zero after a WORD causes the remainder of the command table to be misaligned by one WORD.
A command table consists of command lists. A command list defines how to parse a particular command. For example, the following is a command list for the play command:
"play\0", MCI_PLAY, 0,MCI_COMMAND_HEAD,
"notify\0", MCI_NOTIFY,MCI_FLAG,
"wait\0", MCI_WAIT,MCI_FLAG ,
"from\0", MCI_FROM,MCI_INTEGER,
"to\0", MCI_TO,MCI_INTEGER,
"\0", 0L,MCI_END_COMMAND,
This command list tells the parser which flags are valid and how to create the associated data structure for the message command.
The first entry in a command list must have the MCI_COMMAND_HEAD delimiter in the third column. The MCI_COMMAND_HEAD delimiter designates the verb portion of the string command. For example, the play command list starts with this entry:
"play\0", MCI_PLAY, 0,MCI_COMMAND_HEAD,
The last entry in a command list must have the MCI_END_COMMAND delimiter. For this last entry, the first and second columns contain “\0” and 0L. For example, all command lists end with this entry:
"\0", 0L,MCI_END_COMMAND,
The last entry of the command table must have the MCI_END_COMMAND_LIST delimiter. The first and second columns for this entry also contain “\0” and 0L. For example, the example command table ends with this entry:
"\0", 0L,MCI_END_COMMAND_LIST
If your want to return data in your data structure, you can reserve a field by having
an entry in the command list with the MCI_RETURN in the third column. If you
use MCI_RETURN, you must use it for the entry immediately following the MCI_COMMAND_HEAD. You can use MCI_RETURN only once in a command list. For example, the MCI_GETDEVCAPS command list starts with the following
entries:
"capability\0", MCI_GETDEVCAPS, 0,MCI_COMMAND_HEAD,
"\0", MCI_INTEGER, 0,MCI_RETURN,
The string in the first column of an entry with MCI_RETURN is “\0”. (This column is ignored when you use MCI_RETURN.)
The second column of the MCI_RETURN entry specifies the type of data returned. The parser recognizes the MCI_INTEGER, MCI_STRING, and MCI_RECT keywords for the data type returned.
If you want to return an integer value, use MCI_INTEGER,0 in the second column. For integers, MCI reserves the second DWORD in the data structure for the return value.
If you want to return a string value, use MCI_STRING,0 in the second column. For strings, MCI reserves the second and third DWORDs in the data structure for the return value. These DWORDs correspond to a pointer to a buffer for the null-terminated return string and the size of the buffer supplied by the application.
If you want to return a RECT value, use MCI_RECT,0 in the second column. For rectangles, MCI reserves the second and third DWORDs in the data structure for the return value.
The following example adds an integer return value to the reset command:
"reset\0", MCI_VDISC_RESET, 0, MCI_COMMAND_HEAD,
"\0", MCI_INTEGER, 0,MCI_RETURN,
"notify\0", MCI_NOTIFY, MCI_FLAG,
"wait\0", MCI_WAIT, MCI_FLAG,
"program\0", MCI_VDISC_RESET_PROGRAM,MCI_INTEGER,
"\0", 0L,MCI_END_COMMAND
The corresponding data structure for the reset command using a return value is as follows:
typedef struct {
DWORDdwCallback;
DWORDdwReturn
DWORDdwProgram;
} MCI_VDISC_RESET_PARMS;
MCI interprets an entry with MCI_FLAG in the third column as a flag that is not associated with any field of the data structure. For example, all commands have the following entry for the MCI_NOTIFY flag:
"notify\0", MCI_NOTIFY, MCI_FLAG
The first column of this entry contain the string command used for the flag. The second column contains the value used for the flag.
MCI associates a command list entry with a data structure field when the command list entry has MCI_INTEGER, MCI_STRING, or MCI_RECT in the third column. The order of the entries in the command list implies the order of the fields in a data structure. That is, the command list does not explicitly name the fields of a data structure.
When assigning fields, MCI reserves the first field in the data structure for the handle to a callback function. Thus, MCI uses the second field of the data structure for data associated with the first entry needing space in the data structure. MCI then assigns the third data field to the second entry needing it. This continues for the rest of the list. This association assumes that all the data fields in the data structure are DWORDS. You can intersperse entries that are not modified with values with those that are. For example, a command designated as an MCI_FLAG will not be associated with a field in a data structure. Instead, it will designate a bit flag used in lParam1. If the MCI_FLAG entry separates two entries designated MCI_INTEGER, it would not affect the data structure field assignment.
As an example, the reset videodisc player command example would have the following form:
"reset\0", MCI_VDISC_RESET, 0, MCI_COMMAND_HEAD,
"notify\0", MCI_NOTIFY, MCI_FLAG,
"wait\0", MCI_WAIT, MCI_FLAG,
"program\0", MCI_VDISC_RESET_PROGRAM,MCI_INTEGER,
"\0", 0L,MCI_END_COMMAND
If you specify “\0” in the first column, it designates that entry a default entry. Any flags entered as part of a string command that does not match any entry in the command list will assume the default value. Only one default value can exist in each command list.
MCI uses the MCI_CONSTANT and MCI_END_CONSTANT keywords to associate a range of constants to a flag. The MCI_CONSTANT keyword reserves a DWORD in the associated data structure and delimits the start of a list of constants. The following entries contain the constants. Each constant entry has the MCI_INTEGER keyword in the third column. The list of constants is ended with an entry with the MCI_END_CONSTANT keyword. For example, the set command has the following entries for the audio channel constants:
"audio\0", MCI_SET_AUDIO,MCI_CONSTANT,
"all\0", MCI_SET_AUDIO_ALL,MCI_INTEGER,
"left\0", MCI_SET_AUDIO_LEFT,MCI_INTEGER,
"right\0", MCI_SET_AUDIO_RIGHT,MCI_INTEGER,
"\0", 0L,MCI_END_CONSTANT,
The constant list is actually defining a list of pre-defined integers that can be used in a string command. These pre-defined integers will be passed to your device driver in the field reserved for the flag in the data structure. If the parser finds an integer following a flag rather than one of flag's pre-defined constants, the parser will assign the integer to the data structure field. This lets your device driver use both constants and integers as data for a flag.
The following table summarizes the keywords used to identify the type of entry in the command table:
MCI_COMMAND_HEAD
This keyword specifies that the null-terminated string in the first column corresponds to the verb portion of the string command. The DWORD that follows the command corresponds to the message command passed to DriverProc as uMsg. This entry must be the first entry in the command list and it must be unique to the table.
MCI_RETURN
This keyword specifies that a data structure associated with a command has data fields reserved for return information. If used, the data entry must be the second entry in the command list. The first column of the entry contains “\0”. The second column of this entry contains the type of return value. If the return type is specified as MCI_INTEGER, the second DWORD in the associated data structure is reserved for an integer return value. If the return type is specified as MCI_STRING, the second DWORD in the associated data structure is reserved for a pointer to the return string. The third DWORD is reserved for the length of the string. If the return type is specified as MCI_RECT, the second and third DWORDs in the associated data structure are reserved for the RECT data.
MCI_FLAG
This keyword specifies the MCI flag represented by the command list entry does not have any additional data associated with it. The second column of this entry contains the flag to set.
MCI_INTEGER
This keyword specifies that integer data modifies the MCI flag represented by the command list entry. The second column of this entry contains the flag to set. The data structure for the message command must reserve a DWORD to hold the integer value.
MCI_RECT
This keyword specifies that RECT data modifies the MCI flag represented by the command list entry. The second column of this entry contains the flag to set. The data structure for the message command must reserve two DWORDs to hold the RECT value.
MCI_STRING
This keyword specifies that string data modifies the MCI flag represented by the command list entry. The second column of this entry contains the flag to set. The data structure for the message command must reserve a DWORD to hold a pointer to the string data.
MCI_CONSTANT
This keyword specifies that integer data modifies the MCI flag represented by the command list entry and a list of constants that can be used for the integer data follows it. The second column of this entry contains the flag to set. The data structure for the message command must reserve a DWORD to hold the integer value used inside MCI_CONSTANT.
MCI_END_CONSTANT
This keyword specifies the end of a list of constants. The first and second columns for this entry must contain “\0” and 0L.
MCI_END_COMMAND
This keyword specifies the end of a command list. The first and second columns for this entry must contain “\0” and 0L.
MCI_END_COMMAND_LIST
This keyword specifies the end of the command table. The first and second columns for this entry must contain “\0” and 0L.
The following description tells how the MCI parser uses the command table entries to interpret the command string “play videodisc1 notify from 10 to 20.” The MCI parser reads the string to obtain the command and its destination device. The parser uses the destination to determine the device ID and assigns it to dwDeviceID of DriverProc. The parser also uses this information to determine which command table to search.
When the parser searches the command tables for “play,” it searches the command tables in this order: the device-specific table (your custom command table), then the device-type table (such as the cdaudio command table), and finally the core command table.
When searching the command table, the parser searches the tables for the MCI_COMMAND_HEAD keyword. When found, the parser compares the command listed in the first column with the command from the input string. If the two commands match, the parser uses the DWORD in the second column (in this case MCI_PLAY) as the uMsg parameter for DriverProc.
The parser tries to match each subsequent parameter in the command string with a corresponding entry in the current command list. For the example, the next parameter is “notify.” If the parser does not find this, it reports an error. If the parser does find it, it looks in the third column and finds MCI_FLAG. This tells the parser to or the second column flag MCI_NOTIFY with the bitfield for the lParam1 parameter.
If the example command contained “wait” rather than notify, the parser would behave the same as for the “notify” example except that it would combine MCI_WAIT with the bitfield for the lParam1 parameter.
When the parser interprets the “from” parameter, the MCI_INTEGER keyword indicates that additional data is available from the command string. The parser obtains the string value, converts it to an integer, and stores it in the DWORD of the data structure corresponding to the MCI_FROM flag. The parser also combines the MCI_FROM flag in the bitfield for the lParam1 parameter.
The parser processes the “to” parameter exactly as the “from” parameter except that the integer modifier is stored in DWORD of the data structure corresponding to the MCI_TO flag.
The parser knows it has finished translating a command when it has reached the end of the input command string. The parser can now call DriverProc with the proper parameters.
Because the parser maps the string commands to message commands and data structures passed to the driver, the order of the flags in a command string is irrelevant. The command string “play videodisc1 to 20 from 10 notify” maps exactly to the same command derived from “play videodisc1 notify from 10 to 20”.
The parser checks only for syntax errors. It does not check the input string for invalid combinations of flags. Before handling any MCI command, your device driver should determine if there are invalid combinations of flags.