Click here to Skip to main content
15,860,943 members
Articles / Desktop Programming / Win32

Creating a Sound Recorder in C and C#

Rate me:
Please Sign up or sign in to vote.
4.93/5 (14 votes)
1 Apr 2010CPL16 min read 138.1K   438   59   23
Learn how to record audio and play sound files in C and C#.

Contents 

Contents of this article:

  • Contents
  • Overview
  • Introduction
  • Sending MCI Commands
  • The Wait, Notify, and Test Flags
  • Handling MCI Errors
  • Recording from an Input Device
    • Opening the Device
    • Starting Recording
    • Pausing Recording
    • Resuming Recording
    • Stopping Recording
    • Retrieving Buffer Length
    • Saving the Recorded Buffer
    • Closing the Device
  • Playing a Sound File
    • Loading the File
    • Playing the File
    • Pausing
    • Resuming
    • Retrieving Current Position
    • Retrieving File Length
    • Seeking a Specific Position
    • Closing the Device
    • In a Nutshell
    • MCI and .NET
    • Creating the Managed Signature
    • Receiving MCI_MMNOTIFY
  • Sample Code

Overview

This writing will focus on how you can record sound from an input device and how you can play sound files using MCI (Media Control Interface) in C and C#.

This writing does not involve a discussion or even an introduction to MCI. Instead, it provides technical discussion of what we will need to use to record and to play sound files. If you need an introduction to MCI refer to the MSDN documentation.

We will begin by a discussion to types and functions required to accomplish our tasks. Then we will look on how you can utilize those types and functions in your C or C# application.

Our demonstration examples in this writing will be in C. In the last section we will have a look at .NET and C#. Besides this, there are sample applications written by C and C# attached with the article.

Introduction

Actually, there are many ways to record and play sound files. However, in this writing we will focus on MCI (Media Control Interface) as it is one of the high-level easy-to-use interfaces for controlling all media types.

You can control a multimedia hardware using MCI by either sending commands like Windows messages or by sending string messages to the MCI. The second approach is preferable in scripting languages. As we are concentrating on languages C and C#, we will consider the first approach only.

Because .NET Framework does not include classes for handling MCI (or multimedia at all,) we will need to dig into Windows SDK, specifically WinMM.dll (Windows Multimedia Library) that resides in the system directory.

In order to access this library from your C# application, you will need to create your own marshaling types and PInvoke (Platform Invocation) methods to allow your managed code to communicate with the unmanaged server. That is what the last section of this article is devoted for.

To access this library from your C application, just reference the library object file WinMM.lib in your project. Notice that all of the functions and structures related to the MCI are prefixed with mci.

In the next sections we will talk about the functions and structures that we need to be aware of before digging into coding.

Sending MCI Commands

The key function to Windows multimedia programming using MCI is mciSendCommand(). This function sends a command to the MCI. As we have said, you can program MCI using one of two approaches, you can send Windows-messages-like commands, and you can also send string messages to the MCI. The key function that we will use to send MCI commands is mciSendCommand(). To send string messages, you can use the function mciSendMessage() which is not covered here.

The definition of the function mciSendCommand() is as follows:

C
MCIERROR mciSendCommand(
  MCIDEVICEID IDDevice,
  UINT        uMsg,
  DWORD       fdwCommand,
  DWORD_PTR   dwParam
);

This function receives four input arguments:

  • IDDevice:
    The ID of the device to which to receive the command. For example, the ID of the input device when recording or the output device if playing. As you know, many devices could be connected to the PC. You can forget about this argument and just pass the function 0 to direct the message to the default device (selected in the Sound applet in the Control Panel.)
  • uMsg:
    The message (command) to be sent. Common messages are covered in the next few sections.
  • fdwCommand:
    Flags (options) of the message. Every message has its own options. However, all messages share the flags MCI_WAIT, MCI_NOTIFY, and MCI_TEST (covered soon.)
  • dwParam:
    A structure contains the specific parameter for the command message.

As a result, every command (message) has its name, flags, and structure parameter.

This function returns 0 if succeeded or an error code otherwise.

The Wait, Notify, and Test Flags

