/* * Installer.cs -- * * Written by Joe Mistachkin. * Released to the public domain, use at your own risk! */ using System.Collections.Generic; using System.Diagnostics; using System.EnterpriseServices.Internal; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Windows.Forms; using System.Xml; using Microsoft.Win32; namespace System.Data.SQLite { #region Public Delegates internal delegate void TraceCallback( string message, string category ); /////////////////////////////////////////////////////////////////////////// internal delegate bool FrameworkConfigCallback( string fileName, string invariant, string name, string description, string typeName, AssemblyName assemblyName, object clientData, bool whatIf, bool verbose, ref bool saved, ref string error ); /////////////////////////////////////////////////////////////////////////// internal delegate bool FrameworkRegistryCallback( RegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, object clientData, bool whatIf, bool verbose, ref string error ); /////////////////////////////////////////////////////////////////////////// internal delegate bool VisualStudioRegistryCallback( RegistryKey rootKey, Version vsVersion, Installer.Package package, object clientData, bool whatIf, bool verbose, ref string error ); #endregion /////////////////////////////////////////////////////////////////////////// #region Public Enumerations [Flags()] public enum InstallFlags { None = 0x0, GAC = 0x1, AssemblyFolders = 0x2, DbProviderFactory = 0x4, VsPackage = 0x8, VsDataSource = 0x10, VsDataProvider = 0x20, Framework = GAC | AssemblyFolders | DbProviderFactory, Vs = VsPackage | VsDataSource | VsDataProvider, All = Framework | Vs, AllNoGAC = All & ~GAC, Default = All } /////////////////////////////////////////////////////////////////////////// [Flags()] public enum TracePriority { None = 0x0, Lowest = 0x1, Lower = 0x2, Low = 0x4, Medium = 0x8, High = 0x10, Higher = 0x20, Highest = 0x40, Debug = Medium, /* NOTE: Default for debug messages. */ Trace = Medium /* NOTE: Default for trace messages. */ } #endregion /////////////////////////////////////////////////////////////////////////// #region Installer Class internal static class Installer { #region Private Helper Classes #region AnyPair Class private sealed class AnyPair { #region Public Constructors // // WARNING: This constructor produces an immutable "empty" pair // object. // public AnyPair() : base() { // do nothing. } /////////////////////////////////////////////////////////////////// public AnyPair(T1 x) : this() { this.x = x; } /////////////////////////////////////////////////////////////////// public AnyPair(T1 x, T2 y) : this(x) { this.y = y; } #endregion /////////////////////////////////////////////////////////////////// #region Public Properties private T1 x; public T1 X { get { return x; } } /////////////////////////////////////////////////////////////////// private T2 y; public T2 Y { get { return y; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region TraceOps Class private static class TraceOps { #region Private Constants private const string DefaultDebugFormat = "#{0} @ {1}: {2}"; private const string DefaultTraceFormat = "#{0} @ {1}: {2}"; private const string Iso8601DateTimeOutputFormat = "yyyy.MM.ddTHH:mm:ss.fffffff"; #endregion /////////////////////////////////////////////////////////////////// #region Private Static Data private static object syncRoot = new object(); private static long nextDebugId; private static long nextTraceId; private static TracePriority debugPriority = TracePriority.Debug; private static TracePriority tracePriority = TracePriority.Trace; private static string debugFormat = DefaultDebugFormat; private static string traceFormat = DefaultTraceFormat; #endregion /////////////////////////////////////////////////////////////////// #region Public Static Properties public static TracePriority DebugPriority { get { lock (syncRoot) { return debugPriority; } } set { lock (syncRoot) { debugPriority = value; } } } /////////////////////////////////////////////////////////////////// public static TracePriority TracePriority { get { lock (syncRoot) { return tracePriority; } } set { lock (syncRoot) { tracePriority = value; } } } /////////////////////////////////////////////////////////////////// public static string DebugFormat { get { lock (syncRoot) { return debugFormat; } } set { lock (syncRoot) { debugFormat = value; } } } /////////////////////////////////////////////////////////////////// public static string TraceFormat { get { lock (syncRoot) { return traceFormat; } } set { lock (syncRoot) { traceFormat = value; } } } #endregion /////////////////////////////////////////////////////////////////// #region Interactive Support Methods public static string GetAssemblyTitle( Assembly assembly ) { if (assembly != null) { try { if (assembly.IsDefined( typeof(AssemblyTitleAttribute), false)) { AssemblyTitleAttribute title = (AssemblyTitleAttribute) assembly.GetCustomAttributes( typeof(AssemblyTitleAttribute), false)[0]; return title.Title; } } catch { // do nothing. } } return null; } /////////////////////////////////////////////////////////////////// public static DialogResult ShowMessage( TracePriority tracePriority, TraceCallback debugCallback, TraceCallback traceCallback, Assembly assembly, string message, string category, MessageBoxButtons buttons, MessageBoxIcon icon ) { DialogResult result = DialogResult.OK; DebugAndTrace(tracePriority, debugCallback, traceCallback, message, category); if (SystemInformation.UserInteractive) { string title = GetAssemblyTitle(assembly); if (title == null) title = Application.ProductName; result = MessageBox.Show(message, title, buttons, icon); DebugAndTrace(tracePriority, debugCallback, traceCallback, String.Format( "User choice of {0}.", ForDisplay(result)), category); return result; } DebugAndTrace(tracePriority, debugCallback, traceCallback, String.Format( "Default choice of {0}.", ForDisplay(result)), category); return result; } #endregion /////////////////////////////////////////////////////////////////// #region Tracing Support Methods public static long NextDebugId() { return Interlocked.Increment(ref nextDebugId); } /////////////////////////////////////////////////////////////////// public static long NextTraceId() { return Interlocked.Increment(ref nextTraceId); } /////////////////////////////////////////////////////////////////// public static string TimeStamp(DateTime dateTime) { return dateTime.ToString(Iso8601DateTimeOutputFormat); } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] private static string GetMethodName( StackTrace stackTrace, int level ) { try { // // NOTE: If a valid stack trace was not supplied by the // caller, create one now based on the current // execution stack. // if (stackTrace == null) { // // NOTE: Grab the current execution stack. // stackTrace = new StackTrace(); // // NOTE: Always skip this call frame when we capture // the stack trace. // level++; } // // NOTE: Get the specified stack frame (always add one to // skip this method). // StackFrame stackFrame = stackTrace.GetFrame(level); // // NOTE: Get the method for the stack frame. // MethodBase methodBase = stackFrame.GetMethod(); // // NOTE: Get the type for the method. // Type type = methodBase.DeclaringType; // // NOTE: Get the name of the method. // string name = methodBase.Name; // // NOTE: Return the properly formatted result. // return String.Format( "{0}{1}{2}", type.Name, Type.Delimiter, name); } catch { // do nothing. } return null; } /////////////////////////////////////////////////////////////////// public static void DebugCore( string message, string category ) { lock (syncRoot) { #if DEBUG // // NOTE: Write the message to all the active debug // listeners. // Debug.WriteLine(message, category); Debug.Flush(); #else // // NOTE: For a build without "DEBUG" defined, we cannot // simply use the Debug class (i.e. it will do // nothing); therefore, use the console directly // instead. // Console.WriteLine(String.Format("{1}: {0}", message, category)); #endif } } /////////////////////////////////////////////////////////////////// public static void TraceCore( string message, string category ) { lock (syncRoot) { // // NOTE: Write the message to all the active trace // listeners. // Trace.WriteLine(message, category); Trace.Flush(); } } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] public static string DebugAndTrace( TracePriority tracePriority, TraceCallback debugCallback, TraceCallback traceCallback, Exception exception, string category ) { if (exception != null) return DebugAndTrace(tracePriority, debugCallback, traceCallback, new StackTrace(exception, true), 0, exception.ToString(), category); return null; } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] public static string DebugAndTrace( TracePriority tracePriority, TraceCallback debugCallback, TraceCallback traceCallback, string message, string category ) { return DebugAndTrace( tracePriority, debugCallback, traceCallback, null, 1, message, category); } /////////////////////////////////////////////////////////////////// [MethodImpl(MethodImplOptions.NoInlining)] private static string DebugAndTrace( TracePriority tracePriority, TraceCallback debugCallback, TraceCallback traceCallback, StackTrace stackTrace, int level, string message, string category ) { // // NOTE: Always skip this call frame if the stack trace is // going to be captured by GetMethodName. // if (stackTrace == null) level++; // // NOTE: Format the message for display (once). // string formatted = String.Format("{0}: {1}", GetMethodName(stackTrace, level), message); // // NOTE: If the trace priority of this message is less than // what we currently want to debug, skip it. // if (tracePriority >= DebugPriority) { // // NOTE: If not specified, use the default debug callback. // if (debugCallback == null) debugCallback = DebugCore; // // NOTE: Invoke the debug callback with the formatted // message and the category specified by the // caller. // debugCallback(formatted, category); } // // NOTE: If the trace priority of this message is less than // what we currently want to trace, skip it. // if (tracePriority >= TracePriority) { // // NOTE: If not specified, use the default trace callback. // if (traceCallback == null) traceCallback = TraceCore; // // NOTE: Invoke the trace callback with the formatted // message and the category specified by the // caller. // traceCallback(formatted, category); } return message; } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region MockRegistryKey Class private sealed class MockRegistryKey : IDisposable { #region Private Constructors private MockRegistryKey() { whatIf = true; readOnly = true; safe = true; } #endregion /////////////////////////////////////////////////////////////////// #region Public Constructors public MockRegistryKey( RegistryKey key ) : this() { this.key = key; } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, string subKeyName ) : this(key) { this.subKeyName = subKeyName; } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, string subKeyName, bool whatIf ) : this(key, subKeyName) { this.whatIf = whatIf; } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, string subKeyName, bool whatIf, bool readOnly ) : this(key, subKeyName, whatIf) { this.readOnly = readOnly; } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, string subKeyName, bool whatIf, bool readOnly, bool safe ) : this(key, subKeyName, whatIf, readOnly) { this.safe = safe; } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, bool whatIf ) : this(key, null, whatIf) { // do nothing. } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, bool whatIf, bool readOnly ) : this(key, null, whatIf, readOnly) { // do nothing. } /////////////////////////////////////////////////////////////////// public MockRegistryKey( RegistryKey key, bool whatIf, bool readOnly, bool safe ) : this(key, null, whatIf, readOnly, safe) { // do nothing. } #endregion /////////////////////////////////////////////////////////////////// #region Public Methods public void Close() { // // NOTE: No disposed check here because calling this method // should be just like calling Dispose. // Dispose(true); } /////////////////////////////////////////////////////////////////// public MockRegistryKey CreateSubKey( string subKeyName ) { CheckDisposed(); CheckReadOnly(); if (key == null) return null; if (whatIf) { // // HACK: Attempt to open the specified sub-key. If this // fails, we will simply return the wrapped root key // itself since no writes are allowed in 'what-if' // mode anyhow. // RegistryKey subKey = key.OpenSubKey(subKeyName); return (subKey != null) ? new MockRegistryKey( subKey, whatIf, readOnly, safe) : new MockRegistryKey( key, subKeyName, whatIf, readOnly, safe); } else { return new MockRegistryKey( key.CreateSubKey(subKeyName), whatIf, readOnly, safe); } } /////////////////////////////////////////////////////////////////// public void DeleteSubKey( string subKeyName ) { CheckDisposed(); CheckReadOnly(); if (key == null) return; if (!whatIf) key.DeleteSubKey(subKeyName); } /////////////////////////////////////////////////////////////////// public void DeleteSubKeyTree( string subKeyName ) { CheckDisposed(); CheckReadOnly(); if (key == null) return; if (!whatIf) key.DeleteSubKeyTree(subKeyName); } /////////////////////////////////////////////////////////////////// public void DeleteValue( string name ) { CheckDisposed(); CheckReadOnly(); if (key == null) return; if (!whatIf) key.DeleteValue(name); } /////////////////////////////////////////////////////////////////// public string[] GetSubKeyNames() { CheckDisposed(); if (key == null) return null; return key.GetSubKeyNames(); } /////////////////////////////////////////////////////////////////// public object GetValue( string name, object defaultValue ) { CheckDisposed(); if (key == null) return null; return key.GetValue(name, defaultValue); } /////////////////////////////////////////////////////////////////// public MockRegistryKey OpenSubKey( string subKeyName ) { CheckDisposed(); return OpenSubKey(subKeyName, false); } /////////////////////////////////////////////////////////////////// public MockRegistryKey OpenSubKey( string subKeyName, bool writable ) { CheckDisposed(); if (writable) CheckReadOnly(); if (key == null) return null; RegistryKey subKey = key.OpenSubKey( subKeyName, whatIf ? false : writable); return (subKey != null) ? new MockRegistryKey(subKey, whatIf, readOnly, safe) : null; } /////////////////////////////////////////////////////////////////// public void SetValue( string name, object value ) { CheckDisposed(); CheckReadOnly(); if (key == null) return; if (!whatIf) key.SetValue(name, value); } #endregion /////////////////////////////////////////////////////////////////// #region Public Properties public string Name { get { CheckDisposed(); if (key == null) return null; return !String.IsNullOrEmpty(subKeyName) ? String.Format("{0}\\{1}", key.Name, subKeyName) : key.Name; } } /////////////////////////////////////////////////////////////////// private RegistryKey key; public RegistryKey Key { get { CheckDisposed(); CheckSafe(); return key; } } /////////////////////////////////////////////////////////////////// private string subKeyName; public string SubKeyName { get { CheckDisposed(); return subKeyName; } } /////////////////////////////////////////////////////////////////// private bool whatIf; public bool WhatIf { get { CheckDisposed(); return whatIf; } } /////////////////////////////////////////////////////////////////// private bool readOnly; public bool ReadOnly { get { CheckDisposed(); return readOnly; } } /////////////////////////////////////////////////////////////////// public bool safe; public bool Safe { get { CheckDisposed(); return safe; } } #endregion /////////////////////////////////////////////////////////////////// #region Private Methods private void CheckReadOnly() { // // NOTE: In "read-only" mode, we disallow all write access. // if (!readOnly) return; throw new InvalidOperationException(); } /////////////////////////////////////////////////////////////////// private void CheckSafe() { // // NOTE: In "safe" mode, we disallow all direct access to the // contained registry key. // if (!safe) return; throw new InvalidOperationException(); } #endregion /////////////////////////////////////////////////////////////////// #region System.Object Overrides public override string ToString() { CheckDisposed(); return this.Name; } #endregion /////////////////////////////////////////////////////////////////// #region Implicit Conversion Operators // // BUGBUG: The 'what-if' mode setting here should probably be based // on some static property, not hard-coded to true? // public static implicit operator MockRegistryKey( RegistryKey key ) { return new MockRegistryKey(key, null, true, false, false); } /////////////////////////////////////////////////////////////////// // // BUGBUG: Remove me? This should be safe because in 'what-if' // mode all keys are opened read-only. // public static implicit operator RegistryKey( MockRegistryKey key ) { return (key != null) ? key.Key : null; } #endregion /////////////////////////////////////////////////////////////////// #region IDisposable "Pattern" Members private bool disposed; private void CheckDisposed() /* throw */ { if (!disposed) return; throw new ObjectDisposedException( typeof(MockRegistryKey).Name); } /////////////////////////////////////////////////////////////////// private /* protected virtual */ void Dispose( bool disposing ) { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// if (key != null) { key.Close(); key = null; } } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// // // NOTE: This object is now disposed. // disposed = true; } } #endregion /////////////////////////////////////////////////////////////////// #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /////////////////////////////////////////////////////////////////// #region Destructor ~MockRegistryKey() { Dispose(false); } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region RegistryHelper Class private static class RegistryHelper { #region Public Static Properties private static int subKeysCreated; public static int SubKeysCreated { get { return subKeysCreated; } } /////////////////////////////////////////////////////////////////// private static int subKeysDeleted; public static int SubKeysDeleted { get { return subKeysDeleted; } } /////////////////////////////////////////////////////////////////// private static int keyValuesSet; public static int KeyValuesSet { get { return keyValuesSet; } } /////////////////////////////////////////////////////////////////// private static int keyValuesDeleted; public static int KeyValuesDeleted { get { return keyValuesDeleted; } } #endregion /////////////////////////////////////////////////////////////////// #region Public Static Methods public static RegistryKey GetRootKeyByName( string keyName ) { if (String.IsNullOrEmpty(keyName)) return null; switch (keyName.ToUpperInvariant()) { case "HKCR": case "HKEY_CLASSES_ROOT": return Registry.ClassesRoot; case "HKCC": case "HKEY_CURRENT_CONFIG": return Registry.CurrentConfig; case "HKCU": case "HKEY_CURRENT_USER": return Registry.CurrentUser; case "HKDD": case "HKEY_DYN_DATA": return Registry.DynData; case "HKLM": case "HKEY_LOCAL_MACHINE": return Registry.LocalMachine; case "HKPD": case "HKEY_PERFORMANCE_DATA": return Registry.PerformanceData; case "HKU": case "HKEY_USERS": return Registry.Users; } return null; } /////////////////////////////////////////////////////////////////// public static MockRegistryKey OpenSubKey( MockRegistryKey rootKey, string subKeyName, bool writable, bool whatIf, bool verbose ) { if (rootKey == null) return null; if (verbose) TraceOps.DebugAndTrace(writable ? TracePriority.Highest : TracePriority.Higher, debugCallback, traceCallback, String.Format( "rootKey = {0}, subKeyName = {1}, writable = {2}", ForDisplay(rootKey), ForDisplay(subKeyName), ForDisplay(writable)), traceCategory); // // HACK: Always forbid writable access when operating in // 'what-if' mode. // MockRegistryKey key = rootKey.OpenSubKey( subKeyName, whatIf ? false : writable); return (key != null) ? new MockRegistryKey(key, whatIf, false, false) : null; } /////////////////////////////////////////////////////////////////// public static MockRegistryKey CreateSubKey( MockRegistryKey rootKey, string subKeyName, bool whatIf, bool verbose ) { if (rootKey == null) return null; if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "rootKey = {0}, subKeyName = {1}", ForDisplay(rootKey), ForDisplay(subKeyName)), traceCategory); try { // // HACK: Always open a key, rather than creating one when // operating in 'what-if' mode. // if (whatIf) { // // HACK: Attempt to open the specified sub-key. If // this fails, we will simply return the root // key itself since no writes are allowed in // 'what-if' mode anyhow. // MockRegistryKey key = rootKey.OpenSubKey(subKeyName); return (key != null) ? key : new MockRegistryKey( rootKey, subKeyName, true, false, false); } else { return new MockRegistryKey( rootKey.CreateSubKey(subKeyName), false, false, false); } } finally { subKeysCreated++; } } /////////////////////////////////////////////////////////////////// public static void DeleteSubKey( MockRegistryKey rootKey, string subKeyName, bool whatIf, bool verbose ) { if (rootKey == null) return; if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "rootKey = {0}, subKeyName = {1}", ForDisplay(rootKey), ForDisplay(subKeyName)), traceCategory); if (!whatIf) rootKey.DeleteSubKey(subKeyName); subKeysDeleted++; } /////////////////////////////////////////////////////////////////// public static void DeleteSubKeyTree( MockRegistryKey rootKey, string subKeyName, bool whatIf, bool verbose ) { if (rootKey == null) return; if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "rootKey = {0}, subKeyName = {1}", ForDisplay(rootKey), ForDisplay(subKeyName)), traceCategory); if (!whatIf) rootKey.DeleteSubKeyTree(subKeyName); subKeysDeleted++; } /////////////////////////////////////////////////////////////////// public static string[] GetSubKeyNames( MockRegistryKey key, bool whatIf, bool verbose ) { if (key == null) return null; if (verbose) TraceOps.DebugAndTrace(TracePriority.High, debugCallback, traceCallback, String.Format( "key = {0}", ForDisplay(key)), traceCategory); return key.GetSubKeyNames(); } /////////////////////////////////////////////////////////////////// public static object GetValue( MockRegistryKey key, string name, object defaultValue, bool whatIf, bool verbose ) { if (key == null) return null; if (verbose) TraceOps.DebugAndTrace(TracePriority.High, debugCallback, traceCallback, String.Format( "key = {0}, name = {1}, defaultValue = {2}", ForDisplay(key), ForDisplay(name), ForDisplay(defaultValue)), traceCategory); return key.GetValue(name, defaultValue); } /////////////////////////////////////////////////////////////////// public static void SetValue( MockRegistryKey key, string name, object value, bool whatIf, bool verbose ) { if (key == null) return; if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "key = {0}, name = {1}, value = {2}", ForDisplay(key), ForDisplay(name), ForDisplay(value)), traceCategory); if (!whatIf) key.SetValue(name, value); keyValuesSet++; } /////////////////////////////////////////////////////////////////// public static void DeleteValue( MockRegistryKey key, string name, bool whatIf, bool verbose ) { if (key == null) return; if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "key = {0}, name = {1}", ForDisplay(key), ForDisplay(name)), traceCategory); if (!whatIf) key.DeleteValue(name); keyValuesDeleted++; } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region StringList Class private sealed class StringList : List { public StringList() : base() { // do nothing. } /////////////////////////////////////////////////////////////////// public StringList(IEnumerable collection) : base(collection) { // do nothing. } } #endregion /////////////////////////////////////////////////////////////////////// #region StringDictionary Class private sealed class StringDictionary : Dictionary { public StringDictionary() { // do nothing. } } #endregion /////////////////////////////////////////////////////////////////////// #region VersionList Class private sealed class VersionList : List { public VersionList() : base() { // do nothing. } /////////////////////////////////////////////////////////////////// public VersionList(IEnumerable collection) : base(collection) { // do nothing. } } #endregion /////////////////////////////////////////////////////////////////////// #region VersionMap Class private sealed class VersionMap : Dictionary { public VersionMap() { // do nothing. } } #endregion /////////////////////////////////////////////////////////////////////// #region VersionListMap Class private sealed class VersionListMap : Dictionary { public VersionListMap() { // do nothing. } } #endregion /////////////////////////////////////////////////////////////////////// #region Package Class internal sealed class Package { #region Public Constructors public Package() { // do nothing. } #endregion /////////////////////////////////////////////////////////////////// #region Public Properties private Guid packageId; public Guid PackageId { get { return packageId; } set { packageId = value; } } /////////////////////////////////////////////////////////////////// private Guid serviceId; public Guid ServiceId { get { return serviceId; } set { serviceId = value; } } /////////////////////////////////////////////////////////////////// private Guid dataSourceId; public Guid DataSourceId { get { return dataSourceId; } set { dataSourceId = value; } } /////////////////////////////////////////////////////////////////// private Guid dataProviderId; public Guid DataProviderId { get { return dataProviderId; } set { dataProviderId = value; } } /////////////////////////////////////////////////////////////////// private Guid adoNetTechnologyId; public Guid AdoNetTechnologyId { get { return adoNetTechnologyId; } set { adoNetTechnologyId = value; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region Configuration Class private sealed class Configuration { #region Private Constants private const char Switch = '-'; private const char AltSwitch = '/'; /////////////////////////////////////////////////////////////////// private static readonly char[] SwitchChars = { Switch, AltSwitch }; #endregion /////////////////////////////////////////////////////////////////// #region Private Constructors private Configuration( Assembly assembly, string logFileName, string directory, string coreFileName, string linqFileName, string designerFileName, string debugFormat, string traceFormat, InstallFlags installFlags, TracePriority debugPriority, TracePriority tracePriority, bool install, bool noRuntimeVersion, bool noDesktop, bool noCompact, bool noNetFx20, bool noNetFx40, bool noVs2008, bool noVs2010, bool noTrace, bool noConsole, bool noLog, bool whatIf, bool verbose, bool confirm ) { this.assembly = assembly; this.logFileName = logFileName; this.directory = directory; this.coreFileName = coreFileName; this.linqFileName = linqFileName; this.designerFileName = designerFileName; this.debugFormat = debugFormat; this.traceFormat = traceFormat; this.installFlags = installFlags; this.debugPriority = debugPriority; this.tracePriority = tracePriority; this.install = install; this.noRuntimeVersion = noRuntimeVersion; this.noDesktop = noDesktop; this.noCompact = noCompact; this.noNetFx20 = noNetFx20; this.noNetFx40 = noNetFx40; this.noVs2008 = noVs2008; this.noVs2010 = noVs2010; this.noTrace = noTrace; this.noConsole = noConsole; this.noLog = noLog; this.whatIf = whatIf; this.verbose = verbose; this.confirm = confirm; } #endregion /////////////////////////////////////////////////////////////////// #region Private Static Methods private static void GetDefaultFileNames( ref string directory, ref string coreFileName, ref string linqFileName, ref string designerFileName ) { if (thisAssembly == null) return; directory = Path.GetDirectoryName(thisAssembly.Location); if (String.IsNullOrEmpty(directory)) return; coreFileName = Path.Combine(directory, Installer.CoreFileName); linqFileName = Path.Combine(directory, Installer.LinqFileName); designerFileName = Path.Combine(directory, Installer.DesignerFileName); } /////////////////////////////////////////////////////////////////// private static bool CheckOption( ref string arg ) { string result = arg; if (!String.IsNullOrEmpty(result)) { // // NOTE: Remove all leading switch chars. // result = result.TrimStart(SwitchChars); // // NOTE: How many chars were removed? // int count = arg.Length - result.Length; // // NOTE: Was there at least one? // if (count > 0) { // // NOTE: Ok, replace their original // argument. // arg = result; // // NOTE: Yes, this is a switch. // return true; } } return false; } /////////////////////////////////////////////////////////////////// private static bool MatchOption( string arg, string option ) { if ((arg == null) || (option == null)) return false; return String.Compare(arg, 0, option, 0, arg.Length, StringComparison.OrdinalIgnoreCase) == 0; } /////////////////////////////////////////////////////////////////// private static bool? ParseBoolean( string text ) { if (!String.IsNullOrEmpty(text)) { bool value; if (bool.TryParse(text, out value)) return value; } return null; } /////////////////////////////////////////////////////////////////// private static object ParseEnum( Type enumType, string text, bool noCase ) { if ((enumType == null) || !enumType.IsEnum) return null; if (!String.IsNullOrEmpty(text)) { try { return Enum.Parse(enumType, text, noCase); } catch { // do nothing. } } return null; } #endregion /////////////////////////////////////////////////////////////////// #region Public Static Methods public static Configuration CreateDefault() { string directory = null; string coreFileName = null; string linqFileName = null; string designerFileName = null; GetDefaultFileNames( ref directory, ref coreFileName, ref linqFileName, ref designerFileName); return new Configuration(thisAssembly, null, directory, coreFileName, linqFileName, designerFileName, TraceOps.DebugFormat, TraceOps.TraceFormat, InstallFlags.Default, TracePriority.Debug, TracePriority.Trace, true, false, false, false, false, false, false, false, false, false, false, true, true, false); } /////////////////////////////////////////////////////////////////// public static bool FromArgs( string[] args, bool strict, ref Configuration configuration, ref string error ) { try { if (args == null) return true; if (configuration == null) configuration = Configuration.CreateDefault(); int length = args.Length; for (int index = 0; index < length; index++) { string arg = args[index]; // // NOTE: Skip any argument that is null (?) or an empty // string. // if (String.IsNullOrEmpty(arg)) continue; // // NOTE: We are going to modify the original argument // by removing any leading option characters; // therefore, we use a new string to hold the // modified argument. // string newArg = arg; // // NOTE: All the supported command line options must // begin with an option character (e.g. a minus // or forward slash); attempt to validate that // now. If we fail in strict mode, we are done; // otherwise, just skip this argument and advance // to the next one. // if (!CheckOption(ref newArg)) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Unsupported command line argument: {0}", ForDisplay(arg)), traceCategory); if (strict) return false; continue; } // // NOTE: All the supported command line options must // have a value; therefore, attempt to advance // to it now. If we fail, we are done. // index++; if (index >= length) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Missing value for option: {0}", ForDisplay(arg)), traceCategory); if (strict) return false; break; } // // NOTE: Grab the textual value of this command line // option. // string text = args[index]; // // NOTE: Figure out which command line option this is // (based on a partial name match) and then try // to interpret the textual value as the correct // type. // if (MatchOption(newArg, "strict")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } // // NOTE: Allow the command line arguments to // override the "strictness" setting // provided by our caller. // strict = (bool)value; } else if (MatchOption(newArg, "logFileName")) { configuration.logFileName = text; } else if (MatchOption(newArg, "directory")) { configuration.directory = text; // // NOTE: *SPECIAL* Must refresh the file names // here because the underlying directory // has changed. // string coreFileName = configuration.coreFileName; if (!String.IsNullOrEmpty(coreFileName)) coreFileName = Path.GetFileName(coreFileName); if (String.IsNullOrEmpty(coreFileName)) coreFileName = Installer.CoreFileName; configuration.coreFileName = Path.Combine( configuration.directory, coreFileName); string linqFileName = configuration.linqFileName; if (!String.IsNullOrEmpty(linqFileName)) linqFileName = Path.GetFileName(linqFileName); if (String.IsNullOrEmpty(linqFileName)) linqFileName = Installer.LinqFileName; configuration.linqFileName = Path.Combine( configuration.directory, linqFileName); string designerFileName = configuration.designerFileName; if (!String.IsNullOrEmpty(designerFileName)) designerFileName = Path.GetFileName(designerFileName); if (String.IsNullOrEmpty(designerFileName)) designerFileName = Installer.DesignerFileName; configuration.designerFileName = Path.Combine( configuration.directory, designerFileName); } else if (MatchOption(newArg, "coreFileName")) { configuration.coreFileName = text; } else if (MatchOption(newArg, "linqFileName")) { configuration.linqFileName = text; } else if (MatchOption(newArg, "designerFileName")) { configuration.designerFileName = text; } else if (MatchOption(newArg, "debugFormat")) { configuration.debugFormat = text; TraceOps.DebugFormat = configuration.debugFormat; } else if (MatchOption(newArg, "traceFormat")) { configuration.traceFormat = text; TraceOps.TraceFormat = configuration.traceFormat; } else if (MatchOption(newArg, "debugPriority")) { object value = ParseEnum( typeof(TracePriority), text, true); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.debugPriority = (TracePriority)value; TraceOps.DebugPriority = configuration.debugPriority; } else if (MatchOption(newArg, "tracePriority")) { object value = ParseEnum( typeof(TracePriority), text, true); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.tracePriority = (TracePriority)value; TraceOps.TracePriority = configuration.tracePriority; } else if (MatchOption(newArg, "install")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.install = (bool)value; } else if (MatchOption(newArg, "installFlags")) { object value = ParseEnum( typeof(InstallFlags), text, true); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid install flags value: {0}", ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.installFlags = (InstallFlags)value; } else if (MatchOption(newArg, "noRuntimeVersion")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noRuntimeVersion = (bool)value; } else if (MatchOption(newArg, "whatIf")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.whatIf = (bool)value; } else if (MatchOption(newArg, "verbose")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.verbose = (bool)value; } else if (MatchOption(newArg, "confirm")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.confirm = (bool)value; } else if (MatchOption(newArg, "noDesktop")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noDesktop = (bool)value; } else if (MatchOption(newArg, "noCompact")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noCompact = (bool)value; } else if (MatchOption(newArg, "noNetFx20")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noNetFx20 = (bool)value; } else if (MatchOption(newArg, "noNetFx40")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noNetFx40 = (bool)value; } else if (MatchOption(newArg, "noVs2008")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noVs2008 = (bool)value; } else if (MatchOption(newArg, "noVs2010")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noVs2010 = (bool)value; } else if (MatchOption(newArg, "noTrace")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noTrace = (bool)value; } else if (MatchOption(newArg, "noConsole")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noConsole = (bool)value; } else if (MatchOption(newArg, "noLog")) { bool? value = ParseBoolean(text); if (value == null) { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Invalid {0} boolean value: {1}", ForDisplay(arg), ForDisplay(text)), traceCategory); if (strict) return false; continue; } configuration.noLog = (bool)value; } else { error = TraceOps.DebugAndTrace( TracePriority.Lowest, debugCallback, traceCallback, String.Format( "Unsupported command line option: {0}", ForDisplay(arg)), traceCategory); if (strict) return false; } } return true; } catch (Exception e) { TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, e, traceCategory); error = "Failed to modify configuration."; } return false; } /////////////////////////////////////////////////////////////////// public static bool Process( string[] args, Configuration configuration, bool strict, ref string error ) { try { if (configuration == null) { error = "Invalid configuration."; return false; } Assembly assembly = configuration.assembly; if (assembly == null) { error = "Invalid assembly."; return false; } if (!configuration.noTrace) { if (!configuration.noLog && String.IsNullOrEmpty(configuration.logFileName)) { configuration.logFileName = GetLogFileName(); } /////////////////////////////////////////////////////// if (!configuration.noConsole) { // // NOTE: In verbose mode, debug output (that meets // the configured priority criteria) will be // displayed to the console; otherwise, trace // output (that meets the configured priority // criteria) will be displayed to the console. // if (!configuration.verbose) { Trace.Listeners.Add(new ConsoleTraceListener()); } #if DEBUG else { // // NOTE: For a build with "DEBUG" defined, we // can simply use the Debug class; // otherwise, the console will be used // directly (by DebugCore). // Debug.Listeners.Add(new ConsoleTraceListener()); } #endif } if (!configuration.noLog && !String.IsNullOrEmpty(configuration.logFileName)) { Trace.Listeners.Add(new TextWriterTraceListener( configuration.logFileName)); } } // // NOTE: Dump the configuration now in case we need to // troubleshoot any issues. // if (configuration.debugPriority <= TracePriority.Medium) configuration.Dump(debugCallback); if (configuration.tracePriority <= TracePriority.Medium) configuration.Dump(traceCallback); // // NOTE: Show where we are running from and how we were // invoked. // string location = assembly.Location; TraceOps.DebugAndTrace(TracePriority.Medium, debugCallback, traceCallback, String.Format( "Running executable is: {0}", ForDisplay(location)), traceCategory); TraceOps.DebugAndTrace(TracePriority.Medium, debugCallback, traceCallback, String.Format( "Original command line is: {0}", Environment.CommandLine), traceCategory); if (!configuration.whatIf) { // // NOTE: If the debugger is attached and What-If mode // is [now] disabled, issue a warning. // if (Debugger.IsAttached) TraceOps.DebugAndTrace(TracePriority.Medium, debugCallback, traceCallback, "Forced to disable \"what-if\" mode with " + "debugger attached.", traceCategory); } else { TraceOps.DebugAndTrace(TracePriority.Higher, debugCallback, traceCallback, "No actual changes will be made to this " + "system because \"what-if\" mode is enabled.", traceCategory); } // // NOTE: If the command line has not been manually // confirmed (i.e. via the explicit command line // option), then stop processing now. We enforce // this rule so that simply double-clicking the // executable will not result in any changes being // made to the system. // if (!configuration.confirm) { error = "Cannot continue, the \"confirm\" option is " + "not enabled."; return false; } return true; } catch (Exception e) { TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, e, traceCategory); error = "Failed to process configuration."; } return false; } /////////////////////////////////////////////////////////////////// public static bool CheckRuntimeVersion( Configuration configuration, bool strict, ref string error ) { try { if (configuration == null) { error = "Invalid configuration."; return false; } // // NOTE: What version of the runtime was the core (primary) // assembly compiled against (e.g. "v2.0.50727" or // "v4.0.30319"). // string coreImageRuntimeVersion = GetImageRuntimeVersion( configuration.coreFileName); // // NOTE: We allow the actual image runtime checking to be // bypassed via the "-noRuntimeVersion" command line // option. The command line option is intended for // expert use only. // if (configuration.noRuntimeVersion) { TraceOps.DebugAndTrace(TracePriority.Medium, debugCallback, traceCallback, String.Format( "Assembly is compiled for the .NET Framework {0}; " + "however, installation restrictions based on this " + "fact have been disabled via the command line.", coreImageRuntimeVersion), traceCategory); return true; } // // TODO: Restrict the configuration based on which image // runtime versions (which more-or-less correspond // to .NET Framework versions) are supported by the // versions of Visual Studio that are installed. // if (String.IsNullOrEmpty(coreImageRuntimeVersion)) { error = "invalid core file image runtime version"; return false; } else if (String.Equals( coreImageRuntimeVersion, CLRv2ImageRuntimeVersion, StringComparison.InvariantCulture)) { // // NOTE: For the CLR v2.0 runtime, make sure we disable // any attempt to use it for things that require // an assembly compiled for the CLR v4.0. It is // uncertain if this is actually a problem in // practice as the CLR v4.0 can load and use an // assembly compiled with the CLR v2.0; however, // since this project offers both configurations, // we currently disallow this mismatch. // configuration.noNetFx40 = true; configuration.noVs2010 = true; TraceOps.DebugAndTrace(TracePriority.Medium, debugCallback, traceCallback, String.Format( "Assembly is compiled for the .NET Framework {0}, " + "support for .NET Framework {1} is now disabled.", CLRv2ImageRuntimeVersion, CLRv4ImageRuntimeVersion), traceCategory); } else if (String.Equals( coreImageRuntimeVersion, CLRv4ImageRuntimeVersion, StringComparison.InvariantCulture)) { // // NOTE: For the CLR v4.0 runtime, make sure we disable // any attempt to use it for things that require // an assembly compiled for the CLR v2.0. // configuration.noNetFx20 = true; configuration.noVs2008 = true; TraceOps.DebugAndTrace(TracePriority.Medium, debugCallback, traceCallback, String.Format( "Assembly is compiled for the .NET Framework {0}, " + "support for .NET Framework {1} is now disabled.", ForDisplay(CLRv4ImageRuntimeVersion), ForDisplay(CLRv2ImageRuntimeVersion)), traceCategory); } else { error = String.Format( "unsupported core file image runtime version " + "{0}, must be {1} or {2}", ForDisplay(coreImageRuntimeVersion), ForDisplay(CLRv2ImageRuntimeVersion), ForDisplay(CLRv4ImageRuntimeVersion)); return false; } return true; } catch (Exception e) { TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, e, traceCategory); error = "Failed to check image runtime version."; } return false; } #endregion /////////////////////////////////////////////////////////////////// #region Public Methods public bool HasFlags( InstallFlags hasFlags, bool all ) { if (all) return ((installFlags & hasFlags) == hasFlags); else return ((installFlags & hasFlags) != InstallFlags.None); } /////////////////////////////////////////////////////////////////// public void Dump( TraceCallback traceCallback ) { if (traceCallback != null) { traceCallback(String.Format(NameAndValueFormat, "Assembly", ForDisplay(assembly)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "LogFileName", ForDisplay(logFileName)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "Directory", ForDisplay(directory)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "CoreFileName", ForDisplay(coreFileName)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "LinqFileName", ForDisplay(linqFileName)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "DesignerFileName", ForDisplay(designerFileName)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "DebugFormat", ForDisplay(debugFormat)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "TraceFormat", ForDisplay(traceFormat)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "InstallFlags", ForDisplay(installFlags)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "DebugPriority", ForDisplay(debugPriority)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "TracePriority", ForDisplay(tracePriority)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "Install", ForDisplay(install)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoRuntimeVersion", ForDisplay(noRuntimeVersion)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoDesktop", ForDisplay(noDesktop)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoCompact", ForDisplay(noCompact)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoNetFx20", ForDisplay(noNetFx20)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoNetFx40", ForDisplay(noNetFx40)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoVs2008", ForDisplay(noVs2008)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoVs2010", ForDisplay(noVs2010)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoTrace", ForDisplay(noTrace)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoConsole", ForDisplay(noConsole)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "NoLog", ForDisplay(noLog)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "WhatIf", ForDisplay(whatIf)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "Verbose", ForDisplay(verbose)), traceCategory); traceCallback(String.Format(NameAndValueFormat, "Confirm", ForDisplay(confirm)), traceCategory); } } #endregion /////////////////////////////////////////////////////////////////// #region Public Properties private Assembly assembly; public Assembly Assembly { get { return assembly; } set { assembly = value; } } /////////////////////////////////////////////////////////////////// private string logFileName; public string LogFileName { get { return logFileName; } set { logFileName = value; } } /////////////////////////////////////////////////////////////////// private string directory; public string Directory { get { return directory; } set { directory = value; } } /////////////////////////////////////////////////////////////////// private string coreFileName; public string CoreFileName { get { return coreFileName; } set { coreFileName = value; } } /////////////////////////////////////////////////////////////////// private string linqFileName; public string LinqFileName { get { return linqFileName; } set { linqFileName = value; } } /////////////////////////////////////////////////////////////////// private string designerFileName; public string DesignerFileName { get { return designerFileName; } set { designerFileName = value; } } /////////////////////////////////////////////////////////////////// private string debugFormat; public string DebugFormat { get { return debugFormat; } set { debugFormat = value; } } /////////////////////////////////////////////////////////////////// private string traceFormat; public string TraceFormat { get { return traceFormat; } set { traceFormat = value; } } /////////////////////////////////////////////////////////////////// private InstallFlags installFlags; public InstallFlags InstallFlags { get { return installFlags; } set { installFlags = value; } } /////////////////////////////////////////////////////////////////// private TracePriority debugPriority; public TracePriority DebugPriority { get { return debugPriority; } set { debugPriority = value; } } /////////////////////////////////////////////////////////////////// private TracePriority tracePriority; public TracePriority TracePriority { get { return tracePriority; } set { tracePriority = value; } } /////////////////////////////////////////////////////////////////// private bool install; public bool Install { get { return install; } set { install = value; } } /////////////////////////////////////////////////////////////////// private bool noRuntimeVersion; public bool NoRuntimeVersion { get { return noRuntimeVersion; } set { noRuntimeVersion = value; } } /////////////////////////////////////////////////////////////////// private bool noDesktop; public bool NoDesktop { get { return noDesktop; } set { noDesktop = value; } } /////////////////////////////////////////////////////////////////// private bool noCompact; public bool NoCompact { get { return noCompact; } set { noCompact = value; } } /////////////////////////////////////////////////////////////////// private bool noNetFx20; public bool NoNetFx20 { get { return noNetFx20; } set { noNetFx20 = value; } } /////////////////////////////////////////////////////////////////// private bool noNetFx40; public bool NoNetFx40 { get { return noNetFx40; } set { noNetFx40 = value; } } /////////////////////////////////////////////////////////////////// private bool noVs2008; public bool NoVs2008 { get { return noVs2008; } set { noVs2008 = value; } } /////////////////////////////////////////////////////////////////// private bool noVs2010; public bool NoVs2010 { get { return noVs2010; } set { noVs2010 = value; } } /////////////////////////////////////////////////////////////////// private bool noTrace; public bool NoTrace { get { return noTrace; } set { noTrace = value; } } /////////////////////////////////////////////////////////////////// private bool noConsole; public bool NoConsole { get { return noConsole; } set { noConsole = value; } } /////////////////////////////////////////////////////////////////// private bool noLog; public bool NoLog { get { return noLog; } set { noLog = value; } } /////////////////////////////////////////////////////////////////// private bool whatIf; public bool WhatIf { get { return whatIf; } set { whatIf = value; } } /////////////////////////////////////////////////////////////////// private bool verbose; public bool Verbose { get { return verbose; } set { verbose = value; } } /////////////////////////////////////////////////////////////////// private bool confirm; public bool Confirm { get { return confirm; } set { confirm = value; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region FrameworkList Class private sealed class FrameworkList { #region Public Constructors public FrameworkList() { // do nothing. } #endregion /////////////////////////////////////////////////////////////////// #region Public Methods private RegistryKey rootKey; public RegistryKey RootKey { get { return rootKey; } set { rootKey = value; } } /////////////////////////////////////////////////////////////////// private StringList names; public StringList Names { get { return names; } set { names = value; } } /////////////////////////////////////////////////////////////////// private VersionMap versions; public VersionMap Versions { get { return versions; } set { versions = value; } } /////////////////////////////////////////////////////////////////// private StringList platformNames; public StringList PlatformNames { get { return platformNames; } set { platformNames = value; } } #endregion } #endregion /////////////////////////////////////////////////////////////////////// #region VsList Class private sealed class VsList { #region Public Constructors public VsList() { // do nothing. } #endregion /////////////////////////////////////////////////////////////////// #region Public Properties private RegistryKey rootKey; public RegistryKey RootKey { get { return rootKey; } set { rootKey = value; } } /////////////////////////////////////////////////////////////////// private VersionList versions; public VersionList Versions { get { return versions; } set { versions = value; } } /////////////////////////////////////////////////////////////////// private VersionListMap frameworkVersions; public VersionListMap FrameworkVersions { get { return frameworkVersions; } set { frameworkVersions = value; } } /////////////////////////////////////////////////////////////////// private VersionList installedVersions; public VersionList InstalledVersions { get { return installedVersions; } set { installedVersions = value; } } #endregion } #endregion #endregion /////////////////////////////////////////////////////////////////////// #region Private Constant Data private const string CoreFileName = "System.Data.SQLite.dll"; private const string LinqFileName = "System.Data.SQLite.Linq.dll"; private const string DesignerFileName = "SQLite.Designer.dll"; private const string ProviderName = "SQLite Data Provider"; private const string ProjectName = "System.Data.SQLite"; private const string LegacyProjectName = "SQLite"; private const string InvariantName = "System.Data.SQLite"; private const string FactoryTypeName = "System.Data.SQLite.SQLiteFactory"; private const string Description = ".NET Framework Data Provider for SQLite"; /////////////////////////////////////////////////////////////////////// private const string CLRv2ImageRuntimeVersion = "v2.0.50727"; private const string CLRv4ImageRuntimeVersion = "v4.0.30319"; /////////////////////////////////////////////////////////////////////// private const string NameAndValueFormat = "{0}: {1}"; private const string LogFileSuffix = ".log"; /////////////////////////////////////////////////////////////////////// private static readonly string VsIdFormat = "B"; /////////////////////////////////////////////////////////////////////// private static readonly string FrameworkKeyName = "Software\\Microsoft\\.NETFramework"; /////////////////////////////////////////////////////////////////////// private static readonly string FrameworkSdkKeyName = "Software\\Microsoft\\Microsoft SDKs\\.NETFramework"; /////////////////////////////////////////////////////////////////////// private static readonly string WindowsSdkKeyName = "Software\\Microsoft\\Microsoft SDKs\\Windows"; /////////////////////////////////////////////////////////////////////// private static readonly string XPathForAddElement = "configuration/system.data/DbProviderFactories/add[@invariant=\"{0}\"]"; private static readonly string XPathForRemoveElement = "configuration/system.data/DbProviderFactories/remove[@invariant=\"{0}\"]"; #endregion /////////////////////////////////////////////////////////////////////// #region Private Static Data private static Assembly thisAssembly = Assembly.GetExecutingAssembly(); private static string traceCategory = Path.GetFileName( thisAssembly.Location); /* NOTE: Same for debug and trace. */ private static TraceCallback debugCallback = AppDebug; private static TraceCallback traceCallback = AppTrace; #endregion /////////////////////////////////////////////////////////////////////// #region Trace Handling private static string GetLogFileName() { string result = Path.GetTempFileName(); /* throw */ File.Move(result, result + LogFileSuffix); /* throw */ result += LogFileSuffix; return result; } /////////////////////////////////////////////////////////////////////// private static void AppDebug( string message, string category ) { TraceOps.DebugCore(String.Format( TraceOps.DebugFormat, TraceOps.NextDebugId(), TraceOps.TimeStamp(DateTime.UtcNow), message), category); } /////////////////////////////////////////////////////////////////////// private static void AppTrace( string message, string category ) { TraceOps.TraceCore(String.Format( TraceOps.TraceFormat, TraceOps.NextTraceId(), TraceOps.TimeStamp(DateTime.UtcNow), message), category); } #endregion /////////////////////////////////////////////////////////////////////// #region Generic String Handling private static string ForDisplay( object value ) { if (value == null) return ""; string result; Type type = value.GetType(); if (type == typeof(XmlElement)) { XmlElement element = (XmlElement)value; result = element.OuterXml; } else if (type == typeof(Version)) { Version version = (Version)value; result = String.Format("v{0}", version); } else { result = value.ToString(); if (result.Length == 0) return ""; result = String.Format( type.IsSubclassOf(typeof(ValueType)) ? "{0}" : "\"{0}\"", result); } return result; } #endregion /////////////////////////////////////////////////////////////////////// #region .NET Framework Handling private static string GetImageRuntimeVersion( string fileName ) { try { Assembly assembly = Assembly.ReflectionOnlyLoadFrom(fileName); /* throw */ if (assembly != null) return assembly.ImageRuntimeVersion; } catch { // do nothing. } return null; } /////////////////////////////////////////////////////////////////////// private static string GetFrameworkDirectory( RegistryKey rootKey, Version frameworkVersion, bool whatIf, bool verbose ) { using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, FrameworkKeyName, false, whatIf, verbose)) { if (key == null) return null; object value = RegistryHelper.GetValue( key, "InstallRoot", null, whatIf, verbose); if (!(value is string)) return null; return Path.Combine( (string)value, String.Format("v{0}", frameworkVersion)); } } /////////////////////////////////////////////////////////////////////// private static string GetSdkBinaryFileName( RegistryKey rootKey, string fileName, bool whatIf, bool verbose ) { StringDictionary results = new StringDictionary(); string[] keyNames = { FrameworkKeyName, FrameworkSdkKeyName, WindowsSdkKeyName }; string[] valueNames = { "sdkInstallRootv2.0", "InstallationFolder", "InstallationFolder" }; bool[] useSubKeys = { false, true, true }; for (int index = 0; index < keyNames.Length; index++) { using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyNames[index], false, whatIf, verbose)) { if (key == null) continue; if (useSubKeys[index]) { foreach (string subKeyName in RegistryHelper.GetSubKeyNames( key, whatIf, verbose)) { using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, subKeyName, false, whatIf, verbose)) { if (subKey == null) continue; object value = RegistryHelper.GetValue( subKey, valueNames[index], null, whatIf, verbose); if (!(value is string)) continue; string path = (string)value; if (!Directory.Exists(path)) continue; path = Path.Combine(path, "bin"); if (!Directory.Exists(path)) continue; if (String.IsNullOrEmpty(fileName)) { results.Add(subKey.Name, path); continue; } path = Path.Combine(path, fileName); if (File.Exists(path)) results.Add(subKey.Name, path); } } } else { object value = RegistryHelper.GetValue( key, valueNames[index], null, whatIf, verbose); if (!(value is string)) continue; string path = (string)value; if (!Directory.Exists(path)) continue; path = Path.Combine(path, "bin"); if (!Directory.Exists(path)) continue; if (String.IsNullOrEmpty(fileName)) { results.Add(key.Name, path); continue; } path = Path.Combine(path, fileName); if (File.Exists(path)) results.Add(key.Name, path); } } } // // NOTE: If we found some results, return the last (latest) one. // if (results.Count > 0) return results[new StringList(results.Keys)[results.Count - 1]]; return null; } #endregion /////////////////////////////////////////////////////////////////////// #region Per-Framework/Platform Handling private static void InitializeFrameworkList( Configuration configuration, ref FrameworkList frameworkList ) { if (frameworkList == null) frameworkList = new FrameworkList(); if (frameworkList.RootKey == null) frameworkList.RootKey = Registry.LocalMachine; /////////////////////////////////////////////////////////////////// if (frameworkList.Names == null) { frameworkList.Names = new StringList(); if ((configuration == null) || !configuration.NoDesktop) frameworkList.Names.Add(".NETFramework"); if ((configuration == null) || !configuration.NoCompact) { frameworkList.Names.Add(".NETCompactFramework"); frameworkList.Names.Add(".NETCompactFramework"); frameworkList.Names.Add(".NETCompactFramework"); } } /////////////////////////////////////////////////////////////////// if (frameworkList.Versions == null) { frameworkList.Versions = new VersionMap(); if ((configuration == null) || !configuration.NoDesktop) { VersionList desktopVersionList = new VersionList(); if ((configuration == null) || !configuration.NoNetFx20) desktopVersionList.Add(new Version(2, 0, 50727)); if ((configuration == null) || !configuration.NoNetFx40) desktopVersionList.Add(new Version(4, 0, 30319)); frameworkList.Versions.Add(".NETFramework", desktopVersionList); } if ((configuration == null) || !configuration.NoCompact) { frameworkList.Versions.Add(".NETCompactFramework", new VersionList(new Version[] { new Version(2, 0, 0, 0), new Version(3, 5, 0, 0) })); } } /////////////////////////////////////////////////////////////////// if (frameworkList.PlatformNames == null) { frameworkList.PlatformNames = new StringList(); if ((configuration == null) || !configuration.NoDesktop) frameworkList.PlatformNames.Add(null); if ((configuration == null) || !configuration.NoCompact) { frameworkList.PlatformNames.Add("PocketPC"); frameworkList.PlatformNames.Add("Smartphone"); frameworkList.PlatformNames.Add("WindowsCE"); } } } /////////////////////////////////////////////////////////////////////// private static bool HaveFramework( RegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, bool whatIf, bool verbose ) { string format = !String.IsNullOrEmpty(platformName) ? "Software\\Microsoft\\{0}\\v{1}\\{2}" : "Software\\Microsoft\\{0}\\v{1}"; string keyName = String.Format( format, frameworkName, frameworkVersion, platformName); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) return false; if (platformName != null) // NOTE: Skip non-desktop. return true; string directory = GetFrameworkDirectory( rootKey, frameworkVersion, whatIf, verbose); if (String.IsNullOrEmpty(directory)) return false; if (!Directory.Exists(directory)) return false; return true; } } /////////////////////////////////////////////////////////////////////// private static bool ForEachFrameworkConfig( FrameworkList frameworkList, FrameworkConfigCallback callback, string invariant, string name, string description, string typeName, AssemblyName assemblyName, object clientData, bool whatIf, bool verbose, ref bool saved, ref string error ) { if (frameworkList == null) { error = "invalid framework list"; return false; } RegistryKey rootKey = frameworkList.RootKey; if (rootKey == null) { error = "invalid root key"; return false; } if (!Object.ReferenceEquals(rootKey, Registry.CurrentUser) && !Object.ReferenceEquals(rootKey, Registry.LocalMachine)) { error = "root key must be per-user or per-machine"; return false; } if (frameworkList.Names == null) { error = "no framework names found"; return false; } if (frameworkList.Versions == null) { error = "no framework versions found"; return false; } if (frameworkList.PlatformNames == null) { error = "no platform names found"; return false; } if (frameworkList.Names.Count != frameworkList.PlatformNames.Count) { error = String.Format("framework name count {0} does not " + "match platform name count {1}", frameworkList.Names.Count, frameworkList.PlatformNames.Count); return false; } for (int index = 0; index < frameworkList.Names.Count; index++) { // // NOTE: Grab the name of the framework (e.g. ".NETFramework") // and the name of the platform (e.g. "WindowsCE"). // string frameworkName = frameworkList.Names[index]; string platformName = frameworkList.PlatformNames[index]; // // NOTE: Skip all non-desktop frameworks (i.e. if the platform // name is not null). // if (platformName != null) continue; // // NOTE: Grab the supported versions of this particular // framework. // VersionList frameworkVersionList; if (!frameworkList.Versions.TryGetValue( frameworkName, out frameworkVersionList) || (frameworkVersionList == null)) { continue; } foreach (Version frameworkVersion in frameworkVersionList) { TraceOps.DebugAndTrace(TracePriority.Lower, debugCallback, traceCallback, String.Format( "frameworkName = {0}, frameworkVersion = {1}, " + "platformName = {2}", ForDisplay(frameworkName), ForDisplay(frameworkVersion), ForDisplay(platformName)), traceCategory); if (!HaveFramework( rootKey, frameworkName, frameworkVersion, platformName, whatIf, verbose)) { TraceOps.DebugAndTrace(TracePriority.Low, debugCallback, traceCallback, ".NET Framework not found, skipping...", traceCategory); continue; } if (callback == null) continue; string directory = GetFrameworkDirectory( rootKey, frameworkVersion, whatIf, verbose); if (String.IsNullOrEmpty(directory)) { TraceOps.DebugAndTrace(TracePriority.Low, debugCallback, traceCallback, String.Format( ".NET Framework {0} directory is invalid, " + "skipping...", ForDisplay(frameworkVersion)), traceCategory); continue; } directory = Path.Combine(directory, "Config"); if (!Directory.Exists(directory)) { TraceOps.DebugAndTrace(TracePriority.Low, debugCallback, traceCallback, String.Format( ".NET Framework {0} directory {1} does not exist, " + "skipping...", ForDisplay(frameworkVersion), ForDisplay(directory)), traceCategory); continue; } string fileName = Path.Combine(directory, "machine.config"); if (!File.Exists(fileName)) { TraceOps.DebugAndTrace(TracePriority.Low, debugCallback, traceCallback, String.Format( ".NET Framework {0} file {1} does not exist, " + "skipping...", ForDisplay(frameworkVersion), ForDisplay(fileName)), traceCategory); continue; } bool localSaved = false; if (!callback( fileName, invariant, name, description, typeName, assemblyName, clientData, whatIf, verbose, ref localSaved, ref error)) { return false; } else { if (localSaved && !saved) saved = true; if (verbose) TraceOps.DebugAndTrace(TracePriority.Lowest, debugCallback, traceCallback, String.Format( "localSaved = {0}, saved = {1}", ForDisplay(localSaved), ForDisplay(saved)), traceCategory); } } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ForEachFrameworkRegistry( FrameworkList frameworkList, FrameworkRegistryCallback callback, object clientData, bool whatIf, bool verbose, ref string error ) { if (frameworkList == null) { error = "invalid framework list"; return false; } RegistryKey rootKey = frameworkList.RootKey; if (rootKey == null) { error = "invalid root key"; return false; } if (!Object.ReferenceEquals(rootKey, Registry.CurrentUser) && !Object.ReferenceEquals(rootKey, Registry.LocalMachine)) { error = "root key must be per-user or per-machine"; return false; } if (frameworkList.Names == null) { error = "no framework names found"; return false; } if (frameworkList.Versions == null) { error = "no framework versions found"; return false; } if (frameworkList.PlatformNames == null) { error = "no platform names found"; return false; } if (frameworkList.Names.Count != frameworkList.PlatformNames.Count) { error = String.Format("framework name count {0} does not " + "match platform name count {1}", frameworkList.Names.Count, frameworkList.PlatformNames.Count); return false; } for (int index = 0; index < frameworkList.Names.Count; index++) { // // NOTE: Grab the name of the framework (e.g. ".NETFramework") // and the name of the platform (e.g. "WindowsCE"). // string frameworkName = frameworkList.Names[index]; string platformName = frameworkList.PlatformNames[index]; // // NOTE: Grab the supported versions of this particular // framework. // VersionList frameworkVersionList; if (!frameworkList.Versions.TryGetValue( frameworkName, out frameworkVersionList) || (frameworkVersionList == null)) { continue; } foreach (Version frameworkVersion in frameworkVersionList) { TraceOps.DebugAndTrace(TracePriority.Lower, debugCallback, traceCallback, String.Format( "frameworkName = {0}, frameworkVersion = {1}, " + "platformName = {2}", ForDisplay(frameworkName), ForDisplay(frameworkVersion), ForDisplay(platformName)), traceCategory); if (!HaveFramework( rootKey, frameworkName, frameworkVersion, platformName, whatIf, verbose)) { TraceOps.DebugAndTrace(TracePriority.Low, debugCallback, traceCallback, ".NET Framework not found, skipping...", traceCategory); continue; } if (callback == null) continue; if (!callback( rootKey, frameworkName, frameworkVersion, platformName, clientData, whatIf, verbose, ref error)) { return false; } } } return true; } #endregion /////////////////////////////////////////////////////////////////////// #region Per-Visual Studio Version Handling private static void InitializeVsList( Configuration configuration, ref VsList vsList ) { if (vsList == null) vsList = new VsList(); if (vsList.RootKey == null) vsList.RootKey = Registry.LocalMachine; if (vsList.Versions == null) { vsList.Versions = new VersionList(); // vsList.Versions.Add(new Version(8, 0)); // Visual Studio 2005 if ((configuration == null) || !configuration.NoVs2008) vsList.Versions.Add(new Version(9, 0)); // Visual Studio 2008 if ((configuration == null) || !configuration.NoVs2010) vsList.Versions.Add(new Version(10, 0));// Visual Studio 2010 } if (vsList.FrameworkVersions == null) { vsList.FrameworkVersions = new VersionListMap(); // vsList.FrameworkVersions.Add(new Version(8, 0), // new VersionList(new Version[] { // new Version(2, 0, 50727) })); if ((configuration == null) || !configuration.NoVs2008) vsList.FrameworkVersions.Add(new Version(9, 0), new VersionList(new Version[] { new Version(2, 0, 50727) })); if ((configuration == null) || !configuration.NoVs2010) vsList.FrameworkVersions.Add(new Version(10, 0), new VersionList(new Version[] { new Version(2, 0, 50727), new Version(4, 0, 30319) })); } } /////////////////////////////////////////////////////////////////////// private static bool HaveVsVersion( RegistryKey rootKey, Version vsVersion, bool whatIf, bool verbose ) { if (vsVersion == null) return false; string format = "Software\\Microsoft\\VisualStudio\\{0}"; string keyName = String.Format(format, vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) return false; object value = RegistryHelper.GetValue( key, "InstallDir", null, whatIf, verbose); if (!(value is string)) return false; string directory = (string)value; if (String.IsNullOrEmpty(directory)) return false; if (!Directory.Exists(directory)) return false; return true; } } /////////////////////////////////////////////////////////////////////// private static bool ForEachVsVersionRegistry( VsList vsList, VisualStudioRegistryCallback callback, Package package, object clientData, bool whatIf, bool verbose, ref string error ) { if (vsList == null) { error = "invalid VS list"; return false; } RegistryKey rootKey = vsList.RootKey; if (rootKey == null) { error = "invalid root key"; return false; } if (!Object.ReferenceEquals(rootKey, Registry.CurrentUser) && !Object.ReferenceEquals(rootKey, Registry.LocalMachine)) { error = "root key must be per-user or per-machine"; return false; } if (vsList.Versions == null) { error = "no VS versions found"; return false; } foreach (Version vsVersion in vsList.Versions) { TraceOps.DebugAndTrace(TracePriority.Lower, debugCallback, traceCallback, String.Format( "vsVersion = {0}", ForDisplay(vsVersion)), traceCategory); if (!HaveVsVersion(rootKey, vsVersion, whatIf, verbose)) { TraceOps.DebugAndTrace(TracePriority.Low, debugCallback, traceCallback, "Visual Studio version not found, skipping...", traceCategory); continue; } if (callback == null) continue; if (!callback( rootKey, vsVersion, package, clientData, whatIf, verbose, ref error)) { return false; } } return true; } #endregion /////////////////////////////////////////////////////////////////////// #region Configuration File Handling private static bool AddDbProviderFactory( string fileName, string invariant, string name, string description, string typeName, AssemblyName assemblyName, bool whatIf, bool verbose, ref bool saved, ref string error ) { bool dirty = false; XmlDocument document = new XmlDocument(); document.PreserveWhitespace = true; document.Load(fileName); XmlElement element = document.SelectSingleNode(String.Format( XPathForAddElement, invariant)) as XmlElement; if (element == null) { string[] elementNames = { "system.data", "DbProviderFactories" }; XmlElement previousElement = document.DocumentElement; /* configuration */ foreach (string elementName in elementNames) { element = previousElement.SelectSingleNode( elementName) as XmlElement; if (element == null) { element = document.CreateElement( elementName, String.Empty); previousElement.AppendChild(element); } previousElement = element; } element = document.CreateElement( "add", String.Empty); previousElement.AppendChild(element); dirty = true; } if (!String.Equals(element.GetAttribute("name"), name, StringComparison.InvariantCulture)) { element.SetAttribute("name", name); dirty = true; } if (!String.Equals(element.GetAttribute("invariant"), invariant, StringComparison.InvariantCulture)) { element.SetAttribute("invariant", invariant); dirty = true; } if (!String.Equals(element.GetAttribute("description"), description, StringComparison.InvariantCulture)) { element.SetAttribute("description", description); dirty = true; } string fullTypeName = String.Format("{0}, {1}", typeName, assemblyName); if (!String.Equals(element.GetAttribute("type"), fullTypeName, StringComparison.InvariantCulture)) { element.SetAttribute("type", fullTypeName); dirty = true; } if (dirty || whatIf) { if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "element = {0}", ForDisplay(element)), traceCategory); if (!whatIf) document.Save(fileName); saved = true; } return true; } /////////////////////////////////////////////////////////////////////// private static bool RemoveDbProviderFactory( string fileName, string invariant, bool whatIf, bool verbose, ref bool saved, ref string error ) { bool dirty = false; XmlDocument document = new XmlDocument(); document.PreserveWhitespace = true; document.Load(fileName); XmlElement element = document.SelectSingleNode(String.Format( XPathForAddElement, invariant)) as XmlElement; if (element != null) { element.ParentNode.RemoveChild(element); dirty = true; } element = document.SelectSingleNode(String.Format( XPathForRemoveElement, invariant)) as XmlElement; if (element != null) { element.ParentNode.RemoveChild(element); dirty = true; } if (dirty || whatIf) { if (verbose) TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "element = {0}", ForDisplay(element)), traceCategory); if (!whatIf) document.Save(fileName); saved = true; } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessDbProviderFactory( string fileName, string invariant, string name, string description, string typeName, AssemblyName assemblyName, object clientData, bool whatIf, bool verbose, ref bool saved, ref string error ) { AnyPair pair = clientData as AnyPair; if (pair == null) { error = "invalid framework config callback data"; return false; } if (pair.Y) { return RemoveDbProviderFactory( fileName, invariant, whatIf, verbose, ref saved, ref error) && AddDbProviderFactory( fileName, invariant, name, description, typeName, assemblyName, whatIf, verbose, ref saved, ref error); } else { return RemoveDbProviderFactory( fileName, invariant, whatIf, verbose, ref saved, ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Assembly Folders Handling private static string GetAssemblyFoldersKeyName( string frameworkName, Version frameworkVersion, string platformName ) { string format = !String.IsNullOrEmpty(platformName) ? "Software\\Microsoft\\{0}\\v{1}\\{2}\\AssemblyFoldersEx" : "Software\\Microsoft\\{0}\\v{1}\\AssemblyFoldersEx"; return String.Format(format, frameworkName, frameworkVersion, platformName); } /////////////////////////////////////////////////////////////////////// private static bool AddToAssemblyFolders( RegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, string subKeyName, string directory, bool whatIf, bool verbose, ref string error ) { string keyName = GetAssemblyFoldersKeyName( frameworkName, frameworkVersion, platformName); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, true, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.CreateSubKey( key, subKeyName, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", key, subKeyName); return false; } RegistryHelper.SetValue( subKey, null, directory, whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool RemoveFromAssemblyFolders( RegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, string subKeyName, bool whatIf, bool verbose, ref string error ) { string keyName = GetAssemblyFoldersKeyName( frameworkName, frameworkVersion, platformName); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } RegistryHelper.DeleteSubKey( key, subKeyName, whatIf, verbose); } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessAssemblyFolders( RegistryKey rootKey, string frameworkName, Version frameworkVersion, string platformName, object clientData, bool whatIf, bool verbose, ref string error ) { AnyPair pair = clientData as AnyPair; if (pair == null) { error = "invalid framework callback data"; return false; } if (pair.Y) { return RemoveFromAssemblyFolders( rootKey, frameworkName, frameworkVersion, platformName, LegacyProjectName, whatIf, verbose, ref error) && AddToAssemblyFolders( rootKey, frameworkName, frameworkVersion, platformName, ProjectName, pair.X, whatIf, verbose, ref error); } else { return RemoveFromAssemblyFolders( rootKey, frameworkName, frameworkVersion, platformName, ProjectName, whatIf, verbose, ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Handling private static string GetVsKeyName( Version vsVersion ) { if (vsVersion == null) return null; return String.Format("Software\\Microsoft\\VisualStudio\\{0}", vsVersion); } /////////////////////////////////////////////////////////////////////// #region Visual Studio Version Handling private static bool AddVsVersion( RegistryKey rootKey, Version vsVersion, Package package, object clientData, bool whatIf, bool verbose, ref string error ) { if (vsVersion != null) { VsList vsList = clientData as VsList; if (vsList != null) { if (vsList.InstalledVersions == null) vsList.InstalledVersions = new VersionList(); if (!vsList.InstalledVersions.Contains(vsVersion)) vsList.InstalledVersions.Add(vsVersion); } } return true; } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Data Source Handling private static bool AddVsDataSource( RegistryKey rootKey, Version vsVersion, Package package, bool whatIf, bool verbose, ref string error ) { if (vsVersion == null) { error = "invalid VS version"; return false; } if (package == null) { error = "invalid VS package"; return false; } string keyName = GetVsKeyName(vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "DataSources", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\DataSources", key); return false; } using (MockRegistryKey dataSourceKey = RegistryHelper.CreateSubKey(subKey, package.DataSourceId.ToString(VsIdFormat), whatIf, verbose)) { if (dataSourceKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", key, package.DataSourceId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue( dataSourceKey, null, String.Format( "{0} Database File", ProjectName), whatIf, verbose); RegistryHelper.CreateSubKey(dataSourceKey, String.Format("SupportingProviders\\{0}", package.DataProviderId.ToString(VsIdFormat)), whatIf, verbose); } } } return true; } /////////////////////////////////////////////////////////////////////// private static bool RemoveVsDataSource( RegistryKey rootKey, Version vsVersion, Package package, bool whatIf, bool verbose, ref string error ) { if (vsVersion == null) { error = "invalid VS version"; return false; } if (package == null) { error = "invalid VS package"; return false; } string keyName = GetVsKeyName(vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "DataSources", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\DataSources", key); return false; } RegistryHelper.DeleteSubKeyTree( subKey, package.DataSourceId.ToString(VsIdFormat), whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessVsDataSource( RegistryKey rootKey, Version vsVersion, Package package, object clientData, bool whatIf, bool verbose, ref string error ) { if (package == null) { error = "invalid VS package"; return false; } AnyPair pair = clientData as AnyPair; if (pair == null) { error = "invalid VS callback data"; return false; } if (pair.Y) { return AddVsDataSource( rootKey, vsVersion, package, whatIf, verbose, ref error); } else { return RemoveVsDataSource( rootKey, vsVersion, package, whatIf, verbose, ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Data Provider Handling private static bool AddVsDataProvider( RegistryKey rootKey, Version vsVersion, Package package, string fileName, bool whatIf, bool verbose, ref string error ) { if (vsVersion == null) { error = "invalid VS version"; return false; } if (package == null) { error = "invalid VS package"; return false; } string keyName = GetVsKeyName(vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "DataProviders", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\DataProviders", key); return false; } using (MockRegistryKey dataProviderKey = RegistryHelper.CreateSubKey(subKey, package.DataProviderId.ToString(VsIdFormat), whatIf, verbose)) { if (dataProviderKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", key, package.DataProviderId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue( dataProviderKey, null, Description, whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "InvariantName", InvariantName, whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "Technology", package.AdoNetTechnologyId.ToString(VsIdFormat), whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "CodeBase", fileName, whatIf, verbose); RegistryHelper.SetValue( dataProviderKey, "FactoryService", package.ServiceId.ToString(VsIdFormat), whatIf, verbose); RegistryHelper.CreateSubKey(dataProviderKey, "SupportedObjects\\DataConnectionUIControl", whatIf, verbose); RegistryHelper.CreateSubKey(dataProviderKey, "SupportedObjects\\DataConnectionProperties", whatIf, verbose); RegistryHelper.CreateSubKey(dataProviderKey, "SupportedObjects\\DataConnectionSupport", whatIf, verbose); RegistryHelper.CreateSubKey(dataProviderKey, "SupportedObjects\\DataObjectSupport", whatIf, verbose); RegistryHelper.CreateSubKey(dataProviderKey, "SupportedObjects\\DataViewSupport", whatIf, verbose); } } } return true; } /////////////////////////////////////////////////////////////////////// private static bool RemoveVsDataProvider( RegistryKey rootKey, Version vsVersion, Package package, bool whatIf, bool verbose, ref string error ) { if (vsVersion == null) { error = "invalid VS version"; return false; } string keyName = GetVsKeyName(vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "DataProviders", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\DataProviders", key); return false; } RegistryHelper.DeleteSubKeyTree( subKey, package.DataProviderId.ToString(VsIdFormat), whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessVsDataProvider( RegistryKey rootKey, Version vsVersion, Package package, object clientData, bool whatIf, bool verbose, ref string error ) { AnyPair pair = clientData as AnyPair; if (pair == null) { error = "invalid VS callback data"; return false; } if (pair.Y) { return AddVsDataProvider( rootKey, vsVersion, package, pair.X, whatIf, verbose, ref error); } else { return RemoveVsDataProvider( rootKey, vsVersion, package, whatIf, verbose, ref error); } } #endregion /////////////////////////////////////////////////////////////////////// #region Visual Studio Package Handling private static void InitializeVsPackage( ref Package package ) { if (package == null) { package = new Package(); package.AdoNetTechnologyId = new Guid( "77AB9A9D-78B9-4BA7-91AC-873F5338F1D2"); package.PackageId = new Guid( "DCBE6C8D-0E57-4099-A183-98FF74C64D9C"); package.ServiceId = new Guid( "DCBE6C8D-0E57-4099-A183-98FF74C64D9D"); package.DataSourceId = new Guid( "0EBAAB6E-CA80-4B4A-8DDF-CBE6BF058C71"); package.DataProviderId = new Guid( "0EBAAB6E-CA80-4B4A-8DDF-CBE6BF058C70"); } } /////////////////////////////////////////////////////////////////////// private static bool AddVsPackage( RegistryKey rootKey, Version vsVersion, Package package, string fileName, bool whatIf, bool verbose, ref string error ) { if (vsVersion == null) { error = "invalid VS version"; return false; } if (package == null) { error = "invalid VS package"; return false; } string keyName = GetVsKeyName(vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Packages", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\Packages", key); return false; } using (MockRegistryKey packageKey = RegistryHelper.CreateSubKey( subKey, package.PackageId.ToString(VsIdFormat), whatIf, verbose)) { if (packageKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", key, package.PackageId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue(packageKey, null, String.Format( "{0} Designer Package", ProjectName), whatIf, verbose); RegistryHelper.SetValue(packageKey, "Class", "SQLite.Designer.SQLitePackage", whatIf, verbose); RegistryHelper.SetValue(packageKey, "CodeBase", fileName, whatIf, verbose); RegistryHelper.SetValue(packageKey, "ID", 400, whatIf, verbose); RegistryHelper.SetValue(packageKey, "InprocServer32", Path.Combine(Environment.SystemDirectory, "mscoree.dll"), whatIf, verbose); RegistryHelper.SetValue(packageKey, "CompanyName", "http://system.data.sqlite.org/", whatIf, verbose); RegistryHelper.SetValue(packageKey, "MinEdition", "standard", whatIf, verbose); RegistryHelper.SetValue(packageKey, "ProductName", String.Format("{0} Designer Package", ProjectName), whatIf, verbose); RegistryHelper.SetValue(packageKey, "ProductVersion", "1.0", whatIf, verbose); using (MockRegistryKey toolboxKey = RegistryHelper.CreateSubKey( packageKey, "Toolbox", whatIf, verbose)) { if (toolboxKey == null) { error = String.Format( "could not create registry key: {0}\\Toolbox", packageKey); return false; } RegistryHelper.SetValue( toolboxKey, "Default Items", 3, whatIf, verbose); } } } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Menus", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\Menus", key); return false; } RegistryHelper.SetValue( subKey, package.PackageId.ToString(VsIdFormat), ", 1000, 3", whatIf, verbose); } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Services", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\Services", key); return false; } using (MockRegistryKey serviceKey = RegistryHelper.CreateSubKey( subKey, package.ServiceId.ToString(VsIdFormat), whatIf, verbose)) { if (serviceKey == null) { error = String.Format( "could not create registry key: {0}\\{1}", key, package.ServiceId.ToString(VsIdFormat)); return false; } RegistryHelper.SetValue(serviceKey, null, package.PackageId.ToString(VsIdFormat), whatIf, verbose); RegistryHelper.SetValue(serviceKey, "Name", String.Format("{0} Designer Service", ProjectName), whatIf, verbose); } } } return true; } /////////////////////////////////////////////////////////////////////// private static bool RemoveVsPackage( RegistryKey rootKey, Version vsVersion, Package package, bool whatIf, bool verbose, ref string error ) { if (vsVersion == null) { error = "invalid VS version"; return false; } if (package == null) { error = "invalid VS package"; return false; } string keyName = GetVsKeyName(vsVersion); using (MockRegistryKey key = RegistryHelper.OpenSubKey( rootKey, keyName, false, whatIf, verbose)) { if (key == null) { error = String.Format( "could not open registry key: {0}\\{1}", rootKey, keyName); return false; } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Packages", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\Packages", key); return false; } RegistryHelper.DeleteSubKeyTree( key, package.PackageId.ToString(VsIdFormat), whatIf, verbose); } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Menus", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\Menus", key); return false; } RegistryHelper.DeleteValue( subKey, package.PackageId.ToString(VsIdFormat), whatIf, verbose); } using (MockRegistryKey subKey = RegistryHelper.OpenSubKey( key, "Services", true, whatIf, verbose)) { if (subKey == null) { error = String.Format( "could not open registry key: {0}\\Services", key); return false; } RegistryHelper.DeleteSubKeyTree( subKey, package.ServiceId.ToString(VsIdFormat), whatIf, verbose); } } return true; } /////////////////////////////////////////////////////////////////////// private static bool ProcessVsPackage( RegistryKey rootKey, Version vsVersion, Package package, object clientData, bool whatIf, bool verbose, ref string error ) { AnyPair pair = clientData as AnyPair; if (pair == null) { error = "invalid VS callback data"; return false; } if (pair.Y) { return AddVsPackage( rootKey, vsVersion, package, pair.X, whatIf, verbose, ref error); } else { return RemoveVsPackage( rootKey, vsVersion, package, whatIf, verbose, ref error); } } #endregion #endregion /////////////////////////////////////////////////////////////////////// #region Application Entry Point private static int Main( string[] args ) { Configuration configuration = null; string error = null; /////////////////////////////////////////////////////////////////// #region Command Line Processing if (!Configuration.FromArgs( args, true, ref configuration, ref error) || !Configuration.Process( args, configuration, true, ref error) || !Configuration.CheckRuntimeVersion( configuration, true, ref error)) { TraceOps.ShowMessage(TracePriority.Highest, debugCallback, traceCallback, thisAssembly, error, traceCategory, MessageBoxButtons.OK, MessageBoxIcon.Error); return 1; /* FAILURE */ } #endregion /////////////////////////////////////////////////////////////////// #region .NET Framework / Visual Studio Data Package package = null; FrameworkList frameworkList = null; VsList vsList = null; /////////////////////////////////////////////////////////////////// InitializeVsPackage(ref package); InitializeFrameworkList(configuration, ref frameworkList); InitializeVsList(configuration, ref vsList); #endregion /////////////////////////////////////////////////////////////////// AssemblyName assemblyName = AssemblyName.GetAssemblyName( configuration.CoreFileName); /* throw */ /////////////////////////////////////////////////////////////////// AnyPair directoryPair = new AnyPair( configuration.Directory, configuration.Install); AnyPair fileNamePair = new AnyPair( configuration.DesignerFileName, configuration.Install); /////////////////////////////////////////////////////////////////// #region .NET GAC Install/Remove if (configuration.HasFlags(InstallFlags.GAC, true)) { Publish publish = null; if (!configuration.WhatIf) publish = new Publish(); if (configuration.Install) { if (!configuration.WhatIf) publish.GacInstall(configuration.CoreFileName); /* throw */ TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "GacInstall: assemblyPath = {0}", ForDisplay(configuration.CoreFileName)), traceCategory); if (!configuration.WhatIf) publish.GacInstall(configuration.LinqFileName); /* throw */ TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "GacInstall: assemblyPath = {0}", ForDisplay(configuration.LinqFileName)), traceCategory); } else { if (!configuration.WhatIf) publish.GacRemove(configuration.LinqFileName); /* throw */ TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "GacRemove: assemblyPath = {0}", ForDisplay(configuration.LinqFileName)), traceCategory); if (!configuration.WhatIf) publish.GacRemove(configuration.CoreFileName); /* throw */ TraceOps.DebugAndTrace(TracePriority.Highest, debugCallback, traceCallback, String.Format( "GacRemove: assemblyPath = {0}", ForDisplay(configuration.CoreFileName)), traceCategory); } } #endregion /////////////////////////////////////////////////////////////////// #region .NET AssemblyFolders if (configuration.HasFlags(InstallFlags.AssemblyFolders, true)) { if (!ForEachFrameworkRegistry( frameworkList, ProcessAssemblyFolders, directoryPair, configuration.WhatIf, configuration.Verbose, ref error)) { TraceOps.ShowMessage(TracePriority.Highest, debugCallback, traceCallback, thisAssembly, error, traceCategory, MessageBoxButtons.OK, MessageBoxIcon.Error); return 1; /* FAILURE */ } } #endregion /////////////////////////////////////////////////////////////////// #region .NET DbProviderFactory if (configuration.HasFlags(InstallFlags.DbProviderFactory, true)) { bool saved = false; if (!ForEachFrameworkConfig( frameworkList, ProcessDbProviderFactory, InvariantName, ProviderName, Description, FactoryTypeName, assemblyName, directoryPair, configuration.WhatIf, configuration.Verbose, ref saved, ref error)) { TraceOps.ShowMessage(TracePriority.Highest, debugCallback, traceCallback, thisAssembly, error, traceCategory, MessageBoxButtons.OK, MessageBoxIcon.Error); return 1; /* FAILURE */ } } #endregion /////////////////////////////////////////////////////////////////// #region VS Package if (configuration.HasFlags(InstallFlags.VsPackage, true)) { if (!ForEachVsVersionRegistry( vsList, ProcessVsPackage, package, fileNamePair, configuration.WhatIf, configuration.Verbose, ref error)) { TraceOps.ShowMessage(TracePriority.Highest, debugCallback, traceCallback, thisAssembly, error, traceCategory, MessageBoxButtons.OK, MessageBoxIcon.Error); return 1; /* FAILURE */ } } #endregion /////////////////////////////////////////////////////////////////// #region VS DataSource if (configuration.HasFlags(InstallFlags.VsDataSource, true)) { if (!ForEachVsVersionRegistry( vsList, ProcessVsDataSource, package, fileNamePair, configuration.WhatIf, configuration.Verbose, ref error)) { TraceOps.ShowMessage(TracePriority.Highest, debugCallback, traceCallback, thisAssembly, error, traceCategory, MessageBoxButtons.OK, MessageBoxIcon.Error); return 1; /* FAILURE */ } } #endregion /////////////////////////////////////////////////////////////////// #region VS DataProvider if (configuration.HasFlags(InstallFlags.VsDataProvider, true)) { if (!ForEachVsVersionRegistry( vsList, ProcessVsDataProvider, package, fileNamePair, configuration.WhatIf, configuration.Verbose, ref error)) { TraceOps.ShowMessage(TracePriority.Highest, debugCallback, traceCallback, thisAssembly, error, traceCategory, MessageBoxButtons.OK, MessageBoxIcon.Error); return 1; /* FAILURE */ } } #endregion /////////////////////////////////////////////////////////////////// #region Log Summary TraceOps.DebugAndTrace(TracePriority.Higher, debugCallback, traceCallback, String.Format( "subKeysCreated = {0}, subKeysDeleted = {1}, " + "keyValuesSet = {2}, keyValuesDeleted = {3}", ForDisplay(RegistryHelper.SubKeysCreated), ForDisplay(RegistryHelper.SubKeysDeleted), ForDisplay(RegistryHelper.KeyValuesSet), ForDisplay(RegistryHelper.KeyValuesDeleted)), traceCategory); #endregion /////////////////////////////////////////////////////////////////// return 0; /* SUCCESS */ } #endregion } #endregion }