public T GetObject <T>(string key)
        {
            string value = this[key];

            if (value == null)
            {
                return(default(T));
            }
            else if (typeof(T) == typeof(string))
            {
                // Funny casting because the compiler doesn't know T is string here.
                return((T)(object)value);
            }
            else if (typeof(T) == typeof(CustomActionData))
            {
                // Deserialize nested CustomActionData.
                return((T)(object)new CustomActionData(value));
            }
            else if (value.Length == 0)
            {
                return(default(T));
            }
            else
            {
                return(CustomActionData.Deserialize <T>(value));
            }
        }
 /// <summary>
 /// Gets or sets a data value with a specified key.
 /// </summary>
 /// <param name="key">Case-sensitive data key.</param>
 /// <exception cref="ArgumentException">the key does not consist solely of letters,
 /// numbers, and the period, underscore, and space characters.</exception>
 public string this[string key]
 {
     get
     {
         return(this.data[key]);
     }
     set
     {
         CustomActionData.ValidateKey(key);
         this.data[key] = value;
     }
 }
        /// <summary>
        /// Loads key-value pairs from a string into the data collection.
        /// </summary>
        /// <param name="keyValueList">key-value pair list of the form returned by <see cref="ToString"/></param>
        private void Parse(string keyValueList)
        {
            int itemStart = 0;

            while (itemStart < keyValueList.Length)
            {
                // Find the next non-escaped data separator.
                int semi = itemStart - 2;
                do
                {
                    semi = keyValueList.IndexOf(CustomActionData.DataSeparator, semi + 2);
                }while (semi >= 0 && semi < keyValueList.Length - 1 && keyValueList[semi + 1] == CustomActionData.DataSeparator);

                if (semi < 0)
                {
                    semi = keyValueList.Length;
                }

                // Find the next non-escaped key-value separator.
                int equals = itemStart - 2;
                do
                {
                    equals = keyValueList.IndexOf(CustomActionData.KeyValueSeparator, equals + 2);
                }while (equals >= 0 && equals < keyValueList.Length - 1 && keyValueList[equals + 1] == CustomActionData.KeyValueSeparator);

                if (equals < 0 || equals > semi)
                {
                    equals = semi;
                }

                string key   = keyValueList.Substring(itemStart, equals - itemStart);
                string value = null;

                // If there's a key-value separator before the next data separator, then the item has a value.
                if (equals < semi)
                {
                    value = keyValueList.Substring(equals + 1, semi - (equals + 1));
                    value = CustomActionData.Unescape(value);
                }

                // Add non-duplicate items to the collection.
                if (key.Length > 0 && !this.data.ContainsKey(key))
                {
                    this.data.Add(key, value);
                }

                // Move past the data separator to the next item.
                itemStart = semi + 1;
            }
        }
 /// <summary>
 /// Adds a value to the data collection, using XML serialization to persist the object as a string.
 /// </summary>
 /// <param name="key">Case-sensitive data key.</param>
 /// <param name="value">Data value (may be null).</param>
 /// <exception cref="ArgumentException">the key does not consist solely of letters,
 /// numbers, and the period, underscore, and space characters.</exception>
 /// <exception cref="NotSupportedException">The value type does not support XML serialization.</exception>
 /// <exception cref="InvalidOperationException">The value could not be serialized.</exception>
 public void AddObject <T>(string key, T value)
 {
     if (value == null)
     {
         this.Add(key, null);
     }
     else if (typeof(T) == typeof(string) ||
              typeof(T) == typeof(CustomActionData)) // Serialize nested CustomActionData
     {
         this.Add(key, value.ToString());
     }
     else
     {
         string valueString = CustomActionData.Serialize <T>(value);
         this.Add(key, valueString);
     }
 }
        /// <summary>
        /// Executes a built-in action, custom action, or user-interface wizard action.
        /// </summary>
        /// <param name="action">Name of the action to execute.  Case-sensitive.</param>
        /// <param name="actionData">Optional data to be passed to a deferred custom action.</param>
        /// <exception cref="InvalidHandleException">the Session handle is invalid</exception>
        /// <exception cref="InstallCanceledException">the user exited the installation</exception>
        /// <remarks><p>
        /// The DoAction method executes the action that corresponds to the name supplied. If the
        /// name is not recognized by the installer as a built-in action or as a custom action in
        /// the CustomAction table, the name is passed to the user-interface handler object, which
        /// can invoke a function or a dialog box. If a null action name is supplied, the installer
        /// uses the upper-case value of the ACTION property as the action to perform. If no property
        /// value is defined, the default action is performed, defined as "INSTALL".
        /// </p><p>
        /// Actions that update the system, such as the InstallFiles and WriteRegistryValues
        /// actions, cannot be run by calling MsiDoAction. The exception to this rule is if DoAction
        /// is called from a custom action that is scheduled in the InstallExecuteSequence table
        /// between the InstallInitialize and InstallFinalize actions. Actions that do not update the
        /// system, such as AppSearch or CostInitialize, can be called.
        /// </p><p>
        /// If the called action is a deferred, rollback, or commit custom action, then the supplied
        /// <paramref name="actionData"/> will be available via the <see cref="CustomActionData"/>
        /// property of that custom action's session.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidoaction.asp">MsiDoAction</a>
        /// </p></remarks>
        public void DoAction(string action, CustomActionData actionData)
        {
            if (String.IsNullOrEmpty(action))
            {
                throw new ArgumentNullException("action");
            }

            this.ValidateSessionAccess();

            if (actionData != null)
            {
                this[action] = actionData.ToString();
            }

            uint ret = RemotableNativeMethods.MsiDoAction((int)this.Handle, action);

            if (ret != 0)
            {
                throw InstallerException.ExceptionFromReturnCode(ret);
            }
        }
        /// <summary>
        /// Gets a string representation of the data suitable for persisting in a property.
        /// </summary>
        /// <returns>Data string in the form "Key1=Value1;Key2=Value2"</returns>
        public override string ToString()
        {
            StringBuilder buf = new StringBuilder();

            foreach (KeyValuePair <string, string> item in this.data)
            {
                if (buf.Length > 0)
                {
                    buf.Append(CustomActionData.DataSeparator);
                }

                buf.Append(item.Key);

                if (item.Value != null)
                {
                    buf.Append(CustomActionData.KeyValueSeparator);
                    buf.Append(CustomActionData.Escape(item.Value));
                }
            }

            return(buf.ToString());
        }
 /// <summary>
 /// Adds a key and value to the data collection.
 /// </summary>
 /// <param name="key">Case-sensitive data key.</param>
 /// <param name="value">Data value (may be null).</param>
 /// <exception cref="ArgumentException">the key does not consist solely of letters,
 /// numbers, and the period, underscore, and space characters.</exception>
 public void Add(string key, string value)
 {
     CustomActionData.ValidateKey(key);
     this.data.Add(key, value);
 }
 /// <summary>
 /// Adds an item with key and value to the data collection.
 /// </summary>
 /// <param name="item">Case-sensitive data key, with a data value that may be null.</param>
 /// <exception cref="ArgumentException">the key does not consist solely of letters,
 /// numbers, and the period, underscore, and space characters.</exception>
 public void Add(KeyValuePair <string, string> item)
 {
     CustomActionData.ValidateKey(item.Key);
     this.data.Add(item);
 }