/// <summary>
        /// Creates event data associated with a specific provider with the specified event metadata.
        /// </summary>
        /// <param name="providerName">The name of the provider.</param>
        /// <param name="metadata">The event metadata.</param>
        public EventData(string providerName, EventMetadata metadata)
        {
            if (metadata != null)
            {
                Provider = string.IsNullOrEmpty(providerName) ? string.Empty : providerName;
                Id       = new EventId(metadata.Id);
                Level    = new EventLevelData(metadata.Level.DisplayName, metadata.Level.Value);
                Version  = metadata.Version;
                Task     = new EventTaskData(metadata.Task.Name, metadata.Task.DisplayName, metadata.Task.Value, metadata.Task.EventGuid);
                Opcode   = new EventOpcodeData(metadata.Opcode.DisplayName, metadata.Opcode.Value);
                Keywords = EventKeywordData.GetKeywords(metadata.Keywords);
                LoggedTo = string.IsNullOrEmpty(metadata.LogLink.LogName) ? new EventLogData() : new EventLogData(metadata.LogLink.LogName);
                Message  = metadata.Description ?? string.Empty;

                // only check the template for parameters if the event has an actual message string
                //Parameters = string.IsNullOrEmpty(metadata.Description) ? new OrderedDictionary() : GetEventParametersFromXmlTemplate(metadata.Template);
                Parameters = string.IsNullOrEmpty(metadata.Template) ? new OrderedDictionary() : GetEventParametersFromXmlTemplate(metadata.Template);

                // use the officially defined level information when it exists, otherwise we try to guess the level from Id.Severity which may or may not be accurate
                // previously, there was a question mark added to the end of the level name to denote this case but it was removed since it seems like a valid guess
                if (string.IsNullOrEmpty(Level.Name))
                {
                    Level = new EventLevelData(string.Format(CultureInfo.CurrentCulture, "{0}", Id.Severity), (int)Enum.Parse(typeof(NtSeverity), Id.Severity));
                }

                // odd case but fairly common
                if (string.IsNullOrEmpty(metadata.Description) && !string.IsNullOrEmpty(metadata.Template))
                {
                    Logger.Debug(CultureInfo.CurrentCulture, "Event did not have a message but had message parameters defined. {0}", this);
                }

                // another odd case but seems to be normal for "Classic" events so log only the non-classic instances
                if (!string.IsNullOrEmpty(metadata.Description) && metadata.Description.Contains("%1") && string.IsNullOrEmpty(metadata.Template) && (Keywords.Count(keyword => keyword.Name.Equals("Classic")) == 0))
                {
                    Logger.Debug(CultureInfo.CurrentCulture, "Event had a message with a parameter but no message parameters were defined. {0}", this);
                }
            }
            else
            {
                Provider   = string.Empty;
                Id         = new EventId();
                Level      = new EventLevelData();
                Version    = 0;
                Task       = new EventTaskData();
                Opcode     = new EventOpcodeData();
                Keywords   = new List <EventKeywordData>();
                LoggedTo   = new EventLogData();
                Message    = string.Empty;
                Parameters = new OrderedDictionary();
            }
        }
        /// <summary>
        /// Retrieves event provider data from the system based on event provider metadata.
        /// </summary>
        /// <returns></returns>
        public static IList <EventProviderData> GetProviders()
        {
            IList <EventProviderData> providers = new List <EventProviderData>();

            using (EventLogSession session = new EventLogSession())
            {
                foreach (string providerName in session.GetProviderNames())
                {
                    ProviderMetadata providerMetadata = null;

                    try
                    {
                        providerMetadata = new ProviderMetadata(providerName);

                        EventProviderData providerData = new EventProviderData();
                        providerData.Name             = providerMetadata.Name ?? string.Empty;
                        providerData.DisplayName      = GetProviderDisplayName(providerMetadata) ?? string.Empty;
                        providerData.Guid             = providerMetadata.Id;
                        providerData.FileName         = GetHelpFileNameFromUri(providerMetadata.HelpLink) ?? string.Empty;
                        providerData.MessageFile      = providerMetadata.MessageFilePath ?? string.Empty;
                        providerData.SubstitutionFile = providerMetadata.ParameterFilePath ?? string.Empty;
                        providerData.ResourceFile     = providerMetadata.ResourceFilePath ?? string.Empty;
                        providerData.Levels           = GetProviderEventLevels(providerMetadata);
                        providerData.SendsEventsTo    = providerMetadata.LogLinks.Select(link => new EventLogData(link.LogName)).ToList();

                        try
                        {
                            IList <EventData> events = new List <EventData>();

                            string provName = providerName; // prevents "Access to foreach variable in a closure" warning

                            foreach (EventData eventData in providerMetadata.Events.Select(eventMetadata => new EventData(provName, eventMetadata)).Where(eventData => !events.Contains(eventData)))
                            {
                                events.Add(eventData);
                            }

                            providerData.Events = events;
                        }
                        catch (EventLogException ele)
                        {
                            providerData.Events = new List <EventData>();

                            Logger.Error(ele, CultureInfo.CurrentCulture, "Event provider '{0}' threw a generic event log exception while accessing the event provider Events field: {1}{2}{3}", providerName, ele.Message, Environment.NewLine, ele.StackTrace);
                        } //something is weird with Windows-MsiServer

                        try
                        {
                            providerData.Keywords = EventKeywordData.GetKeywords(providerMetadata.Keywords);
                        }
                        catch (EventLogException ele)
                        {
                            providerData.Keywords = new List <EventKeywordData>();

                            Logger.Error(ele, CultureInfo.CurrentCulture, "Event provider '{0}' threw a generic event log exception while accessing the event provider Keywords field: {1}{2}{3}", providerName, ele.Message, Environment.NewLine, ele.StackTrace);
                        } //something is weird with Windows-MsiServer

                        // Ntfs has 2 entries instead of 1 so make sure we don't add it twice
                        if (!providers.Contains(providerData))
                        {
                            providers.Add(providerData);
                        }
                    }
                    catch (EventLogNotFoundException elnfe)
                    {
                        // Microsoft-Windows-TerminalServices-ServerUSBDevice = The system cannot find the file specified.
                        // Microsoft-Windows-WPD-MTPClassDriver = The system cannot find the file specified
                        // Microsoft-Windows-Sdbus-SQM = The system cannot find the files specified

                        Logger.Error(elnfe, CultureInfo.CurrentCulture, "Event provider '{0}' not found during initial access of the provider while processing providers: {1}{2}{3}", providerName, elnfe.Message, Environment.NewLine, elnfe.StackTrace);
                    }
                    catch (UnauthorizedAccessException uae)
                    {
                        // thrown when running as a normal user and accessing these:
                        // Microsoft-Windows-Security-Auditing
                        // Microsoft-Windows-Eventlog

                        Logger.Error(uae, CultureInfo.CurrentCulture, "Access denied to event provider '{0}' during initial access of the provider while processing providers: {1}{2}{3}", providerName, uae.Message, Environment.NewLine, uae.StackTrace);
                    }
                    catch (EventLogException ele)
                    {
                        // unfortunately vista x64 needs this generic catch statement
                        Logger.Error(ele, CultureInfo.CurrentCulture, "Event provider '{0}' threw a generic event log exception during initial access of the provider while processing providers: {1}{2}{3}", providerName, ele.Message, Environment.NewLine, ele.StackTrace);
                    }
                    finally
                    {
                        providerMetadata?.Dispose();
                    }
                }
            }

            return(providers);
        }