/// <summary>
        ///     Get the <see cref="PropVariant" />.
        /// </summary>
        /// <param name="propertyKey">Property key.</param>
        /// <param name="propVariant"></param>
        internal void GetPropVariant(ShellPropertyKey propertyKey, out PropVariant propVariant)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);

            var key = propertyKey.PropertyKeyNative;

            this.propertyStoreNative.GetValue(ref key, out propVariant);
        }
        /// <summary>
        ///     Set the property value.
        /// </summary>
        /// <param name="propertyKey">Property key.</param>
        /// <param name="propVar">Property value.</param>
        internal void SetValue(ShellPropertyKey propertyKey, ref PropVariant propVar)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);

            var key = propertyKey.PropertyKeyNative;
            var hr  = this.propertyStoreNative.SetValue(ref key, ref propVar);

            if (HRESULT.Failed(hr))
            {
                throw new ShellException(ErrorMessages.ShellPropertySetValue, hr);
            }
        }
        public void ClearValue(ShellPropertyKey propertyKey)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);

            var propVar = new PropVariant();

            try
            {
                SetValue(propertyKey, ref propVar);
            }
            finally
            {
                propVar.Clear();
            }
        }
        internal void SetValue(ShellPropertyKey propertyKey, ref PropVariant propVar, bool allowTruncatedValue)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);

            var key = propertyKey.PropertyKeyNative;
            var hr  = this.propertyStoreNative.SetValue(ref key, ref propVar);

            if (HRESULT.Failed(hr))
            {
                throw new ShellException(ErrorMessages.ShellPropertySetValue, hr);
            }
            else if (!allowTruncatedValue && hr == ShellNativeMethods.InPlaceStringTruncated)
            {
                throw new ArgumentOutOfRangeException(nameof(propVar), ErrorMessages.ShellPropertyValueTruncated);
            }
        }
        /// <summary>
        ///     Write property value.
        /// </summary>
        /// <param name="propertyKey">Property key.</param>
        /// <param name="value">Property value.</param>
        /// <param name="allowTruncatedValue"></param>
        public void WriteProperty(ShellPropertyKey propertyKey, object value, bool allowTruncatedValue)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);
            Contract.Requires <InvalidOperationException>(this.Store != null);

            var propVar = PropVariant.FromObject(value);

            try
            {
                this.Store.SetValue(propertyKey, ref propVar, allowTruncatedValue);
            }
            finally
            {
                propVar.Clear();
            }
        }
        /// <summary>
        ///     Get the property value.
        /// </summary>
        /// <typeparam name="T">Property type.</typeparam>
        /// <param name="propertyKey">Property key.</param>
        /// <returns></returns>
        public T GetValue <T>(ShellPropertyKey propertyKey)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);

            var propVar = default(PropVariant);

            try
            {
                var key = propertyKey.PropertyKeyNative;
                this.propertyStoreNative.GetValue(ref key, out propVar);
                return(propVar.GetValue <T>());
            }
            finally
            {
                propVar.Clear();
            }
        }
        /// <summary>
        ///     Initialize a instance of the <see cref="ShellProperty{T}" /> class.
        /// </summary>
        /// <param name="propertyKey"></param>
        /// <param name="description"></param>
        /// <param name="shellObject"></param>
        /// <remarks>
        ///     This constructor is used in <see cref="ShellPropertyFactory" />.
        ///     Do not change the order of the parameters.
        /// </remarks>
        internal ShellProperty(ShellPropertyKey propertyKey, ShellPropertyDescription description, ShellObject shellObject)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);
            Contract.Requires <ArgumentNullException>(shellObject != null);

            this.ShellObject = shellObject;

            this.PropertyKey            = propertyKey;
            this.description            = description;
            this.AllowSetTruncatedValue = false;

            if (this.Description.ValueType != typeof(T))
            {
                throw new InvalidOperationException(
                          String.Format(ErrorMessages.ShellPropertyUnmatchValueType, typeof(T), this.Description.ValueType));
            }
        }
        /// <summary>
        ///     Write property value.
        /// </summary>
        /// <param name="propertyKey">Property key.</param>
        /// <param name="value">Property value.</param>
        public void WriteProperty(ShellPropertyKey propertyKey, object value)
        {
            Contract.Requires <ArgumentNullException>(propertyKey != null);

            WriteProperty(propertyKey, value, true);
        }
        /// <summary>
        ///     Write property value.
        /// </summary>
        /// <param name="canonicalName">Property canonical name.</param>
        /// <param name="value">Property value.</param>
        /// <param name="allowTruncatedValue"></param>
        public void WriteProperty(string canonicalName, object value, bool allowTruncatedValue)
        {
            var propertyKey = ShellPropertyKey.FromCanonicalName(canonicalName);

            WriteProperty(propertyKey, value, allowTruncatedValue);
        }