f you've ever tried to find classes in the .NET Framework that allow you to play audio and video, you may have noticed the gaping hole where this functionality should be.
The Multimedia Control Interface (MCI) is an aging (but sturdy) standard implemented by Microsoft to provide a common way to send commands to the dizzying array of audio and video devices supported by Windows. Before this standard, every video card and sound card had their own custom APIs, which made multimedia development quite cumbersome. That functionality is implemented inside the Windows Mutli-Media Dynamic Link Library named WinMM.DLL, which became a standard part of Windows many years ago.
By harnessing the power of the MCI and the Windows API, it's possible to get around the limitations of the .NET Framework and achieve rich media functionality. Of course this kind of low-level coding comes with its perils, so it's a good idea to encapsulate such intricacy into a component of its own, as demonstrated here.
Because the .NET Framework does not provide the required functionality you must bypass it and send the commands directly to Windows. The following code shows the declarations needed to implement the MCI's API functions that the MediaPlayer component requires.
Private Declare Function mciSendString Lib _
"winmm.dll" Alias "mciSendStringA" _
(ByVal lpstrCommand As String, _
ByVal lpstrReturnString As String, _
ByVal uReturnLength As Long, _
ByVal hwndCallback As Long) As UInt32
Private Declare Function mciGetErrorString _
Lib "winmm" Alias "mciGetErrorStringA" _
(ByVal dwError As UInt32, _
ByVal lpstrBuffer As String, _
ByVal uLength As Long) As Long
Private Declare Function GetShortPathName _
Lib "kernel32" Alias "GetShortPathNameA" _
(ByVal lpszLongPathName As String, _
ByVal lpszShortPath As String, _
ByVal cchBuffer As Long) As Long
is the primary function used by the MediaPlayer component. It will execute all of the multimedia functionality. It accepts carefully formatted strings such as "play CD from 3 to 6," which plays tracks 3 through 6 of the current audio CD. The MediaPlayer component encapsulates the various string options to provide a more modern, simplified, and error-resistant programmatic interface. This function returns an unsigned integer that will contain zero upon success. If the function call should fail it will return an error number. The mciGetErrorString
function accepts the error number and returns details about the error.
|By harnessing the power of the Multimedia Control Interface (MCI) and the Windows API, it's possible to get around the limitations of the .NET Framework and achieve rich media functionality.|
function doesn't recognize long file names, so file names must be converted to their short forms before they are parameterized. The GetShortPathName
function provides the conversion of long file names to short file names.
Creating the Component
|Author's Note: The techniques covered in this article are designed for Windows Forms applications only. If you're in need of an ASP.NET solution, the author offers a free media player Web control at http://SteveOrr.net. In addition, when working in-depth with audio and video files, it's a good idea to ensure your sound and video drivers are up to date. You may want to encourage your end users to make sure they have updated drivers as well. Old, unreliable drivers are a leading cause of glitches and serious crashes in Windows.
To create a component in Visual Studio .NET, you first need to create a new Class Library project. In the Solution Explorer, right-click on the project and select "Add Component." Visual Studio then creates a class filled only with the necessary component designer-generated code. The most notable line is the one that specifies that the class inherits from System.ComponentModel.Component.
|If your needs are simple, you might be able to scrape by with the new audio capabilities of the .NET Framework version 2.0.|
You'll need to add references to System.Design.dll
to get the required design-time support. Specifically, they support the attribute at the beginning of Listing 1
. In this example, the EditorAttribute
ensures that the FileName
property of the component will permit browsing at design time. That is, an ellipsis button will appear next to the property in the properties window, and clicking it will allow the developer to choose an appropriate media file for the control. Of course the FileName
property can instead be set at run time if preferred.
property in Listing 1
accepts the full path and file name to a media file. It ensures the file has a supported file extension and converts the path to the short version since that's what the MCI functions support. It also accepts a special file name of "CD" to allow the playing of an audio CD instead of a file.
Since the state of the current media file must be managed carefully, there are several methods and properties that take care of such details, as shown in Listing 2
attribute is specified to ensure the Opened
property doesn't appear in the Properties window at design time, since this property is only relevant at run time.
method ensures a valid media file has been specified, then carefully constructs a valid MCI string to send to the mciSendString
Likewise, the Close method builds the necessary MCI string to close the media file.
It's not necessary for the developer using the MediaPlayer component to open and close a media file since this is handled automatically. The Open
methods are provided purely as a means for optimization. Since it can take a significant number of milliseconds to open a media file before playing it, you might choose to open the file in advance for a snappier response when it's time to play.