anx.framework/Visual Studio/MPF11/Dev11/Src/CSharp/ProjectReferenceNode.cs
Konstantin Koch 8287c54432 Included the Visual Studio extension and made the necessary changes to make it run.
Replaced the old VS templates with ones that offer more flexiblity.
Started replacing the Content Project for the samples with our custom project type.
Inlcuded a basic not yet working AssimpImporter.
2015-04-08 14:50:03 +02:00

578 lines
20 KiB
C#

/* ****************************************************************************
*
* 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.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio;
namespace Microsoft.VisualStudio.Project
{
public class ProjectReferenceNode : ReferenceNode
{
#region fields
/// <summary>
/// The name of the assembly this refernce represents
/// </summary>
private Guid referencedProjectGuid;
private string referencedProjectName = String.Empty;
private string referencedProjectRelativePath = String.Empty;
private string referencedProjectFullPath = String.Empty;
private BuildDependency buildDependency;
/// <summary>
/// This is a reference to the automation object for the referenced project.
/// </summary>
private EnvDTE.Project referencedProject;
/// <summary>
/// This state is controlled by the solution events.
/// The state is set to false by OnBeforeUnloadProject.
/// The state is set to true by OnBeforeCloseProject event.
/// </summary>
private bool canRemoveReference = true;
private FileSystemWatcher fileWatcher;
public bool IgnoreFileChanges = false;
#endregion
#region properties
public override string Url
{
get
{
return this.referencedProjectFullPath;
}
}
public virtual string RelativeUrl
{
get
{
return this.referencedProjectRelativePath;
}
}
public override string Caption
{
get
{
return this.referencedProjectName;
}
}
public Guid ReferencedProjectGuid
{
get
{
return this.referencedProjectGuid;
}
}
/// <summary>
/// Possiblity to shortcut and set the dangling project reference icon.
/// It is ussually manipulated by solution listsneres who handle reference updates.
/// </summary>
public bool IsValid
{
get
{
return this.ReferencedProjectObject != null;
}
}
/// <summary>
/// Controls the state whether this reference can be removed or not. Think of the project unload scenario where the project reference should not be deleted.
/// </summary>
public bool CanRemoveReference
{
get
{
return this.canRemoveReference;
}
set
{
this.canRemoveReference = value;
}
}
public string ReferencedProjectName
{
get { return this.referencedProjectName; }
}
/// <summary>
/// Gets the automation object for the referenced project.
/// </summary>
public EnvDTE.Project ReferencedProjectObject
{
get
{
// If the referenced project is null then re-read.
if (this.referencedProject == null)
{
this.referencedProject = VsUtilities.GetProject(this.ProjectMgr.Site, referencedProjectFullPath);
}
return this.referencedProject;
}
set
{
this.referencedProject = value;
}
}
public string UniqueName
{
get
{
return this.ReferencedProjectGuid.ToString("B") + "|" + CommonUtils.TrimUpPaths(this.referencedProjectRelativePath);
}
}
/// <summary>
/// Gets the full path to the assembly generated by this project.
/// </summary>
public string ReferencedProjectOutputPath
{
get
{
// Make sure that the referenced project implements the automation object.
if (null == this.ReferencedProjectObject)
{
return null;
}
EnvDTE.ConfigurationManager confManager = null;
// Get the configuration manager from the project.
try
{
confManager = this.ReferencedProjectObject.ConfigurationManager;
}
catch (COMException)
{
//The project could be not available.
}
if (null == confManager)
{
return null;
}
// Get the active configuration.
EnvDTE.Configuration config = confManager.ActiveConfiguration;
if (null == config)
{
return null;
}
if (null == config.Properties)
{
return null;
}
// Get the output path for the current configuration.
EnvDTE.Property outputPathProperty = config.Properties.Item("OutputPath");
if (null == outputPathProperty || outputPathProperty.Value == null)
{
return null;
}
// Usually the output path is relative to the project path. If it is set as an
// absolute path, this call has no effect.
string outputPath = CommonUtils.GetAbsoluteDirectoryPath(
Path.GetDirectoryName(referencedProjectFullPath),
outputPathProperty.Value.ToString());
// Now get the name of the assembly from the project.
// Some project system throw if the property does not exist. We expect an ArgumentException.
EnvDTE.Property assemblyNameProperty = null;
try
{
assemblyNameProperty = this.ReferencedProjectObject.Properties.Item("OutputFileName");
}
catch (ArgumentException)
{
}
if (null == assemblyNameProperty)
{
return null;
}
// build the full path adding the name of the assembly to the output path.
outputPath = Path.Combine(outputPath, assemblyNameProperty.Value.ToString());
return outputPath;
}
}
public string AssemblyName
{
get
{
// Now get the name of the assembly from the project.
// Some project system throw if the property does not exist. We expect an ArgumentException.
EnvDTE.Property assemblyNameProperty = null;
if (ReferencedProjectObject != null &&
!(ReferencedProjectObject is Automation.OAProject)) // our own projects don't have assembly names
{
try
{
assemblyNameProperty = this.ReferencedProjectObject.Properties.Item(ProjectFileConstants.AssemblyName);
}
catch (ArgumentException)
{
}
if (assemblyNameProperty != null)
{
return assemblyNameProperty.Value.ToString();
}
}
return null;
}
}
private Automation.OAProjectReference projectReference;
public override object Object
{
get
{
if (null == projectReference)
{
projectReference = new Automation.OAProjectReference(this);
}
return projectReference;
}
}
#endregion
#region ctors
/// <summary>
/// Constructor for the ReferenceNode. It is called when the project is reloaded, when the project element representing the refernce exists.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
public ProjectReferenceNode(ProjectNode root, ProjectElement element)
: base(root, element)
{
this.referencedProjectRelativePath = this.ItemNode.GetMetadata(ProjectFileConstants.Include);
Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectRelativePath), "Could not retrieve referenced project path form project file");
string guidString = this.ItemNode.GetMetadata(ProjectFileConstants.Project);
// Continue even if project setttings cannot be read.
try
{
this.referencedProjectGuid = new Guid(guidString);
this.buildDependency = new BuildDependency(this.ProjectMgr, this.referencedProjectGuid);
this.ProjectMgr.AddBuildDependency(this.buildDependency);
}
finally
{
Debug.Assert(this.referencedProjectGuid != Guid.Empty, "Could not retrive referenced project guidproject file");
this.referencedProjectName = this.ItemNode.GetMetadata(ProjectFileConstants.Name);
Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectName), "Could not retrive referenced project name form project file");
}
try
{
this.referencedProjectFullPath = CommonUtils.GetAbsoluteFilePath(this.ProjectMgr.ProjectHome, this.referencedProjectRelativePath);
}
catch { } //Just make an invalid reference.
}
/// <summary>
/// constructor for the ProjectReferenceNode
/// </summary>
public ProjectReferenceNode(ProjectNode root, string referencedProjectName, string projectPath, string projectReference)
: base(root)
{
if (projectReference == null)
{
throw new ArgumentNullException("projectReference");
}
this.referencedProjectName = referencedProjectName;
int indexOfSeparator = projectReference.IndexOf('|');
string fileName = String.Empty;
// Unfortunately we cannot use the path part of the projectReference string since it is not resolving correctly relative pathes.
if (indexOfSeparator != -1)
{
string projectGuid = projectReference.Substring(0, indexOfSeparator);
this.referencedProjectGuid = new Guid(projectGuid);
if (indexOfSeparator + 1 < projectReference.Length)
{
string remaining = projectReference.Substring(indexOfSeparator + 1);
indexOfSeparator = remaining.IndexOf('|');
if (indexOfSeparator == -1)
{
fileName = remaining;
}
else
{
fileName = remaining.Substring(0, indexOfSeparator);
}
}
}
string justTheFileName = Path.GetFileName(fileName);
try
{
this.referencedProjectFullPath = CommonUtils.GetAbsoluteFilePath(Path.GetDirectoryName(projectPath), justTheFileName);
// TODO: Maybe referenced projects should be relative to ProjectDir?
this.referencedProjectRelativePath = CommonUtils.GetRelativeFilePath(this.ProjectMgr.ProjectHome, this.referencedProjectFullPath);
}
catch { } //Make an invalid reference.
this.buildDependency = new BuildDependency(this.ProjectMgr, this.referencedProjectGuid);
this.ProjectMgr.AddBuildDependency(this.buildDependency);
}
#endregion
#region methods
protected override NodeProperties CreatePropertiesObject()
{
return new ProjectReferencesProperties(this);
}
/// <summary>
/// The node is added to the hierarchy and then updates the build dependency list.
/// </summary>
public override void AddReference()
{
if (this.ProjectMgr == null)
{
return;
}
base.AddReference();
this.ProjectMgr.AddBuildDependency(this.buildDependency);
return;
}
/// <summary>
/// Overridden method. The method updates the build dependency list before removing the node from the hierarchy.
/// </summary>
public override void Remove(bool removeFromStorage)
{
if (this.ProjectMgr == null || !this.CanRemoveReference)
{
return;
}
this.ProjectMgr.RemoveBuildDependency(this.buildDependency);
base.Remove(removeFromStorage);
return;
}
/// <summary>
/// Links a reference node to the project file.
/// </summary>
protected override void BindReferenceData()
{
Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectName), "The referencedProjectName field has not been initialized");
Debug.Assert(this.referencedProjectGuid != Guid.Empty, "The referencedProjectName field has not been initialized");
this.ItemNode = new MsBuildProjectElement(this.ProjectMgr, this.referencedProjectRelativePath, ProjectFileConstants.ProjectReference);
this.ItemNode.SetMetadata(ProjectFileConstants.Name, this.referencedProjectName);
this.ItemNode.SetMetadata(ProjectFileConstants.Project, this.referencedProjectGuid.ToString("B"));
this.ItemNode.SetMetadata(ProjectFileConstants.Private, true.ToString());
}
/// <summary>
/// Defines whether this node is valid node for painting the refererence icon.
/// </summary>
/// <returns></returns>
protected override bool CanShowDefaultIcon()
{
if (this.referencedProjectGuid == Guid.Empty || this.ProjectMgr == null || this.ProjectMgr.IsClosed || !this.IsValid)
{
return false;
}
IVsHierarchy hierarchy = null;
hierarchy = VsShellUtilities.GetHierarchy(this.ProjectMgr.Site, this.referencedProjectGuid);
if (hierarchy == null)
{
return false;
}
//If the Project is unloaded return false
if (this.ReferencedProjectObject == null)
{
return false;
}
return (!String.IsNullOrEmpty(this.referencedProjectFullPath) && File.Exists(this.referencedProjectFullPath));
}
/// <summary>
/// Checks if a project reference can be added to the hierarchy. It calls base to see if the reference is not already there, then checks for circular references.
/// </summary>
/// <param name="errorHandler">The error handler delegate to return</param>
/// <returns></returns>
protected override bool CanAddReference(out CannotAddReferenceErrorMessage errorHandler)
{
// When this method is called this refererence has not yet been added to the hierarchy, only instantiated.
if (!base.CanAddReference(out errorHandler))
{
return false;
}
errorHandler = null;
if (this.IsThisProjectReferenceInCycle())
{
errorHandler = new CannotAddReferenceErrorMessage(ShowCircularReferenceErrorMessage);
return false;
}
return true;
}
private bool IsThisProjectReferenceInCycle()
{
return IsReferenceInCycle(this.referencedProjectGuid);
}
private void ShowCircularReferenceErrorMessage()
{
string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.ProjectContainsCircularReferences, CultureInfo.CurrentUICulture), this.referencedProjectName);
string title = string.Empty;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, message, icon, buttons, defaultButton);
}
/// <summary>
/// Recursively search if this project reference guid is in cycle.
/// </summary>
private bool IsReferenceInCycle(Guid projectGuid)
{
// TODO: This has got to be wrong, it doesn't work w/ other project types.
IVsHierarchy hierarchy = VsShellUtilities.GetHierarchy(this.ProjectMgr.Site, projectGuid);
IReferenceContainerProvider provider = hierarchy.GetProject().GetCommonProject() as IReferenceContainerProvider;
if (provider != null)
{
IReferenceContainer referenceContainer = provider.GetReferenceContainer();
Debug.Assert(referenceContainer != null, "Could not found the References virtual node");
foreach (ReferenceNode refNode in referenceContainer.EnumReferences())
{
ProjectReferenceNode projRefNode = refNode as ProjectReferenceNode;
if (projRefNode != null)
{
if (projRefNode.ReferencedProjectGuid == this.ProjectMgr.ProjectIDGuid)
{
return true;
}
if (this.IsReferenceInCycle(projRefNode.ReferencedProjectGuid))
{
return true;
}
}
}
}
return false;
}
#endregion
protected override void ResolveReference()
{
string oldOutputPath = this.ReferencedProjectOutputPath;
base.ResolveReference();
string outputPath = this.ReferencedProjectOutputPath;
if (!string.IsNullOrEmpty(outputPath) && (oldOutputPath != outputPath || fileWatcher == null))
{
if (fileWatcher != null)
fileWatcher.Dispose();
fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(outputPath));
fileWatcher.Changed += fileWatcher_Changed;
fileWatcher.EnableRaisingEvents = true;
}
}
void fileWatcher_Changed(object sender, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed && IgnoreFileChanges == false && e.FullPath == this.ReferencedProjectOutputPath)
{
System.Threading.Tasks.Task.Run(() =>
{
this.RefreshReference(true);
});
}
}
protected override void Dispose(bool disposing)
{
if (fileWatcher != null)
{
fileWatcher.Dispose();
}
base.Dispose(disposing);
}
#if DEV11_OR_LATER
public override bool Equals(IVsReference other)
{
if (other is IVsProjectReference)
{
IVsProjectReference projectReference = (IVsProjectReference)other;
return projectReference.Name == this.Name && projectReference.FullPath == this.FullPath &&
projectReference.Identity == this.ReferencedProjectGuid.ToString("B") && projectReference.ReferenceSpecification == this.UniqueName;
}
return false;
}
#endif
public override string ReferenceType
{
get { return ProjectFileConstants.ProjectReference; }
}
}
}