Introduction
The Media Control Interface exposed through winmm.dll can provide your multimedia applications with a device-independent layer that exposes capabilities for controlling audio and visual peripherals. With it, your code can pretty much support ANY multimedia device! You could quite literally create applications that utilized waveform (MP3, .wav, etc.), MIDI, CD audio, and digital-video. Imagine the possibilities! I know what you're thinking, DirectX right? Well, take it from someone who's swapped sound buffers for years. The real power of MCI is in its ease of use (sometimes to a fault), and portability (no redistributables). This is of course in no way putting down the power and flexibility of DX.
MCI basically provides two ways of accessing the low level multimedia devices. Through a cryptic structure of message (think lots and lots of structures to create), or passing strings into the interface. (I should note that it's not an either or thing; you can use both). The two methods are exposed as two interfaces (semantically speaking), the Command Message Interface, CMI, and the Command String Interface, CSI. winmm.dll provides a function for each. mciSendCommand
sends the well defined structures and constants into the CMI while mciSendString
passes command strings, which need to follow a certain format, into the CSI. The focus in this article will be on the CSI. Mainly because it's a lot easier to get up to speed and start doing pretty amazing things with.
mciSendString
This function takes a string, identified by lpString
, and sends it to the identified MCI device.
MCIERROR mciSendString(
LPCTSTR lpString,
LPTSTR lpszRetVal,
UINT length,
HANDLE hwndCallback
);
All the command information should be included in the lpString
parameter. The RetVal
parameter represents a pointer to the buffer that will receive any returned information. If you are not interested in responses, the value can be null
. If you are expecting any return information (for example, if you are querying for status information), you must specify the length of the return buffer with the length
value. The final argument will be left null
for the purposes of this discussion.
The above function declaration can be implemented with minimal difficulty in .NET. The rest of the article focuses on building a very very very very basic console based media player that will quite literally play any Windows supported media format. Including AVI and MPEG movies!
Of course, the first step is having the InteropServices
namespace declared. Next, I create a signature for mcsSendString
.
using System;
using System.Runtime.InteropServices;
[DllImport("winmm.dll")]
extern static int mciSendString(string command,
IntPtr responseBuffer,int bufferLength, int nothing);
I use an IntPtr
here so I can use a HGlobal
to store the response string. It's just a preference so I feel like a C++ programmer. You can also use integer type arrays, a StringBuilder
, or whatever else works for you.
The simple player we are creating will use the CSI "play" command string identified by the format below:
"play {0} {1} {2}"
The value of {0} would be the device you want to play, {1} and {2} would be used for any flags you might want to add into the mix. The full set of possible combinations is highlighted in the list below:
Value | Meaning | Meaning |
cdaudio | from position | to position |
digitalvideo | from position fullscreen repeat | reverse to position window |
sequencer | from position | to position |
vcr | at time from position reverse | scan to position |
videodisc | fast from position reverse scan | slow speed integer to position |
waveaudio | from position | to position |
This list was acquired form MSDN. You can find the full set at this URL:
As you might have already imagined, cdaudio represents the CD audio device type, as do sequencer, AVIVideo, waveform, videodisc, and sequencer (MIDI) their given device types. For the purposes of our simple console based media player, we will not be utilizing any advanced functionality. All we will specify is "play {0}" where {0} represents the file to play. The interface will automatically select the device based on either the extension recorded in the registry, or the [mci extension] section of the SYSTEM.INI file.
Finally, we place the code below in the Main
method of the console application:
while(true)
{
Console.WriteLine("Type in the fully qualified" +
" path to the file you want to play");
string file = Console.ReadLine();
if(file != "end")
{
string command = string.Format("play {0}",file);
IntPtr response = Marshal.AllocHGlobal(128);
int err = mciSendString(command,response,128,0);
Console.WriteLine("{0} error(s)",err);
}else
break;
}
It basically asks the user for a file name and plays it. I'm not sure about this but I think I once read somewhere that MCI will only pass 128 bytes of data at a time; hence the hardcoded 128 to represent that boundary. Although I do know that there is a limit, the actual number has escaped my mind.
That's it! All you need to do to start writing your own complex MP3 players, recorders, rippers and much much more. Believe it or not MCI even allows accessing media at external URLs. Simply replace the local file name with a fully qualified URI (include the 'http:\\' too). Before you get too happy though, be advised that sendString
does not handle spaces in file names very well.
Final thoughts
Enjoy :-)