Click here to Skip to main content
13,801,907 members
Click here to Skip to main content

Stats

723.1K views
29.8K downloads
281 bookmarked
Posted 14 Sep 2010
Licenced CPOL

PVS.AVPlayer - MCI Audio and Video Library

, 7 Aug 2018
Windows Media Control Interface (MCI) library with many added features
PVS.AVPlayer
PVS.AVPlayer .NET 2.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.5
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5
PVS.AVPlayer .NET 4.5.1
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5.2
PVS.AVPlayer.XML
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7
PVS.AVPlayer .NET 4.7.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer All Source Code
AVPlayerExample
AVPlayerExample
AVPlayerExample.csproj.user
bin
Debug
PVS.AVPlayer.XML
Release
Dialogs
Display Overlays
obj
Debug
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
WingDings3a.ttf
Voice Recorder
FolderView
FolderView
bin
Debug
PVS.AVPlayer.XML
Release
FolderView.csproj.user
obj
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
PVS.AVPlayer
AVPlayerExample.csproj.user
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
Native Methods
Bob.png
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
PVS.AVPlayer.XML
Custom Items
FolderView.csproj.user
Debug
Bob.png
Crystal Italic1.ttf
media7a.ico
media7b.ico
Media8a.ico
Media8b.ico
Subtitles Overlay
Various
How To (C#)
PVSAVPlayerHowTo
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
obj
Debug
Release
Properties
How To (VB.NET)
PVSAVPlayerHowToVB
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
My Project
Application.myapp
obj
Debug
Release
PVSAVPlayerHowTo.vbproj.user
PVS.AVPlayer Examples
AVPlayerExample.ex_
FolderView.ex_
AVPlayerExample.exe
FolderView.exe
PVS.AVPlayer.dll
/****************************************************************

    PVS.AVPlayer - Version 0.91
    August 2018, The Netherlands
    © Copyright 2018 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 10 files:

     1. Player.cs            - Player source code
     2. MouseEvents.cs       - extension source code of Player.cs, provides player display mouse events
     3. PeakMeter.cs         - extension source code of Player.cs, provides player audio peak level values
     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. Signals.cs           - extension source code of Player.cs, provides player media position signaling
     7. CursorHide.cs        - extension source code of Player.cs, hides the mouse cursor during inactivity
     8. Recorder.cs          - Sound Recorder source code
     9. PlayerRecorder.cs    - code used by both Player.cs (and its extension files) and Recorder.cs
    10. Infolabel.cs         - Info Label (custom ToolTip) 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!

    Peter Vegter
    August 2018, The Netherlands

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

#region Usings

using Microsoft.Win32;
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
    {
        // Clean up on library unload/exit
        //static Global()
        //{
        //    AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
        //}

        //private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        //{
        //    try
        //    {
        //        // Close MCI devices
        //        SafeNativeMethods.mciSendString("close all", null, 0, IntPtr.Zero);
        //
        //        // Close Windows CoreAudio API items
        //    }
        //    catch { /* ignore */ }
        //}

        // PVS.AVPlayer Library Version
        internal const float    VERSION                         = 0.91F;
        internal const string   VERSION_STRING                  = "PVS.AVPlayer 0.91";

        internal const string   SUBTITLES_FILE_EXTENSION        = ".srt";
        internal const string   SIGNALS_FILE_EXTENSION          = ".msf";

        // 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;

        // max repeat to get new alias with error MCIERR_DUPLICATE_ALIAS
        internal const int      ALIAS_REPEAT                    = 10;

        // Used with creating player and recorder ID's (deviceId / alias)
        internal static Random  RandomNumber                    = new Random();
    }

    #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 (DeviceInfo Class)

        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

        // ******************************** GetOutputDeviceName

        #region GetOutputDeviceName

        internal static string GetOutputDeviceName()
        {
            lock (di_DeviceLock)
            {
                int error = Global.MCIERR_NO_ERROR;
                OpenTempDevice();

                error = SafeNativeMethods.mciSendString("set " + di_DeviceId + " output 0", null, 0, IntPtr.Zero);
                if (error == Global.MCIERR_NO_ERROR)
                {
                    di_TextBuffer.Length = 0;
                    error = SafeNativeMethods.mciSendString("info " + di_DeviceId + " output", di_TextBuffer, Global.BUFFER_SIZE, IntPtr.Zero);
                }
                CloseTempDevice();
                return error == Global.MCIERR_NO_ERROR ? di_TextBuffer.ToString() : string.Empty;
            }
        }

        #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
    {
        #region Fields (SliderValue Class))

        // 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;

        #endregion

        /// <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)
        {
            if (slider == null) return 0;

            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
    {
        #region Fields (TagInfo Class))

        private bool        _disposed;
        internal TimeSpan   _duration;
        internal int        _trackNumber;

        #endregion

        /// <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 + Preset Display Clip Rounded Rectangle

        #region InfoLabel + Preset Display Clip Rounded Rectangle

        [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
    {
        #region Fields (Audio Class)

        private Player _base;

        #endregion

        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>
        /// Gets or sets the audio volume of the system's default audio output device, value 0 (mute) to 1000 (max) (Windows Vista or later only).
        /// </summary>
        public int MasterVolume
        {
            get
            {
                int volume = 0;

                if (Environment.OSVersion.Version.Major < 6)
                {
                    _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
                }
                else
                {
                   volume = Player.PeakMeter_GetMasterVolume();
                    if (volume == -1)
                    {
                        volume = 0;
                        _base._lastError = Global.MCIERR_DEVICE_NOT_READY;
                    }
                    else _base._lastError = Global.MCIERR_NO_ERROR;
                }
                return volume;
            }

            set
            {
                if (Environment.OSVersion.Version.Major < 6)
                {
                    _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
                }
                else
                {
                    _base._lastError = Global.MCIERR_NO_ERROR;
                    Player.PeakMeter_SetMasterVolume(value);
                }
            }
        }

        /// <summary>
        /// Gets the name of the system's default audio output device.
        /// </summary>
        public string DeviceName
        {
            get
            {
                string name = string.Empty;

                if (Environment.OSVersion.Version.Major < 6)
                {
                    name = DeviceInfo.GetOutputDeviceName();
                }
                else
                {
                    name = Player.PeakMeter_GetName();
                    if (string.IsNullOrEmpty(name)) name = DeviceInfo.GetOutputDeviceName();
                }
                return name;
            }
        }
    }

    #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
    {
        #region Fields (Video Class)

        private Player  _base;
        private bool    _zoomBusy;
        private bool    _boundsBusy;

        #endregion

        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 Player.Display.Mode 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._videoBoundsClip = Rectangle.Intersect(_base._display.DisplayRectangle, _base._videoBounds);
                        _base._hasVideoBounds = true;

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

                        if (_base._hasDisplayShape) _base.AV_UpdateDisplayShape();

                        _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 Player.Display.Mode 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 Player.Display.Mode 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 Player.Display.Mode 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 Player.Display.Mode 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 Player.Display.Mode 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 Player.Display.Mode 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 Fields (Display Class)

        private Player  _base;
        private bool    _dragEnabled;
        private Cursor  _dragCursor     = Cursors.SizeAll;
        private bool    _setDragCursor  = true;
        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: ZoomCenter). See also: Player.Display.SetMode.
        /// </summary>
        public DisplayMode Mode
        {
            get { return _base._displayMode; }
            set
            {
                _base._lastError = Global.MCIERR_NO_ERROR;

                if (_base._displayMode != value)
                {
                    _base._displayMode = value;
                    if (value == DisplayMode.Manual || (value == DisplayMode.Custom && !_base._hasDisplayCustomMode))
                    {
                        if (!_base._hasVideoBounds)
                        {
                            _base._videoBounds.X = _base._videoBounds.Y = 0;
                            _base._videoBounds.Size = _base._display.Size;
                            _base._videoBoundsClip = _base._videoBounds;
                            _base._hasVideoBounds = true;
                        }
                    }
                    else _base._hasVideoBounds = false;

                    if (_base._hasVideo) _base.AV_SetDisplayMode(value);
                    if (_base._hasDisplayShape) _base.AV_UpdateDisplayShape();
                    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 modes Normal, Center, CoverCenter, Custom and Manual when the display window of the player is resized (default: false).
        /// </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 window 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;
                _setDragCursor = value != Cursors.Default;
            }

        }

        /// <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;
                        if (_setDragCursor) _base._display.Cursor = _dragCursor;

                        _base.OnMediaDragFormBegin();
                    }
                }
            }
        }

        private void DragDisplay_MouseMove(object sender, MediaMouseEventArgs e)
        {
            if (_setDragCursor) _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;

            if (_setDragCursor) _base._display.Cursor = Cursors.Default;

            _base.OnMediaDragFormEnd();
        }

        /// <summary>
        /// Sets a custom shape of the display window of the player. See also: Player.Display.Shape.
        /// </summary>
        /// <param name="callback">The method to be called when the shape of the display window needs to be updated. Use null or DisplayShape.Normal to set the normal shape of the display window.</param>
        public int SetShape(DisplayShapeCallback callback)
        {
            return SetShape(callback, true, false);
        }

        /// <summary>
        /// Sets a custom shape of the display window of the player. See also: Player.Display.Shape.
        /// </summary>
        /// <param name="callback">The method to be called when the shape of the display window needs to be updated. Use null or DisplayShape.Normal to set the normal shape of the display window.</param>
        /// <param name="videoShape">A value indicating whether the shape is a video size related shape and the display window should be updated when the size of the video image changes. Use this option only with shapes related to the size of the video image, the shape of the display window is always updated when the size of the display window has changed.</param>
        /// <param name="overlayShape">A value indicating whether display overlays should also take the shape of the display window (may cause unwanted effects with display clones and other uses).</param>
        public int SetShape(DisplayShapeCallback callback, bool videoShape, bool overlayShape)
        {
            return SetDisplayShape(DisplayShape.Custom, callback, videoShape, overlayShape);
        }

        /// <summary>
        /// Sets a custom shape of the display window of the player. See also: Player.Display.Shape.
        /// </summary>
        /// <param name="shape">A value indicating the preset shape of the display window of the player. If used with a form, set the border style of the form to none.</param>
        public int SetShape(DisplayShape shape)
        {
            return SetShape(shape, true, false);
        }

        /// <summary>
        /// Sets a custom shape of the display window of the player. See also: Player.Display.Shape.
        /// </summary>
        /// <param name="shape">A value indicating the preset shape of the display window of the player. If used with a form, set the border style of the form to none.</param>
        /// <param name="videoShape">A value indicating whether the preset shape should be related to the size of the video image (value = true) or to the size of the display window (value = false).</param>
        /// <param name="overlayShape">A value indicating whether display overlays should also take the shape of the display window (may cause unwanted effects with display clones and other uses).</param>
        public int SetShape(DisplayShape shape, bool videoShape, bool overlayShape)
        {
            DisplayShapeCallback callback = null;

            switch (shape)
            {
                case DisplayShape.ArrowDown:
                    callback = _base.AV_PresetArrowDownShape;
                    break;

                case DisplayShape.ArrowLeft:
                    callback = _base.AV_PresetArrowLeftShape;
                    break;

                case DisplayShape.ArrowRight:
                    callback = _base.AV_PresetArrowRightShape;
                    break;

                case DisplayShape.ArrowUp:
                    callback = _base.AV_PresetArrowUpShape;
                    break;

                case DisplayShape.Bars:
                    callback = _base.AV_PresetBarsShape;
                    break;

                case DisplayShape.Beams:
                    callback = _base.AV_PresetBeamsShape;
                    break;

                case DisplayShape.Cross:
                    callback = _base.AV_PresetCrossShape;
                    break;

                case DisplayShape.Custom:
                    if (_base._customShapeCallback != null)
                    {
                        callback = _base._customShapeCallback;
                        // else callback = null - normal shape
                    }
                    break;

                case DisplayShape.Diamond:
                    callback = _base.AV_PresetDiamondShape;
                    break;

                case DisplayShape.Heart:
                    callback = _base.AV_PresetHeartShape;
                    break;

                case DisplayShape.Hexagon:
                    callback = _base.AV_PresetHexagonalShape;
                    break;

                case DisplayShape.Oval:
                    callback = _base.AV_PresetOvalShape;
                    break;

                case DisplayShape.Rounded:
                    callback = _base.AV_PresetRoundedShape;
                    break;

                case DisplayShape.Star:
                    callback = _base.AV_PresetStarShape;
                    break;

                case DisplayShape.Tiles:
                    callback = _base.AV_PresetTilesShape;
                    break;

                case DisplayShape.Triangle:
                    callback = _base.AV_PresetTriangleShape;
                    break;

                default: // case DisplayShape.Normal:
                    // callback = null;
                    break;
            }

            return SetDisplayShape(shape, callback, videoShape, overlayShape);
        }

        private int SetDisplayShape(DisplayShape shape, DisplayShapeCallback callback, bool videoShape, bool overlayShape)
        {
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (callback == null)
            {
                _base.AV_RemoveDisplayShape(true);
            }
            else
            {
                if (_base._hasDisplay)
                {
                    if (callback != _base._displayShapeCallback || videoShape != _base._hasVideoShape || overlayShape != _base._hasOverlayClipping)
                    {
                        _base._displayShapeCallback = callback;
                        if (shape == DisplayShape.Custom) _base._customShapeCallback = callback;

                        _base._currentDisplayShape = shape;
                        _base._hasVideoShape = videoShape;

                        //if (overlayShape) _base.AV_SetOverlayClipping(true);
                        _base.AV_SetOverlayClipping(overlayShape);

                        _base._hasDisplayShape = true;
                        _base.AV_UpdateDisplayShape();

                        if (videoShape)
                        {
                            _base.MediaVideoBoundsChanged += _base.AV_DisplayShapeChanged;
                        }

                        _base.OnMediaDisplayShapeChanged();
                    }
                }
                else
                {
                    if (_base._currentDisplayShape != DisplayShape.Normal) _base.AV_RemoveDisplayShape(true);
                    _base._lastError = Global.MCIERR_NO_WINDOW;
                }
            }
            return _base._lastError;
        }

        /// <summary>
        /// Gets or sets the shape of the display window of the player. When set, the shape is related to the video size with no overlay shape (default: DisplayShape.Normal). See also: Player.Display.SetShape.
        /// </summary>
        public DisplayShape Shape
        {
            get { return _base._currentDisplayShape; }
            set { SetShape(value, true, false); }
        }

        ///// <summary>
        ///// Updates the shape of the display window of the player.
        ///// </summary>
        //public int UpdateShape()
        //{
        //    if (_base._hasDisplayShape) _base.AV_UpdateDisplayShape();

        //    _base._lastError = Global.MCIERR_NO_WINDOW;
        //    return _base._lastError;
        //}

        /// <summary>
        /// Sets a custom display mode (size and location) of the video image on the display of the player. See also: Player.Display.Mode.
        /// </summary>
        /// <param name="callback">The method to be called when the size and/or location of the video image may need to be updated.</param>
        /// <param name="setMode">A value indicating whether this custom display mode should be activated immediately.</param>
        public int SetMode(DisplayModeCallback callback, bool setMode)
        {
            _base._lastError = Global.MCIERR_NO_ERROR;

            if (callback == null)
            {
                _base._displayModeCallback = null;
                _base._hasDisplayCustomMode = false;
                if (_base._displayMode == DisplayMode.Custom)
                {
                    Mode = DisplayMode.ZoomCenter;
                }
            }
            else
            {
                _base._displayModeCallback = callback;
                _base._hasDisplayCustomMode = true;
                if (_base._displayMode == DisplayMode.Custom)
                {
                    if (_base._hasDisplay) _base._display.Invalidate();
                }
                else if (setMode)
                {
                    Mode = DisplayMode.Custom;
                }
            }
            return _base._lastError;
        }
    }

    #endregion

    #region CursorHide Class

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

        private Player _base;

        #endregion

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

        /// <summary>
        /// Adds a form to the list of forms that automatically hide the mouse cursor during inactivity when media is playing.
        /// </summary>
        /// <param name="form">The form to add to the list.</param>
        public int Add(Form form)
        {
            _base._lastError = Player.CH_AddItem(form, _base, _base._display);
            _base._hasCursorHide = Player.CH_HasItems(_base);
            return _base._lastError;
        }

        /// <summary>
        /// Removes a form from the list of forms that automatically hide the mouse cursor during inactivity.
        /// </summary>
        /// <param name="form">The form to remove from the list.</param>
        public int Remove(Form form)
        {
            _base._lastError = Player.CH_RemoveItem(form, _base, _base._display);
            _base._hasCursorHide = Player.CH_HasItems(_base);
            return _base._lastError;
        }

        /// <summary>
        /// Removes all forms added by this player from the list of forms that automatically hide the mouse cursor during inactivity.
        /// </summary>
        public int RemoveAll()
        {
            _base._lastError = Player.CH_RemovePlayerItems(_base);
            _base._hasCursorHide = false;
            return _base._lastError;
        }

        /// <summary>
        /// Gets or sets a value indicating whether auto-hiding of the mouse cursor is enabled. This option can be used to temporarily disable the hiding of the mouse cursor. This setting is used by all players in this assembly.
        /// </summary>
        public bool Enabled
        {
            get { return !Player.ch_Disabled; }

            set
            {
                Player.ch_EventArgs._reason = CursorChangedReason.UserCommand;
                Player.CH_Disabled = !value;
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the number of seconds to wait before the mouse cursor is hidden during inactivity. This setting is used by all players in this assembly (default: 3 seconds).
        /// </summary>
        public int Delay
        {
            get
            {
                return Player.ch_Delay;
            }

            set
            {
                if (value < 1) value = 1;
                else if (value > 30) value = 30;
                if (value != Player.ch_Delay)
                {
                    Player.ch_Delay = value;
                    Player.ch_Timer.Interval = value == 1 ? 500 : 1000; // value  * 500;
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the mouse cursor is automatically hidden.
        /// </summary>
        public bool CursorHidden
        {
            get { return Player.ch_Hidden; }

            set
            {
                if (value) { Player.CH_HideCursor(); }
                else { Player.CH_ShowCursor(); }

                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Shows the mouse cursor if it was hidden by the player.
        /// </summary>
        public int ShowCursor()
        {
            Player.CH_ShowCursor();
            _base._lastError = Global.MCIERR_NO_ERROR;
            return _base._lastError;
        }

        /// <summary>
        /// Hides the mouse cursor if it is visible and the automatic hiding of the cursor by the player is active.
        /// </summary>
        public int HideCursor()
        {
            Player.CH_HideCursor();
            _base._lastError = Global.MCIERR_NO_ERROR;
            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
    {
        #region Fields (Overlay Class)

        private Player _base;

        #endregion

        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. The overlay is clipped when it protrudes outside the parent form of the display of the player. Tip: only enable if a part of the player's display can fall outside its parent form (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
    {
        #region (DisplayClones Class)

        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;

        #endregion

        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 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="control">The display clone to search for.</param>
        public bool Contains(Control control)
        {
            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 == control)
                    {
                        found = true;
                        break;
                    }
                }
            }
            return found;
        }

        /// <summary>
        /// Gets a list (control array) of the display clones of the player.
        /// </summary>
        public Control[] List
        {
            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: CloneLayout.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: CloneLayout.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 setting 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: CloneQuality.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: CloneQuality.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 setting 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: CloneFlip.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: CloneFlip.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 setting 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;
        }

        /// <summary>
        /// Returns the size and location of the video image of the specified display clone.
        /// </summary>
        public Rectangle GetVideoBounds(Control clone)
        {
            Rectangle bounds = Rectangle.Empty;

            if (_base.dc_hasDisplayClones) // && _base._hasVideo)
            {
                for (int i = 0; i < _base.dc_displayClones.Length; i++)
                {
                    if (_base.dc_displayClones[i] != null && _base.dc_displayClones[i].Control == clone)
                    {
                        if ((!_base._hasVideo && !(_base.HasOverlay && _base._overlayHold)) || _base._displayMode == DisplayMode.Stretch || _base.dc_displayClones[i].Layout == CloneLayout.Stretch)// || (_base._hasOverlay && _base._overlayMode == OverlayMode.Display))
                        {
                            bounds = _base.dc_displayClones[i].Control.DisplayRectangle;
                        }
                        else
                        {
                            int         newSize;
                            Rectangle   sourceRect  = _base._videoBoundsClip;
                            Rectangle   destRect    = _base.dc_displayClones[i].Control.DisplayRectangle;

                            double      difX        = (double)destRect.Width / sourceRect.Width;
                            double      difY        = (double)destRect.Height / sourceRect.Height;

                            if (difX < difY)
                            {
                                newSize = (int)(sourceRect.Height * difX);

                                bounds.X = 0;
                                bounds.Y = (destRect.Height - newSize) / 2;
                                bounds.Width = (int)(sourceRect.Width * difX);
                                bounds.Height = newSize;
                            }
                            else
                            {
                                newSize = (int)(sourceRect.Width * difY);

                                bounds.X = (destRect.Width - newSize) / 2;
                                bounds.Y = 0;
                                bounds.Width = newSize;
                                bounds.Height = (int)(sourceRect.Height * difY);
                            }
                        }
                        break;
                    }
                }
            }
            return bounds;
        }
    }

    #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
    {
        #region Fields (MciDevice Class)

        private Player _base;

        #endregion

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

        /// <summary>
        /// Gets the identifier (or alias) of the MCI device associated with the playing media.
        /// </summary>
        public string Identifier
        {
            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;
        }

        /// <summary>
        /// Deletes all temporary MCI files in the temporary folder of the system.
        /// </summary>
        public int DeleteTempFiles()
        {
            string tempDir = Path.GetTempPath();
            if (Directory.Exists(tempDir))
            {
                string[] tempFiles = Directory.GetFiles(tempDir, "mci*.tmp");

                for (int i = 0; i < tempFiles.Length; i++)
                {
                    try { File.Delete(tempFiles[i]); }
                    catch { /* ignore */ }
                }
            }

            _base._lastError = Global.MCIERR_NO_ERROR;
            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
    {
        #region Fields (PointTo Class)

        private Player _base;

        #endregion

        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)
            {
                Point p2 = _base._display.PointToClient(p);
                if (_base._videoBoundsClip.Contains(p2))
                {
                    newP.X = p2.X - _base._videoBounds.X;
                    newP.Y = p2.Y - _base._videoBounds.Y;
                }
            }
            return newP;
        }

        /// <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 int SliderValue(TrackBar slider, Point location)
        {
            return AVPlayer.SliderValue.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 int SliderValue(TrackBar slider, int x, int y)
        {
            return AVPlayer.SliderValue.FromPoint(slider, x, y);
        }
    }

    #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
    {
        #region Fields (ScreenCopy Class)

        private Player _base;

        #endregion

        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;

                    default: // ScreenCopyMode.Video
                        if (_base._hasVideo) r = _base._display.RectangleToScreen(_base._videoBoundsClip);
                        else r = _base._display.RectangleToScreen(_base._display.DisplayRectangle);
                        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>
        /// Sets the Player.ScreenCopy.Mode to the specified value and returns an image from the specified part of the screen.
        /// </summary>
        /// <param name="mode">The value to set the Player.ScreenCopy.Mode to.</param>
        public Image ToImage(ScreenCopyMode mode)
        {
            _base._screenCopyMode = mode;
            return ToImage();
        }

        /// <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>
        /// Sets the Player.ScreenCopy.Mode to the specified value and copies an image from the specified part of the screen to the system's clipboard.
        /// </summary>
        /// <param name="mode">The value to set the Player.ScreenCopy.Mode to.</param>
        public int ToClipboard(ScreenCopyMode mode)
        {
            _base._screenCopyMode = mode;
            return ToClipboard();
        }

        /// <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;
        }

        /// <summary>
        /// Sets the Player.ScreenCopy.Mode to the specified value and saves an image from the specified 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>
        /// <param name="mode">The value to set the Player.ScreenCopy.Mode to.</param>
        public int ToFile(string fileName, System.Drawing.Imaging.ImageFormat imageFormat, ScreenCopyMode mode)
        {
            _base._screenCopyMode = mode;
            return ToFile(fileName, imageFormat);
        }
    }

    #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
    {
        #region Fields (Sliders Class)

        private const int   MAX_SCROLL_VALUE = 60000;
        private Player      _base;

        #endregion

        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)
                    {
                        _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.MouseWheel    -= _base.PositionSlider_MouseWheel;

                        _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;
                        _base._positionSlider.MouseWheel    += _base.PositionSlider_MouseWheel;

                        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 player's position slider (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;
                    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 player's position slider 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 a value indicating whether media signals are turned off while searching with live updates using the player's position slider (default: false).
        /// </summary>
        public bool PositionSkipSignals
        {
            get { return _base._psSkipSignals; }
            set
            {
                _base._psSkipSignals = value;
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the number of milliseconds to be added or subtracted from the player's position slider value when the scroll box is moved by the mouse wheel (default: 0 (not enabled)).
        /// </summary>
        public int PositionMouseWheel
        {
            get { return _base._psMouseWheel; }
            set
            {
                if (value <= 0) _base._psMouseWheel = 0;
                else if (value > MAX_SCROLL_VALUE) _base._psMouseWheel = MAX_SCROLL_VALUE;
                else _base._psMouseWheel = value;
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the number of milliseconds to be added or subtracted from the player's position slider value when the scroll box is moved by the mouse wheel while the Shift key is pressed or by the PageUp and PageDown keys of the keyboard (default: 5000).
        /// </summary>
        public int PositionMouseWheelShift
        {
            get { return _base._psMouseWheelShift; }
            set
            {
                if (value <= 0) _base._psMouseWheelShift = 0;
                else if (value > MAX_SCROLL_VALUE) _base._psMouseWheelShift = MAX_SCROLL_VALUE;
                else _base._psMouseWheelShift = value;
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the shuttle slider (trackbar for Player.Position.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.MouseWheel -= _base.ShuttleSlider_MouseWheel;

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

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

                        _base._shuttleSlider.SmallChange = 1;
                        _base._shuttleSlider.LargeChange = 1;

                        _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;
                        _base._shuttleSlider.MouseWheel += _base.ShuttleSlider_MouseWheel;

                        //_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.MouseWheel -= _base.SpeedSlider_MouseWheel;

                        _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._speedSlider.MouseWheel += _base.SpeedSlider_MouseWheel;
                    }
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        private void SpeedSlider_MouseWheel(object sender, MouseEventArgs e)
        {
            throw new NotImplementedException();
        }

        /// <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 int PointToValue(TrackBar slider, Point location)
        {
            return SliderValue.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 int PointToValue(TrackBar slider, int x, int y)
        {
            return SliderValue.FromPoint(slider, x, y);
        }

        /// <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 Point ValueToPoint(TrackBar slider, int value)
        {
            return SliderValue.ToPoint(slider, value);
        }
    }

    #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 Fields (TaskbarProgress Class)

        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;

                    _base._lastError = Global.MCIERR_NO_ERROR;
                }
                else
                {
                    _base._lastError = Global.MCIERR_UNSUPPORTED_FUNCTION;
                }
            }
            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 a list (form array) of 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
    {
        #region Fields (SystemPanels Class)

        private Player _base;

        #endregion

        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
    {
        #region Fields (Subtitles Class)

        private const int   MAX_DIRECTORY_DEPTH = 3;
        private Player      _base;

        #endregion

        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 Player.Subtitles.Filename) has a subtitles (.srt) file.
        /// </summary>
        public bool Exists
        { get { return _base.Subtitles_Exists() != 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 file name 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.Subtitles_Start(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.Subtitles_Start(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.Subtitles_Start(true);
                }
            }
        }

        /// <summary>
        /// Gets or sets the file name of the subtitles file to search for (without directory and extension, default: string.Empty (the file name 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) + Global.SUBTITLES_FILE_EXTENSION;
                        if (_base.st_SubtitlesEnabled && _base._playing) _base.Subtitles_Start(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.Subtitles_Start(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.Subtitles_Start(true);
                        else
                        {
                            if (_base.st_HasSubtitles) _base.Subtitles_Stop();
                        }
                    }
                }
            }
        }
    }

    #endregion

    #region Signals Class

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

        private const int   MAX_DIRECTORY_DEPTH = 3;
        private Player      _base;

        #endregion

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

        /// <summary>
        /// Gets a value indicating whether media signals of the player are activated (by use of the Player.Events.MediaSignal event - default: false).
        /// </summary>
        public bool Enabled
        { get { return _base.ms_SignalsEnabled; } }

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

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

        /// <summary>
        /// Gets the number of items in the pending (added with the Player.Signals.Add method for the next media to be played) or active media signals of the player.
        /// </summary>
        public int Count
        {
            get
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                if (_base.ms_SignalItems == null) return 0;
                return _base.ms_SignalItems.Count;
            }
        }

        /// <summary>
        /// Adds a new media signal for the next or current playing media to the player. If media signals are added for the next media to be played, no media signals file is used for that media. All media signals are removed from the player when media stops playing. Media signals are reported by Player.Events.MediaSignal events.
        /// </summary>
        /// <param name="startTime">The start time of the media signal to add.</param>
        /// <param name="endTime">The end time of the media signal to add.</param>
        /// <param name="message">The message of the media signal to add.</param>
        public int Add(TimeSpan startTime, TimeSpan endTime, string message)
        {
            _base._lastError = Global.MCIERR_NULL_PARAMETER_BLOCK;
            if (startTime != null && endTime != null && !string.IsNullOrEmpty(message))
            {
                _base._lastError = _base.MS_Add(startTime.TotalMilliseconds, endTime.TotalMilliseconds, message);
            }
            return _base._lastError;
        }

        /// <summary>
        /// Removes all current media signals for the next or current playing media from the player.
        /// </summary>
        /// <returns></returns>
        public int Clear()
        {
            _base.MS_Stop();
            _base._lastError = Global.MCIERR_NO_ERROR;
            return _base._lastError;
        }

        /// <summary>
        /// Gets or sets the text encoding of the messages of media signals (default: Encoding.Default).
        /// </summary>
        public Encoding Encoding
        {
            get { return _base.ms_Encoding; }
            set
            {
                _base._lastError = Global.MCIERR_NO_ERROR;
                _base.ms_Encoding = value;
                if (_base.ms_HasSignalsFile) _base.MS_Start(true);
            }
        }

        /// <summary>
        /// Gets or sets the initial directory to search for media signals files (default: string.Empty (the directory of the playing media)).
        /// </summary>
        public string Directory
        {
            get { return _base.ms_Directory; }
            set
            {
                _base._lastError = Global.MCIERR_FILENAME_REQUIRED;
                if (!string.IsNullOrEmpty(value) && System.IO.Directory.Exists(value))
                {
                    try
                    {
                        _base.ms_Directory = Path.GetDirectoryName(value);
                        if (_base.ms_SignalsEnabled && _base._playing) _base.MS_Start(true);
                        _base._lastError = Global.MCIERR_NO_ERROR;
                    }
                    catch { _base.ms_Directory = string.Empty; }
                }
                else _base.ms_Directory = string.Empty;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the number of nested directories to search for media signals files (values 0 to 3, default: 0 (base directory only)).
        /// </summary>
        public int DirectoryDepth
        {
            get { return _base.ms_DirectoryDepth; }
            set
            {
                if (value <= 0) value = 0;
                else if (value >= MAX_DIRECTORY_DEPTH) value = MAX_DIRECTORY_DEPTH;
                if (value != _base.ms_DirectoryDepth)
                {
                    _base.ms_DirectoryDepth = value;
                    if (_base.ms_SignalsEnabled && _base._playing && !_base.ms_HasSignals) _base.MS_Start(true);
                }
                _base._lastError = Global.MCIERR_NO_ERROR;
            }
        }

        /// <summary>
        /// Gets or sets the file name of the media signals file to search for (without directory and extension, default: string.Empty (the file name of the playing media)). Reset when media starts playing.
        /// </summary>
        public string FileName
        {
            get { return _base.st_FileName; }
            set
            {
                _base._lastError = Global.MCIERR_FILENAME_REQUIRED;
                if (!string.IsNullOrEmpty(value))
                {
                    try
                    {
                        _base.ms_FileName = Path.GetFileNameWithoutExtension(value) + Global.SIGNALS_FILE_EXTENSION;
                        if (_base.ms_SignalsEnabled && _base._playing) _base.MS_Start(true);
                        _base._lastError = Global.MCIERR_NO_ERROR;
                    }
                    catch { _base.ms_FileName = string.Empty; }
                }
                else _base.ms_FileName = string.Empty;
            }
        }

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

    #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
    {
        #region Fields (Position Class)

        private Player _base;

        #endregion

        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 FromBegin
        {
            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 ToEnd
        {
            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 FromStart
        {
            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.StopPosition of the media.
        /// </summary>
        public TimeSpan ToStop
        {
            get
            {
                return _base._playing ?
                    _base._endPositionCurrent == 0 ?
                        TimeSpan.FromMilliseconds(_base._mediaLength - _base.PositionX)
                        : 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.StopPosition 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 of the media.
        /// </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)
        {
            int result = Global.MCIERR_DEVICE_NOT_READY;
            if (_base._playing)
            {
                if (_base._hasVideo) _base.AV_MciSendString("set", "speed 4");
                result = _base.Step(frames);
                if (_base._hasVideo) _base.AV_MciSendString("set", "speed " + _base._speed);
            }
            return result;
        }

    }

    #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 (Media Class)

        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");
        private static Guid     ASF_Header_Extension_Object_Guid        = new Guid("5FBF03B5-A92E-11CF-8EE3-00C00C205365");
        private static Guid     ASF_Metadata_Library_Object             = new Guid("44231C94-9498-49D1-A141-1D134E457054");

        #endregion

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

        /// <summary>
        /// Returns the natural length (duration) of the playing media. See also Player.Media.Duration and .GetLength.
        /// </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 Player.Media.StartPosition to Player.Media.StopPosition. See also Player.Media.Length and .GetLength.
        /// </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. See also Player.Media.Duration and .Length.
        /// </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.BeginToEnd:
                    return TimeSpan.FromMilliseconds(_base._mediaLength);
                //break;

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

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

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

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

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

        /// <summary>
        /// Returns (part of) the file name of the playing media.
        /// </summary>
        /// <param name="part">Specifies the part of the file name 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 stop (or repeat end) position of the playing media.
        /// TimeSpan.Zero or 00:00:00 = natural end of media.
        /// </summary>
        public TimeSpan StopPosition
        {
            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)
                    {
                        if (resetPos)
                        {
                            _base.SetPosition(pos);
                            if (resetRepeat) _base._repeat = false;
                        }
                        else
                        {
                            _base.AV_EndOfMedia(false);
                        }

                        if (_base._paused)
                        {
                            _base.AV_MciSendString("pause");
                            _base.AV_TimerTick(this, EventArgs.Empty);
                        }
                    }
                    _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 Player.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 stop (or repeat 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 Player.Media.StopPosition 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 StopPositionNext
        {
            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")
                if (header[0] == 'I' && header[1] == 'D' && header[2] == '3')
                {
                    int frameSize;
                    //int totalSize = header[9] + (0x80 * header[8]) + (0x8000 * header[7]) + (0x800000 * header[6]);
                    int totalSize = (header[6] << 24) + (header[7] << 16) + (header[8] << 8) + header[9];

                    // check for extended header
                    if ((header[5] & 0x40) == 0x40)
                    {
                        fs.Read(header, 0, 10);
                        //frameSize = header[3] + (0x80 * header[2]) + (0x8000 * header[1]) + (0x800000 * header[0]);
                        frameSize = (header[0] << 24) + (header[1] << 16) + (header[2] << 8) + header[3];
                        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);
                        int frameId = (header[0] << 24) + (header[1] << 16) + (header[2] << 8) + header[3];
                        frameSize = (header[4] << 24) + (header[5] << 16) + (header[6] << 8) + header[7];
                        if ((header[9] & 0x60) != 0) fs.ReadByte();

                        switch (frameId)
                        {
                            //case "APIC": // album art image
                            case (((byte)'A' << 24) + ((byte)'P' << 16) + ((byte)'I' << 8) + (byte)'C'):
                                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
                            case (((byte)'T' << 24) + ((byte)'Y' << 16) + ((byte)'E' << 8) + (byte)'R'):
                                tagInfo.Year = GetMp3String(fs, buffer, frameSize);
                                break;

                            //case "TALB": // Album
                            case (((byte)'T' << 24) + ((byte)'A' << 16) + ((byte)'L' << 8) + (byte)'B'):
                                tagInfo.Album = GetMp3String(fs, buffer, frameSize);
                                break;

                            //case "TIT2": // Title
                            case (((byte)'T' << 24) + ((byte)'I' << 16) + ((byte)'T' << 8) + (byte)'2'):
                                tagInfo.Title = GetMp3String(fs, buffer, frameSize);
                                found++;
                                break;

                            //case "TPE1": // Lead Performers
                            case (((byte)'T' << 24) + ((byte)'P' << 16) + ((byte)'E' << 8) + (byte)'1'):
                                if (tagInfo.Artist == null || tagInfo.Artist == string.Empty) tagInfo.Artist = GetMp3String(fs, buffer, frameSize);
                                else fs.Position += frameSize;
                                found++;
                                break;

                            //case "TPE2": // Band
                            case (((byte)'T' << 24) + ((byte)'P' << 16) + ((byte)'E' << 8) + (byte)'2'):
                                tagInfo.Artist = GetMp3String(fs, buffer, frameSize);
                                found++;
                                break;

                            //case "TLEN": // Duration
                            case (((byte)'T' << 24) + ((byte)'L' << 16) + ((byte)'E' << 8) + (byte)'N'):
                                int time;
                                if (int.TryParse(GetMp3String(fs, buffer, frameSize), out time)) tagInfo._duration = TimeSpan.FromMilliseconds(time);
                                break;

                            //case "TCON": // Genre
                            case (((byte)'T' << 24) + ((byte)'C' << 16) + ((byte)'O' << 8) + (byte)'N'):
                                tagInfo.Genre = GetMp3String(fs, buffer, frameSize);
                                break;

                            //case "TRCK": // Track
                            case (((byte)'T' << 24) + ((byte)'R' << 16) + ((byte)'C' << 8) + (byte)'K'):
                                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 (buffer[0] == 'T' && buffer[1] == 'A' && buffer[2] == 'G')
                    {
                        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, 14);
                    objectCount = (buffer[9] << 8) + buffer[8];

                    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, 10);
                            int titleLength = (buffer[1] << 8) + buffer[0];
                            int authorLength = (buffer[3] << 8) + buffer[2];
                            int copyrightLength = (buffer[5] << 8) + buffer[4];
                            int descriptionLength = (buffer[7] << 8) + buffer[6];
                            int ratingLength = (buffer[9] << 8) + buffer[8];

                            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); // item count
                            int itemCount = (buffer[1] << 8) + buffer[0];

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

                                fs.Read(buffer, 0, 2); // item name length
                                int nameLength = (buffer[1] << 8) + buffer[0];
                                fs.Read(dataBuffer, 0, nameLength); // item name
                                string itemName = Encoding.Unicode.GetString(dataBuffer, 0, nameLength).TrimEnd('\0');

                                fs.Read(buffer, 0, 4); // item length
                                int dataSize = (buffer[3] << 8) + buffer[2];

                                switch (itemName)
                                {
                                    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[dataSize - length];
                                            fs.Read(imageBuffer, 0, dataSize - length);

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

                                            ms.Close();
                                        }
                                        else fs.Position += dataSize;
                                        break;
                                    case "WM/AlbumTitle":
                                        fs.Read(dataBuffer, 0, dataSize);
                                        tagInfo.Album = Encoding.Unicode.GetString(dataBuffer, 0, dataSize).TrimEnd('\0');
                                        break;
                                    case "WM/Genre":
                                        fs.Read(dataBuffer, 0, dataSize);
                                        tagInfo.Genre = Encoding.Unicode.GetString(dataBuffer, 0, dataSize).TrimEnd('\0');
                                        break;
                                    case "WM/Year":
                                        fs.Read(dataBuffer, 0, dataSize);
                                        tagInfo.Year = Encoding.Unicode.GetString(dataBuffer, 0, dataSize).TrimEnd('\0');
                                        break;
                                    case "WM/TrackNumber":
                                        fs.Read(dataBuffer, 0, dataSize);
                                        tagInfo.Track = Encoding.Unicode.GetString(dataBuffer, 0, dataSize).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, dataSize);
                                            tagInfo.Track = Encoding.Unicode.GetString(dataBuffer, 0, dataSize).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 += dataSize;
                                        break;
                                    default:
                                        fs.Position += dataSize;
                                        break;
                                }
                            }
                        }
                        else if (guid == ASF_Header_Extension_Object_Guid)
                        {
                            long stopPosition = fs.Position + (objectLength - 24);

                            fs.Read(buffer, 0, 16); // reserved 1 (buffer max 16)
                            fs.Read(buffer, 0, 2); // reserved 2

                            fs.Read(buffer, 0, 4); // value length
                            int valueLen = BitConverter.ToInt32(buffer, 0);

                            while (fs.Position < stopPosition)
                            {
                                fs.Read(buffer, 0, 16);
                                Guid test = new Guid(buffer);
                                fs.Read(buffer, 0, 8);
                                objectLength = BitConverter.ToInt32(buffer, 0);

                                if (test == ASF_Metadata_Library_Object)
                                {
                                    // object id - already read
                                    // object size - already read
                                    // desciption records:
                                    fs.Read(buffer, 0, 2);
                                    int recCount = (buffer[1] << 8) + buffer[0];

                                    // only take 1

                                    // language list index - 2 bytes
                                    // stream number - 2 bytes
                                    // name length - 2 bytes
                                    // data type - 2 bytes
                                    // data length - 4 bytes
                                    fs.Read(buffer, 0, 12);
                                    int nameLength = (buffer[5] << 8) + buffer[4];
                                    int dataLength = BitConverter.ToInt32(buffer, 8);
                                    // name
                                    fs.Read(dataBuffer, 0, nameLength); // name
                                    string name = Encoding.Unicode.GetString(dataBuffer, 0, nameLength).TrimEnd('\0');
                                    // data

                                    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();
                                    fs.Position = stopPosition;
                                }
                                else
                                {
                                    fs.Position += (objectLength - 24); // skip to next
                                }
                            }
                        }
                        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 file name, in the system's temporary folder for use with the Player.Play methods.
        /// </summary>
        /// <param name="resource">The embedded resource (e.g. Properties.Resources.MyMedia) to save to a new file in the system's temporary folder.</param>
        /// <param name="fileName">The file name (e.g. "MyMedia.mp4") to be used for the new file in the system's temporary folder.</param>
        public string ResourceToFile(byte[] resource, string fileName)
        {
            return _base.AV_ResourceToFile(resource, fileName);
        }
    }

    #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
    {
        #region Fields (Events Class)

        private Player _base;

        #endregion

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

        /// <summary>
        /// Occurs when media has finished playing.
        /// </summary>
        public event EventHandler<EndedEventArgs> 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<EndedEventArgs> 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 media file 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 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 window 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 shape of the display window of the player has changed.
        /// </summary>
        public event EventHandler MediaDisplayShapeChanged
        {
            add { _base.MediaDisplayShapeChanged += value; }
            remove { _base.MediaDisplayShapeChanged -= 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 window 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.MediaPeakLevelChanged += value; }
            remove { _base.MediaPeakLevelChanged -= 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 current media signal of the player has changed.
        /// </summary>
        public event EventHandler<SignalEventArgs> MediaSignal
        {
            add { _base.MediaSignal += value; }
            remove { _base.MediaSignal -= 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 window 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 window 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 window (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 window (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 window (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 window (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 window (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 window (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; }
        }

        /// <summary>
        /// Occurs when the mouse cursor is automatically hidden or shown by the player.
        /// </summary>
        public event EventHandler<CursorHideEventArgs> MediaCursorHideChanged
        {
            add { Player.MediaCursorHideChanged += value; }
            remove { Player.MediaCursorHideChanged -= 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
    {
        #region Fields (InputDevice Class)

        private Recorder _base;

        #endregion

        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
    {
        #region Fields (MciRecDevice Class)

        private Recorder _base;

        #endregion

        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
    {
        #region Fields (RecEvents Class)

        private Recorder _base;

        #endregion

        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 Fields (TaskbarMarquee Class)

        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 | Cookies | Terms of Use | Mobile
Web03 | 2.8.181215.1 | Last Updated 7 Aug 2018
Article Copyright 2010 by Peter Vegter
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid