/// <summary> /// Adds a folder to the collection of ProjectItems with the given name. /// /// The kind must be null, empty string, or the string value of vsProjectItemKindPhysicalFolder. /// Virtual folders are not supported by this implementation. /// </summary> /// <param name="name">The name of the new folder to add</param> /// <param name="kind">A string representing a Guid of the folder kind.</param> /// <returns>A ProjectItem representing the newly added folder.</returns> public override ProjectItem AddFolder(string name, string kind) { Project.CheckProjectIsValid(); //Verify name is not null or empty Utilities.ValidateFileName(this.Project.Project.Site, name); //Verify that kind is null, empty, or a physical folder if (!(string.IsNullOrEmpty(kind) || kind.Equals(EnvDTE.Constants.vsProjectItemKindPhysicalFolder))) { throw new ArgumentException("Parameter specification for AddFolder was not meet", "kind"); } for (HierarchyNode child = this.NodeWithItems.FirstChild; child != null; child = child.NextSibling) { if (child.Caption.Equals(name, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Folder already exists with the name '{0}'", name)); } } ProjectNode proj = this.Project.Project; HierarchyNode newFolder = null; using (AutomationScope scope = new AutomationScope(this.Project.Project.Site)) { //In the case that we are adding a folder to a folder, we need to build up //the path to the project node. name = Path.Combine(this.NodeWithItems.VirtualNodeName, name); newFolder = proj.CreateFolderNodes(name); } return newFolder.GetAutomationObject() as ProjectItem; }
/// <summary> /// Saves or Save Asthe project. /// </summary> /// <param name="isCalledFromSaveAs">Flag determining which Save method called , the SaveAs or the Save.</param> /// <param name="fileName">The name of the project file.</param> private void DoSave(bool isCalledFromSaveAs, string fileName) { Utilities.ArgumentNotNull("fileName", fileName); CheckProjectIsValid(); using (AutomationScope scope = new AutomationScope(this.project.Site)) { // If an empty file name is passed in for Save then make the file name the project name. if (!isCalledFromSaveAs && string.IsNullOrEmpty(fileName)) { // Use the solution service to save the project file. Note that we have to use the service // so that all the shell's elements are aware that we are inside a save operation and // all the file change listenters registered by the shell are suspended. // Get the cookie of the project file from the RTD. IVsRunningDocumentTable rdt = this.project.Site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; Utilities.CheckNotNull(rdt); IVsHierarchy hier; uint itemid; IntPtr unkData; uint cookie; ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, this.project.Url, out hier, out itemid, out unkData, out cookie)); if (IntPtr.Zero != unkData) { Marshal.Release(unkData); } // Verify that we have a cookie. if (0 == cookie) { // This should never happen because if the project is open, then it must be in the RDT. throw new InvalidOperationException(); } // Get the IVsHierarchy for the project. IVsHierarchy prjHierarchy = HierarchyNode.GetOuterHierarchy(this.project); // Now get the soulution. IVsSolution solution = this.project.Site.GetService(typeof(SVsSolution)) as IVsSolution; // Verify that we have both solution and hierarchy. Utilities.CheckNotNull(prjHierarchy); Utilities.CheckNotNull(solution); ErrorHandler.ThrowOnFailure(solution.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_SaveIfDirty, prjHierarchy, cookie)); } else { // We need to make some checks before we can call the save method on the project node. // This is mainly because it is now us and not the caller like in case of SaveAs or Save that should validate the file name. // The IPersistFileFormat.Save method only does a validation that is necessary to be performed. Example: in case of Save As the // file name itself is not validated only the whole path. (thus a file name like file\file is accepted, since as a path is valid) // 1. The file name has to be valid. string fullPath = fileName; try { fullPath = CommonUtils.GetAbsoluteFilePath(this.Project.ProjectFolder, fileName); } // We want to be consistent in the error message and exception we throw. fileName could be for example #¤&%"¤&"% and that would trigger an ArgumentException on Path.IsRooted. catch (ArgumentException ex) { throw new InvalidOperationException(String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), fileName), ex); } // It might be redundant but we validate the file and the full path of the file being valid. The SaveAs would also validate the path. // If we decide that this is performance critical then this should be refactored. Utilities.ValidateFileName(this.project.Site, fullPath); if (!isCalledFromSaveAs) { // 2. The file name has to be the same if (!CommonUtils.IsSamePath(fullPath, this.project.Url)) { throw new InvalidOperationException(); } ErrorHandler.ThrowOnFailure(this.project.Save(fullPath, 1, 0)); } else { ErrorHandler.ThrowOnFailure(this.project.Save(fullPath, 0, 0)); } } } }
/// <summary> /// Removes the project from the current solution. /// </summary> public virtual void Delete() { CheckProjectIsValid(); using (AutomationScope scope = new AutomationScope(this.project.Site)) { this.project.Remove(false); } }
/// <summary> /// Adds an item to the project. /// </summary> /// <param name="path">The full path of the item to add.</param> /// <param name="op">The <paramref name="VSADDITEMOPERATION"/> to use when adding the item.</param> /// <returns>A ProjectItem object. </returns> protected virtual EnvDTE.ProjectItem AddItem(string path, VSADDITEMOPERATION op) { CheckProjectIsValid(); string ext = Path.GetExtension(path); foreach (var extension in this.Project.Project.CodeFileExtensions) { // http://pytools.codeplex.com/workitem/617 // We are currently in create project from existing code mode. The wizard walks all of the top-level // files and adds them. It then lets us handle any subdirectories by calling AddFromDirectory. // But we want to filter the files for both top-level and subdirectories. Therefore we derive from // PageManager and track when we're running the wizard and adding files for the wizard. If we are // currently adding them ignore anything other than a .py/.pyw files - returnning null is fine // here, the wizard doesn't care about the result. if (String.Compare(ext, extension, StringComparison.OrdinalIgnoreCase) == 0) { ProjectNode proj = this.Project.Project; EnvDTE.ProjectItem itemAdded = null; using (AutomationScope scope = new AutomationScope(this.Project.Project.Site)) { VSADDRESULT[] result = new VSADDRESULT[1]; ErrorHandler.ThrowOnFailure(proj.AddItem(this.NodeWithItems.ID, op, path, 0, new string[1] { path }, IntPtr.Zero, result)); string fileName = System.IO.Path.GetFileName(path); string fileDirectory = proj.GetBaseDirectoryForAddingFiles(this.NodeWithItems); string filePathInProject = System.IO.Path.Combine(fileDirectory, fileName); itemAdded = this.EvaluateAddResult(result[0], filePathInProject); } return itemAdded; } } return null; }
/// <summary> /// Creates a new project item from an existing item template file and adds it to the project. /// </summary> /// <param name="fileName">The full path and file name of the template project file.</param> /// <param name="name">The file name to use for the new project item.</param> /// <returns>A ProjectItem object. </returns> public override EnvDTE.ProjectItem AddFromTemplate(string fileName, string name) { CheckProjectIsValid(); ProjectNode proj = this.Project.Project; EnvDTE.ProjectItem itemAdded = null; using (AutomationScope scope = new AutomationScope(this.Project.Project.Site)) { // Determine the operation based on the extension of the filename. // We should run the wizard only if the extension is vstemplate // otherwise it's a clone operation VSADDITEMOPERATION op; if (Utilities.IsTemplateFile(fileName)) { op = VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD; } else { op = VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE; } VSADDRESULT[] result = new VSADDRESULT[1]; // It is not a very good idea to throw since the AddItem might return Cancel or Abort. // The problem is that up in the call stack the wizard code does not check whether it has received a ProjectItem or not and will crash. // The other problem is that we cannot get add wizard dialog back if a cancel or abort was returned because we throw and that code will never be executed. Typical catch 22. ErrorHandler.ThrowOnFailure(proj.AddItem(this.NodeWithItems.ID, op, name, 0, new string[1] { fileName }, IntPtr.Zero, result)); string fileDirectory = proj.GetBaseDirectoryForAddingFiles(this.NodeWithItems); string templateFilePath = System.IO.Path.Combine(fileDirectory, name); itemAdded = this.EvaluateAddResult(result[0], templateFilePath); } return itemAdded; }