Click here to Skip to main content
13,355,538 members (75,936 online)
Click here to Skip to main content

Stats

558.8K views
25.9K downloads
255 bookmarked
Posted 14 Sep 2010

PVS.AVPlayer - MCI Audio and Video Library

, 7 Dec 2017
Windows Media Control Interface (MCI) library with many added features
PVS.AVPlayer
PVS.AVPlayer .NET 2.0
PVS.AVPlayer .NET 3.0
PVS.AVPlayer .NET 3.5
PVS.AVPlayer .NET 4.0
PVS.AVPlayer .NET 4.5
PVS.AVPlayer .NET 4.5.1
PVS.AVPlayer .NET 4.5.2
PVS.AVPlayer .NET 4.6
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer .NET 4.6.1
PVS.AVPlayer.dll
PVS.AVPlayer .NET 4.6.2
PVS.AVPlayer.dll
PVS.AVPlayer.dll
PVS.AVPlayer .NET 4.7.1
PVS.AVPlayer.dll
PVS.AVPlayer .NET 4.7
PVS.AVPlayer.dll
PVS.AVPlayer All Source Code
AVPlayerExample
AVPlayerExample
AVPlayerExample.csproj.user
bin
Debug
Release
Dialogs
Display Overlays
obj
Debug
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
WingDings3a.ttf
Voice Recorder
FolderView
FolderView
bin
Debug
Release
FolderView.csproj.user
obj
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
PVS.AVPlayer
AVPlayerExample.csproj.user
PVS.AVPlayer.dll
Custom Items
Native Methods
Crystal Italic1.ttf
Dial Green 2.png
Dial Green 4.png
Dial Green.png
Dial Red 2.png
Dial Red.png
media7.ico
media7a.ico
Media8.ico
Media8a.ico
VU Meter.png
WingDings3a.ttf
Sound Recorder
Various
About Dialog
PVS.AVPlayer.dll
Custom Items
FolderView.csproj.user
Crystal Italic1.ttf
media7a.ico
media7b.ico
Media8a.ico
Media8b.ico
Subtitles Overlay
Various
How To (C#)
PVSAVPlayerHowTo
bin
PVS.AVPlayer.dll
Release
Properties
How To (VB.NET)
PVSAVPlayerHowToVB
bin
PVS.AVPlayer.dll
Release
Form1.Designer.v_b
Form1.v_b
My Project
app.manifest
Application.Designer.v_b
Application.myapp
AssemblyInfo.v_b
Resources.Designer.v_b
Settings.Designer.v_b
Overlay.Designer.v_b
Overlay.v_b
PVSAVPlayerHowTo.vbproj.user
PVS.AVPlayer Examples
AVPlayerExample.ex_
FolderView.ex_
AVPlayerExample.ex_
FolderView.ex_
PVS.AVPlayer.dll
/****************************************************************

    PVS.AVPlayer - Version 0.77
    December 2017, The Netherlands
    © Copyright 2017 PVS The Netherlands - Free to Use

    ****************

    For use with Microsoft .NET Framework version 2.0 or higher and any CPU.
    Created with Microsoft Visual Studio.

    Articles on CodeProject with information on the use of the PVS.AVPlayer library:
    About the Player: http://www.codeproject.com/Articles/109714/PVS-AVPlayer-MCI-Audio-and-Video-Library
    About the Sound Recorder: http://www.codeproject.com/Articles/1116698/PVS-AVPlayer-MCI-Sound-Recorder

    ****************

    The PVS.AVPlayer library source code is divided into 8 files:

    1. Player.cs            - Player source code
    2. MouseEvents.cs       - extension source code of Player.cs, provides player display mouse events
    3. OutputLevel.cs       - extension source code of Player.cs, provides player audio output level data
    4. DisplayClones.cs     - extension source code of Player.cs, provides player multiple video displays 
    5. Subtitles.cs         - extension source code of Player.cs, provides player SubRip (.srt) subtitles
    6. Recorder.cs          - Sound Recorder source code
    7. PlayerRecorder.cs    - code used by both Player.cs (and its extension files) and Recorder.cs
    8. Infolabel.cs         - Info Label source code

    Required references:
    System
    System.Drawing
    System.Windows.Forms

    ****************

    This file: PlayerRecorder.cs

    Global Class
    DeviceInfo Class
    MCI Direct Access Class
    MP3 Info Data Class
    SafeNativeMethods Class
    TaskbarIndicator Class
    Hide System Object Members Classes
    Grouping Classes Player
    Grouping Classes Recorder
    
    Used by 'Player.cs' (and its extension files) and 'Recorder.cs'.

    ****************

    About Media Control Interface (MCI)
    - for information about MCI, please see https://msdn.microsoft.com/en-us/library/vs/alm/dd757151(v=vs.85).aspx
    - you can find many articles about mci on the internet, search examples: 'c# mci', 'vb mci', 'mcisendstring'
      or the subject of your question.
    - the PVS.AVPlayer library also provides 'direct access' to MCI (e.g. 'Mci.MciSendString').

    ****************

    Thanks!

    Many thanks to Microsoft (Windows, .NET Framework, Visual Studio Express, etc.), all the people
    writing about programming on the internet (a great source for ideas and solving problems),
    the websites publishing those or other writings about programming, the people responding
    to the PVS.AVPlayer articles with comments and suggestions and, of course, CodeProject:
    thank you Deeksha, Smitha and Sean Ewington for the beautiful articles and all!

    TaskbarProgress class based on code by WhoIsRich at:
    stackoverflow.com/questions/1295890/windows-7-progress-bar-in-taskbar-in-c

    Special thanks to Sean Ewington of CodeProject who also took care of publishing the many code
    updates and changes in the PVS.AVPlayer articles in a friendly, fast, and highly competent manner.
    Thank you very much, Sean!

    ****************************************************************/

#region Usings

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

#endregion

[assembly: CLSCompliant(true)]

namespace PVS.AVPlayer
{

    // ******************************** Global Class

    // The Global class contains some constants and a random number generator.
    // Used by both the Player and Recorder class.

    #region Global Class

    internal static class Global
    {
        // PVS.AVPlayer Library Version
        internal const float VERSION                    = 0.77F;
        internal const string VERSION_STRING            = "PVS.AVPlayer 0.77";

        // MCI ErrorCodes
        // For a complete list of mci errorcodes please see: https://msdn.microsoft.com/en-us/library/aa228215%28v=vs.60%29.aspx
        internal const int MCIERR_NO_ERROR              = 0;
        internal const int MCIERR_UNRECOGNIZED_KEYWORD  = 259;
        internal const int MCIERR_INVALID_DEVICE_NAME   = 263;
        internal const int MCIERR_PARSER_INTERNAL       = 271;
        internal const int MCIERR_DEVICE_NOT_READY      = 263;  // actually 276, but this returns the 'proper' message
        internal const int MCIERR_UNSUPPORTED_FUNCTION  = 274;
        internal const int MCIERR_INTERNAL              = 277;
        internal const int MCIERR_OUTOFRANGE            = 282;
        internal const int MCIERR_FILE_NOT_SAVED        = 286;
        internal const int MCIERR_DUPLICATE_ALIAS       = 289;
        internal const int MCIERR_NULL_PARAMETER_BLOCK  = 297;
        internal const int MCIERR_FILENAME_REQUIRED     = 304;
        internal const int MCIERR_WAVE_INPUTUNSPECIFIED = 325;
        internal const int MCIERR_WAVE_INPUTSUNSUITABLE = 328;
        internal const int MCIERR_NO_WINDOW             = 346;
        internal const int MCIERR_CREATEWINDOW          = 347;

        // MCI communication buffer sizes
        internal const int BUFFER_SIZE                  = 512;
        internal const int SMALL_BUFFER_SIZE            = 64;
        internal const int LEVEL_BUFFER_SIZE            = 32;

        // Used with creating player and recorder ID's (deviceId / alias)
        internal static Random RandomNumber             = new Random();
        internal const int ALIAS_REPEAT                 = 10; // max repeat to get new alias with error MCIERR_DUPLICATE_ALIAS
    }

    #endregion


    // ******************************** Device Info

    // The Device Info class is used to get information about the available system input devices.
    // Used by both the Player (audio output levels) and Recorder (sound input levels) class.

    #region Device Info

    internal static class DeviceInfo
    {
        // ******************************** Fields

        #region Fields

        private static bool di_HasDevice;
        private static string di_DeviceId = "T" + Global.RandomNumber.Next(10, 100000000);
        private static StringBuilder di_TextBuffer = new StringBuilder(Global.BUFFER_SIZE);
        private static object di_DeviceLock = new object();

        #endregion

        // ******************************** Open / Close Temporary Device

        #region Open / Close Temporary Device

        private static void OpenTempDevice()
        {
            if (di_HasDevice) return;

            if (SafeNativeMethods.mciSendString("open new type waveaudio alias " + di_DeviceId, null, 0, IntPtr.Zero) != Global.MCIERR_NO_ERROR) return;
            Application.DoEvents();
            di_HasDevice = true;
        }

        private static void CloseTempDevice()
        {
            if (di_HasDevice)
            {
                SafeNativeMethods.mciSendString("close " + di_DeviceId + " wait", null, 0, IntPtr.Zero);
                Application.DoEvents();
                di_HasDevice = false;
            }
        }

        #endregion

        // ******************************** GetInputDeviceIndex / GetInputDeviceName / GetAllInputDeviceNames

        #region GetInputDeviceIndex / GetInputDeviceName / GetAllInputDeviceNames

        internal static int GetInputDeviceIndex(string deviceName)
        {
            string[] deviceNames = GetAllInputDeviceNames();
            int index = -1;

            for (int i = 0; i < deviceNames.Length; i++)
            {
                if (deviceNames[i].IndexOf(deviceName, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }

        internal static string GetInputDeviceName(int deviceIndex)
        {
            if (deviceIndex < 0) return string.Empty;

            lock (di_DeviceLock)
            {
                int error = Global.MCIERR_NO_ERROR;
                OpenTempDevice();

                di_TextBuffer.Length = 0;
                SafeNativeMethods.mciSendString("set " + di_DeviceId + " input " + deviceIndex, null, 0, IntPtr.Zero);
                if (error == Global.MCIERR_NO_ERROR)
                {
                    error = SafeNativeMethods.mciSendString("info " + di_DeviceId + " input", di_TextBuffer, Global.BUFFER_SIZE, IntPtr.Zero);
                }

                CloseTempDevice();
                return error == Global.MCIERR_NO_ERROR ? di_TextBuffer.ToString() : string.Empty;
            }
        }

        internal static string[] GetAllInputDeviceNames()
        {
            lock (di_DeviceLock)
            {
                int error = Global.MCIERR_NO_ERROR;
                StringBuilder deviceNames = new StringBuilder(512);
                string[] separator = { ",," };
                string tempName;
                int deviceCount = 0;

                OpenTempDevice();

                while (error == Global.MCIERR_NO_ERROR)
                {
                    error = SafeNativeMethods.mciSendString("set " + di_DeviceId + " input " + deviceCount, null, 0, IntPtr.Zero);
                    if (error == Global.MCIERR_NO_ERROR)
                    {
                        di_TextBuffer.Length = 0;
                        error = SafeNativeMethods.mciSendString("info " + di_DeviceId + " input", di_TextBuffer, Global.BUFFER_SIZE, IntPtr.Zero);
                        if (error == Global.MCIERR_NO_ERROR)
                        {
                            tempName = di_TextBuffer.ToString();
                            if (tempName == "") error = -1;
                            else
                            {
                                if (deviceCount > 0) deviceNames.Append(separator[0]);
                                deviceNames.Append(tempName);
                                deviceCount++;
                            }
                        }
                    }
                }

                CloseTempDevice();
                if (deviceCount == 0) return new string[0];
                else return deviceNames.ToString().Split(separator, StringSplitOptions.None);
            }
        }

        #endregion

        // ******************************** GetOpenDevicesCount

        #region GetOpenDevicesCount

        internal static int GetOpenDevicesCount()
        {
            int result;

            di_TextBuffer.Length = 0;
            SafeNativeMethods.mciSendString("sysinfo all quantity open", di_TextBuffer, Global.BUFFER_SIZE, IntPtr.Zero);
            int.TryParse(di_TextBuffer.ToString(), out result);
            return result;
        }

        #endregion
    }

    #endregion


    // ******************************** Slider Info

    // The Slider Info class can be used to get information about positions on a trackbar.

    #region Slider Info

    /// <summary>
    /// A static class that provides location information about values of a slider (trackbar).
    /// </summary>
    [CLSCompliant(true)]
    public static class SliderValue
    {
        // standard .Net TrackBar track margins (pixels between border and begin/end of track)
        private const int SLIDER_LEFT_MARGIN = 13;
        private const int SLIDER_RIGHT_MARGIN = 14;
        private const int SLIDER_TOP_MARGIN = 13;
        private const int SLIDER_BOTTOM_MARGIN = 14;

        /// <summary>
        /// Returns the slider value at the specified location on the specified slider (trackbar).
        /// </summary>
        /// <param name="slider">The slider whose value should be obtained.</param>
        /// <param name="location">The relative x- and y-coordinates on the slider.</param>
        public static int FromPoint(TrackBar slider, Point location)
        {
            return FromPoint(slider, location.X, location.Y);
        }

        /// <summary>
        /// Returns the slider value at the specified location on the specified slider (trackbar).
        /// </summary>
        /// <param name="slider">The slider whose value should be obtained.</param>
        /// <param name="x">The relative x-coordinate on the slider (for horizontal oriented sliders).</param>
        /// <param name="y">The relative y-coordinate on the slider (for vertical oriented sliders).</param>
        public static int FromPoint(TrackBar slider, int x, int y)
        {
            float pos = 0;

            if (slider.Orientation == Orientation.Horizontal)
            {
                if (x <= SLIDER_LEFT_MARGIN) pos = 0;
                else if (x >= slider.Width - SLIDER_LEFT_MARGIN) pos = 1;
                else pos = (float)(x - SLIDER_LEFT_MARGIN) / (slider.Width - (SLIDER_LEFT_MARGIN + SLIDER_RIGHT_MARGIN));
            }
            else
            {
                if (y <= SLIDER_TOP_MARGIN) pos = 1;
                else if (y >= slider.Height - SLIDER_TOP_MARGIN) pos = 0;
                else pos = 1 - (float)(y - SLIDER_TOP_MARGIN) / (slider.Height - (SLIDER_TOP_MARGIN + SLIDER_BOTTOM_MARGIN));
            }
            //pos = (pos * (slider.Maximum - slider.Minimum)) + slider.Minimum;
            return (int)(pos * (slider.Maximum - slider.Minimum)) + slider.Minimum;
        }

        /// <summary>
        /// Returns the location of the specified value on the specified slider (trackbar).
        /// </summary>
        /// /// <param name="slider">The slider whose value location should be obtained.</param>
        /// <param name="value">The value of the slider.</param>
        public static Point ToPoint(TrackBar slider, int value)
        {
            Point result = Point.Empty;
            if (slider != null)
            {
                double pos = 0;
                if (value > slider.Minimum)
                {
                    if (value >= slider.Maximum) pos = 1;
                    else pos = (double)(value - slider.Minimum) / (slider.Maximum - slider.Minimum);
                }
                if (slider.Orientation == Orientation.Horizontal) result.X = (int)(pos * (slider.Width - (SLIDER_LEFT_MARGIN + SLIDER_RIGHT_MARGIN)) + 0.5) + SLIDER_LEFT_MARGIN;
                else result.Y = (int)(pos * (slider.Height - (SLIDER_TOP_MARGIN + SLIDER_BOTTOM_MARGIN)) + 0.5) + SLIDER_TOP_MARGIN;
            }
            return result;
        }

        // Can't hide 'Equals' and 'ReferenceEquals' (?)
    }

    #endregion


    // ******************************** MCI Direct Access (without Player or Recorder Instance)

    // The MCI class gives access to the native Win32 functions mciSendString and mciGetErrorString.
    // These 2 functions are all you need to fully control any available MCI device.

    #region MCI Direct Access (without Player or Recorder Instance)

    /// <summary>
    /// Represents a class that provides direct access to (native Win32) MCI functions.
    /// </summary>
    [CLSCompliant(true)]
    public static class Mci
    {
        /// <summary>
        /// Sends a command string to an MCI device. The device that the command is sent to is specified in the command string.
        /// </summary>
        /// <param name="command">String that specifies an MCI command string.</param>
        /// <param name="resultText">A buffer that receives return information. If there's no return information, this parameter can be null.</param>
        /// <param name="hwndCallback">Handle to a callback window if the "notify" flag was specified in the command string or null.</param>
        public static int SendString(string command, StringBuilder resultText, IntPtr hwndCallback)
        {
            try
            {
                return resultText == null ? SafeNativeMethods.mciSendString(command, null, 0, hwndCallback) : SafeNativeMethods.mciSendString(command, resultText, resultText.Capacity, hwndCallback);
            }
            catch { return Global.MCIERR_PARSER_INTERNAL; }
        }

        /// <summary>
        /// Retrieves a string that describes the specified MCI error code.
        /// </summary>
        /// <param name="errorCode">Error code returned by the MCI.SendString method.</param>
        /// <param name="errorText">A buffer that receives a string describing the specified error.</param>
        public static int GetErrorString(int errorCode, StringBuilder errorText)
        {
            try
            {
                return SafeNativeMethods.mciGetErrorString(errorCode, errorText, errorText.Capacity);
            }
            catch { return Global.MCIERR_PARSER_INTERNAL; }
        }

    }

    #endregion


    // ******************************** Tag Info Data Class

    // The Tag Info Data Class holds MP3 (Id3) and WMA (ASF) tag information from audio media files
    // provided by the Player.Media.GetTagInfo methods.

    #region Tag Info Data Class

    /// <summary>
    /// A class that is used to store MP3, WMA and ASF tag information taken from media files.
    /// </summary>
    [CLSCompliant(true)]
    public sealed class TagInfo : HideObjectMembers, IDisposable
    {
        private bool        _disposed;
        internal TimeSpan   _duration;
        internal int        _trackNumber;

        /// <summary>
        /// Gets the main artist(s)/performer(s)/band/orchestra of the audio in the file.
        /// </summary>
        public string Artist { get; internal set; }

        /// <summary>
        /// Gets the actual title of the audio in the file.
        /// </summary>
        public string Title { get; internal set; }

        /// <summary>
        /// Gets the title of the album the audio in the file is taken from.
        /// </summary>
        public string Album { get; internal set; }

        /// <summary>
        /// Gets the order number of the audio file on its original recording (album), may also include the total number of tracks e.g. "3/12".
        /// </summary>
        public string Track { get; internal set; }

        /// <summary>
        /// Gets the order number of the audio file on its original recording (album).
        /// </summary>
        public int TrackNumber { get { return _trackNumber; } }

        /// <summary>
        /// Gets the year of the recording of the audio in the file.
        /// </summary>
        public string Year { get; internal set; }

        /// <summary>
        /// Gets the duration (length) of the audio in the file.
        /// </summary>
        public TimeSpan Duration { get { return _duration; } }

        /// <summary>
        /// Gets the genre of the audio in the file.
        /// </summary>
        public string Genre { get; internal set; }

        /// <summary>
        /// Gets an image (album art) related to the audio in the file.
        /// </summary>
        public Image Image { get; internal set; }

        /// <summary>
        /// Remove the tag information and clean up any resources being used.
        /// </summary>
        public void Dispose()
        {
            if (!_disposed)
            {
                _disposed   = true;
                Artist      = null;
                Title       = null;
                Album       = null;
                Year        = null;
                Genre       = null;
                if (Image  != null)
                {
                    try { Image.Dispose(); }
                    catch { /* ignore */ }
                    Image = null;
                }
            }
        }
    }

    #endregion


    // ******************************** SafeNativeMethods - DLL Import

    // The SafeNativeMethods class provides the PVS.AVPlayer library access to native Win32 functions
    // (DllImport, P/Invoke) and also contains a few related (helper) functions.

    #region SafeNativeMethods - DLL Import

    [System.Security.SuppressUnmanagedCodeSecurity]
    internal static class SafeNativeMethods
    {
        // ******************************** MCI

        #region MCI

        #pragma warning disable IDE1006 // Naming Styles

        [DllImport("winmm.dll", CharSet = CharSet.Unicode)]
        internal static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);

        [DllImport("winmm.dll", CharSet = CharSet.Unicode)]
        internal static extern Int32 mciGetErrorString(Int32 errorCode, StringBuilder errorText, Int32 errorTextSize);

        #pragma warning restore IDE1006 // Naming Styles

        #endregion

        // ******************************** Win32 Windows

        #region Win32 Windows

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);

        internal const uint SWP_NOSIZE = 0x1;
        internal const uint SWP_NOMOVE = 0X2;
        internal const uint SWP_NOZORDER = 0X4;
        internal const uint SWP_HIDEWINDOW = 0x80;
        internal const uint SWP_SHOWWINDOW = 0x40;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [DllImport("user32.dll")]
        internal static extern IntPtr GetForegroundWindow();

        [StructLayout(LayoutKind.Sequential)]
        internal struct Rect
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetWindowRect(IntPtr hwnd, out Rect lpRect);

        #endregion

        // ******************************** BitBlt (VideoCopy)

        #region BitBlt

        internal const int SRCCOPY          = 0xCC0020;
        //internal const int CAPTUREBLT       = 0x40000000;
        //internal const int MIXBLT           = 0x40CC0020;

        internal const uint SRCCOPY_U       = 0xCC0020;
        //internal const uint CAPTUREBLT_U    = 0x40000000;
        //internal const uint MIXBLT_U        = 0x40CC0020;

        internal const int STRETCH_HALFTONE = 4; // quality mode setting for SetStretchBltMode

        [DllImport("gdi32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);

        [DllImport("gdi32.dll")]
        internal static extern bool StretchBlt(IntPtr hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
        IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, uint dwRop);

        [DllImport("gdi32.dll")]
        internal static extern int SetStretchBltMode(IntPtr hdc, int iStretchMode);

        [DllImport("Msimg32.dll")]
        internal static extern bool TransparentBlt(IntPtr hdcDest, // handle to destination DC
        int nXOriginDest, // x-coord of destination upper-left corner
        int nYOriginDest, // y-coord of destination upper-left corner
        int nWidthDest, // width of destination rectangle
        int hHeightDest, // height of destination rectangle
        IntPtr hdcSrc, // handle to source DC
        int nXOriginSrc, // x-coord of source upper-left corner
        int nYOriginSrc, // y-coord of source upper-left corner
        int nWidthSrc, // width of source rectangle
        int nHeightSrc, // height of source rectangle
        int crTransparent // color to make transparent
        );

        #endregion

        // ******************************** Center System Dialogs

        #region Center System Dialogs

        internal static bool CenterSystemDialog(string fileName, string arguments, Form baseForm) //Rectangle itemBounds)
        {
            bool result = true;

            try
            {
                IntPtr hWndCurrent = GetForegroundWindow();

                System.Diagnostics.Process showControlPanel = new System.Diagnostics.Process
                {
                    StartInfo = { FileName = fileName, Arguments = arguments }
                };
                showControlPanel.Start();

                if (baseForm != null)
                {
                    int i = 0;
                    IntPtr hWnd = GetForegroundWindow();
                    while (i < 100 && hWnd == hWndCurrent)
                    {
                        ++i;
                        System.Threading.Thread.Sleep(10);
                        hWnd = GetForegroundWindow();
                    }

                    if (hWnd != hWndCurrent)
                    {
                        Rectangle r1 = new Rectangle();
                        Rectangle r2 = Screen.GetWorkingArea(baseForm);
                        Rect r3;
                        GetWindowRect(hWnd, out r3);

                        r1.Width = r3.Right - r3.Left + 1;
                        r1.Height = r3.Bottom - r3.Top + 1;

                        r1.X = baseForm.Bounds.Left + (baseForm.Bounds.Width - r1.Width) / 2;
                        if (r1.X < r2.X) r1.X = r2.X + 2;
                        else if (r1.X + r1.Width > r2.Width) r1.X = r2.Width - r1.Width - 2;

                        r1.Y = baseForm.Bounds.Top + (baseForm.Bounds.Height - r1.Height) / 2;
                        if (r1.Y < r2.Y) r1.Y = r2.Y + 2;
                        else if (r1.Y + r1.Height > r2.Height) r1.Y = r2.Height - r1.Height - 2;

                        SetWindowPos(hWnd, IntPtr.Zero, r1.X, r1.Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
                    }
                }
            }
            catch { result = false; }
            return result;
        }

        #endregion

        // ******************************** Change System Sleep Mode

        #region Change System Sleep Mode

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern uint SetThreadExecutionState(uint esFlags);

        private const uint ES_NO_SLEEP = 0x80000003;
        private static int _sleepCount;
        private static uint _oldState;

        internal static bool SleepStatus
        {
            //get { return sleepCount > 0; }
            set
            {
                if (value)
                {
                    uint flags = SetThreadExecutionState(ES_NO_SLEEP);
                    if (++_sleepCount == 1) _oldState = flags;
                }
                else
                {
                    if (_sleepCount > 0)
                    {
                        if (--_sleepCount == 0) SetThreadExecutionState(_oldState);
                    }
                }
            }
        }

        #endregion

        // ******************************** Windows Event Hook

        #region Windows Event Hook

        // WINAPI Declarations
        internal const uint EVENT_SYSTEM_MENUSTART      = 0x0004;
        internal const uint EVENT_SYSTEM_MENUEND        = 0x0005;
        internal const uint EVENT_SYSTEM_MENUPOPUPSTART = 0x0006;
        internal const uint EVENT_SYSTEM_MENUPOPUPEND   = 0x0007;
        internal const uint WINEVENT_OUTOFCONTEXT       = 0x0000;
        internal const uint WINEVENT_INCONTEXT          = 0x0004;

        internal delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        [DllImport("user32.dll")]
        internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

        [DllImport("user32.dll")]
        internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);

        #endregion

        // ******************************** InfoLabel

        #region InfoLabel

        [DllImport("Gdi32.dll")] // EntryPoint = "CreateRoundRectRgn")]
        internal static extern IntPtr CreateRoundRectRgn
            (
                int nLeftRect, // x-coordinate of upper-left corner
                int nTopRect, // y-coordinate of upper-left corner
                int nRightRect, // x-coordinate of lower-right corner
                int nBottomRect, // y-coordinate of lower-right corner
                int nWidthEllipse, // height of ellipse
                int nHeightEllipse // width of ellipse
             );

        [DllImport("gdi32.dll")]
        internal static extern bool DeleteObject(IntPtr hObject);

        #endregion
    }

    #endregion


    // ******************************** TaskbarIndicator - Com Import

    // TaskbarIndicator class by WhoIsRich, taken from:
    // stackoverflow.com/questions/1295890/windows-7-progress-bar-in-taskbar-in-c

    #region TaskbarIndicator - Com Import

    internal sealed class TaskbarIndicator
    {
        [ComImport()]
        [Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface ITaskbarList3
        {
            // ITaskbarList
            void HrInit();
            void AddTab(IntPtr hwnd);
            void DeleteTab(IntPtr hwnd);
            void ActivateTab(IntPtr hwnd);
            void SetActiveAlt(IntPtr hwnd);

            // ITaskbarList2
            void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);

            // ITaskbarList3
            void SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);
            void SetProgressState(IntPtr hwnd, TaskbarStates state);
        }

        [ComImport()]
        [Guid("56fdf344-fd6d-11d0-958a-006097c9a090")]
        [ClassInterface(ClassInterfaceType.None)]
        internal class TaskbarInstance { }
    }

    #endregion


    // ******************************** Hide System.Object Members Classes

    // The HideObjectMembers abstract class is inherited by many other classes to hide some methods
    // inherited from System.Object from intellisense.

    #region  Hide System Object Members Classes

    /// <summary>
    /// Internal class that is used to hide System.Object members in derived classes.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public abstract class HideObjectMembers
    {
        #region Hide Inherited System.Object members

        /// <summary>
        /// Gets the type of the current instance.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Type GetType() { return base.GetType(); } // this can't be hidden ???

        /// <summary>
        /// Serves as a hash function for a particular object.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override int GetHashCode() { return base.GetHashCode(); }

        /// <summary>
        /// Returns a string that represents the current object.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string ToString() { return base.ToString(); }

        /// <summary>
        /// Determines whether the specified object is equal to the current object.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Equals(object obj) { return base.Equals(obj); }

        #endregion
    }

    /// <summary>
    /// Internal class that is used to hide System.Object members in the from EventArgs derived classes.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public abstract class HideObjectEventArgs : EventArgs
    {
        #region Hide Inherited System.Object members

        /// <summary>
        /// Gets the type of the current instance.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Type GetType() { return base.GetType(); } // this can't be hidden ???

        /// <summary>
        /// Serves as a hash function for a particular object.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override int GetHashCode() { return base.GetHashCode(); }

        /// <summary>
        /// Returns a string that represents the current object.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string ToString() { return base.ToString(); }

        /// <summary>
        /// Determines whether the specified object is equal to the current object.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Equals(object obj) { return base.Equals(obj); }

        #endregion
    }

    #endregion


    // ******************************** Grouping Classes Player

    // These classes are used to group together Player class methods and properties.

    #region Audio Class

    /// <summary>
    /// A class that is used to group together the Audio methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Audio : HideObjectMembers
    {
        private Player _base;
        internal Audio(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets a value indicating whether the playing media contains audio.
        /// </summary>
        public bool Present
        {
            get { return _base._hasAudio; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the audio output of the player is enabled (default: true).
        /// </summary>
        public bool Enabled
        {
            get { return _base._audioEnabled; }
            set { _base.AV_SetAudioEnabled(value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the audio output of the player is muted (default: false).
        /// </summary>
        public bool Mute
        {
            get { return !_base._audioEnabled; }
            set { _base.AV_SetAudioEnabled(!value); }
        }

        /// <summary>
        /// Gets or sets a value indicating the active audio track of the playing media. May not work for all types of media.
        /// </summary>
        public int Track
        {
            get
            {
                int track = 0;
                if (_base._playing)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("status " + _base._deviceId + " audio stream", _base._mciSmallBuffer2, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR)
                    {
                        for (int i = 0; i < _base._mciSmallBuffer2.Length; ++i)
                        {
                            track = 10 * track + (_base._mciSmallBuffer2[i] - 48);
                        }
                    }
                }
                else
                {
                    //stream = 1;
                    _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                }
                return track;
            }
            set { _base.AV_SetAudioTrack(value); }
        }

        /// <summary>
        /// Gets the number of audio tracks available in the playing media. May not work for all types of media.
        /// </summary>
        public int TrackCount
        {
            get
            {
                int count = 0;
                if (_base._hasAudio)
                {
                    int error = 0;
                    int oldTrack = this.Track;
                    count++;

                    while (error == 0)
                    {
                        error = SafeNativeMethods.mciSendString("setaudio " + _base._deviceId + " stream to " + ++count, null, 0, IntPtr.Zero);
                    }
                    if (--count > 1) _base._lastError = SafeNativeMethods.mciSendString("setaudio " + _base._deviceId + " stream to " + oldTrack, null, 0, IntPtr.Zero);
                    _base._lastError = Global.MCIERR_NO_ERROR;
                }
                else
                {
                    _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                }
                return count;
            }
        }

        /// <summary>
        /// Gets or sets the audio volume of the player, value 0 (mute) to 1000 (max) (default: 1000).
        /// </summary>
        public int Volume
        {
            get { return _base._audioVolume; }
            set { _base.AV_SetAudioVolume(value); }
        }

        /// <summary>
        /// Gets or sets the audio balance of the player, value 0 (left) to 1000 (right) (default: 500).
        /// </summary>
        public int Balance
        {
            get { return _base._audioBalance; }
            set { _base.AV_SetAudioBalance(value, false); }
        }

        /// <summary>
        /// Provides access to the audio output peak level device settings of the player (e.g. Player.Audio.LevelDevice.Enabled).
        /// </summary>
        public LevelDevice LevelDevice
        {
            get { return _base.LevelDevice; }
        }
    }

    #endregion

    #region Video Class

    /// <summary>
    /// A class that is used to group together the Video methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Video : HideObjectMembers
    {
        private Player  _base;
        private bool    _zoomBusy;
        private bool    _boundsBusy;

        internal Video(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets a value indicating whether the playing media contains video.
        /// </summary>
        public bool Present
        {
            get { return _base._hasVideo; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the display of video of the player is enabled (default: true).
        /// </summary>
        public bool Enabled
        {
            get { return _base._videoEnabled; }
            set { _base.AV_SetVideoEnabled(value); }
        }

        /// <summary>
        /// Gets or sets a value indicating the active video track of the playing media. May not work for all types of media.
        /// </summary>
        public int Track
        {
            get
            {
                int track = 0;
                if (_base._playing)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("status " + _base._deviceId + " video stream", _base._mciSmallBuffer2, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR)
                    {
                        for (int i = 0; i < _base._mciSmallBuffer2.Length; ++i)
                        {
                            track = 10 * track + (_base._mciSmallBuffer2[i] - 48);
                        }
                    }
                }
                else
                {
                    _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                }
                return track;
            }
            set { _base.AV_SetVideoTrack(value); }
        }

        /// <summary>
        /// Gets the number of video tracks available in the playing media. May not work for all types of media.
        /// </summary>
        public int TrackCount
        {
            get
            {
                int count = 0;
                if (_base._hasVideo)
                {
                    int error = 0;
                    int oldTrack = this.Track;
                    count++;

                    while (error == 0)
                    {
                        error = SafeNativeMethods.mciSendString("setvideo " + _base._deviceId + " stream to " + ++count, null, 0, IntPtr.Zero);
                    }
                    if (--count > 1) _base._lastError = SafeNativeMethods.mciSendString("setvideo " + _base._deviceId + " stream to " + oldTrack, null, 0, IntPtr.Zero);
                    _base._lastError = Global.MCIERR_NO_ERROR;
                }
                else
                {
                    _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                }
                return count;
            }
        }

        /// <summary>
        /// Gets or sets the size and location of the video image on the display of the player. When set, the displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        public Rectangle Bounds
        {
            get { return _base._videoBounds; }
            set
            {
                if (_base._hasDisplay)
                {
                    if (!_boundsBusy && (value.Width >= 10) && (value.Height >= 10))
                    {
                        _boundsBusy = true;

                        _base._videoBounds = value;
                        _base._hasVideoBounds = true;

                        if (_base._displayMode == DisplayMode.Manual) _base._display.Refresh();
                        else _base.Display.Mode = DisplayMode.Manual;

                        if (_base._hasDisplayClipping) _base.AV_UpdateDisplayClip();

                        _base.OnMediaVideoBoundsChanged();

                        _boundsBusy = false;
                    }
                    else _base._lastError = Global.MCIERR_OUTOFRANGE;
                }
                else _base._lastError = Global.MCIERR_NO_WINDOW;
            }
        }

        /// <summary>
        /// Gets the original size of the video image of the playing media.
        /// </summary>
        public Size SourceSize
        {
            get { return _base._hasVideoSourceSize ? _base._videoSourceSize : Size.Empty; }
        }

        /// <summary>
        /// Gets the nominal video frame rate of the playing media. The units are in frames per second multiplied by 1000.
        /// </summary>
        public int FrameRate
        {
            get
            {
                if (_base._playing && _base._hasVideo)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("status " + _base._deviceId + " nominal frame rate", _base._mciSmallBuffer2, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR)
                    {
                        int number = 0;
                        for (int i = 0; i < _base._mciSmallBuffer2.Length; ++i)
                        {
                            number = 10 * number + (_base._mciSmallBuffer2[i] - 48);
                        }
                        return number;
                    }
                }
                else _base._lastError = Global.MCIERR_NO_ERROR;
                return 0;
            }
        }

        /// <summary>
        /// Enlarges or reduces the size of the video image at the center location of the display of the player. The displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        /// <param name="factor">The factor by which the video image is to be zoomed.</param>
        public int Zoom(double factor)
        {
            if (_base._hasDisplay) return Zoom(factor, _base._display.Width / 2, _base._display.Height / 2);
            _base._lastError = Global.MCIERR_NO_WINDOW;
            return _base._lastError;
        }

        /// <summary>
        /// Enlarges or reduces the size of the video image at the specified display location. The displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        /// <param name="factor">The factor by which the video image is to be zoomed.</param>
        /// <param name="center">The center location of the zoom on the display of the player.</param>
        public int Zoom(double factor, Point center)
        {
            return (Zoom(factor, center.X, center.Y));
        }

        /// <summary>
        /// Enlarges or reduces the size of the video image at the specified display location. The displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        /// <param name="factor">The factor by which the video image is to be zoomed.</param>
        /// <param name="xCenter">The horizontal (x) center location of the zoom on the display of the player.</param>
        /// <param name="yCenter">The vertical (y) center location of the zoom on the display of the player.</param>
        public int Zoom(double factor, int xCenter, int yCenter)
        {
            if (_base._hasVideo && factor > 0)
            {
                _base._lastError = Global.MCIERR_NO_ERROR;

                if (factor != 1)
                {
                    if (_zoomBusy) return _base._lastError;

                    _zoomBusy = true;
                    double width = 0;
                    double height = 0;
                    Rectangle r = new Rectangle(_base._videoBounds.Location, _base._videoBounds.Size);

                    if (r.Width < r.Height)
                    {
                        r.X = (int)Math.Round(-factor * (xCenter - r.X)) + xCenter;
                        width = r.Width * factor;

                        if (width >= 10)
                        {
                            r.Y = (int)Math.Round(-(width / r.Width) * (yCenter - r.Y)) + yCenter;
                            height = (width / r.Width) * r.Height;
                        }
                    }
                    else
                    {
                        r.Y = (int)Math.Round(-factor * (yCenter - r.Y)) + yCenter;
                        height = r.Height * factor;

                        if (height >= 10)
                        {
                            r.X = (int)Math.Round(-(height / r.Height) * (xCenter - r.X)) + xCenter;
                            width = (height / r.Height) * r.Width;
                        }
                    }

                    r.Width = (int)Math.Round(width);
                    r.Height = (int)Math.Round(height);
                    Bounds = r;

                    _zoomBusy = false;
                }
            }
            else _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
            return _base._lastError;
        }

        /// <summary>
        /// Enlarges the specified part of the display of the player to the entire display of the player. The displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        /// <param name="area">The area of the display of the player to enlarge.</param>
        public int Zoom(Rectangle area)
        {
            if (_base._hasVideo)
            {
                if ((area.X >= 0 && area.X <= (_base._display.Width - 8)) && (area.Y >= 0 && area.Y <= (_base._display.Height - 8)) && (area.X + area.Width <= _base._display.Width) && (area.Y + area.Height <= _base._display.Height))
                {
                    double factorX = (double)_base._display.Width / area.Width;
                    double factorY = (double)_base._display.Height / area.Height;

                    Bounds = new Rectangle(
                        (int)((_base._videoBounds.X - area.X) * factorX),
                        (int)((_base._videoBounds.Y - area.Y) * factorY),
                        (int)(_base._videoBounds.Width * factorX),
                        (int)(_base._videoBounds.Height * factorY));
                }
                else _base._lastError = Global.MCIERR_OUTOFRANGE;
            }
            else _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
            return _base._lastError;
        }

        /// <summary>
        /// Moves the location of the video image on the display of the player by the given amount of pixels. The displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        /// <param name="horizontal">The amount of pixels to move the video image in the horizontal (x) direction.</param>
        /// <param name="vertical">The amount of pixels to move the video image in the vertical (y) direction.</param>
        public int Move(int horizontal, int vertical)
        {
            Bounds = new Rectangle(_base._videoBounds.X + horizontal, _base._videoBounds.Y + vertical, _base._videoBounds.Width, _base._videoBounds.Height);
            return _base._lastError;
        }

        /// <summary>
        /// Enlarges or reduces the size of the video image by the given amount of pixels at the center of the video. The displaymode of the player is set to Displaymode.Manual.
        /// </summary>
        /// <param name="horizontal">The amount of pixels to stretch the video image in the horizontal (x) direction.</param>
        /// <param name="vertical">The amount of pixels to stretch the video image in the vertical (y) direction.</param>
        public int Stretch(int horizontal, int vertical)
        {
            Bounds = new Rectangle(_base._videoBounds.X - (horizontal / 2), _base._videoBounds.Y - (vertical / 2), _base._videoBounds.Width + horizontal, _base._videoBounds.Height + vertical);
            return _base._lastError;
        }
    }

    #endregion

    #region Display Class

    /// <summary>
    /// A class that is used to group together the Display methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Display : HideObjectMembers
    {
        #region Display Fields

        private Player  _base;
        private bool    _dragEnabled;
        //private bool    _dragFullScreen;
        private Cursor  _dragCursor = Cursors.SizeAll;
        private Form    _dragForm;
        private Point   _dragLocation;

        #endregion

        internal Display(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets or sets the display window (form or control) of the player that is used to display video and overlays.
        /// </summary>
        public Control Window
        {
            get { return _base._display; }
            set { _base.AV_SetDisplay(value, true); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to prevent 'flashing' of the parent window (form) of the display of the player when the player stops playing media (default: true).
        /// </summary>
        public bool WindowFix
        {
            get { return _base._closeHandlerEnabled; }
            set { _base._closeHandlerEnabled = value; }
        }

        /// <summary>
        /// Gets or sets the display mode (size and location) of the video image on the display of the player (default: ZoomAndCenter).
        /// </summary>
        public DisplayMode Mode
        {
            get { return _base._displayMode; }
            set
            {
                if (_base._displayMode != value)
                {
                    _base._displayMode = value;
                    if (value == DisplayMode.Manual)
                    {
                        if (!_base._hasVideoBounds)
                        {
                            _base._videoBounds.X = _base._videoBounds.Y = 0;
                            _base._videoBounds.Size = _base._display.Size;
                            _base._hasVideoBounds = true;
                        }
                    }
                    else _base._hasVideoBounds = false;

                    if (_base._hasVideo) _base.AV_SetDisplayMode(value);
                    if (_base._hasDisplayClipping) _base.AV_UpdateDisplayClip();
                    if (_base.dc_displayClonesRunning) _base.DisplayClones_Refresh();
                    _base.OnMediaDisplayModeChanged();
                }
            }
        }

        /// <summary>
        /// Provides access to the display clones settings of the player (e.g. Player.Display.Clones.Add).
        /// </summary>
        public DisplayClones Clones
        {
            get { return _base.DisplayClones; }
        }

        /// <summary>
        /// Provides access to the display overlay settings of the player (e.g. Player.Display.Overlay.Window).
        /// </summary>
        public Overlay Overlay
        {
            get { return _base.Overlay; }
        }

        ///// <summary>
        ///// Gets or sets a value indicating whether the fullscreen mode of the player is activated (default: false).
        ///// </summary>
        //public bool FullScreen
        //{
        //    get { return _base._fullScreen; }
        //    set { _base.AV_SetFullScreen(value); }
        //}

        ///// <summary>
        ///// Gets or sets the fullscreen display mode of the player (default: Display).
        ///// </summary>
        //public FullScreenMode FullScreenMode
        //{
        //    get { return _base._fullScreenMode; }
        //    set { _base.FullScreenMode = value; }
        //}

        /// <summary>
        /// Gets or sets a value indicating whether the parent window (form) of the display of the player is redrawn with Display.Mode Normal, Center or Manual when the display of the player is resized (default: not enabled).
        /// </summary>
        public bool ResizeRedraw
        {
            get { return _base._resizeFormRefresh; }
            set { _base._resizeFormRefresh = value; }
        }

        /// <summary>
        /// Gets the size and location of the parent window (form) of the display of the player in its normal window state (e.g. when the form is maximized or fullscreen).
        /// </summary>
        public Rectangle RestoreBounds
        {
            get
            {
                Rectangle r = Rectangle.Empty;

                _base._lastError = Global.MCIERR_NO_ERROR;
                if (_base._fullScreen)
                {
                    r = _base._fsFormBounds;
                }
                else
                {
                    if (_base._hasDisplay)
                    {
                        Form f = _base._display.FindForm();
                        r = f.WindowState == FormWindowState.Normal ? f.Bounds : f.RestoreBounds;
                    }
                    else
                    {
                        _base._lastError = Global.MCIERR_NO_WINDOW;
                    }
                }
                return r;
            }
        }

        /// <summary>
        /// Gets or sets the cursor used when dragging the player's display window when the Player.Display.DragEnabled option is enabled (default: Cursors.SizeAll).
        /// </summary>
        public Cursor DragCursor
        {
            get { return _dragCursor; }
            set { _dragCursor = value; }

        }

        /// <summary>
        /// Gets or sets a value indicating whether the parent window (form) of the display of the player can be moved by dragging the player's display window (default: false).
        /// </summary>
        public bool DragEnabled
        {
            get { return _dragEnabled; }

            set
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                if (value)
                {
                    if (!_dragEnabled)
                    {
                        if (!_base._hasDisplay || _base._display.FindForm() == null)
                        {
                            _base._lastError = Global.MCIERR_NO_WINDOW;
                        }
                        else
                        {
                            _base.Events.MediaMouseDown += DragDisplay_MouseDown;
                            _dragEnabled = true;
                        }
                    }
                }
                else if (_dragEnabled)
                {
                    _base.Events.MediaMouseDown -= DragDisplay_MouseDown;
                    _dragEnabled = false;
                }
            }
        }

        private void DragDisplay_MouseDown(object sender, MediaMouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (!_base._fullScreen)
                {
                    _dragForm = _base._display.FindForm();
                    if (_dragForm.WindowState != FormWindowState.Maximized)
                    {
                        _dragLocation = e.Location;

                        _base.Events.MediaMouseMove += DragDisplay_MouseMove;
                        _base.Events.MediaMouseUp += DragDisplay_MouseUp;
                        _base._display.Cursor = _dragCursor;

                        _base.OnMediaDragFormBegin();
                    }
                }
            }
        }

        private void DragDisplay_MouseMove(object sender, MediaMouseEventArgs e)
        {
            _base._display.Cursor = _dragCursor;
            _dragForm.Left += e.X - _dragLocation.X;
            _dragForm.Top += e.Y - _dragLocation.Y;
            _dragLocation = e.Location;

            _base.OnMediaDragFormMove();
        }

        private void DragDisplay_MouseUp(object sender, MediaMouseEventArgs e)
        {
            _base.Events.MediaMouseMove -= DragDisplay_MouseMove;
            _base.Events.MediaMouseUp -= DragDisplay_MouseUp;
            _base._display.Cursor = Cursors.Default;

            _base.OnMediaDragFormEnd();
        }

        /// <summary>
        /// Activates or deactivates clipping of the display of the player.
        /// </summary>
        /// <param name="callback">The method to be called when the clipping region of the display may need to be updated. Use null or Nothing to deactivate clipping.</param>
        /// <param name="overlayClipping">A value indicating whether clipping of display overlays should also be (de-) activated.</param>
        public int SetClip(DisplayClipCallback callback, bool overlayClipping)
        {
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (callback == null)
            {
                _base.AV_RemoveDisplayClip(overlayClipping);
            }
            else
            {
                if (_base._hasDisplay)
                {
                    _base._displayClipCallback = callback;
                    if (overlayClipping) _base.AV_SetOverlayClipping(true);
                    _base._hasDisplayClipping = true;
                    _base.AV_UpdateDisplayClip();
                }
                else
                {
                    _base._lastError = Global.MCIERR_NO_WINDOW;
                }
            }
            return _base._lastError;
        }
    }

    #endregion

    #region Overlay Class

    /// <summary>
    /// A class that is used to group together the Display Overlay methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Overlay : HideObjectMembers
    {
        private Player _base;
        internal Overlay(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets or sets the display overlay of the player.
        /// </summary>
        public Form Window
        {
            get { return _base._overlay; }
            set {  _base.AV_SetOverlay(value); }
        }

        /// <summary>
        /// Gets or sets the display mode (size and location) of the display overlay of the player (default: Video).
        /// </summary>
        public OverlayMode Mode
        {
            get { return _base._overlayMode; }
            set
            {
                if (value != _base._overlayMode)
                {
                    _base._overlayMode = value;
                    if (_base._hasDisplay && _base._hasOverlayShown) _base._display.Invalidate();
                    _base.OnMediaOverlayModeChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the display overlay of the player is always shown (default: false).
        /// </summary>
        public bool Hold
        {
            get { return _base._overlayHold; }
            set
            {
                if (value != _base._overlayHold)
                {
                    _base._overlayHold = value;
                    if (_base._hasOverlay)
                    {
                        if (value)
                        {
                            if (_base._hasDisplay && !_base._hasOverlayShown && _base._display.FindForm().Visible)
                            {
                                _base.AV_ShowOverlay();
                                if (_base.dc_hasDisplayClones) _base.DisplayClones_Start();
                            }
                        }
                        else if (_base._hasOverlayShown && (!_base._hasDevice))
                        {
                            _base.AV_HideOverlay();
                            if (_base.dc_hasDisplayClones && !_base._playing) _base.DisplayClones_Stop(false);
                        }
                    }
                    _base.OnMediaOverlayHoldChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the display overlay of the player can be activated for input and selection (default: false).
        /// </summary>
        public bool CanFocus
        {
            get { return _base._overlayCanFocus; }
            set
            {
                if (value != _base._overlayCanFocus) _base.AV_SetOverlayCanFocus(value);
            }
        }

        /// <summary>
        /// Gets or sets the number of milliseconds that the visibilty of the display overlay of the player is delayed when restoring the minimized display (form) of the player. Set to 0 to disable (default: 300 ms).
        /// </summary>
        public int Delay
        {
            get { return _base._minimizedInterval; }
            set
            {
                if (value == 0)
                {
                    _base._minimizedInterval = 0;
                    _base._minimizeEnabled = false;
                    _base.AV_MinimizeActivate(false);
                }
                else
                {
                    if (value < 100) value = 100;
                    else if (value > 1500) value = 1500;
                    _base._minimizedInterval = value;
                    _base._minimizeEnabled = true;
                    _base.AV_MinimizeActivate(true);
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the display overlay of the player is active (but not necessarily visible (when the main window is minimized)).
        /// </summary>
        public bool Active
        { get { return _base._hasOverlayShown; } }

        /// <summary>
        /// Gets a value indicating whether the player has a display overlay (set, but not necessarily active).
        /// </summary>
        public bool Present
        { get { return _base._hasOverlay; } }

        /// <summary>
        /// Gets a value indicating whether the display overlay of the player is active and visible.
        /// </summary>
        public bool Visible
        { get { return _base._hasOverlay && _base._overlay.Visible; } }

        /// <summary>
        /// Gets or sets a value indicating whether clipping of the display overlay of the player is enabled. Clipping adjusts the visibility of the overlay to that of the display of the player (default: false).
        /// </summary>
        public bool Clipping
        {
            get { return _base._hasOverlayClipping; }
            set
            {
                if (value != _base._hasOverlayClipping)
                {
                    _base.AV_SetOverlayClipping(value);
                }
            }
        }
    }

    #endregion

    #region DisplayClones Class

    /// <summary>
    /// A class that is used to group together the Display Clones methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class DisplayClones : HideObjectMembers
    {
        private const CloneFlip     DEFAULT_FLIP    = CloneFlip.FlipNone;
        private const CloneLayout   DEFAULT_LAYOUT  = CloneLayout.Zoom;
        private const CloneQuality  DEFAULT_QUALITY = CloneQuality.Auto;
        private const int           MAX_FRAMERATE   = 100;

        private Player              _base;

        internal DisplayClones(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Adds a display clone to the player.
        /// </summary>
        /// <param name="clone">The form or control to add as a display clone.</param>
        public int Add(Control clone)
        {
            return _base.DisplayClones_Add(new Control[] { clone }, DEFAULT_FLIP, DEFAULT_LAYOUT, DEFAULT_QUALITY);
        }

        /// <summary>
        /// Adds a display clone to the player.
        /// </summary>
        /// <param name="clone">The form or control to add as a display clone.</param>
        /// <param name="flip">The video flip to apply to the display clone (default FlipNone).</param>
        /// <param name="layout">The video layout to apply to the display clone (default Zoom).</param>
        /// <param name="quality">The video quality to apply to the display clone (default Auto).</param>
        public int Add(Control clone, CloneFlip flip, CloneLayout layout, CloneQuality quality)
        {
            return _base.DisplayClones_Add(new Control[] { clone }, flip, layout, quality);
        }

        /// <summary>
        /// Adds multiple display clones to the player.
        /// </summary>
        /// <param name="clones">The forms and/or controls to add as display clones.</param>
        public int AddRange(Control[] clones)
        {
            return _base.DisplayClones_Add(clones, DEFAULT_FLIP, DEFAULT_LAYOUT, DEFAULT_QUALITY);
        }

        /// <summary>
        /// Adds multiple display clones to the player.
        /// </summary>
        /// <param name="clones">The forms and/or controls to add as display clones.</param>
        /// <param name="flip">The video flip to apply to the display clones (default FlipNone).</param>
        /// <param name="layout">The video layout to apply to the display clones (default Zoom).</param>
        /// <param name="quality">The video quality to apply to the display clones (default Auto).</param>
        public int AddRange(Control[] clones, CloneFlip flip, CloneLayout layout, CloneQuality quality)
        {
            return _base.DisplayClones_Add(clones, flip, layout, quality);
        }

        /// <summary>
        /// Removes a display clone from the player.
        /// </summary>
        /// <param name="clone">The display clone to remove from the player.</param>
        public int Remove(Control clone)
        {
            return _base.DisplayClones_Remove(new Control[] { clone });
        }

        /// <summary>
        /// Removes multiple display clones from the player.
        /// </summary>
        /// <param name="clones">The display clones to remove from the player.</param>
        public int RemoveRange(Control[] clones)
        {
            return _base.DisplayClones_Remove(clones);
        }

        /// <summary>
        /// Removes all display clones from the player.
        /// </summary>
        public int RemoveAll()
        {
            return _base.DisplayClones_Clear();
        }

        /// <summary>
        /// Removes all display clones from the player.
        /// </summary>
        public int Clear()
        {
            return _base.DisplayClones_Clear();
        }

        /// <summary>
        /// Gets a value indicating whether the player has one or more display clones.
        /// </summary>
        public bool Present
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                return _base.dc_hasDisplayClones;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player has one or more active ('running') display clones.
        /// </summary>
        public bool Active
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                return _base.dc_displayClonesRunning;
            }
        }

        /// <summary>
        /// Gets the number of display clones of the player.
        /// </summary>
        public int Count
        {
            get
            {
                int count        = 0;
                _base._lastError = Global.MCIERR_NO_ERROR;

                if (_base.dc_hasDisplayClones)
                {
                    for (int i = 0; i < _base.dc_displayClones.Length; i++)
                    {
                        if (_base.dc_displayClones != null) count++;
                    }
                }
                return count;
            }
        }

        /// <summary>
        /// Returns a value indicating whether the specified control is a display clone of the player.
        /// </summary>
        /// <param name="clone">The display clone to search for.</param>
        public bool Contains(Control clone)
        {
            bool found       = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        found = true;
                        break;
                    }
                }
            }
            return found;
        }

        /// <summary>
        /// Gets the display clones of the player.
        /// </summary>
        public Control[] Clones
        {
            get
            {
                Control[] items  = null;
                _base._lastError = Global.MCIERR_NO_ERROR;

                if (_base.dc_hasDisplayClones)
                {
                    int index = 0;
                    items = new Control[Count];

                    for (int i = 0; i < _base.dc_displayClones.Length; i++)
                    {
                        if (_base.dc_displayClones[i] != null) items[index++] = _base.dc_displayClones[i].Control;
                    }
                }
                return items;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the number of video frames per second used for displaying the display clones of the player (default: 30 fps).
        /// </summary>
        public int FrameRate
        {
            get { return _base.dc_cloneFrameRate; }
            set
            {
                _base._lastError = Global.MCIERR_NO_ERROR;

                if (value <= 1) value = 1;
                else if (value > MAX_FRAMERATE) value = MAX_FRAMERATE;
                _base.dc_cloneFrameRate = value;
                _base.dc_timerInterval = 1000 / value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the display clones of the player also show display overlays (default: true).
        /// </summary>
        public bool ShowOverlay
        {
            get { return _base.dc_cloneOverlayShow; }
            set
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                _base.dc_cloneOverlayShow = value;

                if (_base.dc_hasDisplayClones)
                {
                    if (value)
                    {
                        if (!_base.dc_displayClonesRunning && _base._hasOverlay && _base._overlayHold)
                        {
                            _base.DisplayClones_Start();
                        }
                    }
                    else if (_base.dc_displayClonesRunning)
                    {
                        if (!_base._playing)
                        {
                            _base.DisplayClones_Stop(false);
                        }
                        else if (!_base._hasVideo) // invalidate clone display
                        {
                            for (int i = 0; i < _base.dc_displayClones.Length; i++)
                            {
                                if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control != null)
                                {
                                    _base.dc_displayClones[i].Control.Invalidate();
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Sets the video layout of the specified display clone (default: Zoom).
        /// </summary>
        /// <param name="clone">The display clone whose video layout needs to be changed.</param>
        /// <param name="layout">The video layout to apply to the display clone.</param>
        public bool SetLayout(Control clone, CloneLayout layout)
        {
            bool changed     = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        _base.dc_displayClones[i].Layout  = layout;
                        _base.dc_displayClones[i].Refresh = true;
                        changed = true;
                        break;
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Sets the video layout of all active display clones of the player (default: Zoom).
        /// </summary>
        /// <param name="layout">The video layout to apply to the display clones.</param>
        public bool SetLayout(CloneLayout layout)
        {
            bool changed     = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null)
                    {
                        _base.dc_displayClones[i].Layout  = layout;
                        _base.dc_displayClones[i].Refresh = true;
                        changed = true;
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Returns the video layout of the specified display clone.
        /// </summary>
        /// <param name="clone">The display clone whose video layout needs to be obtained.</param>
        public CloneLayout GetLayout(Control clone)
        {
            CloneLayout layout = DEFAULT_LAYOUT;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        layout = _base.dc_displayClones[i].Layout;
                        break;
                    }
                }
            }
            return layout;
        }

        /// <summary>
        /// Sets the video quality of the specified display clone (default: Auto).
        /// </summary>
        /// <param name="clone">The display clone whose video quality needs to be changed.</param>
        /// <param name="quality">The video quality to apply to the display clone.</param>
        public bool SetQuality(Control clone, CloneQuality quality)
        {
            bool changed     = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        _base.dc_displayClones[i].Quality = quality;
                        changed = true;
                        break;
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Sets the video quality of all active display clones of the player (default: Auto).
        /// </summary>
        /// <param name="quality">The video quality to apply to the display clones.</param>
        public bool SetQuality(CloneQuality quality)
        {
            bool changed     = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null)
                    {
                        _base.dc_displayClones[i].Quality = quality;
                        changed = true;
                        break;
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Returns the video quality of the specified display clone.
        /// </summary>
        /// <param name="clone">The display clone whose video quality needs to be obtained.</param>
        public CloneQuality GetQuality(Control clone)
        {
            CloneQuality quality = DEFAULT_QUALITY;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        quality = _base.dc_displayClones[i].Quality;
                        break;
                    }
                }
            }
            return quality;
        }

        /// <summary>
        /// Sets the video flip of the specified display clone (default: None).
        /// </summary>
        /// <param name="clone">The display clone whose video flip needs to be changed.</param>
        /// <param name="flip">The video flip to apply to the display clone.</param>
        public bool SetFlip(Control clone, CloneFlip flip)
        {
            bool changed     = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        _base.dc_displayClones[i].Flip = flip;
                        _base.dc_displayClones[i].Refresh = true;
                        changed = true;
                        break;
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Sets the video flip of all active display clones of the player (default: None).
        /// </summary>
        /// <param name="flip">The video flip to apply to the display clones.</param>
        public bool SetFlip(CloneFlip flip)
        {
            bool changed     = false;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null)
                    {
                        _base.dc_displayClones[i].Flip = flip;
                        _base.dc_displayClones[i].Refresh = true;
                        changed = true;
                        break;
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Returns the video flip of the specified display clone.
        /// </summary>
        /// <param name="clone">The display clone whose video flip needs to be obtained.</param>
        public CloneFlip GetFlip(Control clone)
        {
            CloneFlip flipType = DEFAULT_FLIP;

            if (_base.dc_hasDisplayClones)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        flipType = _base.dc_displayClones[i].Flip;
                        break;
                    }
                }
            }
            return flipType;
        }
    }

    #endregion

    #region MciDevice Class

    /// <summary>
    /// A class that is used to group together the MCI Device methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class MciDevice : HideObjectMembers
    {
        private Player _base;
        internal MciDevice(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets the ID of the MCI device associated with the playing media.
        /// </summary>
        public string Id
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                return _base._hasDevice ? _base._deviceId : string.Empty;
            }
        }

        /// <summary>
        /// Gets the type name of the MCI device associated with the playing media.
        /// </summary>
        public string Type
        {
            get
            {
                if (_base._hasDevice)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("capability " + _base._deviceId + " device type", _base._mciSmallBuffer1, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR) return _base._mciSmallBuffer1.ToString();
                }
                else _base._lastError = Global.MCIERR_NO_ERROR;
                return string.Empty;
            }
        }

        /// <summary>
        /// Gets a description (product name) of the MCI device associated with the playing media.
        /// </summary>
        public string Name
        {
            get
            {
                if (_base._hasDevice)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("info " + _base._deviceId + " product", _base._mciSmallBuffer1, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR) return _base._mciSmallBuffer1.ToString();
                }
                else _base._lastError = Global.MCIERR_NO_ERROR;
                return string.Empty;
            }
        }

        /// <summary>
        /// Gets the handle of the MCI video window associated with the playing media.
        /// </summary>
        public IntPtr WindowHandle
        {
            get
            {
                IntPtr handle = IntPtr.Zero;

                if (_base._playing && _base._hasVideo)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("status " + _base._deviceId + " window handle", _base._mciSmallBuffer2, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR)
                    {
                        int number = 0;
                        for (int i = 0; i < _base._mciSmallBuffer2.Length; ++i)
                        {
                            number = 10 * number + (_base._mciSmallBuffer2[i] - 48);
                        }
                        handle = (IntPtr)number;
                    }
                }
                else _base._lastError = Global.MCIERR_NO_ERROR;
                return handle;
            }
        }

        /// <summary>
        /// Gets the number of open MCI devices in this assembly.
        /// </summary>
        public int Count
        {
            get { return DeviceInfo.GetOpenDevicesCount(); }
        }

        /// <summary>
        /// Sends a command to the MCI device associated with the playing media.
        /// </summary>
        /// <param name="command">String that specifies the MCI command.</param>
        public int Command(string command)
        {
            _base._lastError = _base._hasDevice ? SafeNativeMethods.mciSendString(command.Trim() + " " + _base._deviceId, null, 0, IntPtr.Zero) : Global.MCIERR_DEVICE_NOT_READY;
            return _base._lastError;
        }

        /// <summary>
        /// Sends a command to the MCI device associated with the playing media.
        /// </summary>
        /// <param name="command">String that specifies the MCI command.</param>
        /// <param name="parameters">String that specifies the MCI command parameters.</param>
        public int Command(string command, string parameters)
        {
            if (_base._hasDevice)
            {
                string parms = parameters.Trim();
                _base._lastError = parms.Length == 0 ? SafeNativeMethods.mciSendString(command.Trim() + " " + _base._deviceId, null, 0, IntPtr.Zero) : SafeNativeMethods.mciSendString(command.Trim() + " " + _base._deviceId + " " + parms, null, 0, IntPtr.Zero);
            }
            else
            {
                _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
            }
            return _base._lastError;
        }

        /// <summary>
        /// Sends a request to the MCI device associated with the playing media.
        /// </summary>
        /// <param name="request">String that specifies the MCI request.</param>
        /// <param name="parameters">String that specifies the MCI request parameters.</param>
        /// <param name="result">A string that receives return information.</param>
        public int Request(string request, string parameters, out string result)
        {
            result = string.Empty;
            if (_base._hasDevice)
            {
                _base._lastError = SafeNativeMethods.mciSendString(request.Trim() + " " + _base._deviceId + " " + parameters.Trim(), _base._mciBuffer, Global.BUFFER_SIZE, IntPtr.Zero);
                if (_base._lastError == Global.MCIERR_NO_ERROR)
                {
                    result = _base._mciBuffer.ToString();
                }
            }
            else
            {
                _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
            }
            return _base._lastError;
        }
    }

    #endregion

    #region PointTo Class

    /// <summary>
    /// A class that is used to group together the Point To conversion methods of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class PointTo : HideObjectMembers
    {
        private Player _base;
        internal PointTo(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Computes the location of the specified screen point into coordinates of the display of the player.
        /// </summary>
        /// <param name="p">The screen coordinate Point to convert.</param>
        public Point Display(Point p)
        {
            if (_base._hasDisplay && _base._display.Visible) return _base._display.PointToClient(p);
            return new Point(-1, -1);
        }

        /// <summary>
        /// Computes the location of the specified screen point into coordinates of the display overlay of the player.
        /// </summary>
        /// <param name="p">The screen coordinate Point to convert.</param>
        public Point Overlay(Point p)
        {
            if (_base._hasOverlay && _base._overlay.Visible) return _base._overlay.PointToClient(p);
            return new Point(-1, -1);
        }

        /// <summary>
        /// Computes the location of the specified screen point into coordinates of the video image on the display of the player.
        /// </summary>
        /// <param name="p">The screen coordinate Point to convert.</param>
        public Point Video(Point p)
        {
            Point newP = new Point(-1, -1);
            if (_base._hasVideo)
            {
                Rectangle intersect = Rectangle.Intersect(_base._display.DisplayRectangle, _base._videoBounds);
                if (intersect != Rectangle.Empty)
                {
                    Point p2 = _base._display.PointToClient(p);
                    if (intersect.Contains(p2))
                    {
                        newP.X = p2.X - _base._videoBounds.X;
                        newP.Y = p2.Y - _base._videoBounds.Y;
                    }
                }
            }
            return newP;
        }
    }

    #endregion

    #region ScreenCopy Class

    /// <summary>
    /// A class that is used to group together the ScreenCopy methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class ScreenCopy : HideObjectMembers
    {
        private Player _base;
        internal ScreenCopy(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets or sets a value indicating the part of the screen to copy with the Player.ScreenCopy methods (default: Video).
        /// </summary>
        public ScreenCopyMode Mode
        {
            get { return _base._screenCopyMode; }
            set { _base._screenCopyMode = value; }
        }

        /// <summary>
        /// Returns an image from (the Player.ScreenCopy.Mode part of) the screen.
        /// </summary>
        public Image ToImage()
        {
            Bitmap memoryImage = null;

            if (_base._hasDisplay && (_base._hasVideo || _base._hasOverlay))
            {
                Rectangle r;

                switch (_base._screenCopyMode)
                {
                    case ScreenCopyMode.Display:
                        r = _base._display.RectangleToScreen(_base._display.DisplayRectangle);
                        break;
                    case ScreenCopyMode.Form:
                        r = _base._display.FindForm().RectangleToScreen(_base._display.FindForm().DisplayRectangle);
                        break;
                    case ScreenCopyMode.Parent:
                        r = _base._display.Parent.RectangleToScreen(_base._display.Parent.DisplayRectangle);
                        break;
                    case ScreenCopyMode.Screen:
                        r = Screen.GetBounds(_base._display);
                        break;
                    //case ScreenCopyMode.Video:
                    default:
                        r = _base._display.RectangleToScreen(_base._display.DisplayRectangle);
                        if (_base._hasVideo) r = _base._display.RectangleToScreen(Rectangle.Intersect(_base._display.DisplayRectangle, _base._videoBounds));
                        break;
                }

                try
                {
                    memoryImage = new Bitmap(r.Width, r.Height);
                    Graphics memoryGraphics = Graphics.FromImage(memoryImage);
                    memoryGraphics.CopyFromScreen(r.Location.X, r.Location.Y, 0, 0, r.Size);
                    memoryGraphics.Dispose();
                    _base._lastError = Global.MCIERR_NO_ERROR;
                }
                catch
                {
                    if (memoryImage != null) { memoryImage.Dispose(); memoryImage = null; }
                    _base._lastError = Global.MCIERR_INTERNAL;
                }
            }
            else
            {
                _base._lastError = Global.MCIERR_NO_WINDOW;
            }
            return memoryImage;
        }

        /// <summary>
        /// Copies an image from (the Player.ScreenCopy.Mode part of) the screen to the system's clipboard.
        /// </summary>
        public int ToClipboard()
        {
            Image theImage = ToImage();
            if (_base._lastError == Global.MCIERR_NO_ERROR)
            {
                try { Clipboard.SetImage(theImage); }
                catch { _base._lastError = Global.MCIERR_INTERNAL; }
                theImage.Dispose();
            }
            return _base._lastError;
        }

        /// <summary>
        /// Saves an image from (the Player.ScreenCopy.Mode part of) the screen to the specified file.
        /// </summary>
        /// <param name="fileName">The name of the file to save.</param>
        /// <param name="imageFormat">The file format of the image to save.</param>
        public int ToFile(string fileName, System.Drawing.Imaging.ImageFormat imageFormat)
        {
            if ((fileName != null) && (fileName.Length > 3))
            {
                Image theImage = ToImage();
                if (_base._lastError == Global.MCIERR_NO_ERROR)
                {
                    try { theImage.Save(fileName, imageFormat); }
                    catch { _base._lastError = Global.MCIERR_INTERNAL; }
                    theImage.Dispose();
                }
            }
            else
            {
                _base._lastError = Global.MCIERR_FILENAME_REQUIRED;
            }
            return _base._lastError;
        }
    }

    #endregion

    #region Sliders Class

    /// <summary>
    /// A class that is used to group together the Sliders methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Sliders : HideObjectMembers
    {
        private Player _base;
        internal Sliders(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets or sets the media playback position slider (trackbar) that is controlled by the player.
        /// </summary>
        public TrackBar Position
        {
            get { return _base._positionSlider; }
            set
            {
                if (value != _base._positionSlider)
                {
                    if (_base._hasPositionSlider)
                    {
                        //if (!_hasPositionEvents && !_hasOutputLevelEvents) _timer.Stop();
                        _base._timer.Stop();

                        _base._positionSlider.MouseDown -= _base.PositionSlider_MouseDown;
                        _base._positionSlider.MouseUp -= _base.PositionSlider_MouseUp;
                        _base._positionSlider.MouseMove -= _base.PositionSlider_MouseMove;
                        _base._positionSlider.Scroll -= _base.PositionSlider_Scroll;

                        _base._positionSlider = null;
                        _base._hasPositionSlider = false;

                        _base._psTracking = false;
                        _base._psValue = 0;
                        _base._psBusy = false;
                        _base._psSkipped = false;
                    }

                    if (value != null)
                    {
                        _base._positionSlider = value;
                        _base._hasPositionSlider = true;

                        _base._psHorizontal = (_base._positionSlider.Orientation == Orientation.Horizontal);
                        _base._positionSlider.SmallChange = 0;
                        _base._positionSlider.LargeChange = 0;
                        _base._positionSlider.TickFrequency = 0;

                        SetPositionSliderMode(_base._psHandlesProgress);

                        // add events
                        _base._positionSlider.MouseDown += _base.PositionSlider_MouseDown;
                        _base._positionSlider.MouseUp += _base.PositionSlider_MouseUp;
                        _base._positionSlider.MouseMove += _base.PositionSlider_MouseMove;
                        _base._positionSlider.Scroll += _base.PositionSlider_Scroll;

                        if (!_base._playing) _base._positionSlider.Enabled = false;
                    }
                    _base.StartMainTimerCheck();
                    _base._lastError = Global.MCIERR_NO_ERROR;
                }
            }
        }

        /// <summary>
        /// Gets or sets the mode (track or progress) of the media playback position slider (trackbar) that is controlled by the player (default: Track).
        /// </summary>
        public PositionSliderMode PositionMode
        {
            get { return _base._psHandlesProgress ? PositionSliderMode.Progress : PositionSliderMode.Track; }
            set { SetPositionSliderMode(value != PositionSliderMode.Track); }
        }

        private void SetPositionSliderMode(bool progressMode)
        {
            _base._psHandlesProgress = progressMode;
            if (_base._hasPositionSlider)
            {
                if (_base._psHandlesProgress)
                {
                    _base._positionSlider.Minimum = _base._startPositionCurrent;
                    _base._positionSlider.Maximum = _base._endPositionCurrent == 0 ? (_base._mediaLength == 0 ? 10 : _base._mediaLength) : _base._endPositionCurrent; // version 0.47 changed

                    if (_base._playing)
                    {
                        int pos = _base.PositionX;
                        if (pos < _base._positionSlider.Minimum) _base._positionSlider.Value = _base._positionSlider.Minimum;
                        else if (pos > _base._positionSlider.Maximum) _base._positionSlider.Value = _base._positionSlider.Maximum;
                        else _base._positionSlider.Value = pos;
                    }
                }
                else
                {
                    _base._positionSlider.Minimum = 0;
                    _base._positionSlider.Maximum = _base._mediaLength == 0 ? 10 : _base._mediaLength; // version 0.47 changed
                    if (_base._playing) _base._positionSlider.Value = _base.PositionX;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the display of the player is updated immediately when the media playback position slider (trackbar) is dragged (default: true).
        /// </summary>
        public bool PositionLiveUpdate
        {
            get { return _base._psLiveSeek; }
            set
            {
                _base._psLiveSeek = value;
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the shuttle slider (trackbar for 'Step' method) that is controlled by the player.
        /// </summary>
        public TrackBar Shuttle
        {
            get { return _base._shuttleSlider; }
            set
            {
                if (value != _base._shuttleSlider)
                {
                    if (_base._hasShuttleSlider)
                    {
                        _base._shuttleSlider.MouseDown -= _base.ShuttleSlider_MouseDown;
                        _base._shuttleSlider.MouseUp -= _base.ShuttleSlider_MouseUp;

                        _base._shuttleSlider = null;
                        _base._hasShuttleSlider = false;
                    }

                    if (value != null)
                    {
                        _base._shuttleSlider = value;

                        _base._shuttleSlider.SmallChange = 0;
                        _base._shuttleSlider.LargeChange = 0;

                        _base._shuttleSlider.TickFrequency = 1;

                        _base._shuttleSlider.Minimum = -5;
                        _base._shuttleSlider.Maximum = 5;
                        _base._shuttleSlider.Value = 0;

                        _base._shuttleSlider.MouseDown += _base.ShuttleSlider_MouseDown;
                        _base._shuttleSlider.MouseUp += _base.ShuttleSlider_MouseUp;

                        //_shuttleSlider.Enabled = _playing;
                        _base._hasShuttleSlider = true;
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the audio volume slider (trackbar) that is controlled by the player.
        /// </summary>
        public TrackBar AudioVolume
        {
            get { return _base._volumeSlider; }
            set
            {
                if (value != _base._volumeSlider)
                {
                    if (_base._volumeSlider != null)
                    {
                        _base._volumeSlider.Scroll -= _base.VolumeSlider_Scroll;
                        _base._volumeSlider = null;
                    }

                    if (value != null)
                    {
                        _base._volumeSlider = value;

                        _base._volumeSlider.Minimum = 0;
                        _base._volumeSlider.Maximum = 1000;
                        _base._volumeSlider.TickFrequency = 100;
                        _base._volumeSlider.SmallChange = 100;
                        _base._volumeSlider.LargeChange = 100;

                        _base._volumeSlider.Value = _base._audioVolume;

                        _base._volumeSlider.Scroll += _base.VolumeSlider_Scroll;
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the audio balance slider (trackbar) that is controlled by the player.
        /// </summary>
        public TrackBar AudioBalance
        {
            get { return _base._balanceSlider; }
            set
            {
                if (value != _base._balanceSlider)
                {
                    if (_base._balanceSlider != null)
                    {
                        _base._balanceSlider.Scroll -= _base.BalanceSlider_Scroll;
                        _base._balanceSlider = null;
                    }

                    if (value != null)
                    {
                        _base._balanceSlider = value;

                        _base._balanceSlider.Minimum = 0;
                        _base._balanceSlider.Maximum = 1000;
                        _base._balanceSlider.TickFrequency = 100;
                        _base._balanceSlider.SmallChange = 100;
                        _base._balanceSlider.LargeChange = 100;

                        _base._balanceSlider.Value = _base._audioBalance;
                        _base._balanceSlider.Scroll += _base.BalanceSlider_Scroll;
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the playback speed slider (trackbar) that is controlled by the player.
        /// </summary>
        public TrackBar Speed
        {
            get { return _base._speedSlider; }
            set
            {
                if (value != _base._speedSlider)
                {
                    if (_base._speedSlider != null)
                    {
                        _base._speedSlider.Scroll -= _base.SpeedSlider_Scroll;
                        _base._speedSlider.MouseDown -= _base.SpeedSlider_MouseDown;

                        _base._speedSlider = null;
                    }

                    if (value != null)
                    {
                        _base._speedSlider = value;

                        _base._speedSlider.Minimum = 0;
                        _base._speedSlider.Maximum = 12;
                        _base._speedSlider.TickFrequency = 1;
                        _base._speedSlider.SmallChange = 1;
                        _base._speedSlider.LargeChange = 1;

                        _base.SpeedSlider_ValueToSlider(_base._speed);

                        _base._speedSlider.MouseDown += _base.SpeedSlider_MouseDown;
                        _base._speedSlider.Scroll += _base.SpeedSlider_Scroll;
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }
    }

    #endregion

    #region TaskbarProgress Class

    /// <summary>
    /// A class that is used to group together the Taskbar Progress methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class TaskbarProgress : HideObjectMembers
    {
        #region Taskbar Progress Fields

        private Player                  _base;
        private List<TaskbarItem>       _taskbarItems;
        internal TaskbarProgressMode    _progressMode;
        private class TaskbarItem
        {
            internal Form Form;
            internal IntPtr Handle;
        }

        #endregion

        internal TaskbarProgress(Player player)
        {
            _base = player;

            if (!Player._taskbarProgressEnabled)
            {
                if (Environment.OSVersion.Version.Major >= 6) // Windows Vista or later
                {
                    _taskbarItems = new List<TaskbarItem>(4);
                    Player.TaskbarInstance = (TaskbarIndicator.ITaskbarList3)new TaskbarIndicator.TaskbarInstance();
                    Player._taskbarProgressEnabled = true;
                    _progressMode = TaskbarProgressMode.Progress;
                }
            }
            else
            {
                _taskbarItems = new List<TaskbarItem>(4);
            }
        }

        #region Public - Taskbar Progress methods and properties

        /// <summary>
        /// Adds a taskbar progress indicator to the player (Windows Vista or later only).
        /// </summary>
        /// <param name="form">The form whose taskbar item is added as a progress indicator. Multiple forms and duplicates are allowed.</param>
        public int Add(Form form)
        {
            _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            if (Player._taskbarProgressEnabled)
            {
                lock (_taskbarItems)
                {
                    if (form != null) // && form.ShowInTaskbar)
                    {
                        // check if already exists
                        bool exists = false;
                        for (int i = 0; i < _taskbarItems.Count; i++)
                        {
                            if (_taskbarItems[i].Form == form)
                            {
                                exists = true;
                                break;
                            }
                        }

                        if (!exists)
                        {
                            TaskbarItem item = new TaskbarItem();
                            item.Form = form;
                            item.Handle = form.Handle;
                            _taskbarItems.Add(item);

                            if (_base._playing)
                            {
                                if (_base._paused) Player.TaskbarInstance.SetProgressState(item.Handle, TaskbarStates.Paused);
                                SetValue(_base.PositionX);
                            }

                            _base._hasTaskbarProgress = true;
                            _base.StartMainTimerCheck();
                        }
                        _base._lastError = Global.MCIERR_NO_ERROR;
                    }
                    else _base._lastError = Global.MCIERR_OUTOFRANGE;
                }
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes a taskbar progress indicator from the player.
        /// </summary>
        /// <param name="form">The form whose taskbar progress indicator is removed.</param>
        public int Remove(Form form)
        {
            _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            if (Player._taskbarProgressEnabled)
            {
                if (_base._hasTaskbarProgress && form != null)
                {
                    lock (_taskbarItems)
                    {
                        for (int index = _taskbarItems.Count - 1; index >= 0; index--)
                        {
                            if (_taskbarItems[index].Form == form || _taskbarItems[index].Form == null)
                            {
                                if (_taskbarItems[index].Form != null)
                                {
                                    Player.TaskbarInstance.SetProgressState(_taskbarItems[index].Handle, TaskbarStates.NoProgress);
                                }
                                _taskbarItems.RemoveAt(index);
                            }
                        }

                        if (_taskbarItems.Count == 0)
                        {
                            _base._hasTaskbarProgress = false;
                            _base.StopMainTimerCheck();
                            _taskbarItems = new List<TaskbarItem>(4);
                        }
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes all taskbar progress indicators from the player.
        /// </summary>
        public int RemoveAll()
        {
            _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            if (Player._taskbarProgressEnabled)
            {
                if (_base._hasTaskbarProgress)
                {
                    _base._hasTaskbarProgress = false;
                    _base.StopMainTimerCheck();
                    SetState(TaskbarStates.NoProgress);
                    _taskbarItems = new List<TaskbarItem>(4);
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes all taskbar progress indicators from the player.
        /// </summary>
        public int Clear()
        {
            return RemoveAll();
        }

        /// <summary>
        /// Gets the number of taskbar progress indicators of the player.
        /// </summary>
        public int Count
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                return _taskbarItems == null ? 0 : _taskbarItems.Count;
            }
        }

        /// <summary>
        /// Gets the forms that have a taskbar progress indicator of the player.
        /// </summary>
        public Form[] List
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;

                Form[] result = null;
                if (_taskbarItems != null)
                {
                    int count = _taskbarItems.Count;
                    result = new Form[count];
                    for (int i = 0; i < count; i++)
                    {
                        result[i] = _taskbarItems[i].Form;
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Gets or sets the mode (track or progress) of the taskbar progress indicator (default: Progress).
        /// </summary>
        public TaskbarProgressMode Mode
        {
            get { return _progressMode; }

            set
            {
                _progressMode = value;
                if (_base._hasTaskbarProgress && _base.Playing) SetValue(_base.PositionX);
            }
        }

        #endregion

        #region Private - SetValue / SetState

        internal void SetValue(double progressValue)
        {
            double pos = progressValue;
            double total;

            if (_progressMode == TaskbarProgressMode.Track)
            {
                total = _base._mediaLength;
            }
            else
            {
                if (pos < _base._startPositionCurrent)
                {
                    total = _base._endPositionCurrent == 0 ? _base._mediaLength : _base._endPositionCurrent;
                }
                else
                {
                    if (_base._endPositionCurrent == 0) total = _base._mediaLength - _base._startPositionCurrent;
                    else
                    {
                        if (pos <= _base._endPositionCurrent) total = _base._endPositionCurrent - _base._startPositionCurrent;
                        else total = _base._mediaLength - _base._startPositionCurrent;
                    }
                    pos = pos - _base._startPositionCurrent;
                }
            }

            for (int i = 0; i < _taskbarItems.Count; i++)
            {
                if (_taskbarItems[i].Form != null)
                {
                    try { Player.TaskbarInstance.SetProgressValue(_taskbarItems[i].Handle, (ulong)pos, (ulong)total); }
                    catch { _taskbarItems[i].Form = null; }
                }
            }
        }

        internal void SetState(TaskbarStates taskbarState)
        {
            for (int i = 0; i < _taskbarItems.Count; i++)
            {
                if (_taskbarItems[i].Form != null)
                {
                    try { Player.TaskbarInstance.SetProgressState(_taskbarItems[i].Handle, taskbarState); }
                    catch { _taskbarItems[i].Form = null; }
                }
            }
        }

        #endregion
    }

    #endregion

    #region SystemPanels Class

    /// <summary>
    /// A class that is used to group together the System Panels methods of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class SystemPanels : HideObjectMembers
    {
        private Player _base;
        internal SystemPanels(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Opens the System Audio Mixer Control Panel.
        /// </summary>
        public bool ShowAudioMixer()
        {
            return ShowAudioMixer(null);
        }

        /// <summary>
        /// Opens the System Audio Mixer Control Panel.
        /// </summary>
        /// <param name="centerForm">The control panel is centered on top of the specified form.</param>
        public bool ShowAudioMixer(Form centerForm)
        {
            return SafeNativeMethods.CenterSystemDialog("SndVol.exe", "", centerForm) || SafeNativeMethods.CenterSystemDialog("SndVol32.exe", "", centerForm);
        }

        /// <summary>
        /// Opens the System Sound Control Panel.
        /// </summary>
        public bool ShowAudioDevices()
        {
            return ShowAudioDevices(null);
        }

        /// <summary>
        /// Opens the System Sound Control Panel.
        /// </summary>
        /// <param name="centerForm">The control panel is centered on top of the specified form.</param>
        public bool ShowAudioDevices(Form centerForm)
        {
            return SafeNativeMethods.CenterSystemDialog("control", "mmsys.cpl,,0", centerForm);
        }

        /// <summary>
        /// Opens the System Display Control Panel.
        /// </summary>
        public bool ShowDisplaySettings()
        {
            return ShowDisplaySettings(null);
        }

        /// <summary>
        /// Opens the System Display Control Panel.
        /// </summary>
        /// <param name="centerForm">The control panel is centered on top of the specified form.</param>
        public bool ShowDisplaySettings(Form centerForm)
        {
            return SafeNativeMethods.CenterSystemDialog("control", "desk.cpl", centerForm);
        }

        /// <summary>
        /// Opens the System Sound Control Panel.
        /// </summary>
        public bool ShowLevelDevices()
        {
            return ShowLevelDevices(null);
        }

        /// <summary>
        /// Opens the System Sound Control Panel.
        /// </summary>
        /// <param name="centerForm">The control panel is centered on top of the specified form.</param>
        public bool ShowLevelDevices(Form centerForm)
        {
            return SafeNativeMethods.CenterSystemDialog("control", "mmsys.cpl,,1", centerForm);
        }
    }

    #endregion

    #region Subtitles Class

    /// <summary>
    /// A class that is used to group together the Subtitles methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Subtitles : HideObjectMembers
    {
        private const int       MAX_DIRECTORY_DEPTH         = 3;
        private const string    SUBTITLES_FILE_EXTENSION    = ".srt";
        private Player          _base;

        internal Subtitles(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets a value indicating whether the subtitles of the player are activated (by using the Player.Events.MediaSubtitleChanged event; default: false).
        /// </summary>
        public bool Enabled
        {get {  return _base.st_SubtitlesEnabled; } }

        /// <summary>
        /// Gets a value indicating whether the playing media (or the media specified with Subtitles.Filename) has a subtitles file.
        /// </summary>
        public bool Exists
        { get { return _base.SubtitlesExists() != string.Empty; } }

        /// <summary>
        /// Gets a value indicating whether the player has active subtitles.
        /// </summary>
        public bool Present
        { get { return _base.st_HasSubtitles; } }

        /// <summary>
        /// Gets the number of items in the active subtitles of the player.
        /// </summary>
        public int Count
        { get { return _base.st_SubTitleCount; } }

        /// <summary>
        /// Returns the text of the current subtitle (usually obtained with the Player.Events.MediaSubtitleChanged event).
        /// </summary>
        public string GetText()
        {
            return _base.st_SubtitleOn ? _base.st_SubtitleItems[_base.st_CurrentIndex].Text : string.Empty;
        }

        /// <summary>
        /// Returns the start time (including Player.Subtitles.TimeShift) of the current subtitle.
        /// </summary>
        public TimeSpan GetStartTime()
        {
            return _base.st_SubtitleOn ? TimeSpan.FromMilliseconds(_base.st_SubtitleItems[_base.st_CurrentIndex].StartTime + _base.st_TimeShift) : TimeSpan.Zero;
        }

        /// <summary>
        /// Returns the end time (including Player.Subtitles.TimeShift) of the current subtitle.
        /// </summary>
        public TimeSpan GetEndTime()
        {
            return _base.st_SubtitleOn ? TimeSpan.FromMilliseconds(_base.st_SubtitleItems[_base.st_CurrentIndex].EndTime + _base.st_TimeShift) : TimeSpan.Zero;
        }

        /// <summary>
        /// Returns the text of the specified item in the active subtitles of the player.
        /// </summary>
        /// <param name="index">The index of the item in the active subtitles of the player.</param>
        public string GetText(int index)
        {
            if (_base.st_HasSubtitles && index >= 0 && index < _base.st_SubTitleCount)
            {
                return _base.st_SubtitleItems[index].Text;
            }
            return string.Empty;
        }

        /// <summary>
        /// Returns the start time (including Player.Subtitles.TimeShift) of the specified item in the active subtitles of the player.
        /// </summary>
        /// <param name="index">The index of the item in the active subtitles of the player.</param>
        public TimeSpan GetStartTime(int index)
        {
            if (_base.st_HasSubtitles && index >= 0 && index < _base.st_SubTitleCount)
            {
                return TimeSpan.FromMilliseconds(_base.st_SubtitleItems[index].StartTime + _base.st_TimeShift);
            }
            return TimeSpan.Zero;
        }

        /// <summary>
        /// Returns the end time (including Player.Subtitles.TimeShift) of the specified item in the active subtitles of the player.
        /// </summary>
        /// <param name="index">The index of the item in the active subtitles of the player.</param>
        public TimeSpan GetEndTime(int index)
        {
            if (_base.st_HasSubtitles && index >= 0 && index < _base.st_SubTitleCount)
            {
                return TimeSpan.FromMilliseconds(_base.st_SubtitleItems[index].EndTime + _base.st_TimeShift);
            }
            return TimeSpan.Zero;
        }

        /// <summary>
        /// Returns the path and filename of the active subtitles file.
        /// </summary>
        public string GetFileName()
        {
            if (_base.st_HasSubtitles) return _base.st_SubtitlesName;
            return string.Empty;
        }

        /// <summary>
        /// Gets or sets the text encoding of subtitles (default: Encoding.Default).
        /// </summary>
        public Encoding Encoding
        {
            get { return _base.st_Encoding; }
            set
            {
                if (value != _base.st_Encoding)
                {
                    _base.st_Encoding = value;
                    if (_base.st_HasSubtitles) _base.StartSubtitles(true);
                }
            }
        }

        /// <summary>
        /// Gets or sets the initial directory to search for subtitles files (default: string.Empty (the directory of the playing media)).
        /// </summary>
        public string Directory
        {
            get { return _base.st_Directory; }
            set
            {
                if (!string.IsNullOrEmpty(value) && System.IO.Directory.Exists(value))
                {
                    try
                    {
                        _base.st_Directory = Path.GetDirectoryName(value);
                        if (_base.st_SubtitlesEnabled && _base._playing && !_base.st_HasSubtitles) _base.StartSubtitles(true);
                    }
                    catch { _base.st_Directory = string.Empty; }
                }
                else _base.st_Directory = string.Empty;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the number of nested directories to search for subtitles files (values 0 to 3, default: 0 (base directory only)).
        /// </summary>
        public int DirectoryDepth
        {
            get { return _base.st_DirectoryDepth; }
            set
            {
                if (value <= 0) value = 0;
                else if (value >= MAX_DIRECTORY_DEPTH) value = MAX_DIRECTORY_DEPTH;
                if (value != _base.st_DirectoryDepth)
                {
                    _base.st_DirectoryDepth = value;
                    if (_base.st_SubtitlesEnabled && _base._playing && !_base.st_HasSubtitles) _base.StartSubtitles(true);
                }
            }
        }

        /// <summary>
        /// Gets or sets the filename of the subtitles file to search for (without directory and extension, default: string.Empty (the filename of the playing media)). Reset when media starts playing.
        /// </summary>
        public string FileName
        {
            get { return _base.st_FileName; }
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    try
                    {
                        _base.st_FileName = Path.GetFileNameWithoutExtension(value) + SUBTITLES_FILE_EXTENSION;
                        if (_base.st_SubtitlesEnabled && _base._playing && !_base.st_HasSubtitles) _base.StartSubtitles(true);
                    }
                    catch { _base.st_FileName = string.Empty; }
                }
                else _base.st_FileName = string.Empty;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the number of milliseconds that the subtitles appear earlier (negative values) or later (positive values) than specified by the subtitles data. Reset when media ends playing.
        /// </summary>
        public int TimeShift
        {
            get { return _base.st_TimeShift; }
            set
            {
                if (value != _base.st_TimeShift)
                {
                    _base.st_TimeShift = value; // no check (?)
                    if (_base.st_HasSubtitles) _base.OnMediaPositionChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether any HTML tags are removed from the subtitles (default: true).
        /// </summary>
        public bool RemoveHTMLTags
        {
            get { return _base.st_RemoveHTMLTags; }
            set
            {
                if (value != _base.st_RemoveHTMLTags)
                {
                    _base.st_RemoveHTMLTags = value;
                    if (_base.st_HasSubtitles) _base.StartSubtitles(true);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether audio only media can activate subtitles (default: false).
        /// </summary>
        public bool AudioOnly
        {
            get { return _base.st_AudioOnlyEnabled; }
            set
            {
                if (value != _base.st_AudioOnlyEnabled)
                {
                    _base.st_AudioOnlyEnabled = value;
                    if (_base.st_SubtitlesEnabled && _base._playing && !_base._hasVideo)
                    {
                        if (_base.st_AudioOnlyEnabled) _base.StartSubtitles(true);
                        else
                        {
                            if (_base.st_HasSubtitles) _base.StopSubtitles();
                        }
                    }
                }
            }
        }
    }

    #endregion

    #region Position Class

    /// <summary>
    /// A class that is used to group together the Position methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Position : HideObjectMembers
    {
        private Player _base;
        internal Position(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets or sets the playback position of the playing media measured from the natural beginning of the media.
        /// </summary>
        public TimeSpan FromStart
        {
            get { return _base._playing ? TimeSpan.FromMilliseconds(_base.PositionX) : TimeSpan.Zero; }
            set { _base.SetPosition((int)value.TotalMilliseconds); }
        }

        /// <summary>
        /// Gets or sets the playback position of the playing media measured from the natural end of the media.
        /// </summary>
        public TimeSpan FromEnd
        {
            get { return _base._playing ? TimeSpan.FromMilliseconds(_base._mediaLength - _base.PositionX) : TimeSpan.Zero; }
            set { _base.SetPosition(_base._mediaLength - (int)value.TotalMilliseconds); }
        }

        /// <summary>
        /// Gets or sets the playback position of the playing media measured from the Player.Media.StartPosition of the media.
        /// </summary>
        public TimeSpan FromStartPosition
        {
            get { return _base._playing ? TimeSpan.FromMilliseconds(_base.PositionX - _base._startPositionCurrent) : TimeSpan.Zero; }
            set { _base.SetPosition(_base._startPositionCurrent + (int)value.TotalMilliseconds); }
        }

        /// <summary>
        /// Gets or sets the playback position of the playing media measured from the Player.Media.EndPosition of the media.
        /// </summary>
        public TimeSpan FromEndPosition
        {
            get { return _base._playing ? TimeSpan.FromMilliseconds(_base._endPositionCurrent - _base.PositionX) : TimeSpan.Zero; }
            set { _base.SetPosition(_base._endPositionCurrent - (int)value.TotalMilliseconds); }
        }

        /// <summary>
        /// Gets or sets the playback position of the playing media relative to the natural beginning and end of the media. Values from 0 to 1.
        /// </summary>
        public double Track
        {
            get
            {
                if (!_base._playing || _base._mediaLength <= 0) return 0;
                return (double)_base.PositionX / _base._mediaLength;
            }
            set
            {
                if (_base._playing && value >= 0 && value < 1)
                {
                    _base.SetPosition((int)(value * _base._mediaLength));
                }
            }
        }

        /// <summary>
        /// Gets or sets the playback position of the playing media relative to the Player.Media.StartPosition and Player.Media.EndPosition of the media. Values from 0 to 1.
        /// </summary>
        public double Progress
        {
            get
            {
                if (_base._playing)
                {
                    int pos = _base._endPositionCurrent == 0 ? _base._mediaLength : _base._endPositionCurrent;
                    if (pos == 0 || pos <= _base._startPositionCurrent) return 0;

                    int pos2 = (_base.PositionX - _base._startPositionCurrent) / (pos - _base._startPositionCurrent);
                    if (pos2 < 0) return 0;
                    return pos2 > 1 ? 1 : pos2;
                }
                else return 0;
            }
            set
            {
                if (_base._playing && value >= 0 && value < 1)
                {
                    int pos = _base._endPositionCurrent == 0 ? _base._mediaLength : _base._endPositionCurrent;
                    if (pos <= _base._startPositionCurrent) return;

                    _base.SetPosition((int)(value * (pos - _base._startPositionCurrent)) + _base._startPositionCurrent);
                }
            }
        }

        /// <summary>
        /// Rewinds the playback position of the playing media to the Player.Media.StartPosition.
        /// </summary>
        public int Rewind()
        {
            if (_base._hasDevice) _base.SetPosition(_base._startPositionCurrent);
            else _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
            return _base._lastError;
        }

        /// <summary>
        /// Changes the playback position of the playing media in any direction by the given amount of seconds.
        /// </summary>
        /// <param name="seconds">The amount of seconds to skip.</param>
        public int Skip(int seconds)
        {
            if (_base._hasDevice) _base.SetPosition(_base.PositionX + (seconds * 1000));
            else _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
            return _base._lastError;
        }

        /// <summary>
        /// Changes the playback position of the playing media in any direction by the given amount of (video) frames.
        /// </summary>
        /// <param name="frames">The amount of frames to step.</param>
        public int Step(int frames)
        {
            return _base.Step(frames);
        }

    }

    #endregion

    #region Media Class

    /// <summary>
    /// A class that is used to group together the Media methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Media : HideObjectMembers
    {
        #region Fields

        private Player          _base;

        // Media album art information
        private Image           _tagImage;
        private DirectoryInfo   _directoryInfo;
        private string[]        _searchKeyWords = { "*front*", "*cover*" }; // , "*albumart*large*" };
        private string[]        _searchExtensions = { ".jpg", ".jpeg", ".bmp", ".png", ".gif", ".tiff" };

        // Wma Guid
        private static Guid ASF_Header_Guid                         = new Guid("75B22630-668E-11CF-A6D9-00AA0062CE6C");
        private static Guid ASF_Content_Description_Guid            = new Guid("75B22633-668E-11CF-A6D9-00AA0062CE6C");
        private static Guid ASF_Extended_Content_Description_Guid   = new Guid("D2D0A440-E307-11D2-97F0-00A0C95EA850");

        #endregion

        internal Media(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Returns the natural length (duration) of the playing media.
        /// </summary>
        public TimeSpan Length
        {
            get
            {
                if (!_base._hasDevice) return TimeSpan.Zero;
                return TimeSpan.FromMilliseconds(_base._mediaLength);
            }
        }

        /// <summary>
        /// Returns the length (duration) of the playing media from Media.StartPosition to Media.EndPosition.
        /// </summary>
        public TimeSpan Duration
        {
            get
            {
                if (!_base._hasDevice) return TimeSpan.Zero;
                return _base._endPositionCurrent == 0 ? TimeSpan.FromMilliseconds(_base._mediaLength - _base._startPositionCurrent) : TimeSpan.FromMilliseconds(_base._endPositionCurrent - _base._startPositionCurrent);
            }
        }

        /// <summary>
        /// Returns the length (duration) of the specified part of the playing media.
        /// </summary>
        /// <param name="part">Specifies the part of the playing media to get the length (duration) of.</param>
        public TimeSpan GetLength(MediaLength part)
        {
            if (!_base._hasDevice) return TimeSpan.Zero;

            switch (part)
            {
                case MediaLength.StartToEnd:
                    return TimeSpan.FromMilliseconds(_base._mediaLength);
                //break;

                case MediaLength.StartToEndPosition:
                    return _base._endPositionCurrent == 0 ? TimeSpan.FromMilliseconds(_base._mediaLength - _base._startPositionCurrent) : TimeSpan.FromMilliseconds(_base._endPositionCurrent - _base._startPositionCurrent);
                //break;

                case MediaLength.FromStartPosition:
                    return TimeSpan.FromMilliseconds(_base.PositionX - _base._startPositionCurrent);
                //break;

                case MediaLength.ToEnd:
                    return TimeSpan.FromMilliseconds(_base._mediaLength - _base.PositionX);
                //break;

                case MediaLength.ToEndPosition:
                    return _base._endPositionCurrent == 0 ? TimeSpan.FromMilliseconds(_base._mediaLength - _base.PositionX) : TimeSpan.FromMilliseconds(_base._endPositionCurrent - _base.PositionX);
                //break;

                //case MediaLength.FromStart:
                default:
                    return (TimeSpan.FromMilliseconds(_base.PositionX));
                    //break;
            }
        }

        /// <summary>
        /// Returns (part of) the filename of the playing media.
        /// </summary>
        /// <param name="part">Specifies the part of the filename to return.</param>
        public string GetName(MediaName part)
        {
            string mediaName = string.Empty;

            if (_base._hasDevice)
            {
                try
                {
                    switch (part)
                    {
                        case MediaName.FileName:
                            mediaName = Path.GetFileName(_base._fileName);
                            break;
                        case MediaName.DirectoryName:
                            mediaName = Path.GetDirectoryName(_base._fileName);
                            break;
                        case MediaName.PathRoot:
                            mediaName = Path.GetPathRoot(_base._fileName);
                            break;
                        case MediaName.Extension:
                            mediaName = Path.GetExtension(_base._fileName);
                            break;
                        case MediaName.FileNameWithoutExtension:
                            mediaName = Path.GetFileNameWithoutExtension(_base._fileName);
                            break;

                        default: // case MediaName.FullPath:
                            mediaName = _base._fileName;
                            break;
                    }
                }
                catch { /* ignored */ }
            }
            return mediaName;
        }

        /// <summary>
        /// Gets or sets the playback start position of the playing media.
        /// </summary>
        public TimeSpan StartPosition
        {
            get { return TimeSpan.FromMilliseconds(_base._startPositionCurrent); }
            set
            {
                if (!_base._hasDevice || _base._startPositionCurrent == (int)value.TotalMilliseconds) return;

                double pos = _base.PositionX;
                int endpos = _base._endPositionCurrent == 0 ? _base._mediaLength : _base._endPositionCurrent;
                bool repeatSet = false;

                if (value.TotalMilliseconds < endpos)
                {
                    _base._startPositionCurrent = (int)value.TotalMilliseconds;
                    if (_base._hasPositionSlider && _base._psHandlesProgress) _base._positionSlider.Minimum = _base._startPositionCurrent;

                    if (_base._startPositionCurrent > pos || (_base._endPositionCurrent != 0 && pos > _base._endPositionCurrent))
                    {
                        if (!_base._repeat)
                        {
                            _base._repeat = true;
                            repeatSet = true;
                        }
                        _base.AV_EndOfMedia(false);
                        if (_base._paused)
                        {
                            _base.AV_MciSendString("pause");
                            _base.AV_TimerTick(this, EventArgs.Empty);
                        }
                        if (repeatSet) _base._repeat = false;
                    }
                }
                _base.OnMediaStartEndChanged();
            }
        }

        /// <summary>
        /// Gets or sets the playback end position of the playing media.
        /// TimeSpan.Zero or 00:00:00 = natural end of media.
        /// </summary>
        public TimeSpan EndPosition
        {
            get { return TimeSpan.FromMilliseconds(_base._endPositionCurrent); }
            set
            {
                if (!_base._hasDevice || _base._endPositionCurrent == (int)value.TotalMilliseconds || (value == TimeSpan.Zero && _base._endPositionCurrent == _base._mediaLength)) return;

                int newPos = value.TotalMilliseconds == 0 ? _base._mediaLength : (int)value.TotalMilliseconds;
                bool resetPos = false;
                bool resetRepeat = false;

                if (newPos > _base._mediaLength) newPos = _base._mediaLength;
                if (newPos > _base._startPositionCurrent)
                {
                    _base._endPositionCurrent = newPos;
                    if (_base._hasPositionSlider && _base._psHandlesProgress) _base._positionSlider.Maximum = _base._endPositionCurrent;

                    int pos = _base.PositionX;
                    if (_base._endPositionCurrent > pos)
                    {
                        resetPos = true;
                        if (!_base._repeat)
                        {
                            _base._repeat = true;
                            resetRepeat = true;
                        }
                    }

                    if (_base._repeat)
                    {
                        _base.AV_EndOfMedia(false);
                        if (_base._paused)
                        {
                            _base.AV_MciSendString("pause");
                            _base.AV_TimerTick(this, EventArgs.Empty);
                        }
                        if (resetPos)
                        {
                            _base.SetPosition(pos);
                            if (resetRepeat) _base._repeat = false;
                        }
                    }
                    _base.OnMediaStartEndChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the playback start position of the player for the next media to play.
        /// When set and the next media starts playing, the value is copied to
        /// the Media.StartPosition setting of the player and then reset to 00:00:00.
        /// Special use: values between 0 and 100 ms are interpreted as percentages of the length of the next media.
        /// </summary>
        public TimeSpan StartPositionNext
        {
            get { return TimeSpan.FromMilliseconds(_base._startPositionNext); }
            set
            {
                if (_base._startPositionNext != (int)value.TotalMilliseconds)
                {
                    _base._startPositionNext = (int)value.TotalMilliseconds;
                    _base.OnMediaStartEndNextChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the playback end position of the player for the next media to play.
        /// When set and the next media starts playing, the value is copied to the Media.EndPosition setting
        /// of the player and then reset to 00:00:00. TimeSpan.Zero or 00:00:00 = natural end of media.
        /// Special use: values between 0 and 100 ms are interpreted as percentages of the length of the next media.
        /// </summary>
        public TimeSpan EndPositionNext
        {
            get { return TimeSpan.FromMilliseconds(_base._endPositionNext); }
            set
            {
                if (_base._endPositionNext != (int)value.TotalMilliseconds)
                {
                    _base._endPositionNext = (int)value.TotalMilliseconds;
                    _base.OnMediaStartEndNextChanged();
                }
            }
        }

        /// <summary>
        /// Returns mp3 or wma media tag information from the playing media (image retrieved from media or folder).
        /// </summary>
        public TagInfo GetTagInfo()
        {
            if (_base._playing)
            {
                return GetTagInfo(_base._fileName, ImageSource.MediaOrFolder);
            }
            else
            {
                _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                return new TagInfo();
            }
        }

        /// <summary>
        /// Returns mp3 or wma media tag information from the playing media.
        /// </summary>
        /// <param name="imageSource">A value indicating whether and where an album art image is to be obtained.</param>
        public TagInfo GetTagInfo(ImageSource imageSource)
        {
            if (_base._playing)
            {
                return GetTagInfo(_base._fileName, imageSource);
            }
            else
            {
                _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                return new TagInfo();
            }
        }

        /// <summary>
        /// Returns mp3 or wma media tag information from the specified file.
        /// </summary>
        /// <param name="fileName">The path and name of the file whose tag information is to be obtained.</param>
        /// <param name="imageSource">A value indicating whether and where an album art image is to be obtained.</param>
        /// <returns></returns>
        public TagInfo GetTagInfo(string fileName, ImageSource imageSource)
        {
            TagInfo tagInfo = null;
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (fileName == null || fileName == string.Empty) return new TagInfo(); 
            if (!new Uri(fileName).IsFile)
            {
                tagInfo = new TagInfo();
                {
                    try
                    {
                        tagInfo.Title = Path.GetFileNameWithoutExtension(fileName);
                        tagInfo.Album = fileName;
                    }
                    catch { /* ignore */ }
                }
                return tagInfo;
            }

            string extension = Path.GetExtension(fileName);
            if ((string.Compare(extension, ".wma", true) == 0) || (string.Compare(extension, ".asf", true) == 0))
            {
                tagInfo = GetWmaInfo(fileName, imageSource);
            }
            else
            {
                tagInfo = GetMp3Info(fileName, imageSource);
            }

            try
            {
                // Get info from file path
                if (tagInfo.Artist == null || tagInfo.Artist.Length == 0) tagInfo.Artist = Path.GetFileName(Path.GetDirectoryName(fileName));
                if (tagInfo.Title == null || tagInfo.Title.Length == 0) tagInfo.Title = Path.GetFileNameWithoutExtension(fileName);

                // Get album art image (with certain values of 'imageSource')
                if (imageSource == ImageSource.FolderOrMedia || imageSource == ImageSource.FolderOnly || (imageSource == ImageSource.MediaOrFolder && tagInfo.Image == null))
                {
                    GetMediaImage(fileName);
                    if (_tagImage != null) // null image not to replace image retrieved from media file with FolderOrMedia
                    {
                        tagInfo.Image = _tagImage;
                        _tagImage = null;
                    }
                }
            }
            catch { /* ignore */ }

            return tagInfo;
        }

        private TagInfo GetMp3Info(string fileName, ImageSource imageSource)
        {
            FileStream fs   = null;
            TagInfo tagInfo = new TagInfo();
            byte[] header   = new byte[10];
            byte[] buffer   = new byte[256];
            int found       = 0;

            try
            {
                fs = File.OpenRead(fileName);

                // read tag header ID3v2
                fs.Read(header, 0, 10);
                if (Encoding.Default.GetString(header, 0, 3) == "ID3")
                {
                    int totalSize = header[9] + (0x80 * header[8]) + (0x8000 * header[7]) + (0x800000 * header[6]);
                    // check for extended header
                    int frameSize;
                    if ((header[5] & 0x40) == 0x40)
                    {
                        fs.Read(header, 0, 10);
                        frameSize = header[3] + (0x80 * header[2]) + (0x8000 * header[1]) + (0x800000 * header[0]);
                        fs.Position += frameSize;
                    }

                    while (fs.Position < totalSize)
                    {
                        // read frameheader
                        fs.Read(header, 0, 10);
                        string frameId = Encoding.Default.GetString(header, 0, 4);
                        if (frameId == "APIC") frameSize = (0x1000000 * header[4]) + (0x10000 * header[5]) + (0x100 * header[6]) + header[7]; // differs from the description by ID3.org
                        else frameSize = (0x800000 * (header[4] & 0x7F)) + (0x8000 * (header[5] & 0x7F)) + (0x80 * (header[6] & 0x7F)) + (header[7] & 0x7F);
                        if ((header[9] & 0x60) != 0) fs.ReadByte();

                        switch (frameId)
                        {
                            case "APIC": // album art image
                                if (imageSource == ImageSource.None || imageSource == ImageSource.FolderOnly || tagInfo.Image != null) // don't retrieve or already got one
                                {
                                    fs.Position += frameSize;
                                }
                                else
                                {
                                    fs.ReadByte();

                                    int i = 0;
                                    do { buffer[i] = (byte)fs.ReadByte(); }
                                    while (buffer[i++] != 0);
                                    int totalHeader = i;

                                    fs.ReadByte();

                                    i = 0;
                                    do { buffer[i] = (byte)fs.ReadByte(); }
                                    while (buffer[i++] != 0);
                                    totalHeader += i;

                                    byte[] imageBuffer = new byte[frameSize - totalHeader];
                                    fs.Read(imageBuffer, 0, frameSize - totalHeader);

                                    MemoryStream ms = new MemoryStream(imageBuffer);
                                    try { tagInfo.Image = Image.FromStream(ms); }
                                    catch { tagInfo.Image = null; }
                                    ms.Close();
                                }
                                break;
                            case "TYER": // Year
                                tagInfo.Year = GetMp3String(fs, buffer, frameSize);
                                break;
                            case "TALB": // Album
                                tagInfo.Album = GetMp3String(fs, buffer, frameSize);
                                break;
                            case "TIT2": // Title
                                tagInfo.Title = GetMp3String(fs, buffer, frameSize);
                                found++;
                                break;
                            case "TPE1": // Lead Performers
                                if (tagInfo.Artist == null || tagInfo.Artist == string.Empty) tagInfo.Artist = GetMp3String(fs, buffer, frameSize);
                                else fs.Position += frameSize;
                                found++;
                                break;
                            case "TPE2": // Band
                                tagInfo.Artist = GetMp3String(fs, buffer, frameSize);
                                found++;
                                break;
                            case "TLEN": // Duration
                                int time;
                                if (int.TryParse(GetMp3String(fs, buffer, frameSize), out time)) tagInfo._duration = TimeSpan.FromMilliseconds(time);
                                break;
                            case "TCON": // Genre
                                tagInfo.Genre = GetMp3String(fs, buffer, frameSize);
                                break;
                            case "TRCK": // Track
                                tagInfo.Track = GetMp3String(fs, buffer, frameSize);
                                int index = tagInfo.Track.IndexOf('/');
                                if (index == -1)
                                {
                                    int.TryParse(tagInfo.Track, out tagInfo._trackNumber);
                                }
                                else
                                {
                                    string tempString = tagInfo.Track.Remove(index);
                                    int.TryParse(tempString, out tagInfo._trackNumber);
                                }
                                break;
                            default:
                                fs.Position += frameSize;
                                break;
                        }
                    }
                }
            }
            catch { /* ignore */ }

            try
            {
                // If nothing (or not all wanted info) found, try ID3v1:
                if (found < 2)
                {
                    // read tag ID3v1
                    fs.Seek(-128, SeekOrigin.End);
                    fs.Read(buffer, 0, 128);
                    if (Encoding.Default.GetString(buffer, 0, 3) == "TAG")
                    {
                        if (tagInfo.Title == null || tagInfo.Title.Length == 0) tagInfo.Title = Encoding.Default.GetString(buffer, 3, 30).TrimEnd('\0');
                        if (tagInfo.Artist == null || tagInfo.Artist.Length == 0) tagInfo.Artist = Encoding.Default.GetString(buffer, 33, 30).TrimEnd('\0');
                    }
                }
            }
            catch { /* ignore */ }

            if (fs != null) fs.Close();
            return tagInfo;
        }

        private TagInfo GetWmaInfo(string fileName, ImageSource imageSource)
        {
            FileStream fs       = null;
            TagInfo tagInfo     = new TagInfo();
            byte[]  buffer      = new byte[16];
            byte[]  dataBuffer  = new byte[256];
            Guid    guid;
            int     objectCount;
            long    objectLength;

            try
            {
                fs = File.OpenRead(fileName);

                fs.Read(buffer, 0, 16);
                guid = new Guid(buffer);

                // check if wma (asf)
                if (guid == ASF_Header_Guid)
                {
                    fs.Read(buffer, 0, 8); // size
                    fs.Read(buffer, 0, 6); // nr of objects + reserved
                    objectCount = BitConverter.ToUInt16(buffer, 0);

                    for (int i = 0; i < objectCount; i++)
                    {
                        fs.Read(buffer, 0, 16); // read guid
                        guid = new Guid(buffer);
                        fs.Read(buffer, 0, 8); // read length
                        objectLength = BitConverter.ToInt64(buffer, 0);

                        if (guid == ASF_Content_Description_Guid)
                        {
                            fs.Read(buffer, 0, 2);
                            int titleLength = BitConverter.ToInt16(buffer, 0);

                            fs.Read(buffer, 0, 2);
                            int authorLength = BitConverter.ToInt16(buffer, 0);

                            fs.Read(buffer, 0, 2);
                            int copyrightLength = BitConverter.ToInt16(buffer, 0);

                            fs.Read(buffer, 0, 2);
                            int descriptionLength = BitConverter.ToInt16(buffer, 0);

                            fs.Read(buffer, 0, 2);
                            int ratingLength = BitConverter.ToInt16(buffer, 0);

                            if (titleLength > 0)
                            {
                                fs.Read(dataBuffer, 0, titleLength);
                                tagInfo.Title = Encoding.Unicode.GetString(dataBuffer, 0, titleLength).TrimEnd('\0');
                            }
                            if (authorLength > 0)
                            {
                                fs.Read(dataBuffer, 0, authorLength);
                                tagInfo.Artist = Encoding.Unicode.GetString(dataBuffer, 0, authorLength).TrimEnd('\0');
                            }
                            if (copyrightLength > 0)
                            {
                                fs.Read(dataBuffer, 0, copyrightLength);
                                //_copyright = Encoding.Unicode.GetString(infoBuffer, 0, copyrightLength).TrimEnd('\0');
                            }
                            if (descriptionLength > 0)
                            {
                                fs.Read(dataBuffer, 0, descriptionLength);
                                //_description = Encoding.Unicode.GetString(infoBuffer, 0, descriptionLength).TrimEnd('\0');
                            }
                            if (ratingLength > 0)
                            {
                                fs.Read(dataBuffer, 0, ratingLength);
                                //_rating = Encoding.Unicode.GetString(infoBuffer, 0, ratingLength).TrimEnd('\0');
                            }
                        }
                        else if (guid == ASF_Extended_Content_Description_Guid)
                        {
                            fs.Read(buffer, 0, 2); // name length
                            int itemCount = BitConverter.ToUInt16(buffer, 0);

                            for (int j = 0; j < itemCount; j++)
                            {
                                string tempString;
                                int index;

                                fs.Read(buffer, 0, 2); // name length
                                int nameLength = BitConverter.ToUInt16(buffer, 0);
                                fs.Read(dataBuffer, 0, nameLength); // name
                                string name = Encoding.Unicode.GetString(dataBuffer, 0, nameLength).TrimEnd('\0');

                                fs.Read(buffer, 0, 2); // value data type
                                                       //int dataType = BitConverter.ToUInt16(buffer, 0);
                                fs.Read(buffer, 0, 2); // value length
                                int valueLen = BitConverter.ToUInt16(buffer, 0);

                                switch (name)
                                {
                                    case "WM/Picture":
                                        if (imageSource != ImageSource.None && imageSource != ImageSource.FolderOnly && tagInfo.Image == null)
                                        {
                                            int length = 7; // total header length (7 + unicode string)
                                            fs.Read(buffer, 0, 5); // MIME type + Picture type
                                            do // read unicode string (should use length but this is ok here)
                                            {
                                                fs.Read(buffer, 0, 2);
                                                length += 2;
                                            }
                                            while (buffer[0] != 0 || buffer[1] != 0);
                                            fs.Read(buffer, 0, 2); // data length? (not used here anyway)

                                            byte[] imageBuffer = new byte[valueLen - length];
                                            fs.Read(imageBuffer, 0, valueLen - length);

                                            MemoryStream ms = new MemoryStream(imageBuffer);
                                            try { tagInfo.Image = Image.FromStream(ms); }
                                            catch { tagInfo.Image = null; }

                                            ms.Close();
                                        }
                                        else fs.Position += valueLen;
                                        break;
                                    case "WM/AlbumTitle":
                                        fs.Read(dataBuffer, 0, valueLen);
                                        tagInfo.Album = Encoding.Unicode.GetString(dataBuffer, 0, valueLen).TrimEnd('\0');
                                        break;
                                    case "WM/Genre":
                                        fs.Read(dataBuffer, 0, valueLen);
                                        tagInfo.Genre = Encoding.Unicode.GetString(dataBuffer, 0, valueLen).TrimEnd('\0');
                                        break;
                                    case "WM/Year":
                                        fs.Read(dataBuffer, 0, valueLen);
                                        tagInfo.Year = Encoding.Unicode.GetString(dataBuffer, 0, valueLen).TrimEnd('\0');
                                        break;
                                    case "WM/TrackNumber":
                                        fs.Read(dataBuffer, 0, valueLen);
                                        tagInfo.Track = Encoding.Unicode.GetString(dataBuffer, 0, valueLen).TrimEnd('\0');
                                        index = tagInfo.Track.IndexOf('/');
                                        if (index == -1)
                                        {
                                            int.TryParse(tagInfo.Track, out tagInfo._trackNumber);
                                        }
                                        else
                                        {
                                            tempString = tagInfo.Track.Remove(index);
                                            int.TryParse(tempString, out tagInfo._trackNumber);
                                        }
                                        break;
                                    case "WM/Track":
                                        // WM/Track is 'old' WM/TrackNumber
                                        if (tagInfo.Track == null || tagInfo.Track == string.Empty)
                                        {
                                            fs.Read(dataBuffer, 0, valueLen);
                                            tagInfo.Track = Encoding.Unicode.GetString(dataBuffer, 0, valueLen).TrimEnd('\0');
                                            index = tagInfo.Track.IndexOf('/');
                                            if (index == -1)
                                            {
                                                int.TryParse(tagInfo.Track, out tagInfo._trackNumber);
                                            }
                                            else
                                            {
                                                tempString = tagInfo.Track.Remove(index);
                                                int.TryParse(tempString, out tagInfo._trackNumber);
                                            }
                                        }
                                        else fs.Position += valueLen;
                                        break;
                                    default:
                                        fs.Position += valueLen;
                                        break;
                                }
                            }
                        }
                        else
                        {
                            fs.Position += (objectLength - 24); // skip to next
                        }
                    }
                }
            }
            catch { /* ignore */ }

            if (fs != null) fs.Close();
            return tagInfo;
        }

        // Get mp3 information string help function
        private string GetMp3String(FileStream fs, byte[] buffer, int frameSize)
        {
            string result;

            if (frameSize > buffer.Length) buffer = new byte[frameSize];
            fs.Read(buffer, 0, frameSize);
            switch (buffer[1])
            {
                case 0xFF:
                    result = Encoding.Unicode.GetString(buffer, 1, frameSize - 1).TrimEnd('\0');
                    break;
                case 0xFE:
                    result = Encoding.BigEndianUnicode.GetString(buffer, 1, frameSize - 1).TrimEnd('\0');
                    break;
                default:
                    result = Encoding.Default.GetString(buffer, 1, frameSize - 1).TrimEnd('\0');
                    break;
            }

            return result.Trim();
        }

        // Get media information image help function
        private void GetMediaImage(string fileName)
        {
            _directoryInfo = new DirectoryInfo(Path.GetDirectoryName(fileName));
            string searchFileName = Path.GetFileNameWithoutExtension(fileName);
            string searchDirectoryName = _directoryInfo.Name;

            // 1. search using the file name
            if (!SearchMediaImage(searchFileName))
            {
                // 2. search using the directory name
                if (!SearchMediaImage(searchDirectoryName))
                {
                    // 3. search using keywords
                    int i = 0;
                    bool result;
                    do result = SearchMediaImage(_searchKeyWords[i++]);
                    while (!result && i < _searchKeyWords.Length);

                    if (!result)
                    {
                        // 4. find largest file
                        SearchMediaImage("*");
                    }
                }
            }
            _directoryInfo = null;
        }

        // Get media image help function
        private bool SearchMediaImage(string searchName)
        {
            if (searchName.EndsWith(@":\")) return false; // root directory - no folder name (_searchDirectoryName e.g. C:\)

            string      imageFile   = string.Empty;
            long        length      = 0;
            bool        found       = false;

            for (int i = 0; i < _searchExtensions.Length; i++)
            {
                FileInfo[] filesFound = _directoryInfo.GetFiles(searchName + _searchExtensions[i]);

                if (filesFound.Length > 0)
                {
                    for (int j = 0; j < filesFound.Length; j++)
                    {
                        if (filesFound[j].Length > length)
                        {
                            length = filesFound[j].Length;
                            imageFile = filesFound[j].FullName;
                            found = true;
                        }
                    }
                }
            }
            if (found) _tagImage = Image.FromFile(imageFile);
            return found;
        }

        /// <summary>
        /// Returns the path to a new file, created from the specified embedded resource and with the specified filename, in the system's temporary folder for use with the Player.Play methods.
        /// </summary>
        /// <param name="resource">The embedded resource (e.g. Properties.Resources.File) to save to a new file in the temporary folder.</param>
        /// <param name="fileName">The filename (e.g. "File.ext") to be used for the new file in the temporary folder.</param>
        public string ResourceToFile(byte[] resource, string fileName)
        {
            string path = string.Empty;

            if (resource == null || resource.Length <= 0) _base._lastError = Global.MCIERR_NULL_PARAMETER_BLOCK;
            else if (string.IsNullOrEmpty(fileName)) _base._lastError = Global.MCIERR_FILENAME_REQUIRED;
            else
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                try
                {
                    if (Path.IsPathRooted(fileName)) path = fileName;
                    else path = Path.Combine(Path.GetTempPath(), fileName);
                    File.WriteAllBytes(path, resource);
                }
                catch
                {
                    path = string.Empty;
                    _base._lastError = Global.MCIERR_FILE_NOT_SAVED;
                }
            }
            return path;
        }
    }

    #endregion

    #region LevelDevice Class

    /// <summary>
    /// A class that is used to group together the Audio Output Peak Level Device methods and properties of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class LevelDevice : HideObjectMembers
    {
        private Player _base;
        internal LevelDevice(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Gets a value indicating whether the audio output peak level device of the player is enabled (by use of the Player.Events.MediaPeakLevelChanged event) (default: false).
        /// </summary>
        public bool Enabled
        {
            get { return _base._hasPeakLevelEvents; }
        }

        /// <summary>
        /// Gets or sets the name of the audio output peak level device that is used by all players in this assembly to obtain audio output peak level information (a portion of the name can be used to set a device).
        /// The set option is intended to be used only if the automatic device selection of the player fails when subscribing to the Player.Events.MediaPeakLevelChanged event.
        /// </summary>
        public string Name
        {
            get { return (Player._hasLevelDevice || Player._levelDevice_Timer != null) ? Player._levelDevice_Name : string.Empty; }
            set
            {
                string deviceName = Player._levelDevice_Name;

                Player.LevelDevice_SetByName(value);
                if (Player._levelDevice_Name != deviceName)
                {
                    if (_base._hasPeakLevelEvents)
                    {
                        _base._peakLevelArgs._leftLevel = -1;
                        _base._peakLevelArgs._rightLevel = -1;
                        _base._mediaOutputLevelChanged(this, _base._peakLevelArgs);
                    }
                    _base.OnMediaLevelDeviceChanged();
                }
            }
        }

        /// <summary>
        /// Returns the names and indexes (= position in the returned zero-based array) of all enabled system audio input devices, which may include an audio output peak level device.
        /// </summary>
        public string[] GetDevices()
        {
            return DeviceInfo.GetAllInputDeviceNames();
        }

        /// <summary>
        /// Gets a value indicating whether an audio output peak level device is available on the host (computer) system.
        /// </summary>
        public bool Present
        {
            get { return ((Player._hasLevelDevice || Player._levelDevice_Timer != null) || Player.LevelDevice_GetDefaultIndex() != -1) ? true : false; }
        }

        /// <summary>
        /// Gets the number of all enabled system audio input devices, which may include an audio output peak level device.
        /// </summary>
        public int Count
        {
            get { return DeviceInfo.GetAllInputDeviceNames().Length; }
        }

        /// <summary>
        /// Gets or sets the index of the audio output peak level device that is used by all players in this assembly to obtain audio peak level information.
        /// The set option is intended to be used only if the automatic device selection of the player fails when subscribing to the Player.Events.MediaPeakLevelChanged event.
        /// </summary>
        public int Index
        {
            get { return (Player._hasLevelDevice || Player._levelDevice_Timer != null) ? DeviceInfo.GetInputDeviceIndex(Player._levelDevice_Name) : -1; }
            set
            {
                string deviceName = Player._levelDevice_Name;

                Player.LevelDevice_SetByIndex(value);
                if (Player._levelDevice_Name != deviceName)
                {
                    if (_base._hasPeakLevelEvents)
                    {
                        _base._peakLevelArgs._leftLevel = -1;
                        _base._peakLevelArgs._rightLevel = -1;
                        _base._mediaOutputLevelChanged(this, _base._peakLevelArgs);
                    }
                    _base.OnMediaLevelDeviceChanged();
                }
            }
        }

        /// <summary>
        /// Resets the selection of an audio output peak level device to an automatically by the player selected device (if such device is available).
        /// </summary>
        public void Reset()
        {
            string deviceName = Player._levelDevice_Name;

            Player.LevelDevice_SetDefault(true);
            if (Player._levelDevice_Name != deviceName)
            {
                if (_base._hasPeakLevelEvents)
                {
                    _base._peakLevelArgs._leftLevel = -1;
                    _base._peakLevelArgs._rightLevel = -1;
                    _base._mediaOutputLevelChanged(this, _base._peakLevelArgs);
                }
                _base.OnMediaLevelDeviceChanged();
            }
        }
    }

    #endregion

    #region Events Class

    /// <summary>
    /// A class that is used to group together the Events of the PVS.AVPlayer.Player class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class Events : HideObjectMembers
    {
        private Player _base;
        internal Events(Player player)
        {
            _base = player;
        }

        /// <summary>
        /// Occurs when media has finished playing.
        /// </summary>
        public event EventHandler MediaEnded
        {
            add { _base.MediaEnded += value; }
            remove { _base.MediaEnded -= value; }
        }

        /// <summary>
        /// Occurs when media has finished playing, just before the Player.Events.MediaEnded event occurs.
        /// </summary>
        public event EventHandler MediaEndedNotice
        {
            add { _base.MediaEndedNotice += value; }
            remove { _base.MediaEndedNotice -= value; }
        }

        /// <summary>
        /// Occurs when playback of next media is requested.
        /// </summary>
        public event EventHandler MediaNextRequested
        {
            add { _base.MediaNextRequested += value; }
            remove { _base.MediaNextRequested -= value; }
        }

        /// <summary>
        /// Occurs when playback of previous media is requested.
        /// </summary>
        public event EventHandler MediaPreviousRequested
        {
            add { _base.MediaPreviousRequested += value; }
            remove { _base.MediaPreviousRequested -= value; }
        }

        /// <summary>
        /// Occurs when the repeat setting of the player has changed.
        /// </summary>
        public event EventHandler MediaRepeatChanged
        {
            add { _base.MediaRepeatChanged += value; }
            remove { _base.MediaRepeatChanged -= value; }
        }

        /// <summary>
        /// Occurs when media has finished playing and is about to be repeated.
        /// </summary>
        public event EventHandler MediaRepeating
        {
            add { _base.MediaRepeating += value; }
            remove { _base.MediaRepeating -= value; }
        }

        /// <summary>
        /// Occurs when media has finished playing and is repeated.
        /// </summary>
        public event EventHandler MediaRepeated
        {
            add { _base.MediaRepeated += value; }
            remove { _base.MediaRepeated -= value; }
        }

        /// <summary>
        /// Occurs when a mediafile is opened to be played.
        /// </summary>
        public event EventHandler MediaOpened
        {
            add { _base.MediaOpened += value; }
            remove { _base.MediaOpened -= value; }
        }

        /// <summary>
        /// Occurs when media starts playing.
        /// </summary>
        public event EventHandler MediaStarted
        {
            add { _base.MediaStarted += value; }
            remove { _base.MediaStarted -= value; }
        }

        /// <summary>
        /// Occurs when the pause mode of the player is activated (playing media is paused).
        /// </summary>
        public event EventHandler MediaPaused
        {
            add { _base.MediaPaused += value; }
            remove { _base.MediaPaused -= value; }
        }

        /// <summary>
        /// Occurs when the pause mode of the player is deactivated (paused media resumes playing).
        /// </summary>
        public event EventHandler MediaResumed
        {
            add { _base.MediaResumed += value; }
            remove { _base.MediaResumed -= value; }
        }

        /// <summary>
        /// Occurs when media has stopped playing by the player to play other media or by using the Stop function of the player.
        /// </summary>
        public event EventHandler<StoppedEventArgs> MediaStopped
        {
            add { _base.MediaStopped += value; }
            remove { _base.MediaStopped -= value; }
        }

        /// <summary>
        /// Occurs when media has stopped playing, just before the Player.Events.MediaStopped event occurs.
        /// </summary>
        public event EventHandler<StoppedEventArgs> MediaStoppedNotice
        {
            add { _base.MediaStoppedNotice += value; }
            remove { _base.MediaStoppedNotice -= value; }
        }

        /// <summary>
        /// Occurs when the playback position of the playing media has changed.
        /// </summary>
        public event EventHandler<PositionEventArgs> MediaPositionChanged
        {
            add { _base.MediaPositionChanged += value; }
            remove { _base.MediaPositionChanged -= value; }
        }

        /// <summary>
        /// Occurs when the start- and/or endposition of the player for the next media to play has changed.
        /// </summary>
        public event EventHandler MediaStartEndNextChanged
        {
            add { _base.MediaStartEndNextChanged += value; }
            remove { _base.MediaStartEndNextChanged -= value; }
        }

        /// <summary>
        /// Occurs when the start- and/or endposition of the playing media has changed.
        /// </summary>
        public event EventHandler MediaStartEndChanged
        {
            add { _base.MediaStartEndChanged += value; }
            remove { _base.MediaStartEndChanged -= value; }
        }

        /// <summary>
        /// Occurs when the display of the player has changed.
        /// </summary>
        public event EventHandler MediaDisplayChanged
        {
            add { _base.MediaDisplayChanged += value; }
            remove { _base.MediaDisplayChanged -= value; }
        }

        /// <summary>
        /// Occurs when the displaymode of the player has changed.
        /// </summary>
        public event EventHandler MediaDisplayModeChanged
        {
            add { _base.MediaDisplayModeChanged += value; }
            remove { _base.MediaDisplayModeChanged -= value; }
        }

        /// <summary>
        /// Occurs when the fullscreen setting of the player has changed.
        /// </summary>
        public event EventHandler MediaFullScreenChanged
        {
            add { _base.MediaFullScreenChanged += value; }
            remove { _base.MediaFullScreenChanged -= value; }
        }

        /// <summary>
        /// Occurs when the fullscreen mode of the player has changed.
        /// </summary>
        public event EventHandler MediaFullScreenModeChanged
        {
            add { _base.MediaFullScreenModeChanged += value; }
            remove { _base.MediaFullScreenModeChanged -= value; }
        }

        /// <summary>
        /// Occurs when the audio volume of the player has changed.
        /// </summary>
        public event EventHandler MediaAudioVolumeChanged
        {
            add { _base.MediaAudioVolumeChanged += value; }
            remove { _base.MediaAudioVolumeChanged -= value; }
        }

        /// <summary>
        /// Occurs when the audio balance of the player has changed.
        /// </summary>
        public event EventHandler MediaAudioBalanceChanged
        {
            add { _base.MediaAudioBalanceChanged += value; }
            remove { _base.MediaAudioBalanceChanged -= value; }
        }

        /// <summary>
        /// Occurs when the audio enabled setting of the player has changed.
        /// </summary>
        public event EventHandler MediaAudioEnabledChanged
        {
            add { _base.MediaAudioEnabledChanged += value; }
            remove { _base.MediaAudioEnabledChanged -= value; }
        }

        /// <summary>
        /// Occurs when the video enabled setting of the player has changed.
        /// </summary>
        public event EventHandler MediaVideoEnabledChanged
        {
            add { _base.MediaVideoEnabledChanged += value; }
            remove { _base.MediaVideoEnabledChanged -= value; }
        }

        /// <summary>
        /// Occurs when the videobounds of the video on the display of the player have changed (by using VideoBounds, VideoZoom or VideoMove options).
        /// </summary>
        public event EventHandler MediaVideoBoundsChanged
        {
            add { _base.MediaVideoBoundsChanged += value; }
            remove { _base.MediaVideoBoundsChanged -= value; }
        }

        /// <summary>
        /// Occurs when the playback speed setting of the player has changed.
        /// </summary>
        public event EventHandler MediaSpeedChanged
        {
            add { _base.MediaSpeedChanged += value; }
            remove { _base.MediaSpeedChanged -= value; }
        }

        /// <summary>
        /// Occurs when the display overlay of the player has changed.
        /// </summary>
        public event EventHandler MediaOverlayChanged
        {
            add { _base.MediaOverlayChanged += value; }
            remove { _base.MediaOverlayChanged -= value; }
        }

        /// <summary>
        /// Occurs when the display overlay mode setting of the player has changed.
        /// </summary>
        public event EventHandler MediaOverlayModeChanged
        {
            add { _base.MediaOverlayModeChanged += value; }
            remove { _base.MediaOverlayModeChanged -= value; }
        }

        /// <summary>
        /// Occurs when the display overlay hold setting of the player has changed.
        /// </summary>
        public event EventHandler MediaOverlayHoldChanged
        {
            add { _base.MediaOverlayHoldChanged += value; }
            remove { _base.MediaOverlayHoldChanged -= value; }
        }

        /// <summary>
        /// Occurs when the active state of the display overlay of the player has changed.
        /// </summary>
        public event EventHandler MediaOverlayActiveChanged
        {
            add { _base.MediaOverlayActiveChanged += value; }
            remove { _base.MediaOverlayActiveChanged -= value; }
        }

        /// <summary>
        /// Occurs when the audio output peak level device of the player has changed.
        /// </summary>
        public event EventHandler MediaLevelDeviceChanged
        {
            add { _base.MediaLevelDeviceChanged += value; }
            remove { _base.MediaLevelDeviceChanged -= value; }
        }

        /// <summary>
        /// Occurs when the audio output peak level of the player has changed.
        /// </summary>
        public event EventHandler<PeakLevelEventArgs> MediaPeakLevelChanged
        {
            add { _base.MediaOutputLevelChanged += value; }
            remove { _base.MediaOutputLevelChanged -= value; }
        }

        /// <summary>
        /// Occurs when the number of display clones of the player has changed.
        /// </summary>
        public event EventHandler MediaDisplayClonesChanged
        {
            add { _base.MediaDisplayClonesChanged += value; }
            remove { _base.MediaDisplayClonesChanged -= value; }
        }

        /// <summary>
        /// Occurs when the display clones of the player are started (usually when media starts playing or Player.Overlay.Hold has changed).
        /// </summary>
        public event EventHandler MediaDisplayClonesStarted
        {
            add { _base.MediaDisplayClonesStarted += value; }
            remove { _base.MediaDisplayClonesStarted -= value; }
        }

        /// <summary>
        /// Occurs when the display clones of the player are stopped (usually when media stops playing or Player.Overlay.Hold has changed).
        /// </summary>
        public event EventHandler MediaDisplayClonesStopped
        {
            add { _base.MediaDisplayClonesStopped += value; }
            remove { _base.MediaDisplayClonesStopped -= value; }
        }

        /// <summary>
        /// Occurs when the current subtitle of the player has changed.
        /// </summary>
        public event EventHandler<SubtitleEventArgs> MediaSubtitleChanged
        {
            add { _base.MediaSubtitleChanged += value; }
            remove { _base.MediaSubtitleChanged -= value; }
        }

        /// <summary>
        /// Occurs when the active video track of the playing media has changed.
        /// </summary>
        public event EventHandler MediaVideoTrackChanged
        {
            add { _base.MediaVideoTrackChanged += value; }
            remove { _base.MediaVideoTrackChanged -= value; }
        }

        /// <summary>
        /// Occurs when the active audio track of the playing media has changed.
        /// </summary>
        public event EventHandler MediaAudioTrackChanged
        {
            add { _base.MediaAudioTrackChanged += value; }
            remove { _base.MediaAudioTrackChanged -= value; }
        }

        /// <summary>
        /// Occurs when a user begins a drag form operation by pressing the left mouse button on the display of the player.
        /// </summary>
        public event EventHandler MediaDragDisplayBegin
        {
            add { _base.MediaDragDisplayBegin += value; }
            remove { _base.MediaDragDisplayBegin -= value; }
        }

        /// <summary>
        /// Occurs when a user drags a form by dragging the display of the player.
        /// </summary>
        public event EventHandler MediaDragDisplayMove
        {
            add { _base.MediaDragDisplayMove += value; }
            remove { _base.MediaDragDisplayMove -= value; }
        }

        /// <summary>
        /// Occurs when a user finishes a drag form operation by releasing the left mouse button.
        /// </summary>
        public event EventHandler MediaDragDisplayEnd
        {
            add { _base.MediaDragDisplayEnd += value; }
            remove { _base.MediaDragDisplayEnd -= value; }
        }

        ///// <summary>
        ///// Occurs when playing media (and the associated MCI device) is about to be closed.
        ///// </summary>
        //public event EventHandler MediaClosing
        //{
        //    add { _base.MediaClosing += value; }
        //    remove { _base.MediaClosing -= value; }
        //}

        ///// <summary>
        ///// Occurs when playing media (and the associated MCI device) is closed.
        ///// </summary>
        //public event EventHandler MediaClosed
        //{
        //    add { _base.MediaClosed += value; }
        //    remove { _base.MediaClosed -= value; }
        //}

        // ******************************** Mouse events

        /// <summary>
        /// Occurs when the mouse pointer is moved over the display or display overlay of the player.
        /// </summary>
        public event EventHandler<MediaMouseEventArgs> MediaMouseMove
        {
            add { _base.MediaMouseMove += value; }
            remove { _base.MediaMouseMove -= value; }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the display or display overlay of the player and a mouse button is pressed. 
        /// </summary>
        public event EventHandler<MediaMouseEventArgs> MediaMouseDown
        {
            add { _base.MediaMouseDown += value; }
            remove { _base.MediaMouseDown -= value; }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the display or display overlay of the player and a mouse button is released.
        /// </summary>
        public event EventHandler<MediaMouseEventArgs> MediaMouseUp
        {
            add { _base.MediaMouseUp += value; }
            remove { _base.MediaMouseUp -= value; }
        }

        /// <summary>
        /// Occurs when the display or display overlay of the player is clicked.
        /// </summary>
        public event EventHandler<MediaMouseEventArgs> MediaMouseClick
        {
            add { _base.MediaMouseClick += value; }
            remove { _base.MediaMouseClick -= value; }
        }

        /// <summary>
        /// Occurs when the display or display overlay of the player is double-clicked.
        /// </summary>
        public event EventHandler<MediaMouseEventArgs> MediaMouseDoubleClick
        {
            add { _base.MediaMouseDoubleClick += value; }
            remove { _base.MediaMouseDoubleClick -= value; }
        }

        /// <summary>
        /// Occurs when the mouse pointer is over the display or display overlay of the player and the mouse wheel is moved.
        /// </summary>
        public event EventHandler<MediaMouseEventArgs> MediaMouseWheel
        {
            add { _base.MediaMouseWheel += value; }
            remove { _base.MediaMouseWheel -= value; }
        }
    }

    #endregion


    // ******************************** Grouping Classes Recorder

    // These classes are used to group together Recorder class methods and properties.

    #region InputDevice Class

    /// <summary>
    /// A class that is used to group together the Input Device methods and properties of the PVS.AVPlayer.Recorder class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class InputDevice : HideObjectMembers
    {
        private Recorder _base;
        internal InputDevice(Recorder recorder)
        {
            _base = recorder;
        }

        /// <summary>
        /// Gets the number of all enabled system audio input devices.
        /// </summary>
        public int Count
        {
            get { return DeviceInfo.GetAllInputDeviceNames().Length; }
        }

        /// <summary>
        /// Gets or sets the index of the system audio input device used by the recorder.
        /// </summary>
        public int Index
        {
            get { return _base._inputDeviceIndex; }
            set
            {
                int deviceCount = Count;
                if (value < 0 || deviceCount == 0 || value >= deviceCount)
                {
                    if (deviceCount == 0) _base._lastError = Global.MCIERR_WAVE_INPUTSUNSUITABLE;
                    else _base._lastError = Global.MCIERR_OUTOFRANGE;
                }
                else if (_base._isRecording)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("set " + _base._deviceId + " input " + value, null, 0, IntPtr.Zero);
                }
                else
                {
                    _base._lastError = Global.MCIERR_NO_ERROR;
                }

                if (_base._lastError == Global.MCIERR_NO_ERROR)
                {
                    _base._inputDeviceIndex = value;
                    _base._hasInputDeviceName = false;
                    if (_base._hasLevelDevice) _base.SetLevelDevice(false);
                    _base.OnRecorderInputDeviceChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the name of the system audio input device used by the recorder (a portion of the name can be used to set a device).
        /// </summary>
        public string Name
        {
            get
            {
                if (_base._inputDeviceIndex < 0) return string.Empty;
                if (!_base._hasInputDeviceName)
                {
                    _base._inputDeviceName = DeviceInfo.GetInputDeviceName(_base._inputDeviceIndex);
                    _base._hasInputDeviceName = true;
                }
                return _base._inputDeviceName;
            }

            set
            {
                if (Count == 0)
                {
                    _base._lastError = Global.MCIERR_WAVE_INPUTSUNSUITABLE;
                }
                else
                {
                    _base._lastError = Global.MCIERR_INVALID_DEVICE_NAME;
                    if (!string.IsNullOrEmpty(value))
                    {
                        int index = DeviceInfo.GetInputDeviceIndex(value);
                        if (index != -1)
                        {
                            _base._lastError = Global.MCIERR_NO_ERROR;
                            if (_base._isRecording)
                            {
                                _base._lastError = SafeNativeMethods.mciSendString("set " + _base._deviceId + " input " + index, null, 0, IntPtr.Zero);
                            }
                            if (_base._lastError == Global.MCIERR_NO_ERROR)
                            {
                                _base._inputDeviceIndex = index;
                                _base._hasInputDeviceName = false;
                                if (_base._hasLevelDevice) _base.SetLevelDevice(false);
                                _base.OnRecorderInputDeviceChanged();
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Returns the names and indexes (= position in the returned zero-based array) of all enabled system audio input devices.
        /// </summary>
        public string[] GetDevices()
        {
            return DeviceInfo.GetAllInputDeviceNames();
        }
    }

    #endregion

    #region MciRecDevice Class

    /// <summary>
    /// A class that is used to group together the MCI methods and properties of the PVS.AVPlayer.Recorder class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class MciRecDevice : HideObjectMembers
    {
        private Recorder _base;
        internal MciRecDevice(Recorder recorder)
        {
            _base = recorder;
        }

        /// <summary>
        /// Gets the ID of the MCI device associated with the current recording.
        /// </summary>
        public string Id
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                return _base._hasDevice ? _base._deviceId : string.Empty;
            }
        }

        /// <summary>
        /// Gets the type name of the MCI device associated with the current recording.
        /// </summary>
        public string Type
        {
            get
            {
                if (_base._hasDevice)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("capability " + _base._deviceId + " device type", _base._textBuffer, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR) return _base._textBuffer.ToString();
                }
                else _base._lastError = Global.MCIERR_NO_ERROR;
                return string.Empty;
            }
        }

        /// <summary>
        /// Gets a description (product name) of the MCI device associated with the current recording.
        /// </summary>
        public string Name
        {
            get
            {
                if (_base._hasDevice)
                {
                    _base._lastError = SafeNativeMethods.mciSendString("info " + _base._deviceId + " product", _base._textBuffer, Global.SMALL_BUFFER_SIZE, IntPtr.Zero);
                    if (_base._lastError == Global.MCIERR_NO_ERROR) return _base._textBuffer.ToString();
                }
                else _base._lastError = Global.MCIERR_NO_ERROR;
                return string.Empty;
            }
        }

        /// <summary>
        /// Sends a command to the MCI device associated with the current recording.
        /// </summary>
        /// <param name="command">String that specifies the MCI command.</param>
        public int Command(string command)
        {
            _base._lastError = SafeNativeMethods.mciSendString(command.Trim() + " " + _base._deviceId, null, 0, IntPtr.Zero);
            return _base._lastError;
        }

        /// <summary>
        /// Sends a command to the MCI device associated with the current recording.
        /// </summary>
        /// <param name="command">String that specifies the MCI command.</param>
        /// <param name="parameters">String that specifies the MCI command parameters.</param>
        public int Command(string command, string parameters)
        {
            string parms = parameters.Trim();
            _base._lastError = parms.Length == 0 ? SafeNativeMethods.mciSendString(command.Trim() + " " + _base._deviceId, null, 0, IntPtr.Zero) : SafeNativeMethods.mciSendString(command.Trim() + " " + _base._deviceId + " " + parms, null, 0, IntPtr.Zero);
            return _base._lastError;
        }

        /// <summary>
        /// Sends a request to the MCI device associated with the current recording.
        /// </summary>
        /// <param name="request">String that specifies the MCI request.</param>
        /// <param name="parameters">String that specifies the MCI request parameters.</param>
        /// <param name="result">A string that receives return information.</param>
        public int Request(string request, string parameters, out string result)
        {
            _base._lastError = SafeNativeMethods.mciSendString(request.Trim() + " " + _base._deviceId + " " + parameters.Trim(), _base._textBuffer, Global.BUFFER_SIZE, IntPtr.Zero);
            result = _base._lastError == Global.MCIERR_NO_ERROR ? _base._textBuffer.ToString() : string.Empty;
            return _base._lastError;
        }

        /// <summary>
        /// Gets the number of all open MCI devices in this assembly.
        /// </summary>
        public int Count
        {
            get { return DeviceInfo.GetOpenDevicesCount(); }
        }
    }

    #endregion

    #region RecEvents Class

    /// <summary>
    /// A class that is used to group together the Events of the PVS.AVPlayer.Recorder class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class RecEvents : HideObjectMembers
    {
        private Recorder _base;
        internal RecEvents(Recorder recorder)
        {
            _base = recorder;
        }

        /// <summary>
        /// Occurs when a recording has started.
        /// </summary>
        public event EventHandler RecorderStarted
        {
            add { _base.RecorderStarted += value; }
            remove { _base.RecorderStarted -= value; }
        }

        /// <summary>
        /// Occurs when the pause mode of the recorder is activated (recording is paused).
        /// </summary>
        public event EventHandler RecorderPaused
        {
            add { _base.RecorderPaused += value; }
            remove { _base.RecorderPaused -= value; }
        }

        /// <summary>
        /// Occurs when the pause mode of the recorder is deactivated (recording has resumed).
        /// </summary>
        public event EventHandler RecorderResumed
        {
            add { _base.RecorderResumed += value; }
            remove { _base.RecorderResumed -= value; }
        }

        /// <summary>
        /// Occurs when a recording has stopped by using the Stop function of the recorder.
        /// </summary>
        public event EventHandler RecorderStopped
        {
            add { _base.RecorderStopped += value; }
            remove { _base.RecorderStopped -= value; }
        }

        /// <summary>
        /// Occurs when a recording has stopped and the recording can be saved to a file (length > 0).
        /// </summary>
        public event EventHandler RecorderSaveRequest
        {
            add { _base.RecorderSaveRequest += value; }
            remove { _base.RecorderSaveRequest -= value; }
        }

        /// <summary>
        /// Occurs when the recording position of the recorder has changed.
        /// </summary>
        public event EventHandler<RecorderPositionEventArgs> RecorderPositionChanged
        {
            add { _base.RecorderPositionChanged += value; }
            remove { _base.RecorderPositionChanged -= value; }
        }

        /// <summary>
        /// Occurs when the audio input level of the recorder has changed.
        /// </summary>
        public event EventHandler<InputLevelEventArgs> RecorderInputLevelChanged
        {
            add { _base.RecorderInputLevelChanged += value; }
            remove { _base.RecorderInputLevelChanged -= value; }
        }

        /// <summary>
        /// Occurs when the input device of the recorder has changed.
        /// </summary>
        public event EventHandler RecorderInputDeviceChanged
        {
            add { _base.RecorderInputDeviceChanged += value; }
            remove { _base.RecorderInputDeviceChanged -= value; }
        }

        /// <summary>
        /// Occurs when the input channels setting of the recorder has changed.
        /// </summary>
        public event EventHandler RecorderChannelsChanged
        {
            add { _base.RecorderChannelsChanged += value; }
            remove { _base.RecorderChannelsChanged -= value; }
        }

        /// <summary>
        /// Occurs when the input bits per sample setting of the recorder has changed.
        /// </summary>
        public event EventHandler RecorderBitsChanged
        {
            add { _base.RecorderBitsChanged += value; }
            remove { _base.RecorderBitsChanged -= value; }
        }

        /// <summary>
        /// Occurs when the input samples per second setting of the recorder has changed.
        /// </summary>
        public event EventHandler RecorderSampleRateChanged
        {
            add { _base.RecorderSampleRateChanged += value; }
            remove { _base.RecorderSampleRateChanged -= value; }
        }

    }

    #endregion

    #region TaskbarMarquee Class

    /// <summary>
    /// A class that is used to group together the Taskbar Marquee methods and properties of the PVS.AVPlayer.Recorder class.
    /// </summary>
    [CLSCompliant(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class TaskbarMarquee : HideObjectMembers
    {
        #region Taskbar Marquee Fields

        private Recorder            _base;
        private List<TaskbarItem>   _taskbarItems;
        private class TaskbarItem
        {
            internal Form Form;
            internal IntPtr Handle;
        }

        #endregion

        internal TaskbarMarquee(Recorder recorder)
        {
            _base = recorder;

            if (!Recorder._taskbarMarqueeEnabled)
            {
                if (Environment.OSVersion.Version.Major >= 6) // Windows Vista or later
                {
                    _taskbarItems = new List<TaskbarItem>(4);
                    Recorder.TaskbarInstance = (TaskbarIndicator.ITaskbarList3)new TaskbarIndicator.TaskbarInstance();
                    _base._taskbarMode = TaskbarMarqueeMode.FlashingRed;
                    Recorder._taskbarMarqueeEnabled = true;
                }
            }
            else
            {
                _taskbarItems = new List<TaskbarItem>(4);
            }
        }

        #region Public - Taskbar Marquee Indicator methods and properties

        /// <summary>
        /// Adds a taskbar marquee indicator to the recorder (Windows Vista or later only).
        /// </summary>
        /// <param name="form">The form whose taskbar item is added as a recorder marquee indicator. Multiple forms and duplicates are allowed.</param>
        public int Add(Form form)
        {
            _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            if (Recorder._taskbarMarqueeEnabled)
            {
                lock (_taskbarItems)
                {
                    if (form != null) // && form.ShowInTaskbar)
                    {
                        // check if already exists
                        bool exists = false;
                        for (int i = 0; i < _taskbarItems.Count; i++)
                        {
                            if (_taskbarItems[i].Form == form)
                            {
                                exists = true;
                                break;
                            }
                        }

                        if (!exists)
                        {
                            TaskbarItem item = new TaskbarItem();
                            item.Form = form;
                            item.Handle = form.Handle;
                            _taskbarItems.Add(item);

                            if (_base._isRecording)
                            {
                                Recorder.TaskbarInstance.SetProgressState(item.Handle, TaskbarStates.Indeterminate);
                            }
                            _base._hasTaskbarMarquee = true;
                        }
                        _base._lastError = Global.MCIERR_NO_ERROR;
                    }
                    else _base._lastError = Global.MCIERR_OUTOFRANGE;
                }
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes a taskbar marquee indicator from the recorder.
        /// </summary>
        /// <param name="form">The form whose taskbar recorder marquee indicator is removed.</param>
        public int Remove(Form form)
        {
            _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            if (Recorder._taskbarMarqueeEnabled)
            {
                if (_base._hasTaskbarMarquee && form != null)
                {
                    lock (_taskbarItems)
                    {
                        for (int index = _taskbarItems.Count - 1; index >= 0; index--)
                        {
                            if (_taskbarItems[index].Form == form || _taskbarItems[index].Form == null)
                            {
                                if (_taskbarItems[index].Form != null)
                                {
                                    Recorder.TaskbarInstance.SetProgressState(_taskbarItems[index].Handle, TaskbarStates.NoProgress);
                                }
                                _taskbarItems.RemoveAt(index);
                            }
                        }

                        if (_taskbarItems.Count == 0)
                        {
                            _base._hasTaskbarMarquee = false;
                            _taskbarItems = new List<TaskbarItem>(4);
                        }
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes all taskbar marquee indicators from the recorder.
        /// </summary>
        public int RemoveAll()
        {
            _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
            if (Recorder._taskbarMarqueeEnabled)
            {
                if (_base._hasTaskbarMarquee)
                {
                    _base._hasTaskbarMarquee = false;
                    SetState(TaskbarStates.NoProgress);
                    _taskbarItems = new List<TaskbarItem>(4);
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes all taskbar marquee indicators from the recorder.
        /// </summary>
        public int Clear()
        {
            return RemoveAll();
        }

        /// <summary>
        /// Gets the number of taskbar marquee indicators of the recorder.
        /// </summary>
        public int Count
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                return _taskbarItems == null ? 0 : _taskbarItems.Count;
            }
        }

        /// <summary>
        /// Gets the forms that have a taskbar marquee indicator of the recorder.
        /// </summary>
        public Form[] List
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;

                Form[] result = null;
                if (_taskbarItems != null)
                {
                    int count = _taskbarItems.Count;
                    result = new Form[count];
                    for (int i = 0; i < count; i++)
                    {
                        result[i] = _taskbarItems[i].Form;
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the appearance of the taskbar marquee indicators during recordings (default: FlashingRed).
        /// </summary>
        public TaskbarMarqueeMode Mode
        {
            get { return _base._taskbarMode; }
            set
            {
                if (value != _base._taskbarMode)
                {
                    _base._taskbarMode = value;
                    if (_base._hasTaskbarMarquee && _base._isRecording && !_base._isPaused)
                    {
                        SetState(TaskbarStates.Indeterminate);
                        if (value == TaskbarMarqueeMode.FlashingRed)
                        {
                            _base._taskbarCounter = 0;
                            _base._taskbarFlashOn = true;
                            if (!_base._timer.Enabled) _base._timer.Start();
                        }
                        else
                        {
                            // stop _base._timer?
                        }
                    }
                }
            }
        }

        #endregion

        #region Private - SetState

        internal void SetState(TaskbarStates taskbarState)
        {
            if (taskbarState == TaskbarStates.Indeterminate && _base._taskbarMode != TaskbarMarqueeMode.PulsingGreen)
            {
                for (int i = 0; i < _taskbarItems.Count; i++)
                {
                    if (_taskbarItems[i].Form != null)
                    {
                        try
                        {
                            Recorder.TaskbarInstance.SetProgressState(_taskbarItems[i].Handle, TaskbarStates.Error);
                            Recorder.TaskbarInstance.SetProgressValue(_taskbarItems[i].Handle, 1, 1);
                        }
                        catch { _taskbarItems[i].Form = null; }
                    }
                }
            }
            else
            {
                for (int i = 0; i < _taskbarItems.Count; i++)
                {
                    if (_taskbarItems[i].Form != null)
                    {
                        try { Recorder.TaskbarInstance.SetProgressState(_taskbarItems[i].Handle, taskbarState); }
                        catch { _taskbarItems[i].Form = null; }
                    }
                }
            }
        }

        #endregion
    }

    #endregion

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Peter Vegter
United States United States
No Biography provided

You may also be interested in...

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.180111.1 | Last Updated 7 Dec 2017
Article Copyright 2010 by Peter Vegter
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid