Example #1
0
        /// <summary>
        /// Notifies all of our event listeners that an item in the hierarchy has changed.
        /// </summary>
        /// <param name="node">The <see cref="Node"/> that has changed.</param>
        /// <param name="propertyId">The property that has changed.</param>
        public void OnPropertyChanged(Node node, __VSHPROPID propertyId)
        {
            Tracer.VerifyNonNullArgument(node, "node");

            object newValue;

            node.GetProperty(propertyId, out newValue);

            // There are some cases where the collection is changed while we're iterating it.
            // To be safe, we'll create a copy of the collection and iterate over that.
            // We just want a shallow copy, though, and not a deep (Clone) copy.
            ArrayList clone = new ArrayList(this.Values);

            for (int i = 0; i < clone.Count; i++)
            {
                IVsHierarchyEvents eventItem = (IVsHierarchyEvents)clone[i];
                Tracer.WriteLineVerbose(classType, "OnPropertyChanged", "Notifying event listener {0} that '{1}' has changed its {2} property to '{3}'.", this.CookieOf(i), node.Caption, propertyId, newValue);
                try
                {
                    eventItem.OnPropertyChanged(node.HierarchyId, (int)propertyId, 0);
                }
                catch (Exception e)
                {
                    Tracer.WriteLineWarning(classType, "OnPropertyChanged", "There was an exception in the event listener {0} event handling code: {1}", this.CookieOf(i), e.ToString());
                }
            }
        }
Example #2
0
 /// <summary>
 /// Verifies that the current node is named <paramref name="name"/>, showing a message box to the
 /// user if it is not.
 /// </summary>
 /// <param name="node">The <see cref="XmlNode"/> to check.</param>
 /// <param name="name">The expected name of the current node.</param>
 /// <returns>true if the current node is named <paramref name="name"/>; otherwise, false.</returns>
 protected bool VerifyNode(XmlNode node, string name)
 {
     if (node.Name != name)
     {
         Tracer.WriteLineWarning(classType, "VerifyNode", "Missing '{0}' element in the project file '{1}'.", name, this.Project.FilePath);
         if (!this.SilentFailures)
         {
             string message = Package.Instance.Context.NativeResources.GetString(ResId.IDS_E_PROJFILE_MISSINGSECTION, this.Project.FilePath, name);
             Package.Instance.Context.ShowErrorMessageBox(message);
         }
         return(false);
     }
     return(true);
 }
Example #3
0
        /// <summary>
        /// Gets the required attribute from the current node. If the attribute does not exist
        /// or cannot be converted to the target type, then a message box is shown to the user.
        /// </summary>
        /// <param name="node">The <see cref="XmlNode"/> to read.</param>
        /// <param name="name">The name of the attribute to retrieve.</param>
        /// <param name="value">The value of the attribute.</param>
        /// <returns>true if the attribute exists and the value can be converted to the target
        /// type; otherwise, false.</returns>
        protected bool GetRequiredAttribute(XmlNode node, string name, out string value)
        {
            XmlAttribute attribute = node.Attributes[name];

            if (attribute == null)
            {
                value = null;
                Tracer.WriteLineWarning(classType, "GetRequiredAttribute", "Missing required attribute '{0}' from '{1}'.", name, node.Name);
                if (!this.SilentFailures)
                {
                    string message = Package.Instance.Context.NativeResources.GetString(ResId.IDS_E_PROJFILE_MISSINGATTRIBUTE, this.Project.FilePath, name, node.Name);
                    Package.Instance.Context.ShowErrorMessageBox(message);
                }
                return(false);
            }
            value = attribute.Value;
            return(true);
        }
Example #4
0
        /// <summary>
        /// Gets a string from the resource file and formats it using the specified arguments.
        /// </summary>
        /// <param name="name">The resource identifier of the string to retrieve.</param>
        /// <param name="args">An array of objects to use in the formatting. Can be null or empty.</param>
        /// <returns>A formatted string from the resource file.</returns>
        public string GetString(string name, params object[] args)
        {
            string returnString = this.missingManifestString;

            // If we previously tried to get a string from the resource file and we had a
            // MissingManifestResourceException, then we don't want to try again. Exceptions
            // are expensive especially when we could be getting lots of strings.
            if (!this.isMissingManifest)
            {
                try
                {
                    // First give the subclass a chance to retrieve the string
                    if (this.IsStringDefined(name))
                    {
                        returnString = this.manager.GetString(name, CultureInfo.CurrentUICulture);
                    }
                    //\ Try getting the string from our assembly if the subclass can't handle it
                    else if (SconceStrings.IsValidStringName(name))
                    {
                        returnString = thisAssemblyManager.GetString(name, CultureInfo.CurrentUICulture);
                    }
                    else
                    {
                        Tracer.WriteLineWarning(classType, "GetString", "The string id '{0}' is not defined.", name);
                        returnString = name;
                    }

                    // Format the message if there are arguments specified. Note that we format
                    // using the CurrentCulture and not the CurrentUICulture (although in almost all
                    // cases it will be the same).
                    if (returnString != null && args != null && args.Length > 0)
                    {
                        returnString = String.Format(CultureInfo.CurrentCulture, returnString, args);
                    }
                }
                catch (MissingManifestResourceException e)
                {
                    this.isMissingManifest = true;
                    Tracer.Fail("The resource cannot be found in the assembly: {0}", e);
                }
            }

            return(returnString);
        }
Example #5
0
        /// <summary>
        /// Suspends any file change notifications to the environment for the wrapped file in
        /// preparation for a multi-stage file operation such as a rename.
        /// </summary>
        public void Suspend()
        {
            // We only want to suspend once.
            if (this.suspended)
            {
                return;
            }

            // Get the environment's change notifier.
            IVsFileChangeEx changeNotifier = Package.Instance.Context.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx;

            if (changeNotifier == null)
            {
                Tracer.WriteLineWarning(classType, "Suspend", "Could not get an instance of the IVsChangeEx interface.");
                return;
            }

            // Tell the environment to stop sending change notifications for the file.
            int hr = changeNotifier.IgnoreFile(0, this.FilePath, IgnoreChanges);

            Tracer.WriteLineIf(classType, "Suspend", Tracer.Level.Warning, NativeMethods.Failed(hr), "Could not tell the environment to ignore file changes to '{0}': Hr=0x{1:x}", this.FilePath, hr);
            NativeMethods.ThrowOnFailure(hr);

            // Get the IVsDocDataFileChangeControl interface from the DocumentData. We need this
            // to suspend file change notifications to all editors.
            DocumentInfo docInfo = Package.Instance.Context.RunningDocumentTable.FindByPath(this.FilePath);

            if (docInfo != null)
            {
                this.docDataFileChangeControl = docInfo.DocumentData as IVsDocDataFileChangeControl;
                if (this.docDataFileChangeControl != null)
                {
                    hr = this.docDataFileChangeControl.IgnoreFileChanges(IgnoreChanges);
                    NativeMethods.ThrowOnFailure(hr);
                }
            }

            // At this point we can consider ourself suspended.
            this.suspended = true;
            Tracer.WriteLineVerbose(classType, "Suspend", "Suspended file change notifications for '{0}'.", this.FilePath);
        }
