/// <summary>
        /// Browse the server using specified criteria.
        /// </summary>
        /// <param name="parentItemId">Indicates the item identifier from which browsing will begin. If the root branch is to be browsed then a null should be passed.</param>
        /// <param name="filter">The filtering context.</param>
        /// <param name="propertiesQuery">The properties query.</param>
        /// <returns>
        /// Array of browsed elements.
        /// </returns>
        /// <exception cref="System.InvalidOperationException">Browser isn't attached to com object.</exception>
        public OpcDaBrowseElement[] GetElements(string parentItemId, OpcDaElementFilter filter = null,
                                                OpcDaPropertiesQuery propertiesQuery = null)
        {
            if (OpcBrowse == null)
            {
                throw new InvalidOperationException("Browser isn't attached to com object.");
            }

            if (parentItemId == null)
            {
                parentItemId = string.Empty;
            }

            if (filter == null)
            {
                filter = new OpcDaElementFilter();
            }

            bool        returnAllProperties;
            bool        returnPropertyValues;
            IList <int> propertyIds;

            if (propertiesQuery != null)
            {
                returnAllProperties  = propertiesQuery.AllProperties;
                returnPropertyValues = propertiesQuery.ReturnValues;
                propertyIds          = propertiesQuery.PropertyIds;
            }
            else
            {
                returnAllProperties  = false;
                returnPropertyValues = false;
                propertyIds          = new int[0];
            }

            var opcBrowseElements = OpcBrowse.Browse(parentItemId, filter.ElementType, filter.Name,
                                                     filter.VendorSpecific,
                                                     returnAllProperties, returnPropertyValues, propertyIds);

            if (OpcConfiguration.EnableQuirks && opcBrowseElements.Length > 0 && returnAllProperties &&
                NoPropertiesReturned(opcBrowseElements))
            {
                // bad server implementation of Browse. For example some versions of Matrikon simulation server
                // all properties was requested but returned nothing.
                // Requery properties with GetProperties
                var properties = GetProperties(opcBrowseElements.Select(e => e.ItemId).ToArray(), propertiesQuery);
                for (var i = 0; i < opcBrowseElements.Length; i++)
                {
                    opcBrowseElements[i].ItemProperties = properties[i];
                }
            }

            return(opcBrowseElements);
        }
        /// <summary>
        /// Gets properties of items by specified item identifiers.
        /// </summary>
        /// <param name="itemIds">The item identifiers.</param>
        /// <param name="propertiesQuery">The properties query.</param>
        /// <returns>
        /// Array of properties of items.
        /// </returns>
        public OpcDaItemProperties[] GetProperties(IList <string> itemIds, OpcDaPropertiesQuery propertiesQuery = null)
        {
            if (propertiesQuery == null)
            {
                propertiesQuery = new OpcDaPropertiesQuery();
            }

            var properties = OpcBrowse.GetProperties(itemIds, propertiesQuery.ReturnValues, propertiesQuery.PropertyIds);

            if (OpcConfiguration.EnableQuirks && properties.Length > 0 && propertiesQuery.AllProperties &&
                NoPropertiesReturned(properties))
            {
                // TODO: fallback to IOPCItemProperties
            }
            return(properties);
        }