Common flags for all MCI messages are, MCI_WAIT, MCI_NOTIFY, and MCI_TEST.

The Wait flag, MCI_WAIT, indicates that the message should be processed synchronously; means that the function would not return until message processing finishes. For example, if you send a play message with the MCI_WAIT, your application would be suspended until the entire file finishes. Therefore, you should not use MCI_WAIT for play or record messages.

The Notify flag, MCI_NOTIFY, is the reverse of the Wait flag. It indicates that the message should be processed asynchronously; means that the function would return immediately and does not wait for the completion of the message. When the processing of the message finishes, it sends a MM_MCINOTIFY message to the windows specified in the dwCallback member of the message parameter. This flag should be used for play and record messages.

The wParam value of the MM_MCINOTIFY message is usually set to either MCI_NOTIFY_SUCCESSFUL to indicate command success or MCI_NOTIFY_FAILURE otherwise.

The Test flag, MCI_TEST, checks if the device can process this message, it does not process the message. The function would return 0 if the device can process the message or non-zero otherwise. You will use this flag in rare cases only.

Keep in mind that you should choose only one of the three flags, you cannot combine them.

If you didn’t specify any of these three flags, the call would be treated asynchronously and you will not be notified when it completes.

Handling MCI Errors

Every MCI command could succeed or fail. If the command succeeded, mciSendCommand() returns 0 (FALSE/false.) Otherwise, it returns the error code.

MCI defines error codes as constants that are prefixed with MCIERR_ like MCIERR_DEVICE_NOT_INSTALLED and MCIERR_FILE_NOT_FOUND (names are self-explanatory.) You can get the friendly error message of the code using the function mciGetErrorString(). The definition of this function is as follows:

C
BOOL mciGetErrorString(
  DWORD fdwError,
  LPTSTR lpszErrorText,
  UINT cchErrorText
);

This function accepts three arguments:

  • fdwError:
    Error code returned by mciSendCommand() function.
  • lpszErrorText:
    A string buffer that receives the description of the error.
  • cchErrorText:
    Length of the buffer in characters.

The following C example shows how you can display a friendly error message to the user if an error occurred:

C
TCHAR szBuffer[256];
DWORD dwErr;

dwErr = mciSendCommand(/* arguments */);

mciGetErrorStringW(dwErr, szBuffer, 256);
// cchErrorText =
//     sizeof(szBuffer) / sizeof(szBuffer[0])

Recording from an Input Device

To record from an input device using MCI, follow these steps:

  1. Open the input device to receive the data from.
  2. Order the MCI to start the recording process.
  3. When you finish, stop the recording process.
  4. Save the record buffer if applicable.
  5. Close the opened device.

Keep in mind that you should check whether an error occurred or not after sending each command. The previous approach to retrieve error messages would be very useful.

Opening the Device

To open a device just pass the open command MCI_OPEN to the mciSendCommand() function along with its flags and parameter.

The parameter of MCI_OPEN is the structure MCI_OPEN_PARMS. This structure contains information about the open command. The definition of this structure is as following:

C
typedef struct {
    DWORD_PTR    dwCallback;
    MCIDEVICEID  wDeviceID;
    LPCSTR       lpstrDeviceType;
    LPCSTR       lpstrElementName;
    LPCSTR       lpstrAlias;
} MCI_OPEN_PARMS;

Actually, you will make use of only the third and fourth members, lpstrDeviceType and lpstrElementName, of the structure when you open a device. lpstrDeviceType determines the type of the device (digital-audio, digital-video, etc.) that will be used. In our example that records and plays sound files, we will set this member to “waveaudio” to indicate that we are going to work with waveform (WAV) data.

lpstrElementName on the other hand, should be set to an empty string (that is “”) if you are opening an input device for recording. If you want to play a file, set this member to the full path of that file.

Common flags of the command MCI_OPEN are:

  • The Wait, Notify, and Test flags:
    The Wait command is usually used for MCI_OPEN.
  • MCI_OPEN_ELEMENT:
    Mandatory. The lpstrDeviceType of the MCI_OPEN_PARMS is set. It is set to “waveaudio” for WAV data.
  • MCI_OPEN_TYPE:
    Mandatory. The lpstrElementType of the MCI_OPEN_PARMS is set. It is set to an empty string if recording or a path to a file if you want to play it.

