Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Implement an Autoplay Handler

, 18 Sep 2006
Implementing an Autoplay handler in C#.
autoplaydemo_demo.zip
autoplaydemo_src.zip
AutoPlayDemo
Autoplay.ico
AutoPlayDemo.csproj.user
bin
Properties
Settings.settings
Resources
AutoPlayListener
Autoplay.ico
AutoPlayListener.snk
bin
Properties
Resources
Autoplay.ico
RegistryScript
Almdal.RegistryScript.snk
bin
Properties
SharedPipies
Almdal.SharedPipes.snk
bin
Properties
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Win32;

/*******************************************************************************************
 *	Author: Tim Almdal
 *	Date: Sept, 2006
 *			tnalmdal (at) shaw-dot-net
 * 
 *	RegistryScript class. Parses and processes a ATL like registry
 *	Script for update application settings in the registry
 * 
 *  Copyright (c) 2006 T. ALmdal
 *
 *  Permission is hereby granted, free of charge, to any person obtaining 
 *  a copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation 
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 *  and/or sell copies of the Software, and to permit persons to whom the 
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included 
 *  in all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
/*******************************************************************************************/
namespace Almdal.RegistryScript {
	/// <summary>
	/// ParseException for when something goes wrong in the parsing
	/// </summary>
    public class ParseException : ApplicationException {
        public ParseException(string msg) : base(msg) {}
    }

	/// <summary>
	/// Provides registration and unregistration of registry values
	/// </summary>
    public class CRegistryScript {
		#region Private Enums
		/// <summary>
		/// Enumeration of the actions available when a new registry
		/// key is encountered
		/// </summary>
		enum AddType {
            NotSpecified,
            ForceRemove,
            NoRemove,
            Value,
            AddKey,
        }

		/// <summary>
		/// Enumeration of whether we are registering values or unregistering values
		/// </summary>
        enum RegistryProcess {
            Register,
            Unregister
		}
		#endregion

		#region Member Field Definitions
		/// <summary>
		/// The registry update process queue.
		/// </summary>
		OpQueue _processQueue = new OpQueue();

		/// <summary>
		/// The variable substitution dictionary
		/// </summary>
        Dictionary<string, string> _variableMap = new Dictionary<string, string>();

		/// <summary>
		/// Field to provide an indication of the current registry process (Register or Unregister).
		/// </summary>
		private RegistryProcess _process;

		private VariableSubstitution _substRtn;
		#endregion

		public CRegistryScript() { _substRtn = new VariableSubstitution(DoSubstition); } 

		#region Public Methods
		/// <summary>
		/// Performs the registration
		/// </summary>
		/// <param name="resource">The registry script as a string</param>
		public void Register(string resource) {
            _process = RegistryProcess.Register;
            StringTokenizer tokenizer = new StringTokenizer(resource);
			if (_substRtn != null) {
				tokenizer.SubstitionRoutine = _substRtn;
			}
            tokenizer.IgnoreWhiteSpace = true;

            using (IEnumerator<Token> iterator = tokenizer.GetEnumerator()) {
                ParseRootLevel(iterator);
                _processQueue.ProcessRegistryQueue();
            }
		}

		/// <summary>
		/// Performs an unregistration
		/// </summary>
		/// <param name="resource">The registry script as a string</param>
        public void Unregister(string resource) {
            _process = RegistryProcess.Unregister;
            StringTokenizer tokenizer = new StringTokenizer(resource);
			if (_substRtn != null) {
				tokenizer.SubstitionRoutine = _substRtn;
			}
			tokenizer.IgnoreWhiteSpace = true;

            using (IEnumerator<Token> iterator = tokenizer.GetEnumerator()) {
                ParseRootLevel(iterator);
                _processQueue.ProcessRegistryQueue();
            }
		}
		#endregion

		#region Variable Handling

		/// <summary>
		/// This method adds a variable name/valuepair tothe variable substitutuin value
		/// </summary>
		/// <param name="key">The variable name</param>
		/// <param name="value">The replacement variable</param>
		public void AddVariable(string key, string value) {
            _variableMap.Add(key, value);
        }

