/*************************************************************************** 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.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using EnvDTE; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using IServiceProvider = System.IServiceProvider; using VSConstants = Microsoft.VisualStudio.VSConstants; namespace Microsoft.VisualStudio.Project.Automation { /// /// Represents an automation object for a file in a project /// [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] [ComVisible(true), CLSCompliant(false)] public class OAFileItem : OAProjectItem { #region ctors public OAFileItem(OAProject project, FileNode node) : base(project, node) { } #endregion #region overridden methods /// /// Returns the dirty state of the document. /// /// Is thrown if the project is closed or it the service provider attached to the project is invalid. /// Is thrown if the dirty state cannot be retrived. public override bool IsDirty { get { if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) { throw new InvalidOperationException(); } bool isDirty = false; using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { DocumentManager manager = this.Node.GetDocumentManager(); if(manager == null) { throw new InvalidOperationException(); } bool isOpen, isOpenedByUs; uint docCookie; IVsPersistDocData persistDocData; manager.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out persistDocData); } return isDirty; } } /// /// Gets the Document associated with the item, if one exists. /// public override EnvDTE.Document Document { get { if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) { throw new InvalidOperationException(); } EnvDTE.Document document = null; using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { IVsUIHierarchy hier; uint itemid; IVsWindowFrame windowFrame; VsShellUtilities.IsDocumentOpen(this.Node.ProjectMgr.Site, this.Node.Url, VSConstants.LOGVIEWID_Any, out hier, out itemid, out windowFrame); if(windowFrame != null) { object var; ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocCookie, out var)); object documentAsObject; ErrorHandler.ThrowOnFailure(scope.Extensibility.GetDocumentFromDocCookie((int)var, out documentAsObject)); if(documentAsObject == null) { throw new InvalidOperationException(); } else { document = (Document)documentAsObject; } } } return document; } } /// /// Opens the file item in the specified view. /// /// Specifies the view kind in which to open the item (file) /// Window object public override EnvDTE.Window Open(string viewKind) { if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) { throw new InvalidOperationException(); } IVsWindowFrame windowFrame = null; IntPtr docData = IntPtr.Zero; using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { try { // Validate input params Guid logicalViewGuid = VSConstants.LOGVIEWID_Primary; try { if(!(String.IsNullOrEmpty(viewKind))) { logicalViewGuid = new Guid(viewKind); } } catch(FormatException) { // Not a valid guid throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidGuid, CultureInfo.CurrentUICulture), "viewKind"); } uint itemid; IVsHierarchy ivsHierarchy; uint docCookie; IVsRunningDocumentTable rdt = this.Node.ProjectMgr.Site.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(); } ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, this.Node.Url, out ivsHierarchy, out itemid, out docData, out docCookie)); // Open the file using the IVsProject3 interface ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.OpenItem(this.Node.ID, ref logicalViewGuid, docData, out windowFrame)); } finally { if(docData != IntPtr.Zero) { Marshal.Release(docData); } } } // Get the automation object and return it return ((windowFrame != null) ? VsShellUtilities.GetWindowObject(windowFrame) : null); } /// /// Saves the project item. /// /// The name with which to save the project or project item. /// Is thrown if the save operation failes. /// Is thrown if fileName is null. public override void Save(string fileName) { this.DoSave(false, fileName); } /// /// Saves the project item. /// /// The file name with which to save the solution, project, or project item. If the file exists, it is overwritten /// true if the rename was successful. False if Save as failes public override bool SaveAs(string fileName) { try { this.DoSave(true, fileName); } catch(InvalidOperationException) { return false; } catch(COMException) { return false; } return true; } /// /// Gets a value indicating whether the project item is open in a particular view type. /// /// A Constants.vsViewKind* indicating the type of view to check./param> /// A Boolean value indicating true if the project is open in the given view type; false if not. public override bool get_IsOpen(string viewKind) { if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) { throw new InvalidOperationException(); } // Validate input params Guid logicalViewGuid = VSConstants.LOGVIEWID_Primary; try { if(!(String.IsNullOrEmpty(viewKind))) { logicalViewGuid = new Guid(viewKind); } } catch(FormatException) { // Not a valid guid throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidGuid, CultureInfo.CurrentUICulture), "viewKind"); } bool isOpen = false; using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { IVsUIHierarchy hier; uint itemid; IVsWindowFrame windowFrame; isOpen = VsShellUtilities.IsDocumentOpen(this.Node.ProjectMgr.Site, this.Node.Url, logicalViewGuid, out hier, out itemid, out windowFrame); } return isOpen; } /// /// Gets the ProjectItems for the object. /// public override ProjectItems ProjectItems { get { if(this.Project.Project.CanFileNodesHaveChilds) return new OAProjectItems(this.Project, this.Node); else return base.ProjectItems; } } #endregion #region helpers /// /// Saves or Save As the file /// /// Flag determining which Save method called , the SaveAs or the Save. /// The name of the project file. private void DoSave(bool isCalledFromSaveAs, string fileName) { if(fileName == null) { throw new ArgumentNullException("fileName"); } if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) { throw new InvalidOperationException(); } using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { IntPtr docData = IntPtr.Zero; try { IVsRunningDocumentTable rdt = this.Node.ProjectMgr.Site.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; IVsHierarchy ivsHierarchy; uint docCookie; int canceled; string url = this.Node.Url; ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, url, out ivsHierarchy, out itemid, out docData, out docCookie)); // If an empty file name is passed in for Save then make the file name the project name. if(!isCalledFromSaveAs && fileName.Length == 0) { ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.SaveItem(VSSAVEFLAGS.VSSAVE_SilentSave, url, this.Node.ID, docData, out canceled)); } else { Utilities.ValidateFileName(this.Node.ProjectMgr.Site, fileName); // Compute the fullpath from the directory of the existing Url. string fullPath = fileName; if(!Path.IsPathRooted(fileName)) { string directory = Path.GetDirectoryName(url); fullPath = Path.Combine(directory, fileName); } if(!isCalledFromSaveAs) { if(!NativeMethods.IsSamePath(this.Node.Url, fullPath)) { throw new InvalidOperationException(); } ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.SaveItem(VSSAVEFLAGS.VSSAVE_SilentSave, fullPath, this.Node.ID, docData, out canceled)); } else { ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.SaveItem(VSSAVEFLAGS.VSSAVE_SilentSave, fullPath, this.Node.ID, docData, out canceled)); } } if(canceled == 1) { throw new InvalidOperationException(); } } catch(COMException e) { throw new InvalidOperationException(e.Message); } finally { if(docData != IntPtr.Zero) { Marshal.Release(docData); } } } } #endregion } }