You will always combine the flags MCI_WAIT, MCI_OPEN_ELEMENT, and MCI_OPEN_TYPE for the MCI_OPEN command.

When the function returns, the wDeviceID member of the structure is set to the ID of the device opened. You should keep this ID for future calls on that device until you close it using the close command.

The following C code shows how you can open an input device for recording:

C
MCI_OPEN_PARMS parmOpen;
WORD wDeviceID;

parmOpen.dwCallback       = 0;
parmOpen.wDeviceID        = 0;  // the default device
parmOpen.lpstrDeviceType  = TEXT("waveaudio");
parmOpen.lpstrElementName = TEXT("");
parmOpen.lpstrAlias       = 0;

mciSendCommand(0, // the default device
    MCI_OPEN,
    MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
    (DWORD)&parmOpen);

// Keep the device ID for future calls
wDeviceID = parmOpen.wDeviceID;

Starting Recording

After you have opened the input device, you can order the MCI to start the recording process using the command MCI_RECORD. This command requires an opened input device and a parameter of type MCI_RECORD_PARMS. The definition of this structure is as following:

C
typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwFrom;
    DWORD     dwTo;
} MCI_RECORD_PARMS;

Members this structure defines are as following:

  • dwCallback:
    The window handle that should be called after the processing of the command finishes if MCI_NOTIFY is specified in command flags.
  • dwFrom:
    Indicates the position of the buffer (in thousands of a second) to start recording from. In most cases this would be set to zero.
  • dwTo:
    Indicates the position of the buffer to stop recording when it is reached. Unless you want to record for a given period, this member should be zero.

Common flags for MCI_RECORD are:

  • MCI_WAIT, MCI_NOTIFY, and MCI_TEST:
    Usually you will use the MCI_NOTIFY flag. If so, you should handle the MM_MCINOTIFY message.
  • MCI_FROM:
    Set if you used the dwFrom member of the parameter.
  • MCI_TO:
    Set if you used the dwTo member of the parameter.

If you want to record for a specific period, set dwTo member of the structure to that specific period (in thousands of seconds) and combine your flags with MCI_TO. When this period ends, MCI automatically stops the recording process and it sends a MM_MCINOTIFY message if you have set MCI_NOTIFY in the flags.

The following C example shows how you can start recording:

C
MCI_RECORD_PARMS parmRec;

parmRec.dwCallback = 0;
parmRec.dwFrom     = 0;
parmRec.dwTo       = 0;

// We do not need a notification message
// we will send a Stop command, when
// we finish.
mciSendCommand(wDeviceID, MCI_RECORD, 0, (DWORD)&parmRec);

The following code shows how you can record for a specific period:

C
MCI_RECORD_PARMS parmRec;

parmRec.dwCallback = hWnd;  // window handle
parmRec.dwFrom     = 0;
parmRec.dwTo       = 30000; // 30 seconds

// Notify me when you finish the 30 seconds
mciSendCommand(wDeviceID, MCI_RECORD,
    MCI_NOTIFY | MCI_TO, (DWORD)&parmRec);

Pausing Recording

To pause the recording process, just pass the MCI_PAUSE command to the MCI. This command accepts a parameter of the structure MCI_GENERIC_PARMS which is defined as following:

C
typedef struct {
    DWORD_PTR dwCallback;
} MCI_GENERIC_PARMS;

This structure contains only one member, dwCallback. As you know, if you specify MCI_NOTIFY in command flags, MCI will send a MM_MCINOTIFY message to the window specified in this member.

MCI_PAUSE does not have specific flags, just the Wait, Notify, and Test flags.

The following C example shows how you can pause the recording process. Notice that you should already be recording or an error would be returned by the mciSendCommand().

C
MCI_GENERIC_PARMS parmGen;

parmGen.dwCallback = 0;

mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)&parmGen);

Resuming Recording

To resume after pausing, you can send a MCI_RESUME command to MCI. This command is very similar to the MCI_PAUSE command. It accepts the same parameter and the same flags. The example is the same, just change command name.

Stopping Recording

After you finish recording you will need to stop the recording process. To accomplish this, pass the MCI_STOP command along with the device ID and its parameter to the MCI.

This command is the same as MCI_PAUSE and MCI_RESUME. It accepts the same flags and the same parameters, and the example is identical too, just change the command name.

Retrieving Buffer Length

How long have you been recording? This could be easily answered with the MCI_STATUS command. This command queries MCI and retrieves information about the current session.

MCI_STATUS accepts a structure parameter of type MCI_STATUS_PARMS which is defined as following:

C
typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwReturn;
    DWORD     dwItem;
    DWORD     dwTrack;
} MCI_STATUS_PARMS;

This structure defines the following members:

  • dwCallback:
    Discussed previously.
  • dwReturn:
    After returning from the call, it should contains the return value of the query.
  • dwItem:
    The item to query about.
  • dwTracks:
    Length or number of tracks (specific to some query items.)

Common flags that MCI_STATUS accepts:

  • The Wait, Notify, and Test flags:
    You will usually use the Wait flag.
  • MCI_STATUS_ITEM:
    Mandatory in most cases. Indicates that the dwItem of the structure is set.

If you want to query MCI about specific information, pass the MCI_STATUS command to the MCI along with MCI_STATUS_ITEM and MCI_WAIT flags, and set the dwItem field of the parameter to one of the following values (some are for output devices):

  • MCI_STATUS_LENGTH:
    Retrieves the total length of the buffer (in thousands of a second.)
  • MCI_STATUS_MODE:
    Retrieves current mode of the device which can be one of the following values (names are self-explanatory):

     

    • MCI_MODE_NOT_READY
    • MCI_MODE_PAUSE
    • MCI_MODE_PLAY
    • MCI_MODE_STOP
    • MCI_MODE_RECORD
    • MCI_MODE_SEEK
  • MCI_STATUS_NUMBER_OF_TRACKS:
    Retrieves the total number of tracks.
  • MCI_STATUS_POSITION:
    Retrieves current position (in thousands of a second.)
  • MCI_STATUS_READY:
    Returns TRUE (true in C#) if the device is ready or FALSE (false in C#) otherwise.

When the function returns, the dwReturn field of the structure should contain the result of the query item selected.

The following C example retrieves the length of the buffer recorded:

C
MCI_STATUS_PARMS parmStatus;

parmStatus.dwCallback = 0;
parmStatus.dwReturn   = 0;
parmStatus.dwItem     = MCI_STATUS_LENGTH;
parmStatus.dwTrack    = 0;

mciSendCommand(0, MCI_STATUS,
    MCI_WAIT | MCI_STATUS_ITEM, (DWORD)&parmStatus);

// Display the number of seconds
// parmStatus.dwReturn / 1000

Saving the Recorded Buffer

Before you close the device, you can save the current recorded buffer in an audio (waveform) file. The command MCI_SAVE orders MCI to save the buffer to the file specified in its structure parameter.

The parameter of MCI_SAVE is a MCI_SAVE_PARMS structure and it is defined as following:

C
typedef struct {
    DWORD_PTR  dwCallback;
    LPCTSTR    lpfilename;
} MCI_SAVE_PARMS;

This structure contains only two members, dwCallback (discussed before) and lpfilename. lpfilename points to a string buffer contains the name and full path of the file to save.

MCI_SAVE accepts few flags:

  • The Wait, Notify, and Test flags:
    You will always use the Wait (MCI_WAIT) flag.
  • MCI_SAVE_FILE:
    Mandatory. You will always set this flag. It indicates that lpfilename contains the path of the target file.

The following example saves the recorded buffer to the file “recording.wav”:

C
MCI_SAVE_PARMS parmSave;

parmSave.dwCallback = 0;
parmSave.lpfilename = TEXT("recording.wav");
// save to the current directory

mciSendCommand(wDeviceID, MCI_SAVE,
    MCI_WAIT | MCI_SAVE_FILE, (DWORD)&parmSave);

Closing the Device

You strictly should always close the device when you finish working with it, and this is done through the MCI_CLOSE command which accepts a parameter of type MCI_GENERIC_PARMS (discussed before.)

MCI_CLOSE accepts only the Wait, Notify, and Test flags.

The following C example closes the opened device:

C
MCI_GENERIC_PARMS parmsGen;

parmsGen.dwCallback = 0;

mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)&parmsGen);

Playing a Sound File

Keeping in mind what you have learnt from the previous section would be very helpful in our discussion on how to play sound files using MCI.

Actually, the same rules and commands applied in the previous section, will be applied here.

To play a sound file on MCI, you should follow these steps:

  1. Load the audio file. This automatically opens the output device.
  2. Order the MCI to start the playing process.
  3. Close the opened device when you finish.

Again, you should check whether an error occurred or not after each call to mciSendCommand().

Loading the File

As you know, to open a multimedia device, you pass MCI the MCI_OPEN command. To specify a file to be loaded, specify the full path of the file in the lpstrElementName field of the MCI_OPEN_PARMS structure.

The following C example shows how you can open an output device and load a file for playing:

C
MCI_OPEN_PARMS parmOpen;
WORD wDeviceID;

parmOpen.dwCallback       = 0;
parmOpen.wDeviceID        = 0;  // the default device
parmOpen.lpstrDeviceType  = TEXT("waveaudio");
parmOpen.lpstrElementName = TEXT("recording.wav");
parmOpen.lpstrAlias       = 0;

mciSendCommand(0, // the default device
    MCI_OPEN,
    MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
    (DWORD)&parmOpen);

// Keep the device ID for future calls
wDeviceID = parmOpen.wDeviceID;

Playing the File

To start playing the currently loaded file, you can use the MCI_PLAY command. This command accepts a structure parameter of type MCI_PLAY_PARMS which is defined as following:

C
typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwFrom;
    DWORD     dwTo;
} MCI_PLAY_PARMS;

At first sight, you notice that it is identical to the MCI_RECORD_PARMS. Actually, you are right. This structure defines the same members as the MCI_RECORD_PARMS structure. In addition, the description of the members is the same.

dwFrom specifies the position (in thousands of a second) where to start playing. dwTo on the other hand specifies the position (in thousands of a second too) where to end playing. If you set dwFrom, you will need to set the flag MCI_FROM. Conversely, if you set dwTo, you will need to set the flag MCI_TO. If you need to play the file from the start to the end, leave both members and do not specify either MCI_FROM or MCI_TO.

You will most likely combine MCI_PLAY flags with the Notify flag, MCI_NOTIFY, to allow the code to continue execution while the file plays. If you did so, your application would receive MM_MCINOTIFY message to the window specified in the dwCallback member of the parameter structure.

The following C example shows how to start playing a file:

C
MCI_PLAY_PARMS parmPlay;

// Play the file
// from the start to the end

parmRec.dwCallback = 0;
parmRec.dwFrom     = 0;
parmRec.dwTo       = 0;

// notify me when you finish
mciSendCommand(wDeviceID, MCI_PLAY,
    MCI_NOTIFY, (DWORD)&parmRec);

And the following code plays only three minutes from the file:

C
MCI_PLAY_PARMS parmPlay;

// play only 3 minutes
parmRec.dwCallback = 0;
parmRec.dwFrom     = 0;
// 3 * 60 * 1000
parmRec.dwTo       = 180000;

// notify me when you finish
mciSendCommand(wDeviceID, MCI_PLAY,
    MCI_NOTIFY | MCI_TO, (DWORD)&parmRec);

Pausing

As you know, you can pause the playback using the MCI_PAUSE command which accepts the MCI_GENERIC_PARMS parameter.

Resuming

To resume the playback after pausing, you can use the MCI_RESUME command discussed before.

Retrieving Current Position

To retrieve the current position of the file, pass the MCI_STATUS command along with its flags (MCI_WAIT and MCI_STATUS_ITEM) and its parameter (MCI_STATUS_PARMS) to the MCI. Do not forget to set dwItem member of the structure to MCI_STATUS_POSITION to retrieve the current position (in thousands of a second) in the dwReturn member of the structure. The following C example demonstrates this:

C
MCI_STATUS_PARMS parmStatus;

parmStatus.dwCallback = 0;
parmStatus.dwReturn   = 0;
parmStatus.dwItem     = MCI_STATUS_POSITION;
parmStatus.dwTrack    = 0;

mciSendCommand(wDeviceID, MCI_STATUS,
    MCI_WAIT | MCI_STATUS_ITEM, (DWORD)&parmStatus);

// Display the current position
// parmStatus.dwReturn / 1000

Retrieving File Length

Like retrieving current position, you can retrieve full file length the same way. However, you will need to specify the item MCI_STATUS_LENGTH instead.

Seeking a Specific Position

To change the current position, pass MCI the command MCI_SEEK. This command accepts the structure parameter MCI_SEEK_PARMS which is defined as following:

C
typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwTo;
} MCI_SEEK_PARMS;

This parameter defines the following members:

  • dwCallback:
    The window to be notified with the MCI_MMNOTIFY message if the call was carried out asynchronously using the MCI_NOTIFY flag.
  • dwTo:
    The position (in thousands of a second) to jump to.

The command MCI_SEEK accepts the following flags:

  • The Wait, Notify, and Test flags:
    You will always use the Wait flag.
  • MCI_SEEK_TO_END:
    If specified, it would jump to the end of the file.
  • MCI_SEEK_TO_START:
    If specified, it would jump to the start of the file.
  • MCI_TO:
    If specified, MCI would use the value in the dwTo member of the parameter structure.

The following C example jumps to the start of the file:

C
MCI_SEEK_PARMS parmSeek;

parmStatus.dwCallback = 0;
parmStatus.dwTo       = 0;

mciSendCommand(wDeviceID, MCI_SEEK,
    MCI_WAIT | MCI_SEEK_TO_START, (DWORD)&parmSeek);

And the following example jumps to the third minute of the file:

C
MCI_SEEK_PARMS parmSeek;

parmStatus.dwCallback = 0;
parmStatus.dwTo       = 180000;

mciSendCommand(wDeviceID, MCI_SEEK,
    MCI_WAIT | MCI_TO, (DWORD)&parmSeek);

Closing the Device

You should close the device as soon as you finish working with it, and this is done (as you know) through the MCI_CLOSE command.

In a Nutshell

The following table summarizes the commands that you will usually use with audio files along with their parameter structures and flags.

Command Input/Output Parameter Structure Commonly Used Flags
MCI_OPEN In/Out MCI_OPEN_PARMS MCI_WAIT, MCI_OPEN_ELEMENT, and MCI_OPEN_TYPE
MCI_RECORD In MCI_RECORD_PARMS (none) or MCI_NOTIFY
MCI_PLAY Out MCI_PLAY_PARMS MCI_NOTIFY
MCI_PAUSE In/Out MCI_GENERIC_PARMS MCI_WAIT
MCI_RESUME In/Out MCI_GENERIC_PARMS MCI_WAIT
MCI_STOP In/Out MCI_GENERIC_PARMS MCI_WAIT
MCI_SEEK Out MCI_SEEK_PARMS MCI_WAIT and MCI_TO / MCI_SEEK_TO_START / MCI_SEEK_TO_END
MCI_SAVE In MCI_SAVE_PARMS MCI_WAIT and MCI_SAVE_FILE
MCI_STATUS In/Out MCI_STATUS_PARMS MCI_WAIT and MCI_STATUS_ITEM
MCI_CLOSE In/Out MCI_GENERIC_PARMS MCI_WAIT

MCI and .NET

Creating the Managed Signature

Because .NET does not support MCI and it does not allow you to call unmanaged code directly, you will need to create your own marshaling types and PInvoke methods.

Keep in mind that you can get the handle of a window by using Control.Handle property.

The following class is the managed signature of our unmanaged structures and functions along with the required constants:

C#
internal static class SafeNativeMethods
{
    // Constants

    public const string WaveAudio = "waveaudio";