		/// <summary>
		/// The substitution delegate implementation.  Looks up the passed in variable
		/// and returns the value from the substitution table. This methods fails quietly
		/// in that if the variable is not found, it just returns the variable name as its
		/// value.
		/// </summary>
		/// <param name="variable"></param>
		/// <returns>The substitution string</returns>
		private string DoSubstition(string variable) {
			return _variableMap.ContainsKey(variable) ? _variableMap[variable] : variable;
		}

		#endregion	

		#region Routines to create the Process Queue
		/// <summary>
		/// Routine to handle the registry root specification
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		private void ParseRootLevel(IEnumerator<Token> iterator) {
			iterator.MoveNext();
            while (iterator.Current.Kind != TokenKind.EOF) {
                if (iterator.Current.Kind != TokenKind.Word) {
					throw new ParseException("Invalid tolen type. Expecting 'Word', recieved '" + iterator.Current.Kind + "'. " + iterator.Current.ParsePosition);
                }

				RegistryOpQueueItem item;
				if ("HKEY_CLASSES_ROOT".Equals(iterator.Current.Value) || "HKCR".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "ClassesRoot");
				}
				else if ("HKEY_CURRENT_USER".Equals(iterator.Current.Value) || "HKCU".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "CurrentUser");
				}
				else if ("HKEY_LOCAL_MACHINE".Equals(iterator.Current.Value) || "HKLM".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "LocalMachine");
				}
				else if ("HKEY_USERS".Equals(iterator.Current.Value) || "HKU".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "Users");
				}
				else if ("HKEY_PERFORMANCE_DATA".Equals(iterator.Current.Value) || "HKPD".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "PerformanceData");
				}
				else if ("HKEY_DYN_DATA".Equals(iterator.Current.Value) || "HKDD".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "DynData");
				}
				else if ("HKEY_CURRENT_CONFIG".Equals(iterator.Current.Value) || "HKCC".Equals(iterator.Current.Value)) {
                    item = new RegistryOpQueueItem(RegistryOperation.OpenKey, "CurrentConfig");
				}
				else {
				    throw new ParseException("Token '" + iterator.Current.Value + "' is not a valid Root key specifier. " + iterator.Current.ParsePosition);
				}
				_processQueue.Enqueue(item);
				iterator.MoveNext();

                ProcessKey(iterator, item._key, true);
           }
        }

		/// <summary>
		/// Process a Subkey
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		/// <param name="KeyName">Nameof the current subkey</param>
        private void ProcessKey(IEnumerator<Token> iterator, string KeyName) {
            ProcessKey(iterator, KeyName, false);
        }
		/// <summary>
		/// Process a Subkey
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		/// <param name="KeyName">Nameof the current subkey</param>
		/// <param name="rootKey">Flag to indicate that this is a root level key</param>
	    private void ProcessKey(IEnumerator<Token> iterator, string KeyName, bool rootKey) {
			if (iterator.Current.Kind != TokenKind.StartKey) {	//	A subkey definition starts with a {
				throw new ParseException("Invalid Token '" + iterator.Current + "', was expecting Start of key definition. " + iterator.Current.ParsePosition);
			}
			iterator.MoveNext();

			while (iterator.Current.Kind != TokenKind.EndKey &&
				   iterator.Current.Kind != TokenKind.EOF) {

				if (iterator.Current.Kind != TokenKind.Word && iterator.Current.Kind != TokenKind.String) {
					throw new ParseException("Invalid Token '" + iterator.Current + "', was expecting word. " + iterator.Current.ParsePosition);
				}
	            if ("Delete".Equals(iterator.Current.Value)) {
					iterator.MoveNext();						//	move past Delete Keyword
					ProcessDeleteKey(iterator, rootKey);		//	Process the Delete keyword (can only occur at the root level)
	            }
				else if ("val".Equals(iterator.Current.Value)) {
					iterator.MoveNext();						//	move past the 'val' keyword
					ProcessValue(iterator);						//	Process a Value
				}
	            else {
	                ProcessAddKey(iterator);					//	Add a subkey to the current key
				}
			}

			if (iterator.Current.Kind != TokenKind.EndKey) {	//	A key definition ends with a}
				throw new ParseException("Invalid Token '" + iterator.Current + "', was expecting closing brace. " + iterator.Current.ParsePosition);
			}													//	Add a close key command to the process queue
             _processQueue.Enqueue(new RegistryOpQueueItem(RegistryOperation.CloseKey, KeyName));
			iterator.MoveNext();								//	move past the endKey
	    }

		/// <summary>
		/// Process a delete key command.
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		/// <param name="rootKey">Flag to indicate if this keyword occurred at the root level</param>
	    private void ProcessDeleteKey(IEnumerator<Token> iterator, bool rootKey) {
	        if (!rootKey) {
	            throw new ParseException("The 'Delete' keyword is only allowed at the root level. " + iterator.Current.ParsePosition);
	        }

	        if (iterator.Current.Kind != TokenKind.Word) {
	            throw new ParseException("The missing the key value to delete. " + iterator.Current.ParsePosition);
	        }

	        if (_process == RegistryProcess.Register) {
				_processQueue.Enqueue(new RegistryOpQueueItem(RegistryOperation.DeleteKey, iterator.Current.Value));
	        }
			iterator.MoveNext();							//	move past key name
		}

		/// <summary>
		/// Extract the registry value from the token stream.
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		private void ProcessValue(IEnumerator<Token> iterator) {
			ProcessValue(iterator, null);
		}
		/// <summary>
		/// Extract the registry value from the token stream.  If the name paramter is null, then
		/// Extract the value name before extracting actual value.  If the name is an empty string,
		/// then this value will eventaully become the default key value
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		/// <param name="valueName"></param>
		private void ProcessValue(IEnumerator<Token> iterator, string valueName) {
			string name = valueName;
			if (valueName == null) {
				if (iterator.Current.Kind != TokenKind.Word) {
					throw new ParseException("The missing the registry value name. " + iterator.Current.ParsePosition);
				}
				name = iterator.Current.Value;
				iterator.MoveNext();							//	 move past the name
				if (iterator.Current.Kind != TokenKind.Assignment) {
					throw new ParseException("The assignment operator not found. " + iterator.Current.ParsePosition);
				}
				iterator.MoveNext();							//	 move past the assignment operator
			}

			RegistryOperation op = _process == RegistryProcess.Register ? RegistryOperation.AddValue : RegistryOperation.DeleteValue;
			RegistryOpQueueItem item = new RegistryOpQueueItem(op, name);

			if (iterator.Current.Kind != TokenKind.Word) {
				throw new ParseException("The missing the value type specifier. " + iterator.Current.ParsePosition);
			}
			if ("s".Equals(iterator.Current.Value)) {
                item._type = RegistryValueKind.String;
			}
			else if ("d".Equals(iterator.Current.Value)) {
                item._type = RegistryValueKind.DWord;
			}
			else if ("m".Equals(iterator.Current.Value)) {
                item._type = RegistryValueKind.MultiString;
			}
			else if ("b".Equals(iterator.Current.Value)) {
                item._type = RegistryValueKind.Binary;
			}
			else {
				throw new ParseException("The value type specifier '"+iterator.Current.Value+" is invalid. " + iterator.Current.ParsePosition);
			}
			iterator.MoveNext();								//	 move past the type specifier

			if (iterator.Current.Kind != TokenKind.String) {
				throw new ParseException("The missing the value text. " + iterator.Current.ParsePosition);
			}
			item._value = iterator.Current.Value; ;

			_processQueue.Enqueue(item);						//	Add the Key processing command to the process queue.
			iterator.MoveNext();								//	 move past the field value
		}

		/// <summary>
		/// Determine the key name and optional default value.  Recursively process the sub keys.
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		private void ProcessAddKey(IEnumerator<Token> iterator) {
			if (iterator.Current.Kind != TokenKind.Word && iterator.Current.Kind != TokenKind.String) {
				throw new ParseException("The expecting a Registry key name, \'ForceRemove\', or \'NoRemove\'" + iterator.Current.ParsePosition);
			}

			AddType addType = DetermineKeyAddType(iterator);	//	Determine what type of add.

			string key;											//	Determine the subkey name
			if (iterator.Current.Kind == TokenKind.StartKey) {	//	it could start with a {
				StringBuilder sb = new StringBuilder(128);
				sb.Append('{');
				iterator.MoveNext();							//	move past the name part
				sb.Append(iterator.Current.Value);
				key = sb.ToString();
			}
			else {
				key = iterator.Current.Value;
			}

			iterator.MoveNext();								//	move past the key name

			RegistryOpQueueItem item;							//	Add the subkey to the process queue
			switch (addType) {
				case AddType.AddKey:
					RegistryOperation op = _process == RegistryProcess.Register ? RegistryOperation.AddKey : RegistryOperation.DeleteKey;
					_processQueue.Enqueue(new RegistryOpQueueItem(op, key));
					break;
				case AddType.ForceRemove:
                    if (_process == RegistryProcess.Register) {
                        item = new RegistryOpQueueItem(RegistryOperation.DeleteAddKey, key);
                        _processQueue.Enqueue(item);
                    }
                    else {
                        _processQueue.Enqueue(new RegistryOpQueueItem(RegistryOperation.DeleteKey, key));
                    }
					break;
				case AddType.NoRemove:
					if (_process == RegistryProcess.Register) {
						_processQueue.Enqueue(new RegistryOpQueueItem(RegistryOperation.AddKey, key));
					}
					else {
						_processQueue.Enqueue(new RegistryOpQueueItem(RegistryOperation.OpenKey, key));
					}
					break;
			}

			if (iterator.Current.Kind == TokenKind.Assignment) {//	if there is an '=' sign, process the value
				iterator.MoveNext();							//	move past the assignment string
				ProcessValue(iterator, string.Empty);			//	Process the value and specify and empty string as
																//	the name, so we won't look for it.
			}

            if (iterator.Current.Kind == TokenKind.StartKey) {	//	If there is a {, then process the subkeys
                ProcessKey(iterator, key);
            }
            else {                                              //  If there is no sub keys then close this key
                _processQueue.Enqueue(new RegistryOpQueueItem(RegistryOperation.CloseKey, key));
            }
		}

		/// <summary>
		/// Determine what kind of key add this is.  
		///		Is it a normal (add on register, remove on unregister);
		///		Is it a NoRemove (add or open on rgister, leave on unregister; or,
		///		Is it a ForceRemove (delete, then add on register, remove on unregister)
		/// </summary>
		/// <param name="iterator">The token iterator</param>
		/// <returns></returns>
		private AddType DetermineKeyAddType(IEnumerator<Token> iterator) {
			AddType addType = AddType.NotSpecified;
			bool finished = false;
			while (!finished) {
				if (iterator.Current.Kind == TokenKind.StartKey) {		//	 a key name could start with a {
					addType = (addType == AddType.NotSpecified) ? AddType.AddKey : addType;
					finished = true;
				}
				else if (iterator.Current.Kind == TokenKind.String) {	//	 a key name could be a string
					addType = (addType == AddType.NotSpecified) ? AddType.AddKey : addType;
					finished = true;
				}
				else if ("ForceRemove".Equals(iterator.Current.Value)) {//	There is a forceremove keyword
					if (addType != AddType.NotSpecified) {
						throw new ParseException("More than one add type was specified. " + iterator.Current.ParsePosition);
					}
					addType = AddType.ForceRemove;
					iterator.MoveNext();
				}
				else if ("NoRemove".Equals(iterator.Current.Value)) {	// NoRemove keyword
					if (addType != AddType.NotSpecified) {
						throw new ParseException("More than one add type was specified. " + iterator.Current.ParsePosition);
					}
					addType = AddType.NoRemove;
					iterator.MoveNext();
				}
				else {													//	Just a simple key name
					addType = (addType == AddType.NotSpecified) ? AddType.AddKey : addType;
					finished = true;
				}
			}
			return addType;
		}
		#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)

About the Author

Tim Almdal

Canada Canada
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 18 Sep 2006
Article Copyright 2006 by Tim Almdal
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid