System.Data.SQLite
Check-in [297b44b1ef]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhance the design-time components installer to support creating a log file of all registry write operations.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 297b44b1efd98e05b43f43a516392e724a6a02d5
User & Date: mistachkin 2017-12-06 22:38:29
Context
2017-12-06
22:47
Coding style improvements to the design-time components installer. check-in: 9e4a7c8f46 user: mistachkin tags: trunk
22:38
Enhance the design-time components installer to support creating a log file of all registry write operations. check-in: 297b44b1ef user: mistachkin tags: trunk
22:32
Fix typo. Fix RegistryKey lifetime issue. Cleanup output format and improve code. Closed-Leaf check-in: a5dd05dc21 user: mistachkin tags: designTimeInstaller
2017-12-04
18:01
Minor corrections to test suite infrastructure. check-in: 8314652c94 user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to tools/install/Installer.cs.

   485    485               {
   486    486                   return dateTime.ToString(Iso8601DateTimeOutputFormat);
   487    487               }
   488    488   
   489    489               ///////////////////////////////////////////////////////////////////
   490    490   
   491    491               [MethodImpl(MethodImplOptions.NoInlining)]
   492         -            private static string GetMethodName(
          492  +            public static string GetMethodName(
   493    493                   StackTrace stackTrace,
   494    494                   int level
   495    495                   )
   496    496               {
   497    497                   try
   498    498                   {
   499    499                       //
................................................................................
   553    553               ///////////////////////////////////////////////////////////////////
   554    554   
   555    555               public static void DebugCore(
   556    556                   string message,
   557    557                   string category
   558    558                   )
   559    559               {
   560         -                lock (syncRoot)
          560  +                lock (syncRoot) /* TRANSACTIONAL */
   561    561                   {
   562    562                       if (debugListeners != null)
   563    563                       {
   564    564                           foreach (TraceListener listener in debugListeners)
   565    565                           {
   566    566                               listener.WriteLine(message, category);
   567    567                               listener.Flush();
................................................................................
   573    573               ///////////////////////////////////////////////////////////////////
   574    574   
   575    575               public static void TraceCore(
   576    576                   string message,
   577    577                   string category
   578    578                   )
   579    579               {
   580         -                lock (syncRoot)
          580  +                lock (syncRoot) /* TRANSACTIONAL */
   581    581                   {
   582    582                       //
   583    583                       // NOTE: Write the message to all the active trace
   584    584                       //       listeners.
   585    585                       //
   586    586                       Trace.WriteLine(message, category);
   587    587                       Trace.Flush();
................................................................................
  1184   1184                   //       should be just like calling Dispose.
  1185   1185                   //
  1186   1186                   Dispose(true);
  1187   1187               }
  1188   1188   
  1189   1189               ///////////////////////////////////////////////////////////////////
  1190   1190   
         1191  +            public void DisableClose()
         1192  +            {
         1193  +                CheckDisposed();
         1194  +
         1195  +                noClose = true;
         1196  +            }
         1197  +
         1198  +            ///////////////////////////////////////////////////////////////////
         1199  +
  1191   1200               public MockRegistryKey CreateSubKey(
  1192   1201                   string subKeyName
  1193   1202                   )
  1194   1203               {
  1195   1204                   CheckDisposed();
  1196   1205                   CheckReadOnly();
  1197   1206   
................................................................................
  1421   1430               ///////////////////////////////////////////////////////////////////
  1422   1431   
  1423   1432               public bool safe;
  1424   1433               public bool Safe
  1425   1434               {
  1426   1435                   get { CheckDisposed(); return safe; }
  1427   1436               }
         1437  +
         1438  +            ///////////////////////////////////////////////////////////////////
         1439  +
         1440  +            public bool noClose;
         1441  +            public bool NoClose
         1442  +            {
         1443  +                get { CheckDisposed(); return noClose; }
         1444  +            }
  1428   1445               #endregion
  1429   1446   
  1430   1447               ///////////////////////////////////////////////////////////////////
  1431   1448   
  1432   1449               #region Private Methods
  1433   1450               private void CheckReadOnly()
  1434   1451               {
................................................................................
  1462   1479               public override string ToString()
  1463   1480               {
  1464   1481                   CheckDisposed();
  1465   1482   
  1466   1483                   return this.Name;
  1467   1484               }
  1468   1485               #endregion
         1486  +
         1487  +            ///////////////////////////////////////////////////////////////////
         1488  +
         1489  +            #region Public Static Methods
         1490  +            public static bool NameEquals(
         1491  +                string name1,
         1492  +                string name2
         1493  +                )
         1494  +            {
         1495  +                return String.Equals(
         1496  +                    name1, name2, StringComparison.OrdinalIgnoreCase);
         1497  +            }
         1498  +
         1499  +            ///////////////////////////////////////////////////////////////////
         1500  +
         1501  +            public static bool ValueEquals(
         1502  +                object value1,
         1503  +                object value2
         1504  +                )
         1505  +            {
         1506  +                if ((value1 == null) || (value2 == null))
         1507  +                    return ((value1 == null) && (value2 == null));
         1508  +
         1509  +                if (Object.ReferenceEquals(value1, value2))
         1510  +                    return true;
         1511  +
         1512  +                Type type1 = value1.GetType();
         1513  +                Type type2 = value2.GetType();
         1514  +
         1515  +                if (!Object.ReferenceEquals(type1, type2))
         1516  +                    return false;
         1517  +
         1518  +                if (type1 == typeof(int)) // DWord
         1519  +                {
         1520  +                    return ((int)value1 == (int)value2);
         1521  +                }
         1522  +                else if (type1 == typeof(long)) // QWord
         1523  +                {
         1524  +                    return ((long)value1 == (long)value2);
         1525  +                }
         1526  +                else if (type1 == typeof(string)) // String / ExpandString
         1527  +                {
         1528  +                    return String.Equals(
         1529  +                        (string)value1, (string)value2,
         1530  +                        StringComparison.Ordinal);
         1531  +                }
         1532  +                else if (type1 == typeof(string[])) // MultiString
         1533  +                {
         1534  +                    string[] array1 = (string[])value1;
         1535  +                    string[] array2 = (string[])value2;
         1536  +
         1537  +                    int length1 = array1.Length;
         1538  +
         1539  +                    if (length1 != array2.Length)
         1540  +                        return false;
         1541  +
         1542  +                    for (int index1 = 0; index1 < length1; index1++)
         1543  +                    {
         1544  +                        if (!String.Equals(
         1545  +                                array1[index1], array2[index1],
         1546  +                                StringComparison.Ordinal))
         1547  +                        {
         1548  +                            return false;
         1549  +                        }
         1550  +                    }
         1551  +
         1552  +                    return true;
         1553  +                }
         1554  +                else if (type1 == typeof(byte[])) // Binary
         1555  +                {
         1556  +                    byte[] array1 = (byte[])value1;
         1557  +                    byte[] array2 = (byte[])value2;
         1558  +
         1559  +                    int length1 = array1.Length;
         1560  +
         1561  +                    if (length1 != array2.Length)
         1562  +                        return false;
         1563  +
         1564  +                    for (int index1 = 0; index1 < length1; index1++)
         1565  +                        if (array1[index1] != array2[index1])
         1566  +                            return false;
         1567  +
         1568  +                    return true;
         1569  +                }
         1570  +
         1571  +                return false;
         1572  +            }
         1573  +
         1574  +            ///////////////////////////////////////////////////////////////////
         1575  +
         1576  +            public static int ValueHashCode(
         1577  +                object value
         1578  +                )
         1579  +            {
         1580  +                int result = 0;
         1581  +
         1582  +                if (value != null)
         1583  +                {
         1584  +                    Type type = value.GetType();
         1585  +
         1586  +                    if ((type == typeof(int)) || // DWord
         1587  +                        (type == typeof(long)) || // QWord
         1588  +                        (type == typeof(string))) // String / ExpandString
         1589  +                    {
         1590  +                        result = value.GetHashCode();
         1591  +                    }
         1592  +                    else if ((type == typeof(string[])) || // MultiString
         1593  +                        (type == typeof(byte[]))) // Binary
         1594  +                    {
         1595  +                        Array array = (Array)value;
         1596  +                        int length = array.Length;
         1597  +
         1598  +                        for (int index = 0; index < length; index++)
         1599  +                        {
         1600  +                            object element = array.GetValue(index);
         1601  +
         1602  +                            if (element == null)
         1603  +                                continue;
         1604  +
         1605  +                            result ^= element.GetHashCode();
         1606  +                        }
         1607  +                    }
         1608  +                }
         1609  +
         1610  +                return result;
         1611  +            }
         1612  +
         1613  +            ///////////////////////////////////////////////////////////////////
         1614  +
         1615  +            public static string ValueToString(
         1616  +                object value,
         1617  +                string delimiter,
         1618  +                string nullValue
         1619  +                )
         1620  +            {
         1621  +                string result = null;
         1622  +
         1623  +                if (value != null)
         1624  +                {
         1625  +                    Type type = value.GetType();
         1626  +
         1627  +                    if ((type == typeof(int)) || // DWord
         1628  +                        (type == typeof(long)) || // QWord
         1629  +                        (type == typeof(string))) // String / ExpandString
         1630  +                    {
         1631  +                        result = value.ToString();
         1632  +                    }
         1633  +                    else if ((type == typeof(string[])) || // MultiString
         1634  +                        (type == typeof(byte[]))) // Binary
         1635  +                    {
         1636  +                        StringBuilder builder = new StringBuilder();
         1637  +                        Array array = (Array)value;
         1638  +                        int length = array.Length;
         1639  +
         1640  +                        for (int index = 0; index < length; index++)
         1641  +                        {
         1642  +                            if ((index > 0) && (delimiter != null))
         1643  +                                builder.Append(delimiter);
         1644  +
         1645  +                            object element = array.GetValue(index);
         1646  +
         1647  +                            if (element == null)
         1648  +                            {
         1649  +                                if (nullValue != null)
         1650  +                                    builder.Append(nullValue);
         1651  +
         1652  +                                continue;
         1653  +                            }
         1654  +
         1655  +                            builder.Append(element.ToString());
         1656  +                        }
         1657  +
         1658  +                        result = builder.ToString();
         1659  +                    }
         1660  +                }
         1661  +
         1662  +                return result;
         1663  +            }
         1664  +
         1665  +            ///////////////////////////////////////////////////////////////////
         1666  +
         1667  +            public static bool Equals(
         1668  +                MockRegistryKey key1,
         1669  +                MockRegistryKey key2
         1670  +                )
         1671  +            {
         1672  +                if ((key1 == null) || (key2 == null))
         1673  +                    return ((key1 == null) && (key2 == null));
         1674  +
         1675  +                if (Object.ReferenceEquals(key1, key2))
         1676  +                    return true;
         1677  +
         1678  +                return NameEquals(key1.Name, key2.Name);
         1679  +            }
         1680  +
         1681  +            ///////////////////////////////////////////////////////////////////
         1682  +
         1683  +            public static int GetHashCode(
         1684  +                MockRegistryKey key
         1685  +                )
         1686  +            {
         1687  +                if (key != null)
         1688  +                {
         1689  +                    string name = key.Name;
         1690  +
         1691  +                    if (name != null)
         1692  +                        return name.GetHashCode();
         1693  +                }
         1694  +
         1695  +                return 0;
         1696  +            }
         1697  +            #endregion
  1469   1698   
  1470   1699               ///////////////////////////////////////////////////////////////////
  1471   1700   
  1472   1701               #region Implicit Conversion Operators
  1473   1702               //
  1474   1703               // BUGBUG: Remove me?  This should be safe because in "what-if"
  1475   1704               //         mode all keys are opened read-only.
................................................................................
  1507   1736                       {
  1508   1737                           ////////////////////////////////////
  1509   1738                           // dispose managed resources here...
  1510   1739                           ////////////////////////////////////
  1511   1740   
  1512   1741                           if (key != null)
  1513   1742                           {
  1514         -                            key.Close();
         1743  +                            if (!noClose)
         1744  +                                key.Close();
         1745  +
  1515   1746                               key = null;
  1516   1747                           }
  1517   1748                       }
  1518   1749   
  1519   1750                       //////////////////////////////////////
  1520   1751                       // release unmanaged resources here...
  1521   1752                       //////////////////////////////////////
................................................................................
  1602   1833           };
  1603   1834           #endregion
  1604   1835   
  1605   1836           ///////////////////////////////////////////////////////////////////////
  1606   1837   
  1607   1838           private static class RegistryHelper
  1608   1839           {
         1840  +            #region Private Data
         1841  +            //
         1842  +            // NOTE: This is used to synchronize access to the list of logged
         1843  +            //       registry operations (just below).
         1844  +            //
         1845  +            private static object syncRoot = new object();
         1846  +
         1847  +            //
         1848  +            // NOTE: This is the list of registry write operations when it is
         1849  +            //       set to non-null.
         1850  +            //
         1851  +            private static RegistryOperationList operationList;
         1852  +            #endregion
         1853  +
         1854  +            ///////////////////////////////////////////////////////////////////
         1855  +
  1609   1856               #region Public Static Properties
  1610   1857               private static MockRegistry readOnlyRegistry;
  1611   1858               public static MockRegistry ReadOnlyRegistry
  1612   1859               {
  1613   1860                   get { return readOnlyRegistry; }
  1614   1861               }
  1615   1862   
................................................................................
  1661   1908                   get { return keyValuesDeleted; }
  1662   1909               }
  1663   1910               #endregion
  1664   1911   
  1665   1912               ///////////////////////////////////////////////////////////////////
  1666   1913   
  1667   1914               #region Public Static Methods
         1915  +            public static void EnableOrDisableOperationList(
         1916  +                bool enable
         1917  +                )
         1918  +            {
         1919  +                lock (syncRoot) /* TRANSACTIONAL */
         1920  +                {
         1921  +                    if (enable)
         1922  +                    {
         1923  +                        if (operationList == null)
         1924  +                            operationList = new RegistryOperationList();
         1925  +                    }
         1926  +                    else if (operationList != null)
         1927  +                    {
         1928  +                        operationList.Dispose();
         1929  +                        operationList = null;
         1930  +                    }
         1931  +                }
         1932  +            }
         1933  +
         1934  +            ///////////////////////////////////////////////////////////////////
         1935  +
  1668   1936               public static void ReinitializeDefaultRegistries(
  1669   1937                   bool whatIf,
  1670   1938                   bool safe
  1671   1939                   )
  1672   1940               {
  1673   1941                   if (readOnlyRegistry != null)
  1674   1942                   {
................................................................................
  1710   1978                   MockRegistry registry,
  1711   1979                   string name
  1712   1980                   )
  1713   1981               {
  1714   1982                   if (registry == null)
  1715   1983                       return null;
  1716   1984   
  1717         -                if (String.Equals(
  1718         -                        name, RegistryRootKeyNames.HKEY_CLASSES_ROOT,
  1719         -                        StringComparison.OrdinalIgnoreCase) ||
  1720         -                    String.Equals(
  1721         -                        name, RegistryRootKeyNames.HKCR,
  1722         -                        StringComparison.OrdinalIgnoreCase))
         1985  +                if (MockRegistryKey.NameEquals(
         1986  +                        name, RegistryRootKeyNames.HKEY_CLASSES_ROOT) ||
         1987  +                    MockRegistryKey.NameEquals(
         1988  +                        name, RegistryRootKeyNames.HKCR))
  1723   1989                   {
  1724   1990                       return registry.ClassesRoot;
  1725   1991                   }
  1726         -                else if (String.Equals(
  1727         -                        name, RegistryRootKeyNames.HKEY_CURRENT_CONFIG,
  1728         -                        StringComparison.OrdinalIgnoreCase) ||
  1729         -                    String.Equals(
  1730         -                        name, RegistryRootKeyNames.HKCC,
  1731         -                        StringComparison.OrdinalIgnoreCase))
         1992  +                else if (MockRegistryKey.NameEquals(
         1993  +                        name, RegistryRootKeyNames.HKEY_CURRENT_CONFIG) ||
         1994  +                    MockRegistryKey.NameEquals(
         1995  +                        name, RegistryRootKeyNames.HKCC))
  1732   1996                   {
  1733   1997                       return registry.CurrentConfig;
  1734   1998                   }
  1735         -                else if (String.Equals(
  1736         -                        name, RegistryRootKeyNames.HKEY_CURRENT_USER,
  1737         -                        StringComparison.OrdinalIgnoreCase) ||
  1738         -                    String.Equals(
  1739         -                        name, RegistryRootKeyNames.HKCU,
  1740         -                        StringComparison.OrdinalIgnoreCase))
         1999  +                else if (MockRegistryKey.NameEquals(
         2000  +                        name, RegistryRootKeyNames.HKEY_CURRENT_USER) ||
         2001  +                    MockRegistryKey.NameEquals(
         2002  +                        name, RegistryRootKeyNames.HKCU))
  1741   2003                   {
  1742   2004                       return registry.CurrentUser;
  1743   2005                   }
  1744         -                else if (String.Equals(
  1745         -                        name, RegistryRootKeyNames.HKEY_DYN_DATA,
  1746         -                        StringComparison.OrdinalIgnoreCase) ||
  1747         -                    String.Equals(
  1748         -                        name, RegistryRootKeyNames.HKDD,
  1749         -                        StringComparison.OrdinalIgnoreCase))
         2006  +                else if (MockRegistryKey.NameEquals(
         2007  +                        name, RegistryRootKeyNames.HKEY_DYN_DATA) ||
         2008  +                    MockRegistryKey.NameEquals(
         2009  +                        name, RegistryRootKeyNames.HKDD))
  1750   2010                   {
  1751   2011                       return registry.DynData;
  1752   2012                   }
  1753         -                else if (String.Equals(
  1754         -                        name, RegistryRootKeyNames.HKEY_LOCAL_MACHINE,
  1755         -                        StringComparison.OrdinalIgnoreCase) ||
  1756         -                    String.Equals(
  1757         -                        name, RegistryRootKeyNames.HKLM,
  1758         -                        StringComparison.OrdinalIgnoreCase))
         2013  +                else if (MockRegistryKey.NameEquals(
         2014  +                        name, RegistryRootKeyNames.HKEY_LOCAL_MACHINE) ||
         2015  +                    MockRegistryKey.NameEquals(
         2016  +                        name, RegistryRootKeyNames.HKLM))
  1759   2017                   {
  1760   2018                       return registry.LocalMachine;
  1761   2019                   }
  1762         -                else if (String.Equals(
  1763         -                        name, RegistryRootKeyNames.HKEY_PERFORMANCE_DATA,
  1764         -                        StringComparison.OrdinalIgnoreCase) ||
  1765         -                    String.Equals(
  1766         -                        name, RegistryRootKeyNames.HKPD,
  1767         -                        StringComparison.OrdinalIgnoreCase))
         2020  +                else if (MockRegistryKey.NameEquals(
         2021  +                        name, RegistryRootKeyNames.HKEY_PERFORMANCE_DATA) ||
         2022  +                    MockRegistryKey.NameEquals(
         2023  +                        name, RegistryRootKeyNames.HKPD))
  1768   2024                   {
  1769   2025                       return registry.PerformanceData;
  1770   2026                   }
  1771         -                else if (String.Equals(
  1772         -                        name, RegistryRootKeyNames.HKEY_USERS,
  1773         -                        StringComparison.OrdinalIgnoreCase) ||
  1774         -                    String.Equals(
  1775         -                        name, RegistryRootKeyNames.HKU,
  1776         -                        StringComparison.OrdinalIgnoreCase))
         2027  +                else if (MockRegistryKey.NameEquals(
         2028  +                        name, RegistryRootKeyNames.HKEY_USERS) ||
         2029  +                    MockRegistryKey.NameEquals(
         2030  +                        name, RegistryRootKeyNames.HKU))
  1777   2031                   {
  1778   2032                       return registry.Users;
  1779   2033                   }
  1780   2034   
  1781   2035                   return null;
  1782   2036               }
  1783   2037   
................................................................................
  1935   2189                           return new MockRegistryKey(
  1936   2190                               rootKey.CreateSubKey(subKeyName), whatIf, false,
  1937   2191                               false);
  1938   2192                       }
  1939   2193                   }
  1940   2194                   finally
  1941   2195                   {
         2196  +                    MaybeLogOperation(GetMethodName(), rootKey, subKeyName);
         2197  +
  1942   2198                       subKeysCreated++;
  1943   2199                   }
  1944   2200               }
  1945   2201   
  1946   2202               ///////////////////////////////////////////////////////////////////
  1947   2203   
  1948   2204               [MethodImpl(MethodImplOptions.NoInlining)]
................................................................................
  1965   2221   
  1966   2222                   if (rootKey == null)
  1967   2223                       return;
  1968   2224   
  1969   2225                   if (!whatIf)
  1970   2226                       rootKey.DeleteSubKey(subKeyName, throwOnMissing);
  1971   2227   
         2228  +                MaybeLogOperation(GetMethodName(), rootKey, subKeyName);
         2229  +
  1972   2230                   subKeysDeleted++;
  1973   2231               }
  1974   2232   
  1975   2233               ///////////////////////////////////////////////////////////////////
  1976   2234   
  1977   2235               [MethodImpl(MethodImplOptions.NoInlining)]
  1978   2236               public static void DeleteSubKeyTree(
................................................................................
  1992   2250                   }
  1993   2251   
  1994   2252                   if (rootKey == null)
  1995   2253                       return;
  1996   2254   
  1997   2255                   if (!whatIf)
  1998   2256                       rootKey.DeleteSubKeyTree(subKeyName);
         2257  +
         2258  +                MaybeLogOperation(GetMethodName(), rootKey, subKeyName);
  1999   2259   
  2000   2260                   subKeysDeleted++;
  2001   2261               }
  2002   2262   
  2003   2263               ///////////////////////////////////////////////////////////////////
  2004   2264   
  2005   2265               [MethodImpl(MethodImplOptions.NoInlining)]
................................................................................
  2074   2334   
  2075   2335                   if (key == null)
  2076   2336                       return;
  2077   2337   
  2078   2338                   if (!whatIf)
  2079   2339                       key.SetValue(name, value);
  2080   2340   
         2341  +                MaybeLogOperation(GetMethodName(), key, name, value);
         2342  +
  2081   2343                   keyValuesWritten++;
  2082   2344               }
  2083   2345   
  2084   2346               ///////////////////////////////////////////////////////////////////
  2085   2347   
  2086   2348               [MethodImpl(MethodImplOptions.NoInlining)]
  2087   2349               public static void DeleteValue(
................................................................................
  2102   2364   
  2103   2365                   if (key == null)
  2104   2366                       return;
  2105   2367   
  2106   2368                   if (!whatIf)
  2107   2369                       key.DeleteValue(name, throwOnMissing);
  2108   2370   
         2371  +                MaybeLogOperation(GetMethodName(), key, name, null);
         2372  +
  2109   2373                   keyValuesDeleted++;
  2110   2374               }
         2375  +
         2376  +            ///////////////////////////////////////////////////////////////////
         2377  +
         2378  +            [MethodImpl(MethodImplOptions.NoInlining)]
         2379  +            public static int WriteOperationList(
         2380  +                string fileName,
         2381  +                bool verbose
         2382  +                )
         2383  +            {
         2384  +                int count = 0;
         2385  +
         2386  +                if (String.IsNullOrEmpty(fileName))
         2387  +                {
         2388  +                    if (verbose)
         2389  +                    {
         2390  +                        TraceOps.DebugAndTrace(TracePriority.Highest,
         2391  +                            debugCallback, traceCallback,
         2392  +                            "Registry operation log file name not set.",
         2393  +                            traceCategory);
         2394  +                    }
         2395  +
         2396  +                    return count;
         2397  +                }
         2398  +
         2399  +                lock (syncRoot) /* TRANSACTIONAL */
         2400  +                {
         2401  +                    if (operationList == null)
         2402  +                    {
         2403  +                        if (verbose)
         2404  +                        {
         2405  +                            TraceOps.DebugAndTrace(TracePriority.Highest,
         2406  +                                debugCallback, traceCallback,
         2407  +                                "Registry operation list is invalid.",
         2408  +                                traceCategory);
         2409  +                        }
         2410  +
         2411  +                        return count;
         2412  +                    }
         2413  +
         2414  +                    using (StreamWriter streamWriter = new StreamWriter(
         2415  +                            fileName))
         2416  +                    {
         2417  +                        foreach (RegistryOperation operation in operationList)
         2418  +                        {
         2419  +                            if (operation == null)
         2420  +                                continue;
         2421  +
         2422  +                            streamWriter.WriteLine(operation.ToString());
         2423  +                            count++;
         2424  +                        }
         2425  +
         2426  +                        streamWriter.Flush();
         2427  +                    }
         2428  +                }
         2429  +
         2430  +                if (verbose)
         2431  +                {
         2432  +                    TraceOps.DebugAndTrace(TracePriority.Highest,
         2433  +                        debugCallback, traceCallback, String.Format(
         2434  +                        "Wrote {0} registry operations to its log file.",
         2435  +                        count), traceCategory);
         2436  +                }
         2437  +
         2438  +                return count;
         2439  +            }
         2440  +            #endregion
         2441  +
         2442  +            ///////////////////////////////////////////////////////////////////
         2443  +
         2444  +            #region Private Static Methods
         2445  +            [MethodImpl(MethodImplOptions.NoInlining)]
         2446  +            private static string GetMethodName()
         2447  +            {
         2448  +                return TraceOps.GetMethodName(null, 1);
         2449  +            }
         2450  +
         2451  +            ///////////////////////////////////////////////////////////////////
         2452  +
         2453  +            private static void MaybeLogOperation(
         2454  +                string methodName,
         2455  +                MockRegistryKey key,
         2456  +                string subKeyName
         2457  +                )
         2458  +            {
         2459  +                MaybeLogOperation(methodName, key, subKeyName, null, null);
         2460  +            }
         2461  +
         2462  +            ///////////////////////////////////////////////////////////////////
         2463  +
         2464  +            private static void MaybeLogOperation(
         2465  +                string methodName,
         2466  +                MockRegistryKey key,
         2467  +                string valueName,
         2468  +                object value
         2469  +                )
         2470  +            {
         2471  +                MaybeLogOperation(methodName, key, null, valueName, value);
         2472  +            }
         2473  +
         2474  +            ///////////////////////////////////////////////////////////////////
         2475  +
         2476  +            private static void MaybeLogOperation(
         2477  +                string methodName,
         2478  +                MockRegistryKey key,
         2479  +                string subKeyName,
         2480  +                string valueName,
         2481  +                object value
         2482  +                )
         2483  +            {
         2484  +                lock (syncRoot) /* TRANSACTIONAL */
         2485  +                {
         2486  +                    if (operationList == null)
         2487  +                        return;
         2488  +
         2489  +                    operationList.Add(new RegistryOperation(
         2490  +                        methodName, key, subKeyName, valueName, value));
         2491  +                }
         2492  +            }
         2493  +            #endregion
         2494  +        }
         2495  +        #endregion
         2496  +
         2497  +        ///////////////////////////////////////////////////////////////////////
         2498  +
         2499  +        #region RegistryOperationList Class
         2500  +        [Serializable()]
         2501  +        private sealed class RegistryOperationList :
         2502  +            List<RegistryOperation>, IDisposable
         2503  +        {
         2504  +            #region Public Constructors
         2505  +            public RegistryOperationList()
         2506  +            {
         2507  +                // do nothing.
         2508  +            }
         2509  +            #endregion
         2510  +
         2511  +            ///////////////////////////////////////////////////////////////////
         2512  +
         2513  +            #region IDisposable "Pattern" Members
         2514  +            private bool disposed;
         2515  +            private void CheckDisposed() /* throw */
         2516  +            {
         2517  +                if (!disposed)
         2518  +                    return;
         2519  +
         2520  +                throw new ObjectDisposedException(
         2521  +                    typeof(RegistryOperationList).Name);
         2522  +            }
         2523  +
         2524  +            ///////////////////////////////////////////////////////////////////
         2525  +
         2526  +            private /* protected virtual */ void Dispose(
         2527  +                bool disposing
         2528  +                )
         2529  +            {
         2530  +                if (!disposed)
         2531  +                {
         2532  +                    if (disposing)
         2533  +                    {
         2534  +                        ////////////////////////////////////
         2535  +                        // dispose managed resources here...
         2536  +                        ////////////////////////////////////
         2537  +
         2538  +                        foreach (RegistryOperation operation in this)
         2539  +                        {
         2540  +                            if (operation == null)
         2541  +                                continue;
         2542  +
         2543  +                            operation.Dispose();
         2544  +                        }
         2545  +
         2546  +                        Clear();
         2547  +                    }
         2548  +
         2549  +                    //////////////////////////////////////
         2550  +                    // release unmanaged resources here...
         2551  +                    //////////////////////////////////////
         2552  +
         2553  +                    //
         2554  +                    // NOTE: This object is now disposed.
         2555  +                    //
         2556  +                    disposed = true;
         2557  +                }
         2558  +            }
         2559  +            #endregion
         2560  +
         2561  +            ///////////////////////////////////////////////////////////////////
         2562  +
         2563  +            #region IDisposable Members
         2564  +            public void Dispose()
         2565  +            {
         2566  +                Dispose(true);
         2567  +                GC.SuppressFinalize(this);
         2568  +            }
         2569  +            #endregion
         2570  +
         2571  +            ///////////////////////////////////////////////////////////////////
         2572  +
         2573  +            #region Destructor
         2574  +            ~RegistryOperationList()
         2575  +            {
         2576  +                Dispose(false);
         2577  +            }
         2578  +            #endregion
         2579  +        }
         2580  +        #endregion
         2581  +
         2582  +        ///////////////////////////////////////////////////////////////////////
         2583  +
         2584  +        #region RegistryOperation Class
         2585  +        private sealed class RegistryOperation
         2586  +        {
         2587  +            #region Private Constants
         2588  +            private const char FieldDelimiter = '\t';
         2589  +            #endregion
         2590  +
         2591  +            ///////////////////////////////////////////////////////////////////
         2592  +
         2593  +            #region Public Constructors
         2594  +            public RegistryOperation(
         2595  +                string methodName,
         2596  +                MockRegistryKey key,
         2597  +                string subKeyName,
         2598  +                string valueName,
         2599  +                object value
         2600  +                )
         2601  +            {
         2602  +                this.methodName = methodName;
         2603  +                this.subKeyName = subKeyName;
         2604  +                this.valueName = valueName;
         2605  +                this.value = value;
         2606  +
         2607  +                SetKey(key);
         2608  +            }
         2609  +            #endregion
         2610  +
         2611  +            ///////////////////////////////////////////////////////////////////
         2612  +
         2613  +            #region Private Methods
         2614  +            private void SetKey(
         2615  +                MockRegistryKey key
         2616  +                )
         2617  +            {
         2618  +                if (key != null)
         2619  +                {
         2620  +                    //
         2621  +                    // NOTE: Make sure this copy of the root registry key
         2622  +                    //       cannot be used to accidentally make registry
         2623  +                    //       changes.  Also, prevent this MockRegistryKey
         2624  +                    //       object from closing its underlying registry
         2625  +                    //       key as we will need it later.  This instance
         2626  +                    //       will close it.
         2627  +                    //
         2628  +                    this.key = new MockRegistryKey(key, true, true, true);
         2629  +
         2630  +                    key.DisableClose();
         2631  +                }
         2632  +                else
         2633  +                {
         2634  +                    this.key = null;
         2635  +                }
         2636  +            }
         2637  +            #endregion
         2638  +
         2639  +            ///////////////////////////////////////////////////////////////////
         2640  +
         2641  +            #region Public Properties
         2642  +            private string methodName;
         2643  +            public string MethodName
         2644  +            {
         2645  +                get { CheckDisposed(); return methodName; }
         2646  +            }
         2647  +
         2648  +            ///////////////////////////////////////////////////////////////////
         2649  +
         2650  +            private MockRegistryKey key;
         2651  +            public MockRegistryKey Key
         2652  +            {
         2653  +                get { CheckDisposed(); return key; }
         2654  +            }
         2655  +
         2656  +            ///////////////////////////////////////////////////////////////////
         2657  +
         2658  +            private string subKeyName;
         2659  +            public string SubKeyName
         2660  +            {
         2661  +                get { CheckDisposed(); return subKeyName; }
         2662  +            }
         2663  +
         2664  +            ///////////////////////////////////////////////////////////////////
         2665  +
         2666  +            private string valueName;
         2667  +            public string ValueName
         2668  +            {
         2669  +                get { CheckDisposed(); return valueName; }
         2670  +            }
         2671  +
         2672  +            ///////////////////////////////////////////////////////////////////
         2673  +
         2674  +            private object value;
         2675  +            public object Value
         2676  +            {
         2677  +                get { CheckDisposed(); return value; }
         2678  +            }
         2679  +            #endregion
         2680  +
         2681  +            ///////////////////////////////////////////////////////////////////
         2682  +
         2683  +            #region System.Object Overrides
         2684  +            public override bool Equals(
         2685  +                object obj
         2686  +                )
         2687  +            {
         2688  +                CheckDisposed();
         2689  +
         2690  +                RegistryOperation operation = obj as RegistryOperation;
         2691  +
         2692  +                if (operation == null)
         2693  +                    return false;
         2694  +
         2695  +                if (!String.Equals(operation.methodName, methodName))
         2696  +                    return false;
         2697  +
         2698  +                if (!MockRegistryKey.Equals(operation.key, key))
         2699  +                    return false;
         2700  +
         2701  +                if (!MockRegistryKey.NameEquals(
         2702  +                        operation.subKeyName, subKeyName))
         2703  +                {
         2704  +                    return false;
         2705  +                }
         2706  +
         2707  +                if (!String.Equals(operation.valueName, valueName))
         2708  +                    return false;
         2709  +
         2710  +                if (!MockRegistryKey.ValueEquals(operation.value, value))
         2711  +                    return false;
         2712  +
         2713  +                return true;
         2714  +            }
         2715  +
         2716  +            ///////////////////////////////////////////////////////////////////
         2717  +
         2718  +            public override int GetHashCode()
         2719  +            {
         2720  +                CheckDisposed();
         2721  +
         2722  +                int result = 0;
         2723  +
         2724  +                if (methodName != null)
         2725  +                    result ^= methodName.GetHashCode();
         2726  +
         2727  +                result ^= MockRegistryKey.GetHashCode(key);
         2728  +
         2729  +                if (subKeyName != null)
         2730  +                    result ^= subKeyName.GetHashCode();
         2731  +
         2732  +                if (valueName != null)
         2733  +                    result ^= valueName.GetHashCode();
         2734  +
         2735  +                result ^= MockRegistryKey.ValueHashCode(value);
         2736  +
         2737  +                return result;
         2738  +            }
         2739  +
         2740  +            ///////////////////////////////////////////////////////////////////
         2741  +
         2742  +            public override string ToString()
         2743  +            {
         2744  +                CheckDisposed();
         2745  +
         2746  +                StringBuilder builder = new StringBuilder();
         2747  +
         2748  +                builder.Append(ForDisplay(methodName));
         2749  +                builder.Append(FieldDelimiter);
         2750  +                builder.Append(ForDisplay(key));
         2751  +                builder.Append(FieldDelimiter);
         2752  +                builder.Append(ForDisplay(subKeyName));
         2753  +                builder.Append(FieldDelimiter);
         2754  +                builder.Append(ForDisplay(valueName));
         2755  +                builder.Append(FieldDelimiter);
         2756  +
         2757  +                builder.Append(ForDisplay(
         2758  +                    MockRegistryKey.ValueToString(value, ", ", "<null>")));
         2759  +
         2760  +                return builder.ToString();
         2761  +            }
         2762  +            #endregion
         2763  +
         2764  +            ///////////////////////////////////////////////////////////////////
         2765  +
         2766  +            #region IDisposable "Pattern" Members
         2767  +            private bool disposed;
         2768  +            private void CheckDisposed() /* throw */
         2769  +            {
         2770  +                if (!disposed)
         2771  +                    return;
         2772  +
         2773  +                throw new ObjectDisposedException(
         2774  +                    typeof(RegistryOperation).Name);
         2775  +            }
         2776  +
         2777  +            ///////////////////////////////////////////////////////////////////
         2778  +
         2779  +            private /* protected virtual */ void Dispose(
         2780  +                bool disposing
         2781  +                )
         2782  +            {
         2783  +                if (!disposed)
         2784  +                {
         2785  +                    if (disposing)
         2786  +                    {
         2787  +                        ////////////////////////////////////
         2788  +                        // dispose managed resources here...
         2789  +                        ////////////////////////////////////
         2790  +
         2791  +                        if (key != null)
         2792  +                        {
         2793  +                            key.Close();
         2794  +                            key = null;
         2795  +                        }
         2796  +                    }
         2797  +
         2798  +                    //////////////////////////////////////
         2799  +                    // release unmanaged resources here...
         2800  +                    //////////////////////////////////////
         2801  +
         2802  +                    //
         2803  +                    // NOTE: This object is now disposed.
         2804  +                    //
         2805  +                    disposed = true;
         2806  +                }
         2807  +            }
         2808  +            #endregion
         2809  +
         2810  +            ///////////////////////////////////////////////////////////////////
         2811  +
         2812  +            #region IDisposable Members
         2813  +            public void Dispose()
         2814  +            {
         2815  +                Dispose(true);
         2816  +                GC.SuppressFinalize(this);
         2817  +            }
         2818  +            #endregion
         2819  +
         2820  +            ///////////////////////////////////////////////////////////////////
         2821  +
         2822  +            #region Destructor
         2823  +            ~RegistryOperation()
         2824  +            {
         2825  +                Dispose(false);
         2826  +            }
  2111   2827               #endregion
  2112   2828           }
  2113   2829           #endregion
  2114   2830   
  2115   2831           ///////////////////////////////////////////////////////////////////////
  2116   2832   
  2117   2833           #region StringList Class
................................................................................
  2330   3046   
  2331   3047               ///////////////////////////////////////////////////////////////////
  2332   3048   
  2333   3049               #region Private Constructors
  2334   3050               private Configuration(
  2335   3051                   Assembly assembly,
  2336   3052                   string logFileName,
         3053  +                string registryLogFileName,
  2337   3054                   string directory,
  2338   3055                   string coreFileName,
  2339   3056                   string linqFileName,
  2340   3057                   string ef6FileName,
  2341   3058                   string designerFileName,
  2342   3059                   string registryVersion,
  2343   3060                   string configVersion,
................................................................................
  2380   3097                   bool debug,
  2381   3098                   bool verbose,
  2382   3099                   bool confirm
  2383   3100                   )
  2384   3101               {
  2385   3102                   this.assembly = assembly;
  2386   3103                   this.logFileName = logFileName;
         3104  +                this.registryLogFileName = registryLogFileName;
  2387   3105                   this.directory = directory;
  2388   3106                   this.coreFileName = coreFileName;
  2389   3107                   this.linqFileName = linqFileName;
  2390   3108                   this.ef6FileName = ef6FileName;
  2391   3109                   this.designerFileName = designerFileName;
  2392   3110                   this.registryVersion = registryVersion;
  2393   3111                   this.configVersion = configVersion;
................................................................................
  2607   3325                   string designerFileName = null;
  2608   3326   
  2609   3327                   GetDefaultFileNames(
  2610   3328                       ref directory, ref coreFileName, ref linqFileName,
  2611   3329                       ref ef6FileName, ref designerFileName);
  2612   3330   
  2613   3331                   return new Configuration(
  2614         -                    thisAssembly, null, directory, coreFileName, linqFileName,
  2615         -                    ef6FileName, designerFileName, null, null, null,
  2616         -                    TraceOps.DebugFormat, TraceOps.TraceFormat,
         3332  +                    thisAssembly, null, null, directory, coreFileName,
         3333  +                    linqFileName, ef6FileName, designerFileName, null, null,
         3334  +                    null, TraceOps.DebugFormat, TraceOps.TraceFormat,
  2617   3335                       InstallFlags.Default, ProviderFlags.Default,
  2618   3336                       TracePriority.Default, TracePriority.Default, false, true,
  2619   3337                       false, false, false, false, false, false, false, false,
  2620   3338                       false, false, false, false, false, false, false, false,
  2621   3339                       false, false, false, false, false, false, false, false,
  2622   3340                       false, true, true, false, false, false);
  2623   3341               }
................................................................................
  3470   4188                                   if (strict)
  3471   4189                                       return false;
  3472   4190   
  3473   4191                                   continue;
  3474   4192                               }
  3475   4193   
  3476   4194                               configuration.providerFlags = (ProviderFlags)value;
         4195  +                        }
         4196  +                        else if (MatchOption(newArg, "registryLogFileName"))
         4197  +                        {
         4198  +                            configuration.registryLogFileName = text;
  3477   4199                           }
  3478   4200                           else if (MatchOption(newArg, "registryVersion"))
  3479   4201                           {
  3480   4202                               configuration.registryVersion = text;
  3481   4203                           }
  3482   4204                           else if (MatchOption(newArg, "strict"))
  3483   4205                           {
................................................................................
  3783   4505                       {
  3784   4506                           TraceOps.DebugAndTrace(TracePriority.MediumHigh,
  3785   4507                               debugCallback, traceCallback,
  3786   4508                               "No actual changes will be made to this " +
  3787   4509                               "system because \"what-if\" mode is enabled.",
  3788   4510                               traceCategory);
  3789   4511                       }
         4512  +
         4513  +                    //
         4514  +                    // NOTE: If the registry log file name has been set, its
         4515  +                    //       value will be used verbatim as the place where
         4516  +                    //       all registry write operations will (eventually)
         4517  +                    //       be logged.  Make sure the registry helper class
         4518  +                    //       has a valid operation list; otherwise, it will
         4519  +                    //       not perform any logging.
         4520  +                    //
         4521  +                    if (configuration.registryLogFileName != null)
         4522  +                    {
         4523  +                        RegistryHelper.EnableOrDisableOperationList(true);
         4524  +
         4525  +                        TraceOps.DebugAndTrace(TracePriority.MediumHigh,
         4526  +                            debugCallback, traceCallback, String.Format(
         4527  +                            "Registry logging to file {0} enabled.",
         4528  +                            ForDisplay(configuration.registryLogFileName)),
         4529  +                            traceCategory);
         4530  +                    }
  3790   4531   
  3791   4532                       //
  3792   4533                       // NOTE: If the command line has not been manually
  3793   4534                       //       confirmed (i.e. via the explicit command line
  3794   4535                       //       option), then stop processing now.  We enforce
  3795   4536                       //       this rule so that simply double-clicking the
  3796   4537                       //       executable will not result in any changes being
................................................................................
  4318   5059                       traceCallback(String.Format(NameAndValueFormat,
  4319   5060                           "Assembly", ForDisplay(assembly)),
  4320   5061                           traceCategory);
  4321   5062   
  4322   5063                       traceCallback(String.Format(NameAndValueFormat,
  4323   5064                           "LogFileName", ForDisplay(logFileName)),
  4324   5065                           traceCategory);
         5066  +
         5067  +                    traceCallback(String.Format(NameAndValueFormat,
         5068  +                        "RegistryLogFileName",
         5069  +                        ForDisplay(registryLogFileName)),
         5070  +                        traceCategory);
  4325   5071   
  4326   5072                       traceCallback(String.Format(NameAndValueFormat,
  4327   5073                           "Directory", ForDisplay(directory)),
  4328   5074                           traceCategory);
  4329   5075   
  4330   5076                       traceCallback(String.Format(NameAndValueFormat,
  4331   5077                           "CoreFileName", ForDisplay(coreFileName)),
................................................................................
  4663   5409   
  4664   5410               private string logFileName;
  4665   5411               public string LogFileName
  4666   5412               {
  4667   5413                   get { return logFileName; }
  4668   5414                   set { logFileName = value; }
  4669   5415               }
         5416  +
         5417  +            ///////////////////////////////////////////////////////////////////
         5418  +
         5419  +            private string registryLogFileName;
         5420  +            public string RegistryLogFileName
         5421  +            {
         5422  +                get { return registryLogFileName; }
         5423  +                set { registryLogFileName = value; }
         5424  +            }
  4670   5425   
  4671   5426               ///////////////////////////////////////////////////////////////////
  4672   5427   
  4673   5428               private string directory;
  4674   5429               public string Directory
  4675   5430               {
  4676   5431                   get { return directory; }
................................................................................
  5455   6210               }
  5456   6211               else if (type == typeof(DataReceivedEventArgs))
  5457   6212               {
  5458   6213                   DataReceivedEventArgs eventArgs = (DataReceivedEventArgs)value;
  5459   6214   
  5460   6215                   result = ForDisplay(eventArgs.Data); /* RECURSIVE */
  5461   6216               }
         6217  +            else if (type == typeof(MockRegistryKey))
         6218  +            {
         6219  +                MockRegistryKey key = (MockRegistryKey)value;
         6220  +
         6221  +                result = ForDisplay(key.ToString()); /* RECURSIVE */
         6222  +            }
  5462   6223               else
  5463   6224               {
  5464   6225                   result = value.ToString();
  5465   6226   
  5466   6227                   if (result.Length == 0)
  5467   6228                       return "<empty>";
  5468   6229   
................................................................................
  8362   9123                       TraceOps.DebugAndTrace(TracePriority.MediumHigh,
  8363   9124                           debugCallback, traceCallback, String.Format(
  8364   9125                           "filesCreated = {0}, filesModified = {1}, " +
  8365   9126                           "filesDeleted = {2}", ForDisplay(filesCreated),
  8366   9127                           ForDisplay(filesModified), ForDisplay(filesDeleted)),
  8367   9128                           traceCategory);
  8368   9129                       #endregion
         9130  +
         9131  +                    ///////////////////////////////////////////////////////////
         9132  +
         9133  +                    #region Write Registry Log (Optional)
         9134  +                    //
         9135  +                    // NOTE: If applicable, write the list of registry write
         9136  +                    //       operations now.
         9137  +                    //
         9138  +                    RegistryHelper.WriteOperationList(
         9139  +                        configuration.RegistryLogFileName,
         9140  +                        configuration.Verbose);
         9141  +
         9142  +                    RegistryHelper.EnableOrDisableOperationList(false);
         9143  +                    #endregion
  8369   9144   
  8370   9145                       ///////////////////////////////////////////////////////////
  8371   9146   
  8372   9147                       TraceOps.DebugAndTrace(TracePriority.MediumHigh,
  8373   9148                           debugCallback, traceCallback, "Success.",
  8374   9149                           traceCategory);
  8375   9150