    public const uint MM_MCINOTIFY = 0x3B9;

    public const uint MCI_NOTIFY_SUCCESSFUL = 0x0001;
    public const uint MCI_NOTIFY_SUPERSEDED = 0x0002;
    public const uint MCI_NOTIFY_ABORTED = 0x0004;
    public const uint MCI_NOTIFY_FAILURE = 0x0008;

    public const uint MCI_OPEN = 0x0803;
    public const uint MCI_CLOSE = 0x0804;
    public const uint MCI_PLAY = 0x0806;
    public const uint MCI_SEEK = 0x0807;
    public const uint MCI_STOP = 0x0808;
    public const uint MCI_PAUSE = 0x0809;
    public const uint MCI_RECORD = 0x080F;
    public const uint MCI_RESUME = 0x0855;
    public const uint MCI_SAVE = 0x0813;
    public const uint MCI_LOAD = 0x0850;
    public const uint MCI_STATUS = 0x0814;

    public const uint MCI_SAVE_FILE = 0x00000100;
    public const uint MCI_OPEN_ELEMENT = 0x00000200;
    public const uint MCI_OPEN_TYPE = 0x00002000;
    public const uint MCI_LOAD_FILE = 0x00000100;
    public const uint MCI_STATUS_POSITION = 0x00000002;
    public const uint MCI_STATUS_LENGTH = 0x00000001;
    public const uint MCI_STATUS_ITEM = 0x00000100;

    public const uint MCI_NOTIFY = 0x00000001;
    public const uint MCI_WAIT = 0x00000002;
    public const uint MCI_FROM = 0x00000004;
    public const uint MCI_TO = 0x00000008;

