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

920 lines
37 KiB
C#
Raw Blame History

/* ****************************************************************************
*
* 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.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using IServiceProvider = System.IServiceProvider;
using MSBuild = Microsoft.Build.Evaluation;
using System.Reflection;
using System.Runtime.Versioning;
using Microsoft.Build.Utilities;
using System.Xml;
namespace Microsoft.VisualStudio.Project
{
public static class VsUtilities
{
private const string defaultMSBuildVersion = "4.0";
/// <summary>
/// Look in the registry under the current hive for the path
/// of MSBuild
/// </summary>
/// <returns></returns>
/// <summary>
/// Is Visual Studio in design mode.
/// </summary>
/// <param name="serviceProvider">The service provider.</param>
/// <returns>true if visual studio is in design mode</returns>
public static bool IsVisualStudioInDesignMode(IServiceProvider site)
{
VsUtilities.ArgumentNotNull("site", site);
IVsMonitorSelection selectionMonitor = site.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection;
uint cookie = 0;
int active = 0;
Guid designContext = VSConstants.UICONTEXT_DesignMode;
ErrorHandler.ThrowOnFailure(selectionMonitor.GetCmdUIContextCookie(ref designContext, out cookie));
ErrorHandler.ThrowOnFailure(selectionMonitor.IsCmdUIContextActive(cookie, out active));
return active != 0;
}
/// <include file='doc\VsShellUtilities.uex' path='docs/doc[@for="Utilities.IsInAutomationFunction"]/*' />
/// <devdoc>
/// Is an extensibility object executing an automation function.
/// </devdoc>
/// <param name="serviceProvider">The service provider.</param>
/// <returns>true if the extensiblity object is executing an automation function.</returns>
public static bool IsInAutomationFunction(IServiceProvider serviceProvider)
{
VsUtilities.ArgumentNotNull("serviceProvider", serviceProvider);
IVsExtensibility3 extensibility = serviceProvider.GetService(typeof(EnvDTE.IVsExtensibility)) as IVsExtensibility3;
if (extensibility == null)
{
throw new InvalidOperationException();
}
int inAutomation = 0;
ErrorHandler.ThrowOnFailure(extensibility.IsInAutomationFunction(out inAutomation));
return inAutomation != 0;
}
/// <summary>
/// Creates a semicolon delinited list of strings. This can be used to provide the properties for VSHPROPID_CfgPropertyPagesCLSIDList, VSHPROPID_PropertyPagesCLSIDList, VSHPROPID_PriorityPropertyPagesCLSIDList
/// </summary>
/// <param name="guids">An array of Guids.</param>
/// <returns>A semicolon delimited string, or null</returns>
public static string CreateSemicolonDelimitedListOfStringFromGuids(Guid[] guids)
{
if (guids == null || guids.Length == 0)
{
return String.Empty;
}
// Create a StringBuilder with a pre-allocated buffer big enough for the
// final string. 39 is the length of a GUID in the "B" form plus the final ';'
StringBuilder stringList = new StringBuilder(39 * guids.Length);
for (int i = 0; i < guids.Length; i++)
{
stringList.Append(guids[i].ToString("B"));
stringList.Append(";");
}
return stringList.ToString().TrimEnd(';');
}
private static char[] curlyBraces = new char[] { '{', '}' };
/// <summary>
/// Take list of guids as a single string and generate an array of Guids from it
/// </summary>
/// <param name="guidList">Semi-colon separated list of Guids</param>
/// <returns>Array of Guids</returns>
public static Guid[] GuidsArrayFromSemicolonDelimitedStringOfGuids(string guidList)
{
if (guidList == null)
{
return null;
}
List<Guid> guids = new List<Guid>();
string[] guidsStrings = guidList.Split(';');
foreach (string guid in guidsStrings)
{
if (!String.IsNullOrEmpty(guid))
guids.Add(new Guid(guid.Trim(curlyBraces)));
}
return guids.ToArray();
}
public static void CheckNotNull(object value)
{
if (value == null)
{
throw new InvalidOperationException();
}
}
public static void ArgumentNotNull(string name, object value)
{
if (value == null)
{
throw new ArgumentNullException(name);
}
}
public static void ArgumentNotNullOrEmpty(string name, string value)
{
if (String.IsNullOrEmpty(value))
{
throw new ArgumentNullException(name);
}
}
/// <summary>
/// Validates a file path by validating all file parts. If the
/// the file name is invalid it throws an exception if the project is in automation. Otherwise it shows a dialog box with the error message.
/// </summary>
/// <param name="serviceProvider">The service provider</param>
/// <param name="filePath">A full path to a file name</param>
/// <exception cref="InvalidOperationException">In case of failure an InvalidOperationException is thrown.</exception>
public static void ValidateFileName(IServiceProvider serviceProvider, string filePath)
{
string errorMessage = String.Empty;
if (String.IsNullOrEmpty(filePath))
{
errorMessage = String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), filePath);
}
else if (filePath.Length > NativeMethods.MAX_PATH)
{
errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), filePath);
}
else if (ContainsInvalidFileNameChars(filePath))
{
errorMessage = String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), filePath);
}
if (errorMessage.Length == 0)
{
string fileName = Path.GetFileName(filePath);
if (String.IsNullOrEmpty(fileName) || IsFileNameInvalid(fileName))
{
errorMessage = String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), filePath);
}
}
if (errorMessage.Length > 0)
{
// If it is not called from an automation method show a dialog box.
if (!VsUtilities.IsInAutomationFunction(serviceProvider))
{
string title = null;
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
VsShellUtilities.ShowMessageBox(serviceProvider, title, errorMessage, icon, buttons, defaultButton);
}
else
{
throw new InvalidOperationException(errorMessage);
}
}
}
/// <summary>
/// Creates a CALPOLESTR from a list of strings
/// It is the responsability of the caller to release this memory.
/// </summary>
/// <param name="guids"></param>
/// <returns>A CALPOLESTR that was created from the the list of strings.</returns>
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CALPOLESTR")]
public static CALPOLESTR CreateCALPOLESTR(IList<string> strings)
{
CALPOLESTR calpolStr = new CALPOLESTR();
if (strings != null)
{
// Demand unmanaged permissions in order to access unmanaged memory.
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
calpolStr.cElems = (uint)strings.Count;
int size = Marshal.SizeOf(typeof(IntPtr));
calpolStr.pElems = Marshal.AllocCoTaskMem(strings.Count * size);
IntPtr ptr = calpolStr.pElems;
foreach (string aString in strings)
{
IntPtr tempPtr = Marshal.StringToCoTaskMemUni(aString);
Marshal.WriteIntPtr(ptr, tempPtr);
ptr = new IntPtr(ptr.ToInt64() + size);
}
}
return calpolStr;
}
/// <summary>
/// Creates a CADWORD from a list of tagVsSccFilesFlags. Memory is allocated for the elems.
/// It is the responsability of the caller to release this memory.
/// </summary>
/// <param name="guids"></param>
/// <returns>A CADWORD created from the list of tagVsSccFilesFlags.</returns>
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CADWORD")]
public static CADWORD CreateCADWORD(IList<tagVsSccFilesFlags> flags)
{
CADWORD cadWord = new CADWORD();
if (flags != null)
{
// Demand unmanaged permissions in order to access unmanaged memory.
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
cadWord.cElems = (uint)flags.Count;
int size = Marshal.SizeOf(typeof(UInt32));
cadWord.pElems = Marshal.AllocCoTaskMem(flags.Count * size);
IntPtr ptr = cadWord.pElems;
foreach (tagVsSccFilesFlags flag in flags)
{
Marshal.WriteInt32(ptr, (int)flag);
ptr = new IntPtr(ptr.ToInt64() + size);
}
}
return cadWord;
}
/// <summary>
/// Splits a bitmap from a Stream into an ImageList
/// </summary>
/// <param name="imageStream">A Stream representing a Bitmap</param>
/// <returns>An ImageList object representing the images from the given stream</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public static ImageList GetImageList(Stream imageStream)
{
ImageList ilist = new ImageList();
if (imageStream == null)
{
return ilist;
}
ilist.ColorDepth = ColorDepth.Depth24Bit;
ilist.ImageSize = new Size(16, 16);
Bitmap bitmap = new Bitmap(imageStream);
if (bitmap.Height != ilist.ImageSize.Height)
{
Bitmap newBitmap = new Bitmap((int)Math.Ceiling(bitmap.Width * ((float)ilist.ImageSize.Height / bitmap.Height)), ilist.ImageSize.Height);
using (Graphics g = Graphics.FromImage(newBitmap))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(bitmap, 0, 0, newBitmap.Width, newBitmap.Height);
}
bitmap.Dispose();
bitmap = newBitmap;
}
ilist.Images.AddStrip(bitmap);
ilist.TransparentColor = Color.Magenta;
return ilist;
}
/// <summary>
/// Gets the active configuration name.
/// </summary>
/// <param name="automationObject">The automation object.</param>
/// <returns>The name of the active configuartion.</returns>
public static string GetActiveConfigurationName(EnvDTE.Project automationObject)
{
VsUtilities.ArgumentNotNull("automationObject", automationObject);
string currentConfigName = string.Empty;
if (automationObject.ConfigurationManager != null)
{
EnvDTE.Configuration activeConfig = automationObject.ConfigurationManager.ActiveConfiguration;
if (activeConfig != null)
{
currentConfigName = activeConfig.ConfigurationName;
}
}
return currentConfigName;
}
/// <summary>
/// Gets the active platform name.
/// </summary>
/// <param name="automationObject">The automation object.</param>
/// <returns>The name of the active platform.</returns>
public static string GetActivePlatformName(EnvDTE.Project automationObject)
{
VsUtilities.ArgumentNotNull("automationObject", automationObject);
string currentPlatformName = string.Empty;
if (automationObject.ConfigurationManager != null)
{
EnvDTE.Configuration activeConfig = automationObject.ConfigurationManager.ActiveConfiguration;
if (activeConfig != null)
{
currentPlatformName = activeConfig.PlatformName;
}
}
return currentPlatformName;
}
/// <summary>
/// Verifies that two objects represent the same instance of a COM object.
/// This essentially compares the IUnkown pointers of the 2 objects.
/// This is needed in scenario where aggregation is involved.
/// </summary>
/// <param name="obj1">Can be an object, interface or IntPtr</param>
/// <param name="obj2">Can be an object, interface or IntPtr</param>
/// <returns>True if the 2 items represent the same thing</returns>
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj")]
public static bool IsSameComObject(object obj1, object obj2)
{
bool isSame = false;
IntPtr unknown1 = IntPtr.Zero;
IntPtr unknown2 = IntPtr.Zero;
try
{
// If we have 2 null, then they are not COM objects and as such "it's not the same COM object"
if (obj1 != null && obj2 != null)
{
unknown1 = QueryInterfaceIUnknown(obj1);
unknown2 = QueryInterfaceIUnknown(obj2);
isSame = IntPtr.Equals(unknown1, unknown2);
}
}
finally
{
if (unknown1 != IntPtr.Zero)
{
Marshal.Release(unknown1);
}
if (unknown2 != IntPtr.Zero)
{
Marshal.Release(unknown2);
}
}
return isSame;
}
/// <summary>
/// Retrieve the IUnknown for the managed or COM object passed in.
/// </summary>
/// <param name="objToQuery">Managed or COM object.</param>
/// <returns>Pointer to the IUnknown interface of the object.</returns>
public static IntPtr QueryInterfaceIUnknown(object objToQuery)
{
bool releaseIt = false;
IntPtr unknown = IntPtr.Zero;
IntPtr result;
try
{
if (objToQuery is IntPtr)
{
unknown = (IntPtr)objToQuery;
}
else
{
// This is a managed object (or RCW)
unknown = Marshal.GetIUnknownForObject(objToQuery);
releaseIt = true;
}
// We might already have an IUnknown, but if this is an aggregated
// object, it may not be THE IUnknown until we QI for it.
Guid IID_IUnknown = VSConstants.IID_IUnknown;
ErrorHandler.ThrowOnFailure(Marshal.QueryInterface(unknown, ref IID_IUnknown, out result));
}
finally
{
if (releaseIt && unknown != IntPtr.Zero)
{
Marshal.Release(unknown);
}
}
return result;
}
/// <summary>
/// Returns true if thename that can represent a path, absolut or relative, or a file name contains invalid filename characters.
/// </summary>
/// <param name="name">File name</param>
/// <returns>true if file name is invalid</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
Justification = "The name is validated.")]
public static bool ContainsInvalidFileNameChars(string name)
{
if (String.IsNullOrEmpty(name))
{
return true;
}
try
{
if (Path.IsPathRooted(name) && !name.StartsWith(@"\\", StringComparison.Ordinal))
{
string root = Path.GetPathRoot(name);
name = name.Substring(root.Length);
}
}
// The Path methods used by ContainsInvalidFileNameChars return argument exception if the filePath contains invalid characters.
catch (ArgumentException)
{
return true;
}
Microsoft.VisualStudio.Shell.Url uri = new Microsoft.VisualStudio.Shell.Url(name);
// This might be confusing bur Url.IsFile means that the uri represented by the name is either absolut or relative.
if (uri.IsFile)
{
string[] segments = uri.Segments;
if (segments != null && segments.Length > 0)
{
foreach (string segment in segments)
{
if (IsFilePartInValid(segment))
{
return true;
}
}
// Now the last segment should be specially taken care, since that cannot be all dots or spaces.
string lastSegment = segments[segments.Length - 1];
string filePart = Path.GetFileNameWithoutExtension(lastSegment);
// if the file is only an extension (.foo) then it's ok, otherwise we need to do the special checks.
if (filePart.Length != 0 && (IsFileNameAllGivenCharacter('.', filePart) || IsFileNameAllGivenCharacter(' ', filePart)))
{
return true;
}
}
}
else
{
// The assumption here is that we got a file name.
string filePart = Path.GetFileNameWithoutExtension(name);
if (IsFileNameAllGivenCharacter('.', filePart) || IsFileNameAllGivenCharacter(' ', filePart))
{
return true;
}
return IsFilePartInValid(name);
}
return false;
}
/// Cehcks if a file name is valid.
/// </devdoc>
/// <param name="fileName">The name of the file</param>
/// <returns>True if the file is valid.</returns>
public static bool IsFileNameInvalid(string fileName)
{
if (String.IsNullOrEmpty(fileName))
{
return true;
}
if (IsFileNameAllGivenCharacter('.', fileName) || IsFileNameAllGivenCharacter(' ', fileName))
{
return true;
}
return IsFilePartInValid(fileName);
}
/// <summary>
/// Initializes the in memory project. Sets BuildEnabled on the project to true.
/// </summary>
/// <param name="engine">The build engine to use to create a build project.</param>
/// <param name="fullProjectPath">The full path of the project.</param>
/// <returns>A loaded msbuild project.</returns>
public static MSBuild.Project InitializeMsBuildProject(MSBuild.ProjectCollection buildEngine, string fullProjectPath)
{
VsUtilities.ArgumentNotNullOrEmpty("fullProjectPath", fullProjectPath);
// Call GetFullPath to expand any relative path passed into this method.
fullProjectPath = CommonUtils.NormalizePath(fullProjectPath);
// Check if the project already has been loaded with the fullProjectPath. If yes return the build project associated to it.
List<MSBuild.Project> loadedProject = new List<MSBuild.Project>(buildEngine.GetLoadedProjects(fullProjectPath));
MSBuild.Project buildProject = loadedProject != null && loadedProject.Count > 0 && loadedProject[0] != null ? loadedProject[0] : null;
if (buildProject == null)
{
buildProject = buildEngine.LoadProject(fullProjectPath);
}
return buildProject;
}
/// <summary>
/// Loads a project file for the file. If the build project exists and it was loaded with a different file then it is unloaded first.
/// </summary>
/// <param name="engine">The build engine to use to create a build project.</param>
/// <param name="fullProjectPath">The full path of the project.</param>
/// <param name="exitingBuildProject">An Existing build project that will be reloaded.</param>
/// <returns>A loaded msbuild project.</returns>
public static MSBuild.Project ReinitializeMsBuildProject(MSBuild.ProjectCollection buildEngine, string fullProjectPath, MSBuild.Project exitingBuildProject)
{
// If we have a build project that has been loaded with another file unload it.
try
{
if (exitingBuildProject != null && exitingBuildProject.ProjectCollection != null && !CommonUtils.IsSamePath(exitingBuildProject.FullPath, fullProjectPath))
{
buildEngine.UnloadProject(exitingBuildProject);
}
}
// We catch Invalid operation exception because if the project was unloaded while we touch the ParentEngine the msbuild API throws.
// Is there a way to figure out that a project was unloaded?
catch (InvalidOperationException)
{
}
return VsUtilities.InitializeMsBuildProject(buildEngine, fullProjectPath);
}
/// <summary>
/// Initialize the build engine. Sets the build enabled property to true. The engine is initialzed if the passed in engine is null or does not have its bin path set.
/// </summary>
/// <param name="engine">An instance of MSBuild.ProjectCollection build engine, that will be checked if initialized.</param>
/// <param name="engine">The service provider.</param>
/// <returns>The buildengine to use.</returns>
public static MSBuild.ProjectCollection InitializeMsBuildEngine(MSBuild.ProjectCollection existingEngine, IServiceProvider serviceProvider)
{
VsUtilities.ArgumentNotNull("serviceProvider", serviceProvider);
if (existingEngine == null)
{
MSBuild.ProjectCollection buildEngine = MSBuild.ProjectCollection.GlobalProjectCollection;
return buildEngine;
}
return existingEngine;
}
/// <summary>>
/// Checks if the file name is all the given character.
/// </summary>
private static bool IsFileNameAllGivenCharacter(char c, string fileName)
{
// A valid file name cannot be all "c" .
int charFound = 0;
for (charFound = 0; charFound < fileName.Length && fileName[charFound] == c; ++charFound) ;
if (charFound >= fileName.Length)
{
return true;
}
return false;
}
private const string _reservedName = "(\\b(nul|con|aux|prn)\\b)|(\\b((com|lpt)[0-9])\\b)";
private const string _invalidChars = "([\\/:*?\"<>|#%])";
private const string _regexToUseForFileName = _reservedName + "|" + _invalidChars;
private static Regex _unsafeFileNameCharactersRegex = new Regex(_regexToUseForFileName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
private static Regex _unsafeCharactersRegex = new Regex(_invalidChars, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
/// <summary>
/// Checks whether a file part contains valid characters. The file part can be any part of a non rooted path.
/// </summary>
/// <param name="filePart"></param>
/// <returns></returns>
private static bool IsFilePartInValid(string filePart)
{
if (String.IsNullOrEmpty(filePart))
{
return true;
}
String fileNameToVerify = filePart;
// Define a regular expression that covers all characters that are not in the safe character sets.
// It is compiled for performance.
// The filePart might still be a file and extension. If it is like that then we must check them separately, since different rules apply
string extension = String.Empty;
try
{
extension = Path.GetExtension(filePart);
}
// We catch the ArgumentException because we want this method to return true if the filename is not valid. FilePart could be for example #<23>&%"<22>&"% and that would throw ArgumentException on GetExtension
catch (ArgumentException)
{
return true;
}
if (!String.IsNullOrEmpty(extension))
{
// Check the extension first
bool isMatch = _unsafeCharactersRegex.IsMatch(extension);
if (isMatch)
{
return isMatch;
}
// We want to verify here everything but the extension.
// We cannot use GetFileNameWithoutExtension because it might be that for example (..\\filename.txt) is passed in and that should fail, since that is not a valid filename.
fileNameToVerify = filePart.Substring(0, filePart.Length - extension.Length);
if (String.IsNullOrEmpty(fileNameToVerify))
{
// http://pytools.codeplex.com/workitem/497
// .foo is ok
return false;
}
}
// We verify CLOCK$ outside the regex since for some reason the regex is not matching the clock\\$ added.
if (String.Equals(fileNameToVerify, "CLOCK$", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return _unsafeFileNameCharactersRegex.IsMatch(fileNameToVerify);
}
/// <summary>
/// Copy a directory recursively to the specified non-existing directory
/// </summary>
/// <param name="source">Directory to copy from</param>
/// <param name="target">Directory to copy to</param>
public static void RecursivelyCopyDirectory(string source, string target)
{
// Make sure it doesn't already exist
if (Directory.Exists(target))
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderAlreadyExists, CultureInfo.CurrentUICulture), target));
Directory.CreateDirectory(target);
DirectoryInfo directory = new DirectoryInfo(source);
// Copy files
foreach (FileInfo file in directory.GetFiles())
{
file.CopyTo(Path.Combine(target, file.Name));
}
// Now recurse to child directories
foreach (DirectoryInfo child in directory.GetDirectories())
{
RecursivelyCopyDirectory(child.FullName, Path.Combine(target, child.Name));
}
}
/// <summary>
/// Canonicalizes a file name, including:
/// - determines the full path to the file
/// - casts to upper case
/// Canonicalizing a file name makes it possible to compare file names using simple simple string comparison.
///
/// Note: this method does not handle shared drives and UNC drives.
/// </summary>
/// <param name="anyFileName">A file name, which can be relative/absolute and contain lower-case/upper-case characters.</param>
/// <returns>Canonicalized file name.</returns>
public static string CanonicalizeFileName(string anyFileName)
{
// Get absolute path
// Note: this will not handle UNC paths
FileInfo fileInfo = new FileInfo(anyFileName);
string fullPath = fileInfo.FullName;
// Cast to upper-case
fullPath = fullPath.ToUpperInvariant();
return fullPath;
}
/// <summary>
/// Determines if a file is a template.
/// </summary>
/// <param name="fileName">The file to check whether it is a template file</param>
/// <returns>true if the file is a template file</returns>
public static bool IsTemplateFile(string fileName)
{
if (String.IsNullOrEmpty(fileName))
{
return false;
}
string extension = Path.GetExtension(fileName);
return (String.Equals(extension, ".vstemplate", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".vsz", StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Save dirty files
/// </summary>
/// <returns>Whether succeeded</returns>
public static bool SaveDirtyFiles() {
var rdt = ServiceProvider.GlobalProvider.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
if (rdt != null) {
// Consider using (uint)(__VSRDTSAVEOPTIONS.RDTSAVEOPT_SaveIfDirty | __VSRDTSAVEOPTIONS.RDTSAVEOPT_PromptSave)
// when VS settings include prompt for save on build
var saveOpt = (uint)__VSRDTSAVEOPTIONS.RDTSAVEOPT_SaveIfDirty;
var hr = rdt.SaveDocuments(saveOpt, null, VSConstants.VSITEMID_NIL, VSConstants.VSCOOKIE_NIL);
if (hr == VSConstants.E_ABORT) {
return false;
}
}
return true;
}
public static EnvDTE.Project GetProject(IServiceProvider services, string projectPath)
{
EnvDTE.DTE dte = (EnvDTE.DTE)services.GetService(typeof(EnvDTE.DTE));
return GetProject(dte, projectPath);
}
public static EnvDTE.Project GetProject(EnvDTE.DTE dte, string projectPath)
{
if ((null == dte) || (null == dte.Solution))
{
return null;
}
// Search for the project in the collection of the projects in the
// current solution.
foreach (EnvDTE.Project prj in dte.Solution.Projects)
{
//Skip this project if it is an umodeled project (unloaded)
if (string.Compare(EnvDTE.Constants.vsProjectKindUnmodeled, prj.Kind, StringComparison.OrdinalIgnoreCase) == 0)
{
continue;
}
// Get the full path of the current project.
EnvDTE.Property pathProperty = null;
try
{
if (prj.Properties == null)
{
continue;
}
pathProperty = prj.Properties.Item("FullPath");
if (null == pathProperty)
{
// The full path should alway be availabe, but if this is not the
// case then we have to skip it.
continue;
}
}
catch (ArgumentException)
{
continue;
}
string prjPath = pathProperty.Value.ToString();
EnvDTE.Property fileNameProperty = null;
// Get the name of the project file.
try
{
fileNameProperty = prj.Properties.Item("FileName");
if (null == fileNameProperty)
{
// Again, this should never be the case, but we handle it anyway.
continue;
}
}
catch (ArgumentException)
{
continue;
}
prjPath = Path.Combine(prjPath, fileNameProperty.Value.ToString());
// If the full path of this project is the same as the one of this
// reference, then we have found the right project.
if (CommonUtils.IsSamePath(prjPath, projectPath))
{
return prj;
}
}
return null;
}
/*private static Dictionary<string, Dictionary<string, Version>> frameworkCache = new Dictionary<string, Dictionary<string, Version>>();
private static void CacheFrameworkAssemblies(FrameworkName framework)
{
Dictionary<string, Version> dictionary;
if (!frameworkCache.TryGetValue(framework.FullName, out dictionary))
{
dictionary = new Dictionary<string, Version>();
var frameworkPaths = ToolLocationHelper.GetPathToReferenceAssemblies(framework);
foreach (var path in frameworkPaths)
{
string frameworkList = Path.Combine(path, "RedistList\\FrameworkList.xml");
if (File.Exists(frameworkList))
{
using (XmlReader xmlReader = XmlReader.Create(File.OpenRead(frameworkList)))
{
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "File")
{
string text = null;
Version version = null;
if (xmlReader.MoveToAttribute("AssemblyName"))
{
text = xmlReader.Value;
}
if (xmlReader.MoveToAttribute("Version"))
{
Version.TryParse(xmlReader.Value, out version);
}
if (!string.IsNullOrEmpty(text) && version != null)
{
string key = Path.Combine(path, text + ".dll");
if (!dictionary.ContainsKey(key))
{
dictionary[key] = version;
}
}
}
}
}
}
}
frameworkCache.Add(framework.FullName, dictionary);
}
}
public static bool IsFrameworkAssembly(string assemblyLocation, FrameworkName framework)
{
if (assemblyLocation == null)
throw new ArgumentNullException("assembly");
if (framework == null)
throw new ArgumentNullException("framework");
CacheFrameworkAssemblies(framework);
Dictionary<string, Version> assemblies;
if (frameworkCache.TryGetValue(framework.FullName, out assemblies))
{
Version version;
if (assemblies.TryGetValue(assemblyLocation, out version))
{
return true;
}
}
return false;
}*/
}
}