Example #6
0
        /// <summary>
        /// Does the actual work of changing the caption after all of the verifications have been done
        /// that it's Ok to move the file.
        /// </summary>
        /// <param name="newCaption">The new caption.</param>
        /// <param name="newPath">The new absolute path.</param>
        public override void MoveNodeOnCaptionChange(string newCaption, string newPath)
        {
            string oldCaption            = this.Caption;
            string oldPath               = this.AbsolutePath;
            string oldRelativePath       = this.RelativePath;
            bool   updatedWindowCaptions = false;
            bool   removedNode           = false;
            Node   newNode               = null;

            // If we are currently selected, cache the value so we can restore the selection
            // after the addition.
            bool wasSelected = this.Selected;

            // Tell the environment to stop listening to file change events on the old file.
            using (FileChangeNotificationSuspender notificationSuspender = new FileChangeNotificationSuspender(oldPath))
            {
                // Make sure the environment says we can start the rename.
                if (!this.Hierarchy.AttachedProject.Tracker.CanRenameFile(oldPath, newPath))
                {
                    return;
                }

                // Move the file on the file system to match the new name.
                if (!this.IsVirtual && File.Exists(oldPath) && !File.Exists(newPath))
                {
                    Tracer.WriteLineInformation(classType, "MoveNodeOnCaptionChange", "Renaming the file '{0}' to '{1}'.", oldPath, newPath);
                    string newDirectoryName = Path.GetDirectoryName(newPath);
                    if (!Directory.Exists(newDirectoryName))
                    {
                        Directory.CreateDirectory(newDirectoryName);
                    }
                    File.Move(oldPath, newPath);
                }

                try
                {
                    // Update all of the windows that currently have this file opened in an editor.
                    this.UpdateOpenWindowCaptions(newCaption);
                    updatedWindowCaptions = true;

                    // We have to remove the node and re-add it so that we can have the sorting preserved.
                    // Also, if the extension has changed then we'll have to recreate a new type-specific
                    // FileNode. The easy way is to remove ourself from the project then tell the project
                    // to add an existing file.

                    string newRelativePath = PackageUtility.MakeRelative(this.Hierarchy.RootNode.AbsoluteDirectory, newPath);

                    // Remove ourself from the hierarchy.
                    this.Parent.Children.Remove(this);
                    removedNode = true;

                    // We have now been removed from the hierarchy. Do NOT call any virtual methods or
                    // methods that depend on our state after this point.

                    // Re-add ourself as a new incarnation (different object). Our life ends here.
                    newNode = this.Hierarchy.AddExistingFile(newRelativePath, true);

                    if (newNode != null)
                    {
                        // We need to set our hierarchy Id to match the new hierachy Id in case Visual Studio
                        // calls back into us for something.
                        this.SetHierarchyId(newNode.HierarchyId);

                        // Select the new node if we were previously selected.
                        if (wasSelected)
                        {
                            newNode.Select();
                        }

                        // Tell the RDT to rename the document.
                        Context.RunningDocumentTable.RenameDocument(oldPath, newPath, newNode.HierarchyId);
                    }
                }
                catch (Exception e)
                {
                    if (ErrorUtility.IsExceptionUnrecoverable(e))
                    {
                        throw;
                    }

                    // Rollback the file move
                    Tracer.WriteLineWarning(classType, "MoveNodeOnCaptionChange", "There was an error in renaming the document. Exception: {0}", e);
                    File.Move(newPath, oldPath);

                    // Remove the node that we just added.
                    if (newNode != null)
                    {
                        newNode.RemoveFromProject();
                    }

                    // Re-add a new node since we've already removed the old node.
                    if (removedNode || this.Parent == null)
                    {
                        newNode = this.Hierarchy.AddExistingFile(oldRelativePath, true);
                        this.SetHierarchyId(newNode.HierarchyId);
                        if (wasSelected)
                        {
                            newNode.Select();
                        }
                    }

                    // Rollback the caption update on open windows
                    if (updatedWindowCaptions)
                    {
                        this.UpdateOpenWindowCaptions(oldCaption);
                    }

                    // Rethrow the exception
                    throw;
                }

                // Tell the environment that we're done renaming the document.
                this.Hierarchy.AttachedProject.Tracker.OnFileRenamed(oldPath, newPath);

                // Update the property browser.
                IVsUIShell vsUIShell = (IVsUIShell)this.Hierarchy.ServiceProvider.GetServiceOrThrow(typeof(SVsUIShell), typeof(IVsUIShell), classType, "MoveNodeOnCaptionChange");
                vsUIShell.RefreshPropertyBrowser(0);
            }
        }
Example #7
0
        /// <summary>
        /// Reads the &lt;WindowsInstallerXml&gt; node.
        /// </summary>
        /// <param name="node">The <see cref="XmlNode"/> to parse.</param>
        /// <returns>true if the node was read correctly; otherwise, false.</returns>
        protected bool ReadProjectNode(XmlNode node)
        {
            if (!this.VerifyNode(node, this.ProjectElementName))
            {
                return(false);
            }

            // Read and validate all of the required attributes.
            // -------------------------------------------------

            // SchemaVersion
            Version fileSchemaVersion = this.SchemaVersion;
            string  schemaString      = XmlHelperMethods.GetAttributeString(node, AttributeNames.SchemaVersion, this.SchemaVersion.ToString());

            try
            {
                fileSchemaVersion = new Version(schemaString);
            }
            catch (Exception e)
            {
                Tracer.WriteLineWarning(classType, "ReadProjectNode", "Cannot parse the SchemaVersion attribute {0}: {1}.", schemaString, e.ToString());
            }
            if (fileSchemaVersion < this.SchemaVersion)
            {
                // Right now we only support version 1.0 schemas, but if we ever change the schema
                // with new versions then we would need to ask the user if he/she wants to upgrade
                // the project to the new schema version.
            }
            else if (fileSchemaVersion > this.SchemaVersion)
            {
                string projectFileName = Path.GetFileName(this.Project.FilePath);
                string message         = Package.Instance.Context.NativeResources.GetString(ResId.IDS_E_PROJECTFILENEWERVERSION, projectFileName);
                Package.Instance.Context.ShowErrorMessageBox(message);
                return(false);
            }

            // ProjectGuid
            this.Project.ProjectGuid = XmlHelperMethods.GetAttributeGuid(node, AttributeNames.ProjectGuid, Guid.NewGuid());

            // Read the rest of the nodes in the project file in any order.
            // -----------------------------------------------------------
            foreach (XmlNode childNode in node.ChildNodes)
            {
                bool success = true;

                switch (childNode.Name)
                {
                case ElementNames.BuildSettings:
                    success = this.ReadBuildSettingsNode(childNode);
                    break;

                case ElementNames.Configurations:
                    success = this.ReadCollectionNode(childNode, ElementNames.Configurations, ElementNames.Configuration, new ReadCollectionItem(this.ReadConfigurationNode));
                    break;

                case ElementNames.Files:
                    success = this.ReadCollectionNode(childNode, ElementNames.Files, ElementNames.File, new ReadCollectionItem(this.ReadFileNode));
                    break;
                }

                // We can't have this in the switch block because the reference node is not a constant string value.
                if (childNode.Name == this.ReferencesElementName)
                {
                    success = this.ReadCollectionNode(childNode, this.ReferencesElementName, this.ReferenceElementName, new ReadCollectionItem(this.ReadLibraryReferenceNode));
                }

                if (!success)
                {
                    this.Project.Unavailable = true;
                    return(false);
                }
            }

            return(true);
        }
        public static void TraceRunningDocuments()
        {
            // Get the RDT (Running Document Table)
            IVsRunningDocumentTable rdt = Package.Instance.Context.ServiceProvider.GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable;

            if (rdt == null)
            {
                Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "Cannot get an instance of IVsRunningDocumentTable to use for enumerating the running documents.");
                return;
            }

            // Get the enumerator for the currently running documents.
            IEnumRunningDocuments enumerator;
            int hr = rdt.GetRunningDocumentsEnum(out enumerator);

            if (NativeMethods.Failed(hr))
            {
                Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "Cannot get an instance of IEnumRunningDocuments to use for enumerating the running documents.");
                return;
            }

            // Enumerate.
            StringCollection traceLines = new StringCollection();

            uint[] cookies = new uint[1];
            uint   fetchCount;

            while (true)
            {
                hr = enumerator.Next(1, cookies, out fetchCount);
                if (NativeMethods.Failed(hr))
                {
                    Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "The enumeration failed for the running documents. Hr=0x{0:X}", hr);
                    return;
                }

                if (fetchCount == 0)
                {
                    break;
                }

                uint cookie = cookies[0];

                // We shouldn't be getting a nil cookie.
                if (cookie == DocumentInfo.NullCookie)
                {
                    Tracer.WriteLineWarning(classType, "TraceRunningDocuments", "There is a null cookie value in the RDT, which shouldn't be happening.");
                }
                else
                {
                    // Now we have a document cookie, so let's get some information about it.
                    DocumentInfo docInfo = Package.Instance.Context.RunningDocumentTable.FindByCookie(cookie);
                    string       traceMessage;
                    if (docInfo == null)
                    {
                        traceMessage = PackageUtility.SafeStringFormatInvariant("The document with cookie '{0}' could not be found in the RDT. There's something weird going on.", cookie);
                    }
                    else
                    {
                        // Here's where we actually do the trace finally.
                        traceMessage = PackageUtility.SafeStringFormatInvariant("RDT document: Cookie={0} Path={1} IsOpen={2} IsDirty={3}", docInfo.Cookie, docInfo.AbsolutePath, docInfo.IsOpen, docInfo.IsDirty);
                    }

                    // We don't want to trace immediately because we want all of these lines to appear together. If we
                    // trace immediately, then the messages will be split up.
                    traceLines.Add(traceMessage);
                }
            }

            // Now trace all of the messages at once.
            foreach (string traceMessage in traceLines)
            {
                Tracer.WriteLine(classType, "TraceRunningDocuments", Tracer.Level.Information, traceMessage);
            }
        }