/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * ***************************************************************************/ 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 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)] public class OAFileItem : OAProjectItem { #region ctors public OAFileItem(OAProject project, FileNode node) : base(project, node) { } #endregion private new FileNode Node { get { return (FileNode)base.Node; } } public override string Name { get { return this.Node.FileName; } set { UIThread.Instance.RunSync(() => base.Name = value); } } #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 { CheckProjectIsValid(); bool isDirty = false; using (AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { UIThread.Instance.RunSync(() => { DocumentManager manager = this.Node.GetDocumentManager(); VsUtilities.CheckNotNull(manager); 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 { CheckProjectIsValid(); EnvDTE.Document document = null; using (AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { UIThread.Instance.RunSync(() => { 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)); VsUtilities.CheckNotNull(documentAsObject); 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) { CheckProjectIsValid(); IVsWindowFrame windowFrame = null; IntPtr docData = IntPtr.Zero; using (AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { UIThread.Instance.RunSync(() => { 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"); VsUtilities.CheckNotNull(rdt); ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, this.Node.Url, out ivsHierarchy, out itemid, out docData, out docCookie)); // Open the file using the IVsProject interface // We get the outer hierarchy so that projects can customize opening. var project = Node.ProjectMgr.GetOuterInterface(); ErrorHandler.ThrowOnFailure(project.OpenItem(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) { UIThread.Instance.RunSync(() => { 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 { UIThread.Instance.RunSync(() => { 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) { CheckProjectIsValid(); // 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)) { UIThread.Instance.RunSync(() => { 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 { return UIThread.Instance.RunSync(() => { if (this.Project.ProjectNode.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) { VsUtilities.ArgumentNotNull("fileName", fileName); CheckProjectIsValid(); using (AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) { UIThread.Instance.RunSync(() => { 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"); VsUtilities.CheckNotNull(rdt); // 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 { VsUtilities.ValidateFileName(this.Node.ProjectMgr.Site, fileName); // Compute the fullpath from the directory of the existing Url. string fullPath = CommonUtils.GetAbsoluteFilePath(Path.GetDirectoryName(url), fileName); if (!isCalledFromSaveAs) { if (!CommonUtils.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 } }