// NAnt - A .NET build tool // Copyright (C) 2001-2003 Gerry Shaw // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // Dmitry Jemerov using System; using System.Collections; using System.Collections.Specialized; using System.CodeDom.Compiler; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Xml; using Microsoft.Win32; using NAnt.Core; using NAnt.Core.Functions; using NAnt.Core.Tasks; using NAnt.Core.Util; using NAnt.VSNet.Tasks; namespace NAnt.VSNet { /// /// Base class for all project classes. /// public abstract class ProjectBase { #region Protected Instance Constructors /// /// Initializes a new instance of the class. /// protected ProjectBase(XmlElement xmlDefinition, SolutionTask solutionTask, TempFileCollection temporaryFiles, GacCache gacCache, ReferencesResolver referencesResolver, DirectoryInfo outputDir) { if (xmlDefinition == null) { throw new ArgumentNullException("xmlDefinition"); } if (solutionTask == null) { throw new ArgumentNullException("solutionTask"); } if (temporaryFiles == null) { throw new ArgumentNullException("temporaryFiles"); } if (gacCache == null) { throw new ArgumentNullException("gacCache"); } if (referencesResolver == null) { throw new ArgumentNullException("referencesResolver"); } _projectConfigurations = CollectionsUtil.CreateCaseInsensitiveHashtable(); _buildConfigurations = CollectionsUtil.CreateCaseInsensitiveHashtable(); _extraOutputFiles = CollectionsUtil.CreateCaseInsensitiveHashtable(); // ensure the specified project is actually supported by this project VerifyProjectXml(xmlDefinition); _solutionTask = solutionTask; _temporaryFiles = temporaryFiles; _outputDir = outputDir; _gacCache = gacCache; _refResolver = referencesResolver; _productVersion = DetermineProductVersion(xmlDefinition); } #endregion Protected Instance Constructors #region Public Instance Properties /// /// Gets the Visual Studio product version of the project. /// /// /// The Visual Studio product version of the project. /// public ProductVersion ProductVersion { get { return _productVersion; } } /// /// Gets the name of the VS.NET project. /// public abstract string Name { get; } /// /// Gets the type of the project. /// /// /// The type of the project. /// public abstract ProjectType Type { get; } /// /// Gets the path of the VS.NET project. /// public abstract string ProjectPath { get; } /// /// Gets the directory containing the VS.NET project. /// public abstract DirectoryInfo ProjectDirectory { get; } /// /// Get the location of the project. /// public abstract ProjectLocation ProjectLocation { get; } /// /// Get the directory in which intermediate build output that is not /// specific to the build configuration will be stored. /// /// /// /// For projects, this is defined /// as <Project Directory<\obj. /// /// /// For projects, this is defined /// as %HOMEPATH%\VSWebCache\<Machine Name>\<Project Directory>\obj. /// /// public virtual DirectoryInfo ObjectDir { get { switch (ProjectLocation) { case ProjectLocation.Web: string webCachePath = FileUtils.CombinePaths( FileUtils.GetHomeDirectory(), "VSWebCache"); string machinePath= FileUtils.CombinePaths(webCachePath, Environment.MachineName); string projectPath = FileUtils.CombinePaths(machinePath, Name); return new DirectoryInfo(FileUtils.CombinePaths(projectPath, "obj")); case ProjectLocation.Local: return new DirectoryInfo( FileUtils.CombinePaths(ProjectDirectory.FullName, "obj")); default: throw new NotSupportedException(ProjectLocation.ToString()); } } } /// /// Gets or sets the unique identifier of the VS.NET project. /// public abstract string Guid { get; set; } public string[] Configurations { get { return (string[]) new ArrayList(_projectConfigurations.Keys).ToArray(typeof(string)); } } /// /// Gets a case-insensitive list of project configurations. /// /// /// The key of the is the name of the /// configuration and the value is a /// instance. /// public Hashtable ProjectConfigurations { get { return _projectConfigurations; } } /// /// Gets a list of project configurations that can be build. /// /// /// /// Project configurations that are not in this list do not need to be /// compiled (unless the project was not loaded through a solution file). /// /// /// The key of the is the name of the /// configuration and the value is a /// instance. /// /// public Hashtable BuildConfigurations { get { return _buildConfigurations; } } public abstract ArrayList References { get; } public SolutionTask SolutionTask { get { return _solutionTask; } } public TempFileCollection TemporaryFiles { get { return _temporaryFiles; } } /// /// Gets the extra set of output files for the project. /// /// /// The extra set of output files for the project. /// /// /// The key of the case-insensitive is the /// full path of the output file and the value is the path relative to /// the output directory. /// public Hashtable ExtraOutputFiles { get { return _extraOutputFiles; } } #endregion Public Instance Properties #region Protected Instance Properties protected DirectoryInfo OutputDir { get { return _outputDir; } } protected GacCache GacCache { get { return _gacCache; } } public ReferencesResolver ReferencesResolver { get { return _refResolver; } } /// /// Gets the set of projects that the project depends on. /// /// /// The set of projects that the project depends on. /// public ProjectBaseCollection ProjectDependencies { get { return _projectDependencies; } } protected virtual string DevEnvDir { get { string vs7CommonDirKeyName = @"SOFTWARE\Microsoft\VisualStudio\" + ProductVersionNumber + @"\Setup\VS"; RegistryKey vs7CommonDirKey = Registry.LocalMachine.OpenSubKey( vs7CommonDirKeyName); if (vs7CommonDirKey == null) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, "Registry key \"{0}\" could not be found.", vs7CommonDirKeyName), Location.UnknownLocation); } string vs7CommonDir = vs7CommonDirKey.GetValue("VS7CommonDir") as string; if (vs7CommonDir == null) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, "Value \"VS7CommonDir\" does not exist in registry key" + " \"{0}\".", vs7CommonDirKeyName), Location.UnknownLocation); } return FileUtils.CombinePaths(vs7CommonDir, @"IDE\"); } } #endregion Protected Instance Properties #region Private Instance Properties /// /// TODO: refactor this !!! /// private Version ProductVersionNumber { get { switch (ProductVersion) { case ProductVersion.Rainier: return new Version(7, 0); case ProductVersion.Everett: return new Version(7, 1); default: throw new Exception("Invalid product version \"" + ProductVersion + "\"."); } } } #endregion Private Instance Properties #region Public Instance Methods public abstract ProjectReferenceBase CreateProjectReference( ProjectBase project, bool isPrivateSpecified, bool isPrivate); public bool Compile(string solutionConfiguration) { ConfigurationBase projectConfig = (ConfigurationBase) BuildConfigurations[solutionConfiguration]; if (projectConfig == null) { Log(Level.Info, "Skipping '{0}' [{1}] ...", Name, solutionConfiguration); return true; } /* ConfigurationBase config = (ConfigurationBase) ProjectConfigurations[projectConfiguration]; if (config == null) { Log(Level.Info, "Skipping '{0}': configuration '{1}' does not exist.", Name, configuration); return true; } */ Log(Level.Info, "Building '{0}' [{1}] ...", Name, projectConfig.Name); // ensure project-level object directory exists, no need to do this // for the project-level output directory as we already this before if (!ObjectDir.Exists) { ObjectDir.Create(); ObjectDir.Refresh(); } // build the project BuildResult result = Build(solutionConfiguration); return (result != BuildResult.Failed); } public string GetOutputPath(string solutionConfiguration) { // obtain project configuration (corresponding with solution configuration) ConfigurationBase config = (ConfigurationBase) BuildConfigurations[solutionConfiguration]; if (config == null) { return null; } return config.OutputPath; } public ConfigurationBase GetConfiguration(string solutionConfiguration) { // obtain project configuration (corresponding with solution configuration) return (ConfigurationBase) BuildConfigurations[solutionConfiguration]; } public StringCollection GetAssemblyReferences(string solutionConfiguration) { Hashtable uniqueReferences = CollectionsUtil.CreateCaseInsensitiveHashtable(); foreach (ReferenceBase reference in References) { StringCollection references = reference.GetAssemblyReferences(solutionConfiguration); // avoid ambiguous references when the same assembly is // referenced multiple times // // this should only be possible for VB.NET project, as they // also include the assemblies referenced by referenced projects // // Project A references // Assembly 1 // Assembly 2 // Project B references // Assembly 3 // Project A // // then to compile Project B, VB.NET will use the following // assembly references: // // Assembly 1 // Assembly 2 // Assembly 3 // Project Output of Project A // // see bug #1178862 foreach (string assemblyFile in references) { try { // try to obtain AssemblyName of referenced assembly AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyFile); if (!uniqueReferences.ContainsKey(assemblyName.FullName)) { uniqueReferences.Add(assemblyName.FullName, assemblyFile); } } catch (Exception ex) { // ignore assemblies that cannot be found or loaded Log(Level.Warning, "Referenced assembly \"{0}\" could not" + " be loaded: {1}", assemblyFile, ex.Message); } } } StringCollection assemblyReferences = new StringCollection(); foreach (DictionaryEntry entry in uniqueReferences) { assemblyReferences.Add((string) entry.Value); } return assemblyReferences; } /// /// Gets the complete set of output files for the project. /// configuration. /// /// The solution configuration that is built. /// The set of output files to be updated. /// /// The key of the case-insensitive is the /// full path of the output file and the value is the path relative to /// the output directory. /// public virtual void GetOutputFiles(string solutionConfiguration, Hashtable outputFiles) { // obtain project configuration (corresponding with solution configuration) ConfigurationBase config = (ConfigurationBase) BuildConfigurations[solutionConfiguration]; if (config == null) { throw new BuildException(string.Format(CultureInfo.InvariantCulture, "Solution configuration '{0}' does not exist for project '{1}'.", solutionConfiguration, Name), Location.UnknownLocation); } foreach (ReferenceBase reference in References) { if (!reference.CopyLocal) { continue; } // only add output files of reference if we did not add them // already if (!outputFiles.ContainsKey(config.BuildPath)) { reference.GetOutputFiles(solutionConfiguration, outputFiles); } } // determine output file of project string projectOutputFile = config.BuildPath; // check if project has output file (eg. NMake project does not // necessarily have an output file) if (projectOutputFile != null && File.Exists(projectOutputFile)) { // get list of files related to project output file (eg. debug symbols, // xml doc, ...), this will include the project output file itself ReferenceBase.GetRelatedFiles(projectOutputFile, outputFiles); // NOTE: // // for now we assume that the extra output files do not need // to be copied/deployed if there's no main output file or // the main output file does not exist (eg. because of a compile // error) // add extra project-level output files foreach (DictionaryEntry de in ExtraOutputFiles) { outputFiles[(string) de.Key] = (string) de.Value; } // add extra configuration-level output files foreach (DictionaryEntry de in config.ExtraOutputFiles) { outputFiles[(string) de.Key] = (string) de.Value; } } } /// /// Gets a value indicating whether building the project for the specified /// build configuration results in managed output. /// /// The build configuration. /// /// if the project output for the given build /// configuration is managed; otherwise, . /// public abstract bool IsManaged(string configuration); #endregion Public Instance Methods #region Protected Internal Instance Methods /// /// Expands the given macro. /// /// The macro to expand. /// /// The expanded macro or if the macro is not /// supported. /// protected internal virtual string ExpandMacro(string macro) { // perform case-insensitive expansion of macros switch (macro.ToLower(CultureInfo.InvariantCulture)) { case "projectname": // E.g. WindowsApplication1 return Name; case "projectpath": // E.g. C:\Doc...\Visual Studio Projects\WindowsApplications1\WindowsApplications1.csproj return ProjectPath; case "projectfilename": // E.g. WindowsApplication1.csproj return Path.GetFileName(ProjectPath); case "projectext": // .csproj return Path.GetExtension(ProjectPath); case "projectdir": // ProjectPath without ProjectFileName at the end return Path.GetDirectoryName(ProjectPath) + Path.DirectorySeparatorChar; case "devenvdir": return DevEnvDir; default: return null; } } #endregion Protected Internal Instance Methods #region Protected Instance Methods /// /// Returns the Visual Studio product version of the specified project /// XML fragment. /// /// XML fragment representing the project file. /// /// The Visual Studio product version of the specified project XML /// file. /// /// /// The product version could not be determined. /// -or- /// The product version is not supported. /// protected abstract ProductVersion DetermineProductVersion(XmlElement docElement); /// /// Verifies whether the specified XML fragment represents a valid project /// that is supported by this . /// /// XML fragment representing the project file. /// /// The XML fragment is not supported by this . /// -or- /// The XML fragment does not represent a valid project (for this ). /// protected abstract void VerifyProjectXml(XmlElement docElement); /// /// Prepares the project for being built. /// /// The solution configuration that is built. /// /// The default implementation will ensure that none of the output files /// are marked read-only. /// protected virtual void Prepare(string solutionConfiguration) { // determine the output files of the project Hashtable outputFiles = CollectionsUtil.CreateCaseInsensitiveHashtable(); GetOutputFiles(solutionConfiguration, outputFiles); // use the task to ensure none of the output files are // marked read-only AttribTask attribTask = new AttribTask(); // parent is solution task attribTask.Parent = SolutionTask; // inherit project from solution task attribTask.Project = SolutionTask.Project; // inherit namespace manager from solution task attribTask.NamespaceManager = SolutionTask.NamespaceManager; // inherit verbose setting from solution task attribTask.Verbose = SolutionTask.Verbose; // only output warning messages or higher, unless // we're running in verbose mode if (!attribTask.Verbose) { attribTask.Threshold = Level.Warning; } // make sure framework specific information is set attribTask.InitializeTaskConfiguration(); // set parent of child elements attribTask.AttribFileSet.Parent = attribTask; // inherit project for child elements from containing task attribTask.AttribFileSet.Project = attribTask.Project; // inherit namespace manager from containing task attribTask.AttribFileSet.NamespaceManager = attribTask.NamespaceManager; // we want to reset the read-only attribute of all output files attribTask.ReadOnlyAttrib = false; // obtain project configuration (corresponding with solution configuration) ConfigurationBase config = (ConfigurationBase) BuildConfigurations[solutionConfiguration]; // add all output files to the fileset foreach (DictionaryEntry de in outputFiles) { attribTask.AttribFileSet.Includes.Add(FileUtils.CombinePaths( config.OutputDir.FullName, (string) de.Value)); } // increment indentation level attribTask.Project.Indent(); try { // execute task attribTask.Execute(); } finally { // restore indentation level attribTask.Project.Unindent(); } } protected abstract BuildResult Build(string solutionConfiguration); /// /// Copies the specified file if the destination file does not exist, or /// the source file has been modified since it was previously copied. /// /// The file to copy. /// The destination file. /// The in which context the operation will be performed. protected void CopyFile(FileInfo srcFile, FileInfo destFile, Task parent) { // create instance of Copy task CopyTask ct = new CopyTask(); // parent is solution task ct.Parent = parent; // inherit project from parent task ct.Project = parent.Project; // inherit namespace manager from parent task ct.NamespaceManager = parent.NamespaceManager; // inherit verbose setting from parent task ct.Verbose = parent.Verbose; // only output warning messages or higher, unless // we're running in verbose mode if (!ct.Verbose) { ct.Threshold = Level.Warning; } // make sure framework specific information is set ct.InitializeTaskConfiguration(); // set parent of child elements ct.CopyFileSet.Parent = ct; // inherit project for child elements from containing task ct.CopyFileSet.Project = ct.Project; // inherit namespace manager from containing task ct.CopyFileSet.NamespaceManager = ct.NamespaceManager; // set file to copy ct.SourceFile = srcFile; // set file ct.ToFile = destFile; // increment indentation level ct.Project.Indent(); try { // execute task ct.Execute(); } finally { // restore indentation level ct.Project.Unindent(); } } protected bool ExecuteBuildEvent(string buildEvent, string buildCommandLine, string batchFile, string workingDirectory, ConfigurationBase config) { // create the batch file using (StreamWriter sw = new StreamWriter(batchFile)) { sw.WriteLine("@echo off"); // replace any VS macros in the command line with real values buildCommandLine = config.ExpandMacros(buildCommandLine); // handle linebreak charaters buildCommandLine = buildCommandLine.Replace(" ", "\n"); sw.WriteLine(buildCommandLine); sw.WriteLine("if errorlevel 1 goto EventReportError"); sw.WriteLine("goto EventEnd"); sw.WriteLine(":EventReportError"); sw.WriteLine("echo Project error: A tool returned an error code from the build event"); sw.WriteLine("exit 1"); sw.WriteLine(":EventEnd"); } // execute the batch file ProcessStartInfo psi = new ProcessStartInfo(batchFile); psi.UseShellExecute = false; psi.RedirectStandardOutput = true; // For logging psi.WorkingDirectory = workingDirectory; // start the process now Process batchEvent = Process.Start(psi); // keep logging output from the process for as long as it exists while (true) { string logContents = batchEvent.StandardOutput.ReadLine(); if (logContents == null) { break; } Log(Level.Verbose, " [" + buildEvent.ToLower(CultureInfo.InvariantCulture) + "] " + logContents); } batchEvent.WaitForExit(); // notify if there where problems running the batch file or it // returned errors int exitCode = batchEvent.ExitCode; if (exitCode == 0) { Log(Level.Verbose, "{0} succeeded (exit code = 0)", buildEvent); } else { Log(Level.Error, "{0} failed with exit code = {1}", buildEvent, exitCode); } return (exitCode == 0) ? true : false; } /// /// Logs a message with the given priority. /// /// The message priority at which the specified message is to be logged. /// The message to be logged. /// /// The actual logging is delegated to the underlying task. /// protected void Log(Level messageLevel, string message) { if (SolutionTask != null) { SolutionTask.Log(messageLevel, message); } } /// /// Logs a message with the given priority. /// /// The message priority at which the specified message is to be logged. /// The message to log, containing zero or more format items. /// An array containing zero or more objects to format. /// /// The actual logging is delegated to the underlying task. /// protected void Log(Level messageLevel, string message, params object[] args) { if (SolutionTask != null) { SolutionTask.Log(messageLevel, message, args); } } #endregion Protected Instance Methods #region Protected Static Methods protected static XmlDocument LoadXmlDocument(string fileName) { return ProjectFactory.LoadProjectXml(fileName); } #endregion Protected Static Methods #region Private Instance Fields private readonly ProductVersion _productVersion; private readonly SolutionTask _solutionTask; private readonly TempFileCollection _temporaryFiles; private readonly DirectoryInfo _outputDir; private readonly Hashtable _projectConfigurations; private readonly Hashtable _buildConfigurations; private readonly GacCache _gacCache; private readonly ReferencesResolver _refResolver; private readonly Hashtable _extraOutputFiles; private readonly ProjectBaseCollection _projectDependencies = new ProjectBaseCollection(); #endregion Private Instance Fields } /// /// Specifies the type of the project. /// public enum ProjectType { /// /// A Visual Basic.NET project. /// VB = 0, /// /// A Visual C# project. /// CSharp = 1, /// /// A Visual C++ project. /// VisualC = 2, /// /// A Visual J# project. /// JSharp = 3 } /// /// Specifies the result of the build. /// public enum BuildResult { /// /// The build failed. /// Failed = 0, /// /// The build succeeded. /// Success = 1, /// /// The build succeeded and the output was updated. /// SuccessOutputUpdated = 2, } public enum ProductVersion { /// /// Visual Studio.NET 2002 /// Rainier = 1, /// /// Visual Studio.NET 2003 /// Everett = 2, } /// /// Indentifies the physical location of a managed project. /// public enum ProjectLocation { /// /// A local project. /// Local = 1, /// /// A web project. /// Web = 2, } /// /// Contains a collection of elements. /// [Serializable()] public class ProjectBaseCollection : CollectionBase { #region Public Instance Constructors /// /// Initializes a new instance of the class. /// public ProjectBaseCollection() { } /// /// Initializes a new instance of the class /// with the specified instance. /// public ProjectBaseCollection(ProjectBaseCollection value) { AddRange(value); } /// /// Initializes a new instance of the class /// with the specified array of instances. /// public ProjectBaseCollection(ProjectBase[] value) { AddRange(value); } #endregion Public Instance Constructors #region Public Instance Properties /// /// Gets or sets the element at the specified index. /// /// The zero-based index of the element to get or set. [System.Runtime.CompilerServices.IndexerName("Item")] public ProjectBase this[int index] { get { return (ProjectBase) base.List[index]; } set { base.List[index] = value; } } /// /// Gets the with the specified GUID. /// /// The GUID of the to get. /// /// Performs a case-insensitive lookup. /// [System.Runtime.CompilerServices.IndexerName("Item")] public ProjectBase this[string guid] { get { if (guid == null) { throw new ArgumentNullException("guid"); } // try to locate instance by guid (case-insensitive) foreach (ProjectBase project in base.List) { if (string.Compare(project.Guid, guid, true, CultureInfo.InvariantCulture) == 0) { return project; } } return null; } } #endregion Public Instance Properties #region Public Instance Methods /// /// Adds a to the end of the collection. /// /// The to be added to the end of the collection. /// The position into which the new element was inserted. public int Add(ProjectBase item) { return base.List.Add(item); } /// /// Adds the elements of a array to the end of the collection. /// /// The array of elements to be added to the end of the collection. public void AddRange(ProjectBase[] items) { for (int i = 0; (i < items.Length); i = (i + 1)) { Add(items[i]); } } /// /// Adds the elements of a to the end of the collection. /// /// The to be added to the end of the collection. public void AddRange(ProjectBaseCollection items) { for (int i = 0; (i < items.Count); i = (i + 1)) { Add(items[i]); } } /// /// Determines whether a is in the collection. /// /// The to locate in the collection. /// /// if is found in the /// collection; otherwise, . /// public bool Contains(ProjectBase item) { return base.List.Contains(item); } /// /// Determines whether a with the specified /// GUID is in the collection, using a case-insensitive lookup. /// /// The GUID to locate in the collection. /// /// if a with GUID /// is found in the collection; otherwise, /// . /// public bool Contains(string value) { return this[value] != null; } /// /// Copies the entire collection to a compatible one-dimensional array, starting at the specified index of the target array. /// /// The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing. /// The zero-based index in at which copying begins. public void CopyTo(ProjectBase[] array, int index) { base.List.CopyTo(array, index); } /// /// Retrieves the index of a specified object in the collection. /// /// The object for which the index is returned. /// /// The index of the specified . If the is not currently a member of the collection, it returns -1. /// public int IndexOf(ProjectBase item) { return base.List.IndexOf(item); } /// /// Inserts a into the collection at the specified index. /// /// The zero-based index at which should be inserted. /// The to insert. public void Insert(int index, ProjectBase item) { base.List.Insert(index, item); } /// /// Returns an enumerator that can iterate through the collection. /// /// /// A for the entire collection. /// public new ProjectBaseEnumerator GetEnumerator() { return new ProjectBaseEnumerator(this); } /// /// Removes a member from the collection. /// /// The to remove from the collection. public void Remove(ProjectBase item) { base.List.Remove(item); } /// /// Remove items with the specified guid from the collection. /// /// The guid of the project to remove from the collection. public void Remove(string guid) { ProjectBase removeProject = null; // try to locate instance by guid (case-insensitive) foreach (ProjectBase project in base.List) { if (string.Compare(project.Guid, guid, true, CultureInfo.InvariantCulture) == 0) { removeProject = project; break; } } if (removeProject != null) { base.InnerList.Remove(removeProject); } } #endregion Public Instance Methods } /// /// Enumerates the elements of a . /// public class ProjectBaseEnumerator : IEnumerator { #region Internal Instance Constructors /// /// Initializes a new instance of the class /// with the specified . /// /// The collection that should be enumerated. internal ProjectBaseEnumerator(ProjectBaseCollection arguments) { IEnumerable temp = (IEnumerable) (arguments); _baseEnumerator = temp.GetEnumerator(); } #endregion Internal Instance Constructors #region Implementation of IEnumerator /// /// Gets the current element in the collection. /// /// /// The current element in the collection. /// public ProjectBase Current { get { return (ProjectBase) _baseEnumerator.Current; } } object IEnumerator.Current { get { return _baseEnumerator.Current; } } /// /// Advances the enumerator to the next element of the collection. /// /// /// if the enumerator was successfully advanced /// to the next element; if the enumerator has /// passed the end of the collection. /// public bool MoveNext() { return _baseEnumerator.MoveNext(); } bool IEnumerator.MoveNext() { return _baseEnumerator.MoveNext(); } /// /// Sets the enumerator to its initial position, which is before the /// first element in the collection. /// public void Reset() { _baseEnumerator.Reset(); } void IEnumerator.Reset() { _baseEnumerator.Reset(); } #endregion Implementation of IEnumerator #region Private Instance Fields private IEnumerator _baseEnumerator; #endregion Private Instance Fields } }