/// <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) { if(this.Project == null || this.Project.Project == null || this.Project.Project.Site == null || this.Project.Project.IsClosed) { throw new InvalidOperationException(); } 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; }
/// <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) { if(this.Project == null || this.Project.Project == null || this.Project.Project.Site == null || this.Project.Project.IsClosed) { throw new InvalidOperationException(); } //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> /// 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) { if(this.Project == null || this.Project.Project == null || this.Project.Project.Site == null || this.Project.Project.IsClosed) { throw new InvalidOperationException(); } 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; }
/// <summary> /// Sets the value of the property at the specified index. /// </summary> /// <param name="index1">The index of the item to set.</param> /// <param name="index2">Reserved for future use.</param> /// <param name="index3">Reserved for future use.</param> /// <param name="index4">Reserved for future use.</param> /// <param name="value">The value to set.</param> public void set_IndexedValue(object index1, object index2, object index3, object index4, object value) { ParameterInfo[] par = pi.GetIndexParameters(); int len = Math.Min(par.Length, 4); if(len == 0) { this.Value = value; } else { object[] index = new object[len]; Array.Copy(new object[4] { index1, index2, index3, index4 }, index, len); using(AutomationScope scope = new AutomationScope(this.parent.Target.Node.ProjectMgr.Site)) { this.pi.SetValue(this.parent.Target, value, index); } } }
/// <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) { if(fileName == null) { throw new ArgumentNullException("fileName"); } if(this.project == null || this.project.Site == null || this.project.IsClosed) { throw new InvalidOperationException(); } 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; if(null == rdt) { throw new InvalidOperationException(); } 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. if((null == prjHierarchy) || (null == solution)) { throw new InvalidOperationException(); } 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 necesseray 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 { if(!Path.IsPathRooted(fileName)) { fullPath = Path.Combine(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) { throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); } // 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(!NativeMethods.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() { if(this.project == null || this.project.Site == null || this.project.IsClosed) { throw new InvalidOperationException(); } using(AutomationScope scope = new AutomationScope(this.project.Site)) { this.project.Remove(false); } }