NestedProjectNode.cs :  » Development » StyleCop » Microsoft » VisualStudio » Package » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » StyleCop 
StyleCop » Microsoft » VisualStudio » Package » NestedProjectNode.cs
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/

using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using System.Diagnostics;
using System.Globalization;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using System.Net;
using MSBuildMicrosoft.Build.BuildEngine;
using IOleServiceProviderMicrosoft.VisualStudio.OLE.Interop.IServiceProvider;
using IServiceProviderSystem.IServiceProvider;
using ShellConstantsMicrosoft.VisualStudio.Shell.Interop.Constants;
using OleConstantsMicrosoft.VisualStudio.OLE.Interop.Constants;
using ErrorHandlerMicrosoft.VisualStudio.ErrorHandler;
using VSLangProj80;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.VisualStudio.Package{
    [CLSCompliant(false), ComVisible(true)]
    public class NestedProjectNode : HierarchyNode, IPropertyNotifySink
    {

        #region fields
        private IVsHierarchy nestedHierarchy;

        Guid projectInstanceGuid = Guid.Empty;

        private string projectName = String.Empty;

        private string projectPath = String.Empty;

        private ImageHandler imageHandler;

        /// <summary>
        /// Defines an object that will be a mutex for this object for synchronizing thread calls.
        /// </summary>
        private static volatile object Mutex = new object();

        /// <summary>
        /// Sets the dispose flag on the object.
        /// </summary>
        private bool isDisposed;

        // A cooike retrieved when advising on property chnanged events.
        private uint projectPropertyNotifySinkCookie;
        #endregion

        #region properties
        internal IVsHierarchy NestedHierarchy
        {
            get
            {
                return this.nestedHierarchy;
            }
        }
        #endregion

        #region virtual properties
        /// <summary>
        /// Returns the __VSADDVPFLAGS that will be passed in when calling AddVirtualProjectEx
        /// </summary>
        protected virtual uint VirtualProjectFlags
        {
            get { return 0; }
        }
        #endregion

        #region overridden properties
        /// <summary>
        /// The path of the nested project.
        /// </summary>
        public override string Url
        {
            get
            {
                return this.projectPath;
            }
        }

        /// <summary>
        /// The Caption of the nested project.
        /// </summary>
        public override string Caption
        {
            get
            {
                return Path.GetFileNameWithoutExtension(this.projectName);
            }
        }

        public override Guid ItemTypeGuid
        {
            get
            {
                return VSConstants.GUID_ItemType_SubProject;
            }
        }

        /// <summary>
        /// Defines whether a node can execute a command if in selection.
        /// We do this in order to let the nested project to handle the execution of its own commands.
        /// </summary>
        public override bool CanExecuteCommand
        {
            get
            {
                return false;
            }
        }

        public override int SortPriority
        {
            get { return DefaultSortOrderNode.NestedProjectNode; }
        }

        protected bool IsDisposed
        {
            get { return this.isDisposed; }
            set { this.isDisposed = value; }
        }



        #endregion

        #region ctor

        protected NestedProjectNode()
        {
        }

        public NestedProjectNode(ProjectNode root, ProjectElement element)
            : base(root, element)
        {
            this.IsExpanded = true;
        }
        #endregion

        #region IPropertyNotifySink Members
        /// <summary>
        /// Notifies a sink that the [bindable] property specified by dispID has changed. 
        /// If dispID is DISPID_UNKNOWN, then multiple properties have changed together. 
        /// The client (owner of the sink) should then retrieve the current value of each property of interest from the object that generated the notification.
        /// In our case we will care about the  VSLangProj80.VsProjPropId.VBPROJPROPID_FileName and update the changes in the parent project file.
        /// </summary>
        /// <param name="dispid">Dispatch identifier of the property that is about to change or DISPID_UNKNOWN if multiple properties are about to change.</param>
        public virtual void OnChanged(int dispid)
        {
            if (dispid == (int)VSLangProj80.VsProjPropId.VBPROJPROPID_FileName)
            {
                // Get the filename of the nested project. Inetead of asking the label on the nested we ask the filename, since the label might not yet been set.
                IVsProject3 nestedProject = this.nestedHierarchy as IVsProject3;

                if (nestedProject != null)
                {
                    string document;
                    nestedProject.GetMkDocument(VSConstants.VSITEMID_ROOT, out document);
                    this.RenameNestedProjectInParentProject(Path.GetFileNameWithoutExtension(document));

                    // We need to redraw the caption since for some reason, by intervining to the OnChanged event the Caption is not updated.
                    this.ReDraw(UIHierarchyElement.Caption);
                }
            }
        }

        /// <summary>
        /// Notifies a sink that a [requestedit] property is about to change and that the object is asking the sink how to proceed.
        /// </summary>
        /// <param name="dispid">Dispatch identifier of the property that is about to change or DISPID_UNKNOWN if multiple properties are about to change.</param>
        public virtual void OnRequestEdit(int dispid)
        {

        }

        #endregion

        #region public methods
        #endregion

        #region overridden methods

        /// <summary>
        /// Get the automation object for the NestedProjectNode
        /// </summary>
        /// <returns>An instance of the Automation.OANestedProjectItem type if succeded</returns>
        public override object GetAutomationObject()
        {
            //Validate that we are not disposed or the project is closing
            if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                return null;
            }

            return new Automation.OANestedProjectItem(this.ProjectMgr.GetAutomationObject() as Automation.OAProject, this);
        }

        /// <summary>
        /// Gets properties of a given node or of the hierarchy.
        /// </summary>
        /// <param name="propId">Identifier of the hierarchy property</param>
        /// <returns>It return an object which type is dependent on the propid.</returns>
        public override object GetProperty(int propId)
        {
            __VSHPROPID vshPropId = (__VSHPROPID)propId;
            switch (vshPropId)
            {
                default:
                    return base.GetProperty(propId);

                case __VSHPROPID.VSHPROPID_Expandable:
                    return true;

                case __VSHPROPID.VSHPROPID_BrowseObject:
                case __VSHPROPID.VSHPROPID_HandlesOwnReload:
                    return this.DelegateGetPropertyToNested(propId);
            }
        }


        /// <summary>
        /// Gets properties whose values are GUIDs.
        /// </summary>
        /// <param name="propid">Identifier of the hierarchy property</param>
        /// <param name="guid"> Pointer to a GUID property specified in propid</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public override int GetGuidProperty(int propid, out Guid guid)
        {
            guid = Guid.Empty;
            switch ((__VSHPROPID)propid)
            {
                case __VSHPROPID.VSHPROPID_ProjectIDGuid:
                    guid = this.projectInstanceGuid;
                    break;

                default:
                    return base.GetGuidProperty(propid, out guid);
            }

            CCITracing.TraceCall(String.Format(CultureInfo.CurrentCulture, "Guid for {0} property", propid));
            if (guid.CompareTo(Guid.Empty) == 0)
            {
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }

            return VSConstants.S_OK;
        }


        /// <summary>
        /// Determines whether the hierarchy item changed.
        /// </summary>
        /// <param name="itemId">Item identifier of the hierarchy item contained in VSITEMID</param>
        /// <param name="punkDocData">Pointer to the IUnknown interface of the hierarchy item. </param>
        /// <param name="pfDirty">TRUE if the hierarchy item changed.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public override int IsItemDirty(uint itemId, IntPtr punkDocData, out int pfDirty)
        {
            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
            Debug.Assert(punkDocData != IntPtr.Zero, "docData intptr was zero");

            // Get an IPersistFileFormat object from docData object 
            IPersistFileFormat persistFileFormat = Marshal.GetTypedObjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFileFormat;
            Debug.Assert(persistFileFormat != null, "The docData object does not implement the IPersistFileFormat interface");

            // Call IsDirty on the IPersistFileFormat interface
            ErrorHandler.ThrowOnFailure(persistFileFormat.IsDirty(out pfDirty));

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Saves the hierarchy item to disk.
        /// </summary>
        /// <param name="dwSave">Flags whose values are taken from the VSSAVEFLAGS enumeration.</param>
        /// <param name="silentSaveAsName">File name to be applied when dwSave is set to VSSAVE_SilentSave. </param>
        /// <param name="itemid">Item identifier of the hierarchy item saved from VSITEMID. </param>
        /// <param name="punkDocData">Pointer to the IUnknown interface of the hierarchy item saved.</param>
        /// <param name="pfCancelled">TRUE if the save action was canceled. </param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public override int SaveItem(VSSAVEFLAGS dwSave, string silentSaveAsName, uint itemid, IntPtr punkDocData, out int pfCancelled)
        {
            // Don't ignore/unignore file changes 
            // Use Advise/Unadvise to work around rename situations
            try
            {
                this.StopObservingNestedProjectFile();
                Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
                Debug.Assert(punkDocData != IntPtr.Zero, "docData intptr was zero");

                // Get an IPersistFileFormat object from docData object (we don't call release on punkDocData since did not increment its ref count)
                IPersistFileFormat persistFileFormat = Marshal.GetTypedObjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFileFormat;
                Debug.Assert(persistFileFormat != null, "The docData object does not implement the IPersistFileFormat interface");

                IVsUIShell uiShell = this.GetService(typeof(SVsUIShell)) as IVsUIShell;
                string newName;
                uiShell.SaveDocDataToFile(dwSave, persistFileFormat, silentSaveAsName, out newName, out pfCancelled);

                // When supported do a rename of the nested project here 
            }
            finally
            {
                // Succeeded or not we must hook to the file change events
                // Don't ignore/unignore file changes 
                // Use Advise/Unadvise to work around rename situations
                this.ObserveNestedProjectFile();
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Gets the icon handle. It tries first the nested to get the icon handle. If that is not supported it will get it from
        /// the image list of the nested if that is supported. If neither of these is supported a default image will be shown.
        /// </summary>
        /// <returns>An object representing the icon.</returns>
        public override object GetIconHandle(bool open)
        {
            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");

            object iconHandle = null;
            this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconHandle, out iconHandle);
            if (iconHandle == null)
            {
                if (null == imageHandler)
                {
                    InitImageHandler();
                }
                // Try to get an icon from the nested hierrachy image list.
                if (imageHandler.ImageList != null)
                {
                    object imageIndexAsObject = null;
                    if (this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconIndex, out imageIndexAsObject) == VSConstants.S_OK &&
                        imageIndexAsObject != null)
                    {
                        int imageIndex = (int)imageIndexAsObject;
                        if (imageIndex < imageHandler.ImageList.Images.Count)
                        {
                            iconHandle = imageHandler.GetIconHandle(imageIndex);
                        }
                    }
                }

                if (null == iconHandle)
                {
                    iconHandle = this.ProjectMgr.ImageHandler.GetIconHandle((int)ProjectNode.ImageName.Application);
                }
            }

            return iconHandle;
        }

        /// <summary>
        /// Return S_OK. Implementation of Closing a nested project is done in CloseNestedProject which is called by CloseChildren.
        /// </summary>
        /// <returns>S_OK</returns>
        public override int Close()
        {
            return VSConstants.S_OK;
        }


        /// <summary>
        /// Returns the moniker of the nested project.
        /// </summary>
        /// <returns></returns>
        public override string GetMkDocument()
        {
            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
            if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                return String.Empty;
            }

            return this.projectPath;
        }

        /// <summary>
        /// Called by the shell when a node has been renamed from the GUI
        /// </summary>
        /// <param name="label">The name of the new label.</param>
        /// <returns>A success or failure value.</returns>
        public override int SetEditLabel(string label)
        {
            int result = this.DelegateSetPropertyToNested((int)__VSHPROPID.VSHPROPID_EditLabel, label);
            if (ErrorHandler.Succeeded(result))
            {
                this.RenameNestedProjectInParentProject(label);
            }

            return result;
        }

        /// <summary>
        /// Called by the shell to get the node caption when the user tries to rename from the GUI
        /// </summary>
        /// <returns>the node cation</returns>
        public override string GetEditLabel()
        {
            return (string)this.DelegateGetPropertyToNested((int)__VSHPROPID.VSHPROPID_EditLabel);
        }      

        /// <summary>
        /// This is temporary until we have support for re-adding a nested item
        /// </summary>
        protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation)
        {
            return false;
        }

        /// <summary>
        /// Delegates the call to the inner hierarchy.
        /// </summary>
        /// <param name="reserved">Reserved parameter defined at the IVsPersistHierarchyItem2::ReloadItem parameter.</param>
        protected internal override void ReloadItem(uint reserved)
        {
            #region precondition
            if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                throw new InvalidOperationException();
            }

            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
            #endregion

            IVsPersistHierarchyItem2 persistHierachyItem = this.nestedHierarchy as IVsPersistHierarchyItem2;

            // We are expecting that if we get called then the nestedhierarchy supports IVsPersistHierarchyItem2, since then hierrachy should support handling its own reload.
            // There should be no errormessage to the user since this is an internal error, that it cannot be fixed at user level.
            if (persistHierachyItem == null)
            {
                throw new InvalidOperationException();
            }

            ErrorHandler.ThrowOnFailure(persistHierachyItem.ReloadItem(VSConstants.VSITEMID_ROOT, reserved));
        }

        /// <summary>
        /// Flag indicating that changes to a file can be ignored when item is saved or reloaded. 
        /// </summary>
        /// <param name="ignoreFlag">Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring).</param>
        protected internal override void IgnoreItemFileChanges(bool ignoreFlag)
        {
            #region precondition
            if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                throw new InvalidOperationException();
            }

            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
            #endregion

            this.IgnoreNestedProjectFile(ignoreFlag);

            IVsPersistHierarchyItem2 persistHierachyItem = this.nestedHierarchy as IVsPersistHierarchyItem2;

            // If the IVsPersistHierarchyItem2 is not implemented by the nested just return
            if (persistHierachyItem == null)
            {
                return;
            }

            ErrorHandler.ThrowOnFailure(persistHierachyItem.IgnoreItemFileChanges(VSConstants.VSITEMID_ROOT, ignoreFlag ? 1 : 0));
        }

        /// <summary>
        /// Sets the VSADDFILEFLAGS that will be used to call the  IVsTrackProjectDocumentsEvents2 OnAddFiles
        /// </summary>
        /// <param name="files">The files to which an array of VSADDFILEFLAGS has to be specified.</param>
        /// <returns></returns>
        protected internal override VSADDFILEFLAGS[] GetAddFileFlags(string[] files)
        {
            if (files == null || files.Length == 0)
            {
                return new VSADDFILEFLAGS[1] { VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags };
            }

            VSADDFILEFLAGS[] addFileFlags = new VSADDFILEFLAGS[files.Length];

            for (int i = 0; i < files.Length; i++)
            {
                addFileFlags[i] = VSADDFILEFLAGS.VSADDFILEFLAGS_IsNestedProjectFile;
            }

            return addFileFlags;
        }

        /// <summary>
        /// Sets the VSQUERYADDFILEFLAGS that will be used to call the  IVsTrackProjectDocumentsEvents2 OnQueryAddFiles
        /// </summary>
        /// <param name="files">The files to which an array of VSADDFILEFLAGS has to be specified.</param>
        /// <returns></returns>
        protected internal override VSQUERYADDFILEFLAGS[] GetQueryAddFileFlags(string[] files)
        {
            if (files == null || files.Length == 0)
            {
                return new VSQUERYADDFILEFLAGS[1] { VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags };
            }

            VSQUERYADDFILEFLAGS[] queryAddFileFlags = new VSQUERYADDFILEFLAGS[files.Length];

            for (int i = 0; i < files.Length; i++)
            {
                queryAddFileFlags[i] = VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_IsNestedProjectFile;
            }

            return queryAddFileFlags;
        }

        /// <summary>
        /// Sets the VSREMOVEFILEFLAGS that will be used to call the  IVsTrackProjectDocumentsEvents2 OnRemoveFiles
        /// </summary>
        /// <param name="files">The files to which an array of VSREMOVEFILEFLAGS has to be specified.</param>
        /// <returns></returns>
        protected internal override VSREMOVEFILEFLAGS[] GetRemoveFileFlags(string[] files)
        {
            if (files == null || files.Length == 0)
            {
                return new VSREMOVEFILEFLAGS[1] { VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_NoFlags };
            }

            VSREMOVEFILEFLAGS[] removeFileFlags = new VSREMOVEFILEFLAGS[files.Length];

            for (int i = 0; i < files.Length; i++)
            {
                removeFileFlags[i] = VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_IsNestedProjectFile;
            }

            return removeFileFlags;
        }

        /// <summary>
        /// Sets the VSQUERYREMOVEFILEFLAGS that will be used to call the  IVsTrackProjectDocumentsEvents2 OnQueryRemoveFiles
        /// </summary>
        /// <param name="files">The files to which an array of VSQUERYREMOVEFILEFLAGS has to be specified.</param>
        /// <returns></returns>
        protected internal override VSQUERYREMOVEFILEFLAGS[] GetQueryRemoveFileFlags(string[] files)
        {
            if (files == null || files.Length == 0)
            {
                return new VSQUERYREMOVEFILEFLAGS[1] { VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags };
            }

            VSQUERYREMOVEFILEFLAGS[] queryRemoveFileFlags = new VSQUERYREMOVEFILEFLAGS[files.Length];

            for (int i = 0; i < files.Length; i++)
            {
                queryRemoveFileFlags[i] = VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_IsNestedProjectFile;
            }

            return queryRemoveFileFlags;
        }
        #endregion

        #region virtual methods
        /// <summary>
        /// Initialize the nested hierarhy node.
        /// </summary>
        /// <param name="fileNameParam">The file name of the nested project.</param>
        /// <param name="destinationParam">The location of the nested project.</param>
        /// <param name="projectNameParam">The name of the project.</param>
        /// <param name="createFlags">The nested project creation flags </param>
        /// <remarks>This methos should be called just after a NestedProjectNode object is created.</remarks>
        public virtual void Init(string fileNameParam, string destinationParam, string projectNameParam, __VSCREATEPROJFLAGS createFlags)
        {
            if (String.IsNullOrEmpty(fileNameParam))
            {
                throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "fileNameParam");
            }

            if (String.IsNullOrEmpty(destinationParam))
            {
                throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "destinationParam");
            }

            this.projectName = Path.GetFileName(fileNameParam);
            this.projectPath = Path.Combine(destinationParam, this.projectName);

            // get the IVsSolution interface from the global service provider
            IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution;
            Debug.Assert(solution != null, "Could not get the IVsSolution object from the services exposed by this project");
            if (solution == null)
            {
                throw new InvalidOperationException();
            }

            // Get the project type guid from project element        
            string typeGuidString = this.ItemNode.GetMetadataAndThrow(ProjectFileConstants.TypeGuid, new InvalidOperationException());
            Guid projectFactoryGuid = Guid.Empty;
            if (!String.IsNullOrEmpty(typeGuidString))
            {
                projectFactoryGuid = new Guid(typeGuidString);
            }

            // Get the project factory.
            IVsProjectFactory projectFactory;
            ErrorHandler.ThrowOnFailure(solution.GetProjectFactory((uint)0, new Guid[] { projectFactoryGuid }, fileNameParam, out projectFactory));

            this.CreateProjectDirectory();

            //Create new project using factory
            int cancelled;
            Guid refiid = NativeMethods.IID_IUnknown;
            IntPtr projectPtr = IntPtr.Zero;
            
            // For a nested project the creation at unsafe location is governed by the parent project since the nested project will end up in the cone of the parent project.
            bool dontShowAgain = DontShowAgainDialog.ReadDontShowAgainValue(ProjectFactory.DontShowProjectSecurityWarningAgain);

            try
            {
                DontShowAgainDialog.WriteDontShowAgainValue(ProjectFactory.DontShowProjectSecurityWarningAgain, 1);
                ErrorHandler.ThrowOnFailure(projectFactory.CreateProject(fileNameParam, destinationParam, projectNameParam, (uint)createFlags, ref refiid, out projectPtr, out cancelled));

                if (projectPtr != IntPtr.Zero)
                {
                    this.nestedHierarchy = Marshal.GetTypedObjectForIUnknown(projectPtr, typeof(IVsHierarchy)) as IVsHierarchy;
                    Debug.Assert(this.nestedHierarchy != null, "Nested hierarchy could not be created");
                    Debug.Assert(cancelled == 0);
                }
            }
            finally
            {
                if (projectPtr != IntPtr.Zero)
                {
                    // We created a new instance of the project, we need to call release to decrement the ref count
                    // the RCW (this.nestedHierarchy) still has a reference to it which will keep it alive
                    Marshal.Release(projectPtr);
                }

                // Revert back the old value that security questions about unsafe location are stil asked if that was the value.
                if (!dontShowAgain)
                {
                    DontShowAgainDialog.WriteDontShowAgainValue(ProjectFactory.DontShowProjectSecurityWarningAgain, 0);
                }
            }

            if (cancelled != 0 && this.nestedHierarchy == null)
            {
                ErrorHandler.ThrowOnFailure(VSConstants.OLE_E_PROMPTSAVECANCELLED);
            }

            // Link into the nested VS hierarchy.
            ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchy, this.ProjectMgr));
            ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchyItemid, (object)(int)this.ID));

            this.LockRDTEntry();

            this.ConnectPropertyNotifySink();
        }

        /// <summary>
        /// Links a nested project as a virtual project to the solution.
        /// </summary>
        protected internal virtual void AddVirtualProject()
        {
            // This is the second step in creating and adding a nested project. The inner hierarchy must have been
            // already initialized at this point. 
            #region precondition
            if (this.nestedHierarchy == null)
            {
                throw new InvalidOperationException();
            }
            #endregion
            // get the IVsSolution interface from the global service provider
            IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution;
            Debug.Assert(solution != null, "Could not get the IVsSolution object from the services exposed by this project");
            if (solution == null)
            {
                throw new InvalidOperationException();
            }

            this.InitializeInstanceGuid();

            // Add virtual project to solution.
            ErrorHandler.ThrowOnFailure(solution.AddVirtualProjectEx(this.nestedHierarchy, this.VirtualProjectFlags, ref this.projectInstanceGuid));

            // Now set up to listen on file changes on the nested project node.
            this.ObserveNestedProjectFile();
        }

        /// <summary>
        /// The method that does the cleanup.
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
      // Everybody can go here.
      if (!this.isDisposed)
      {
        try
        {
          // Synchronize calls to the Dispose simulteniously.
          lock (Mutex)
          {
            if (disposing)
            {
              this.DisconnectPropertyNotifySink();
              this.StopObservingNestedProjectFile();
              this.imageHandler.Close();
            }            
          }
        }
        finally
        {
          base.Dispose(disposing);
          this.isDisposed = true;
        }
      }
        }

        /// <summary>
        /// Creates the project directory if it does not exist.
        /// </summary>
        /// <returns></returns>
        protected virtual void CreateProjectDirectory()
        {
            string directoryName = Path.GetDirectoryName(this.projectPath);

            if (!Directory.Exists(directoryName))
            {
                Directory.CreateDirectory(directoryName);
            }
        }


        /// <summary>
        /// Lock the RDT Entry for the nested project.
        /// By default this document is marked as "Dont Save as". That means the menu File->SaveAs is disabled for the
        /// nested project node.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "RDT")]
        protected virtual void LockRDTEntry()
        {
            // Define flags for the nested project document
            _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_VirtualDocument | _VSRDTFLAGS.RDT_ProjSlnDocument; ;

            // Request the RDT service
            IVsRunningDocumentTable rdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
            Debug.Assert(rdt != null, " Could not get running document table from the services exposed by this project");
            if (rdt == null)
            {
                throw new InvalidOperationException();
            }

            // First we see if someone else has opened the requested view of the file.
            uint itemid;
            IntPtr docData = IntPtr.Zero;
            IVsHierarchy ivsHierarchy;
            uint docCookie;
            IntPtr projectPtr = IntPtr.Zero;

            try
            {
                ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)flags, this.projectPath, out ivsHierarchy, out itemid, out docData, out docCookie));
                flags |= _VSRDTFLAGS.RDT_EditLock;

                if (ivsHierarchy != null && docCookie != (uint)ShellConstants.VSDOCCOOKIE_NIL)
                {
                    if (docCookie != this.DocCookie)
                    {
                        this.DocCookie = docCookie;
                    }
                }
                else
                {

                    // get inptr for hierarchy
                    projectPtr = Marshal.GetIUnknownForObject(this.nestedHierarchy);
                    Debug.Assert(projectPtr != IntPtr.Zero, " Project pointer for the nested hierarchy has not been initialized");
                    ErrorHandler.ThrowOnFailure(rdt.RegisterAndLockDocument((uint)flags, this.projectPath, this.ProjectMgr, this.ID, projectPtr, out docCookie));

                    this.DocCookie = docCookie;
                    Debug.Assert(this.DocCookie != (uint)ShellConstants.VSDOCCOOKIE_NIL, "Invalid cookie when registering document in the running document table.");

                    //we must also set the doc cookie on the nested hier
                    this.SetDocCookieOnNestedHier(this.DocCookie);
                }
            }
            finally
            {
                // Release all Inptr's that that were given as out pointers
                if (docData != IntPtr.Zero)
                {
                    Marshal.Release(docData);
                }
                if (projectPtr != IntPtr.Zero)
                {
                    Marshal.Release(projectPtr);
                }
            }

        }

        /// <summary>
        /// Unlock the RDT entry for the nested project
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "RDT")]
        protected virtual void UnlockRDTEntry()
        {
            if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                return;
            }
            // First we see if someone else has opened the requested view of the file.
            IVsRunningDocumentTable rdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
            if (rdt != null && this.DocCookie != (int)ShellConstants.VSDOCCOOKIE_NIL)
            {
                _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_EditLock;

                ErrorHandler.ThrowOnFailure(rdt.UnlockDocument((uint)flags, (uint)this.DocCookie));
            }

            this.DocCookie = (int)ShellConstants.VSDOCCOOKIE_NIL;
        }

        /// <summary>
        /// Renames the project file in the parent project structure.
        /// </summary>
        /// <param name="label">The new label.</param>
        protected virtual void RenameNestedProjectInParentProject(string label)
        {
            string existingLabel = this.Caption;

            if (String.Compare(existingLabel, label, StringComparison.Ordinal) == 0)
            {
                return;
            }

            string oldFileName = this.projectPath;
            string oldPath = this.Url;

            try
            {
                this.StopObservingNestedProjectFile();
                this.ProjectMgr.SuspendMSBuild();

                // Check out the project file if necessary.
                if (!this.ProjectMgr.QueryEditProjectFile(false))
                {
                    throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
                }


                string newFileName = label + Path.GetExtension(oldFileName);
                this.SaveNestedProjectItemInProjectFile(newFileName);

                string projectDirectory = Path.GetDirectoryName(oldFileName);

                // update state.
                this.projectName = newFileName;
                this.projectPath = Path.Combine(projectDirectory, this.projectName);

                // Unload and lock the RDT entries
                this.UnlockRDTEntry();
                this.LockRDTEntry();

                // Since actually this is a rename in our hierarchy notify the tracker that a rename has happened.
                this.ProjectMgr.Tracker.OnItemRenamed(oldPath, this.projectPath, VSRENAMEFILEFLAGS.VSRENAMEFILEFLAGS_IsNestedProjectFile);
            }
            finally
            {
                this.ObserveNestedProjectFile();
                this.ProjectMgr.ResumeMSBuild(this.ProjectMgr.ReEvaluateProjectFileTargetName);
            }
        }
        /// <summary>
        /// Saves the nested project information in the project file.
        /// </summary>
        /// <param name="newFileName"></param>
        protected virtual void SaveNestedProjectItemInProjectFile(string newFileName)
        {
            string existingInclude = this.ItemNode.Item.Include;
            string existingRelativePath = Path.GetDirectoryName(existingInclude);
            string newRelativePath = Path.Combine(existingRelativePath, newFileName);
            this.ItemNode.Rename(newRelativePath);
        }
        #endregion    

        #region helper methods
        /// <summary>
        /// Closes a nested project and releases the nested hierrachy pointer.
        /// </summary>
        internal void CloseNestedProjectNode()
        {
            if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                return;
            }

            uint itemid = VSConstants.VSITEMID_NIL;
            try
            {
                this.DisconnectPropertyNotifySink();

                IVsUIHierarchy hier;

                IVsWindowFrame windowFrame;
                VsShellUtilities.IsDocumentOpen(this.ProjectMgr.Site, this.projectPath, Guid.Empty, out hier, out itemid, out windowFrame);


                if (itemid == VSConstants.VSITEMID_NIL)
                {
                    this.UnlockRDTEntry();
                }

                IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution;
                if (solution == null)
                {
                    throw new InvalidOperationException();
                }

                ErrorHandler.ThrowOnFailure(solution.RemoveVirtualProject(this.nestedHierarchy, 0));

            }
            finally
            {
                this.StopObservingNestedProjectFile();

                // if we haven't already release the RDT cookie, do so now.
                if (itemid == VSConstants.VSITEMID_NIL)
                {
                    this.UnlockRDTEntry();
                }
        
        this.Dispose(true);
            }
        }

        private void InitializeInstanceGuid()
        {
            if (this.projectInstanceGuid != Guid.Empty)
            {
                return;
            }

            Guid instanceGuid = Guid.Empty;

            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");

            // This method should be called from the open children method, then we can safely use the IsNewProject property
            if (this.ProjectMgr.IsNewProject)
            {
                instanceGuid = Guid.NewGuid();
                this.ItemNode.SetMetadata(ProjectFileConstants.InstanceGuid, instanceGuid.ToString("B"));
                this.nestedHierarchy.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, ref instanceGuid);

            }
            else
            {
                // Get a guid from the nested hiererachy.
                Guid nestedHiererachyInstanceGuid;
                this.nestedHierarchy.GetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, out nestedHiererachyInstanceGuid);

                // Get instance guid from the project file. If it does not exist then we create one.
                string instanceGuidAsString = this.ItemNode.GetMetadata(ProjectFileConstants.InstanceGuid);

                // 1. nestedHiererachyInstanceGuid is empty and instanceGuidAsString is empty then create a new one.
                // 2. nestedHiererachyInstanceGuid is empty and instanceGuidAsString not empty use instanceGuidAsString and update the nested project object by calling SetGuidProperty.
                // 3. nestedHiererachyInstanceGuid is not empty instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update the outer project element.
                // 4. nestedHiererachyInstanceGuid is not empty instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update the outer project element.

                if (nestedHiererachyInstanceGuid == Guid.Empty && String.IsNullOrEmpty(instanceGuidAsString))
                {
                    instanceGuid = Guid.NewGuid();
                }
                else if (nestedHiererachyInstanceGuid == Guid.Empty && !String.IsNullOrEmpty(instanceGuidAsString))
                {
                    instanceGuid = new Guid(instanceGuidAsString);

                    this.nestedHierarchy.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, ref instanceGuid);
                }
                else if (nestedHiererachyInstanceGuid != Guid.Empty)
                {
                    instanceGuid = nestedHiererachyInstanceGuid;

                    // If the instanceGuidAsString is empty then creating a guid out of it would throw an exception.
                    if (String.IsNullOrEmpty(instanceGuidAsString) || nestedHiererachyInstanceGuid != new Guid(instanceGuidAsString))
                    {
                        this.ItemNode.SetMetadata(ProjectFileConstants.InstanceGuid, instanceGuid.ToString("B"));
                    }
                }
            }

            this.projectInstanceGuid = instanceGuid;
        }

        private void SetDocCookieOnNestedHier(uint itemDocCookie)
        {
            object docCookie = (int)itemDocCookie;
            this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ItemDocCookie, docCookie);
        }

        private void InitImageHandler()
        {
            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");

            if (null == imageHandler)
            {
                imageHandler = new ImageHandler();
            }
            object imageListAsPointer = null;
            this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconImgList, out imageListAsPointer);
            if (imageListAsPointer != null)
            {
                this.imageHandler.ImageList = Utilities.GetImageList(imageListAsPointer);
            }
        }

        /// <summary>
        /// Delegates Getproperty calls to the inner nested.
        /// </summary>
        /// <param name="propID">The property to delegate.</param>
        /// <returns>The return of the GetProperty from nested.</returns>
        private object DelegateGetPropertyToNested(int propID)
        {
            object returnValue = null;
            if (!this.ProjectMgr.IsClosed)
            {
                Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");

                // Do not throw since some project types will return E_FAIL if they do not support a property.
                this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, propID, out returnValue);
            }
            return returnValue;
        }

        /// <summary>
        /// Delegates Setproperty calls to the inner nested.
        /// </summary>
        /// <param name="propID">The property to delegate.</param>
        /// <param name="value">The property to set.</param>
        /// <returns>The return of the SetProperty from nested.</returns>
        private int DelegateSetPropertyToNested(int propID, object value)
        {
            if (this.ProjectMgr.IsClosed)
            {
                return VSConstants.E_FAIL;
            }

            Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");

            // Do not throw since some project types will return E_FAIL if they do not support a property.
            return this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, propID, value);
        }

        /// <summary>
        /// Starts observing changes on this file.
        /// </summary>
        private void ObserveNestedProjectFile()
        {
            ProjectContainerNode parent = this.ProjectMgr as ProjectContainerNode;
            Debug.Assert(parent != null, "The parent project for nested projects should be subclassed from ProjectContainerNode");
            parent.NestedProjectNodeReloader.ObserveItem(this.GetMkDocument(), this.ID);
        }

        /// <summary>
        /// Stops observing changes on this file.
        /// </summary>
        private void StopObservingNestedProjectFile()
        {
            ProjectContainerNode parent = this.ProjectMgr as ProjectContainerNode;
            Debug.Assert(parent != null, "The parent project for nested projects should be subclassed from ProjectContainerNode");
            parent.NestedProjectNodeReloader.StopObservingItem(this.GetMkDocument());
        }

        /// <summary>
        /// Ignores observing changes on this file depending on the boolean flag.
        /// </summary>
        /// <param name="ignoreFlag">Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring).</param>
        private void IgnoreNestedProjectFile(bool ignoreFlag)
        {
            ProjectContainerNode parent = this.ProjectMgr as ProjectContainerNode;
            Debug.Assert(parent != null, "The parent project for nested projects should be subclassed from ProjectContainerNode");
            parent.NestedProjectNodeReloader.IgnoreItemChanges(this.GetMkDocument(), ignoreFlag);
        }

        /// <summary>
        /// We need to advise property notify sink on project properties so that
        /// we know when the project file is renamed through a property. 
        /// </summary>
        private void ConnectPropertyNotifySink()
        {
            if (this.projectPropertyNotifySinkCookie != (uint)ShellConstants.VSCOOKIE_NIL)
            {
                return;
            }

            IConnectionPoint connectionPoint = this.GetConnectionPointFromPropertySink();
            if (connectionPoint != null)
            {
                connectionPoint.Advise(this, out this.projectPropertyNotifySinkCookie);
            }
        }

        /// <summary>
        /// Disconnects the propertynotify sink
        /// </summary>
        private void DisconnectPropertyNotifySink()
        {
            if (this.projectPropertyNotifySinkCookie == (uint)ShellConstants.VSCOOKIE_NIL)
            {
                return;
            }

            IConnectionPoint connectionPoint = this.GetConnectionPointFromPropertySink();
            if (connectionPoint != null)
            {
                connectionPoint.Unadvise(this.projectPropertyNotifySinkCookie);
                this.projectPropertyNotifySinkCookie = (uint)ShellConstants.VSCOOKIE_NIL;
            }
        }

        /// <summary>
        /// Gets a ConnectionPoint for the IPropertyNotifySink interface.
        /// </summary>
        /// <returns></returns>
        private IConnectionPoint GetConnectionPointFromPropertySink()
        {
            IConnectionPoint connectionPoint = null;
            object browseObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_BrowseObject);
            IConnectionPointContainer connectionPointContainer = browseObject as IConnectionPointContainer;

            if (connectionPointContainer != null)
            {
                Guid guid = typeof(IPropertyNotifySink).GUID;
                connectionPointContainer.FindConnectionPoint(ref guid, out connectionPoint);
            }

            return connectionPoint;
        }
        #endregion       
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.