    // Structures

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_OPEN_PARMS
    {
        public IntPtr dwCallback;
        public uint wDeviceID;
        public IntPtr lpstrDeviceType;
        public IntPtr lpstrElementName;
        public IntPtr lpstrAlias;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_RECORD_PARMS
    {
        public IntPtr dwCallback;
        public uint dwFrom;
        public uint dwTo;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_PLAY_PARMS
    {
        public IntPtr dwCallback;
        public uint dwFrom;
        public uint dwTo;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_GENERIC_PARMS
    {
        public IntPtr dwCallback;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_SEEK_PARMS
    {
        public IntPtr dwCallback;
        public uint dwTo;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_SAVE_PARMS
    {
        public IntPtr dwCallback;
        public IntPtr lpfilename;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_STATUS_PARMS
    {
        public IntPtr dwCallback;
        public uint dwReturn;
        public uint dwItem;
        public uint dwTrack;
    } ;

    // Functions

    [DllImport("winmm.dll", CharSet = CharSet.Ansi,
        BestFitMapping = true, ThrowOnUnmappableChar = true)]
    [return: MarshalAs(UnmanagedType.U4)]
    public static extern uint mciSendCommand(
        uint mciId,
        uint uMsg,
        uint dwParam1,
        IntPtr dwParam2);

    [DllImport("winmm.dll", CharSet = CharSet.Ansi,
        BestFitMapping = true, ThrowOnUnmappableChar = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool mciGetErrorString(
        uint mcierr,
        [MarshalAs(UnmanagedType.LPStr)]
        System.Text.StringBuilder pszText,
        uint cchText);
}

Receiving MCI_MMNOTIFY

In C, you can handle the MCI_MMNOTIFY message in the way you handle any other message. Just add the handler to the window procedure (WndProc) function.

In .NET, you do not have a WndProc function. However, .NET emulates this function using the protected Control.WndProc() function. You can override this function in your form and do the required processing. The following example demonstrates this:

C#
public partial class MainForm : Form
{
    . . . 

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == SafeNativeMethods.MM_MCINOTIFY)
        {
            // Handle the message
        }

        // DO NOT REMOVE the following line
        base.WndProc(ref m);
    }
}

Sample Code

Attached with this writing the sample applications, SampleRec (C#) and SMPREC (C).

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)


Written By
Technical Lead
Egypt Egypt
Mohammad Elsheimy is a developer, trainer, and technical writer currently hired by one of the leading fintech companies in Middle East, as a technical lead.

Mohammad is a MCP, MCTS, MCPD, MCSA, MCSE, and MCT expertized in Microsoft technologies, data management, analytics, Azure and DevOps solutions. He is also a Project Management Professional (PMP) and a Quranic Readings college (Al-Azhar) graduate specialized in Quranic readings, Islamic legislation, and the Arabic language.

Mohammad was born in Egypt. He loves his machine and his code more than anything else!

Currently, Mohammad runs two blogs: "Just Like [a] Magic" (http://JustLikeAMagic.com) and "مع الدوت نت" (http://WithdDotNet.net), both dedicated for programming and Microsoft technologies.

You can reach Mohammad at elsheimy[at]live[dot]com

Comments and Discussions

 
Questionrecording quality Pin
Member 1472265211-Feb-20 7:30
Member 1472265211-Feb-20 7:30 
QuestionMany thanks great article + answer for SndException Pin
kmf1-Mar-14 6:25
kmf1-Mar-14 6:25 
Questionsimple rec with 64 Bit Pin
hirosh de silva26-Feb-14 23:28
hirosh de silva26-Feb-14 23:28 
Questiongenerates error Pin
vineethnair4-Sep-13 23:08
vineethnair4-Sep-13 23:08 
Questionerror coming while debugging Pin
Member 100052574-Aug-13 3:21
Member 100052574-Aug-13 3:21 
Questionerror Pin
Member 98070235-Mar-13 1:40
Member 98070235-Mar-13 1:40 
QuestionSndException Pin
Member 794828614-Oct-12 11:01
Member 794828614-Oct-12 11:01 
QuestionERREUR Pin
Remix Mixdox13-Aug-12 1:46
Remix Mixdox13-Aug-12 1:46 
Questionhw to run dis file? Pin
MuditaS1-Feb-11 5:45
MuditaS1-Feb-11 5:45 
AnswerRe: hw to run dis file? Pin
hirosh de silva26-Feb-14 23:49
hirosh de silva26-Feb-14 23:49 
Open the "SimpleRec.csproj" file through visual Studio 2008 or newer version, then debug, you can see the "Geming.SimpleRec" file in bin/debug folder
QuestionCFileDialog Differences Pin
daknife5617-Aug-10 15:50
daknife5617-Aug-10 15:50 
AnswerRe: CFileDialog Differences Pin
Mohammad Elsheimy20-Sep-10 1:35
Mohammad Elsheimy20-Sep-10 1:35 
SuggestionRecording quality Pin
Julien Villers3-Jun-10 22:48
professionalJulien Villers3-Jun-10 22:48 
GeneralRe: Recording quality Pin
Mohammad Elsheimy8-Jun-10 11:00
Mohammad Elsheimy8-Jun-10 11:00 
GeneralRe: Recording quality Pin
Julien Villers9-May-11 3:30
professionalJulien Villers9-May-11 3:30 
GeneralRe: Recording quality Pin
Julien Villers6-Nov-11 9:38
professionalJulien Villers6-Nov-11 9:38 
GeneralRe: Recording quality Pin
The Squig22-Oct-12 1:38
The Squig22-Oct-12 1:38 
GeneralCan't download!!! Pin
Juan Carlos Culquichicon Valentin30-May-10 8:11
Juan Carlos Culquichicon Valentin30-May-10 8:11 
GeneralRe: Can't download!!! Pin
Mohammad Elsheimy30-May-10 10:34
Mohammad Elsheimy30-May-10 10:34 
GeneralError in c# ver Pin
sanctification24-May-10 12:46
sanctification24-May-10 12:46 
GeneralRe: Error in c# ver Pin
Mohammad Elsheimy27-May-10 5:00
Mohammad Elsheimy27-May-10 5:00 
GeneralRe: Error in c# ver PinPopular
scotchfaster20-Jul-10 9:00
scotchfaster20-Jul-10 9:00 
GeneralRegarding ( Record only System Creating Sound Not what ever we talk too ) Pin
sunil1501878-Apr-10 18:51
sunil1501878-Apr-10 18:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.