PageRenderTime 46ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/DevTools/MPF/ProjectNode.cs

https://bitbucket.org/greenboxdevelopment/greenbox3d
C# | 6613 lines | 5798 code | 317 blank | 498 comment | 222 complexity | 59e07259d68891bbfa288652f7048cf8 MD5 | raw file
  1. // ProjectNode.cs
  2. //
  3. // Copyright (c) 2013 The GreenBox Development LLC, all rights reserved
  4. //
  5. // This file is a proprietary part of GreenBox3D, disclosing the content
  6. // of this file without the owner consent may lead to legal actions
  7. using System;
  8. using System.CodeDom.Compiler;
  9. using System.Collections.Generic;
  10. using System.Collections.Specialized;
  11. using System.ComponentModel;
  12. using System.Diagnostics;
  13. using System.Diagnostics.CodeAnalysis;
  14. using System.Globalization;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Runtime.InteropServices;
  18. using System.Runtime.Versioning;
  19. using System.Text;
  20. using System.Xml;
  21. using EnvDTE;
  22. using Microsoft.Build.BackEnd;
  23. using Microsoft.Build.Evaluation;
  24. using Microsoft.Build.Execution;
  25. using Microsoft.Build.Framework;
  26. using Microsoft.Build.Utilities;
  27. using Microsoft.VisualStudio.OLE.Interop;
  28. using Microsoft.VisualStudio.Project.Automation;
  29. using Microsoft.VisualStudio.Shell;
  30. using Microsoft.VisualStudio.Shell.Interop;
  31. using Constants = EnvDTE.Constants;
  32. using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
  33. using IServiceProvider = System.IServiceProvider;
  34. using MSBuild = Microsoft.Build.Evaluation;
  35. using MSBuildConstruction = Microsoft.Build.Construction;
  36. using MSBuildExecution = Microsoft.Build.Execution;
  37. using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
  38. using Process = System.Diagnostics.Process;
  39. using ProjectItem = EnvDTE.ProjectItem;
  40. using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;
  41. using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID;
  42. namespace Microsoft.VisualStudio.Project
  43. {
  44. /// <summary>
  45. /// Manages the persistent state of the project (References, options, files, etc.) and deals with user interaction via a GUI in the form a hierarchy.
  46. /// </summary>
  47. [CLSCompliant(false)]
  48. [ComVisible(true)]
  49. public abstract partial class ProjectNode : HierarchyNode,
  50. IVsGetCfgProvider,
  51. IVsProject3,
  52. IVsAggregatableProject,
  53. IVsProjectFlavorCfgProvider,
  54. IPersistFileFormat,
  55. IVsProjectBuildSystem,
  56. IVsBuildPropertyStorage,
  57. IVsComponentUser,
  58. IVsDependencyProvider,
  59. IVsSccProject2,
  60. IBuildDependencyUpdate,
  61. IProjectEventsListener,
  62. IProjectEventsProvider,
  63. IReferenceContainerProvider,
  64. IVsProjectSpecialFiles,
  65. IVsProjectUpgrade,
  66. IVsDesignTimeAssemblyResolution,
  67. IVsSetTargetFrameworkWorkerCallback
  68. {
  69. #region nested types
  70. public enum ImageName
  71. {
  72. OfflineWebApp = 0,
  73. WebReferencesFolder = 1,
  74. OpenReferenceFolder = 2,
  75. ReferenceFolder = 3,
  76. Reference = 4,
  77. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SDL")]
  78. SDLWebReference = 5,
  79. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "DISCO")]
  80. DISCOWebReference = 6,
  81. Folder = 7,
  82. OpenFolder = 8,
  83. ExcludedFolder = 9,
  84. OpenExcludedFolder = 10,
  85. ExcludedFile = 11,
  86. DependentFile = 12,
  87. MissingFile = 13,
  88. WindowsForm = 14,
  89. WindowsUserControl = 15,
  90. WindowsComponent = 16,
  91. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")]
  92. XMLSchema = 17,
  93. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")]
  94. XMLFile = 18,
  95. WebForm = 19,
  96. WebService = 20,
  97. WebUserControl = 21,
  98. WebCustomUserControl = 22,
  99. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ASP")]
  100. ASPPage = 23,
  101. GlobalApplicationClass = 24,
  102. WebConfig = 25,
  103. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "HTML")]
  104. HTMLPage = 26,
  105. StyleSheet = 27,
  106. ScriptFile = 28,
  107. TextFile = 29,
  108. SettingsFile = 30,
  109. Resources = 31,
  110. Bitmap = 32,
  111. Icon = 33,
  112. Image = 34,
  113. ImageMap = 35,
  114. XWorld = 36,
  115. Audio = 37,
  116. Video = 38,
  117. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CAB")]
  118. CAB = 39,
  119. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "JAR")]
  120. JAR = 40,
  121. DataEnvironment = 41,
  122. PreviewFile = 42,
  123. DanglingReference = 43,
  124. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XSLT")]
  125. XSLTFile = 44,
  126. Cursor = 45,
  127. AppDesignerFolder = 46,
  128. Data = 47,
  129. Application = 48,
  130. DataSet = 49,
  131. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PFX")]
  132. PFX = 50,
  133. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SNK")]
  134. SNK = 51,
  135. ImageLast = 51
  136. }
  137. /// <summary>
  138. /// Flags for specifying which events to stop triggering.
  139. /// </summary>
  140. [Flags]
  141. internal enum EventTriggering
  142. {
  143. TriggerAll = 0,
  144. DoNotTriggerHierarchyEvents = 1,
  145. DoNotTriggerTrackerEvents = 2
  146. }
  147. #endregion
  148. #region constants
  149. /// <summary>
  150. /// The user file extension.
  151. /// </summary>
  152. internal const string PerUserFileExtension = ".user";
  153. /// <summary>
  154. /// The VS command that allows projects to open Windows Explorer to the project directory.
  155. /// </summary>
  156. private const VsCommands2K ExploreFolderInWindowsCommand = (VsCommands2K)1635;
  157. private readonly Guid GUID_MruPage = new Guid("{19B97F03-9594-4c1c-BE28-25FF030113B3}");
  158. #endregion
  159. #region fields
  160. private static readonly FrameworkName DefaultTargetFrameworkMoniker = new FrameworkName(".NETFramework",
  161. new Version(4, 0));
  162. private static Guid addComponentLastActiveTab = VSConstants.GUID_SolutionPage;
  163. private static uint addComponentDialogSizeX = 0;
  164. private static uint addComponentDialogSizeY = 0;
  165. /// <summary>
  166. /// List of output groups names and their associated target
  167. /// </summary>
  168. private static readonly KeyValuePair<string, string>[] outputGroupNames =
  169. {
  170. // Name Target (MSBuild)
  171. new KeyValuePair<string, string>("Built", "BuiltProjectOutputGroup"),
  172. new KeyValuePair<string, string>("ContentFiles", "ContentFilesProjectOutputGroup"),
  173. new KeyValuePair<string, string>("LocalizedResourceDlls", "SatelliteDllsProjectOutputGroup"),
  174. new KeyValuePair<string, string>("Documentation", "DocumentationProjectOutputGroup"),
  175. new KeyValuePair<string, string>("Symbols", "DebugSymbolsProjectOutputGroup"),
  176. new KeyValuePair<string, string>("SourceFiles", "SourceFilesProjectOutputGroup"),
  177. new KeyValuePair<string, string>("XmlSerializer", "SGenFilesOutputGroup"),
  178. };
  179. /// <summary>A project will only try to build if it can obtain a lock on this object</summary>
  180. private static volatile object BuildLock = new object();
  181. /// <summary>
  182. /// The build dependency list passed to IVsDependencyProvider::EnumDependencies
  183. /// </summary>
  184. private readonly List<IVsBuildDependency> buildDependencyList = new List<IVsBuildDependency>();
  185. /// <summary>
  186. /// Used to map types to CATID. This provide a generic way for us to do this
  187. /// and make it simpler for a project to provide it's CATIDs for the different type of objects
  188. /// for which it wants to support extensibility. This also enables us to have multiple
  189. /// type mapping to the same CATID if we choose to.
  190. /// </summary>
  191. private readonly Dictionary<Type, Guid> catidMapping = new Dictionary<Type, Guid>();
  192. /// <summary>Maps integer ids to project item instances</summary>
  193. private readonly EventSinkCollection itemIdMap = new EventSinkCollection();
  194. private Url baseUri;
  195. /// <summary>
  196. /// MSBuild engine we are going to use
  197. /// </summary>
  198. private ProjectCollection buildEngine;
  199. private bool buildInProcess;
  200. private bool buildIsPrepared;
  201. private Logger buildLogger;
  202. private MSBuild.Project buildProject;
  203. private bool canFileNodesHaveChilds;
  204. /// <summary>
  205. /// Control if command with potential destructive behavior such as delete should
  206. /// be enabled for nodes of this project.
  207. /// </summary>
  208. private bool canProjectDeleteItems;
  209. private ConfigProvider configProvider;
  210. private ProjectInstance currentConfig;
  211. private DesignTimeAssemblyResolution designTimeAssemblyResolution;
  212. /// <summary>
  213. /// Flag for controling query edit should communicate with the scc manager.
  214. /// </summary>
  215. private bool disableQueryEdit;
  216. /// <summary>
  217. /// Field for determining whether sourcecontrol should be disabled.
  218. /// </summary>
  219. private bool disableScc;
  220. private string errorString;
  221. private EventTriggering eventTriggeringFlag = EventTriggering.TriggerAll;
  222. private string filename;
  223. private ImageHandler imageHandler;
  224. private bool invokeMSBuildWhenResumed;
  225. private bool isClosed;
  226. private bool isDirty;
  227. private bool isDisposed;
  228. private bool isNewProject;
  229. private bool isProjectEventsListener = true;
  230. /// <summary>
  231. /// Flag for controling how many times we register with the Scc manager.
  232. /// </summary>
  233. private bool isRegisteredWithScc;
  234. /// <summary>
  235. /// This property returns the time of the last change made to this project.
  236. /// It is not the time of the last change on the project file, but actually of
  237. /// the in memory project settings. In other words, it is the last time that
  238. /// SetProjectDirty was called.
  239. /// </summary>
  240. private DateTime lastModifiedTime;
  241. /// <summary>
  242. /// A cached copy of project options.
  243. /// </summary>
  244. private ProjectOptions options;
  245. /// <summary>
  246. /// Member to store output base relative path. Used by OutputBaseRelativePath property
  247. /// </summary>
  248. private string outputBaseRelativePath = "bin";
  249. /// <summary>
  250. /// The internal package implementation.
  251. /// </summary>
  252. private ProjectPackage package;
  253. private IProjectEvents projectEventsProvider;
  254. private Guid projectIdGuid;
  255. private bool projectOpened;
  256. private string sccAuxPath;
  257. private string sccLocalPath;
  258. private string sccProjectName;
  259. private string sccProvider;
  260. private bool showProjectInSolutionPage = true;
  261. /// <summary>A service provider call back object provided by the IDE hosting the project manager</summary>
  262. private ServiceProvider site;
  263. /// <summary>
  264. /// Defines if Project System supports Project Designer
  265. /// </summary>
  266. private bool supportsProjectDesigner;
  267. private uint suspendMSBuildCounter;
  268. private TaskProvider taskProvider;
  269. /// <summary>
  270. /// Token processor used by the project sample.
  271. /// </summary>
  272. private TokenProcessor tokenProcessor;
  273. private TrackDocumentsHelper tracker;
  274. private bool useProvidedLogger;
  275. private string warningString;
  276. /// <summary>
  277. /// Used for flavoring to hold the XML fragments
  278. /// </summary>
  279. private XmlDocument xmlFragments;
  280. public static ServiceProvider ServiceProvider { get; set; }
  281. #endregion
  282. #region abstract properties
  283. /// <summary>
  284. /// This Guid must match the Guid you registered under
  285. /// HKLM\Software\Microsoft\VisualStudio\%version%\Projects.
  286. /// Among other things, the Project framework uses this
  287. /// guid to find your project and item templates.
  288. /// </summary>
  289. public abstract Guid ProjectGuid { get; }
  290. /// <summary>
  291. /// Returns a caption for VSHPROPID_TypeName.
  292. /// </summary>
  293. /// <returns></returns>
  294. public abstract string ProjectType { get; }
  295. #endregion
  296. #region virtual properties
  297. /// <summary>
  298. /// This is the project instance guid that is peristed in the project file
  299. /// </summary>
  300. [Browsable(false)]
  301. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
  302. public virtual Guid ProjectIDGuid
  303. {
  304. get { return projectIdGuid; }
  305. set
  306. {
  307. if (projectIdGuid != value)
  308. {
  309. projectIdGuid = value;
  310. if (buildProject != null)
  311. {
  312. SetProjectProperty("ProjectGuid", projectIdGuid.ToString("B"));
  313. }
  314. }
  315. }
  316. }
  317. #endregion
  318. #region properties
  319. #region overridden properties
  320. public override int MenuCommandId
  321. {
  322. get { return VsMenus.IDM_VS_CTXT_PROJNODE; }
  323. }
  324. public override string Url
  325. {
  326. get { return GetMkDocument(); }
  327. }
  328. public override string Caption
  329. {
  330. get
  331. {
  332. // Default to file name
  333. string caption = buildProject.FullPath;
  334. if (String.IsNullOrEmpty(caption))
  335. {
  336. if (buildProject.GetProperty(ProjectFileConstants.Name) != null)
  337. {
  338. caption = buildProject.GetProperty(ProjectFileConstants.Name).EvaluatedValue;
  339. if (caption == null || caption.Length == 0)
  340. {
  341. caption = ItemNode.GetMetadata(ProjectFileConstants.Include);
  342. }
  343. }
  344. }
  345. else
  346. {
  347. caption = Path.GetFileNameWithoutExtension(caption);
  348. }
  349. return caption;
  350. }
  351. }
  352. public override Guid ItemTypeGuid
  353. {
  354. get { return ProjectGuid; }
  355. }
  356. public override int ImageIndex
  357. {
  358. get { return (int)ImageName.Application; }
  359. }
  360. #endregion
  361. #region virtual properties
  362. public virtual string ErrorString
  363. {
  364. get
  365. {
  366. if (errorString == null)
  367. {
  368. errorString = SR.GetString(SR.Error, CultureInfo.CurrentUICulture);
  369. }
  370. return errorString;
  371. }
  372. }
  373. public virtual string WarningString
  374. {
  375. get
  376. {
  377. if (warningString == null)
  378. {
  379. warningString = SR.GetString(SR.Warning, CultureInfo.CurrentUICulture);
  380. }
  381. return warningString;
  382. }
  383. }
  384. /// <summary>
  385. /// The target name that will be used for evaluating the project file (i.e., pseudo-builds).
  386. /// This target is used to trigger a build with when the project system changes.
  387. /// Example: The language projrcts are triggering a build with the Compile target whenever
  388. /// the project system changes.
  389. /// </summary>
  390. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ReEvaluate")]
  391. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Re")]
  392. protected internal virtual string ReEvaluateProjectFileTargetName
  393. {
  394. get { return null; }
  395. }
  396. /// <summary>
  397. /// This is the object that will be returned by EnvDTE.Project.Object for this project
  398. /// </summary>
  399. protected internal virtual object ProjectObject
  400. {
  401. get { return null; }
  402. }
  403. /// <summary>
  404. /// Override this property to specify when the project file is dirty.
  405. /// </summary>
  406. protected virtual bool IsProjectFileDirty
  407. {
  408. get
  409. {
  410. string document = GetMkDocument();
  411. if (String.IsNullOrEmpty(document))
  412. {
  413. return isDirty;
  414. }
  415. return (isDirty || !File.Exists(document));
  416. }
  417. }
  418. /// <summary>
  419. /// True if the project uses the Project Designer Editor instead of the property page frame to edit project properties.
  420. /// </summary>
  421. protected virtual bool SupportsProjectDesigner
  422. {
  423. get { return supportsProjectDesigner; }
  424. set { supportsProjectDesigner = value; }
  425. }
  426. protected virtual Guid ProjectDesignerEditor
  427. {
  428. get { return VSConstants.GUID_ProjectDesignerEditor; }
  429. }
  430. /// <summary>
  431. /// Defines the flag that supports the VSHPROPID.ShowProjInSolutionPage
  432. /// </summary>
  433. protected virtual bool ShowProjectInSolutionPage
  434. {
  435. get { return showProjectInSolutionPage; }
  436. set { showProjectInSolutionPage = value; }
  437. }
  438. #endregion
  439. /// <summary>
  440. /// Gets or sets the ability of a project filenode to have child nodes (sub items).
  441. /// Example would be C#/VB forms having resx and designer files.
  442. /// </summary>
  443. protected internal bool CanFileNodesHaveChilds
  444. {
  445. get { return canFileNodesHaveChilds; }
  446. set { canFileNodesHaveChilds = value; }
  447. }
  448. /// <summary>
  449. /// Get and set the Token processor.
  450. /// </summary>
  451. public TokenProcessor FileTemplateProcessor
  452. {
  453. get
  454. {
  455. if (tokenProcessor == null)
  456. tokenProcessor = new TokenProcessor();
  457. return tokenProcessor;
  458. }
  459. set { tokenProcessor = value; }
  460. }
  461. /// <summary>
  462. /// Gets a service provider object provided by the IDE hosting the project
  463. /// </summary>
  464. [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
  465. public IServiceProvider Site
  466. {
  467. get { return site; }
  468. }
  469. /// <summary>
  470. /// Gets an ImageHandler for the project node.
  471. /// </summary>
  472. public ImageHandler ImageHandler
  473. {
  474. get
  475. {
  476. if (null == imageHandler)
  477. {
  478. imageHandler =
  479. new ImageHandler(
  480. typeof(ProjectNode).Assembly.GetManifestResourceStream(
  481. "Microsoft.VisualStudio.Project.Resources.imagelis.bmp"));
  482. }
  483. return imageHandler;
  484. }
  485. }
  486. /// <summary>
  487. /// This property returns the time of the last change made to this project.
  488. /// It is not the time of the last change on the project file, but actually of
  489. /// the in memory project settings. In other words, it is the last time that
  490. /// SetProjectDirty was called.
  491. /// </summary>
  492. public DateTime LastModifiedTime
  493. {
  494. get { return lastModifiedTime; }
  495. }
  496. /// <summary>
  497. /// Determines whether this project is a new project.
  498. /// </summary>
  499. public bool IsNewProject
  500. {
  501. get { return isNewProject; }
  502. }
  503. /// <summary>
  504. /// Gets the path to the folder containing the project.
  505. /// </summary>
  506. public string ProjectFolder
  507. {
  508. get { return Path.GetDirectoryName(filename); }
  509. }
  510. /// <summary>
  511. /// Gets or sets the project filename.
  512. /// </summary>
  513. public string ProjectFile
  514. {
  515. get { return Path.GetFileName(filename); }
  516. set { SetEditLabel(value); }
  517. }
  518. /// <summary>
  519. /// Gets the Base Uniform Resource Identifier (URI).
  520. /// </summary>
  521. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")]
  522. public Url BaseURI
  523. {
  524. get
  525. {
  526. if (baseUri == null && buildProject != null)
  527. {
  528. string path = Path.GetDirectoryName(buildProject.FullPath);
  529. // Uri/Url behave differently when you have trailing slash and when you dont
  530. if (!path.EndsWith("\\", StringComparison.Ordinal) && !path.EndsWith("/", StringComparison.Ordinal))
  531. path += "\\";
  532. baseUri = new Url(path);
  533. }
  534. Debug.Assert(baseUri != null,
  535. "Base URL should not be null. Did you call BaseURI before loading the project?");
  536. return baseUri;
  537. }
  538. }
  539. /// <summary>
  540. /// Gets whether or not the project is closed.
  541. /// </summary>
  542. public bool IsClosed
  543. {
  544. get { return isClosed; }
  545. }
  546. /// <summary>
  547. /// Gets whether or not the project is being built.
  548. /// </summary>
  549. public bool BuildInProgress
  550. {
  551. get { return buildInProcess; }
  552. }
  553. /// <summary>
  554. /// Gets or set the relative path to the folder containing the project ouput.
  555. /// </summary>
  556. public virtual string OutputBaseRelativePath
  557. {
  558. get { return outputBaseRelativePath; }
  559. set
  560. {
  561. if (Path.IsPathRooted(value))
  562. {
  563. throw new ArgumentException("Path must not be rooted.");
  564. }
  565. outputBaseRelativePath = value;
  566. }
  567. }
  568. public FrameworkName TargetFrameworkMoniker
  569. {
  570. get
  571. {
  572. if (options == null)
  573. {
  574. GetProjectOptions();
  575. }
  576. if (options != null)
  577. {
  578. return options.TargetFrameworkMoniker ?? DefaultTargetFrameworkMoniker;
  579. }
  580. else
  581. {
  582. return DefaultTargetFrameworkMoniker;
  583. }
  584. }
  585. set
  586. {
  587. if (options == null)
  588. {
  589. GetProjectOptions();
  590. }
  591. if (value == null)
  592. {
  593. value = DefaultTargetFrameworkMoniker;
  594. }
  595. if (options.TargetFrameworkMoniker != value)
  596. {
  597. OnTargetFrameworkMonikerChanged(options, options.TargetFrameworkMoniker, value);
  598. }
  599. }
  600. }
  601. /// <summary>
  602. /// Version of this node as an IVsHierarchy that can be safely passed to native code from a background thread.
  603. /// </summary>
  604. public IVsHierarchy InteropSafeIVsHierarchy { get; protected set; }
  605. /// <summary>
  606. /// Version of this node as an IVsUIHierarchy that can be safely passed to native code from a background thread.
  607. /// </summary>
  608. public IVsUIHierarchy InteropSafeIVsUIHierarchy { get; protected set; }
  609. /// <summary>
  610. /// Version of this node as an IVsProject3 that can be safely passed to native code from a background thread.
  611. /// </summary>
  612. public IVsProject3 InteropSafeIVsProject3 { get; protected set; }
  613. /// <summary>
  614. /// Version of this node as an IVsSccProject2 that can be safely passed to native code from a background thread.
  615. /// </summary>
  616. public IVsSccProject2 InteropSafeIVsSccProject2 { get; protected set; }
  617. /// <summary>
  618. /// Version of this node as an IVsUIHierWinClipboardHelperEvents that can be safely passed to native code from a background thread.
  619. /// </summary>
  620. public IVsUIHierWinClipboardHelperEvents InteropSafeIVsUIHierWinClipboardHelperEvents { get; protected set; }
  621. public IVsComponentUser InteropSafeIVsComponentUser { get; protected set; }
  622. /// <summary>
  623. /// Gets or sets the flag whether query edit should communicate with the scc manager.
  624. /// </summary>
  625. protected bool DisableQueryEdit
  626. {
  627. get { return disableQueryEdit; }
  628. set { disableQueryEdit = value; }
  629. }
  630. /// <summary>
  631. /// Gets a collection of integer ids that maps to project item instances
  632. /// </summary>
  633. internal EventSinkCollection ItemIdMap
  634. {
  635. get { return itemIdMap; }
  636. }
  637. /// <summary>
  638. /// Get the helper object that track document changes.
  639. /// </summary>
  640. internal TrackDocumentsHelper Tracker
  641. {
  642. get { return tracker; }
  643. }
  644. /// <summary>
  645. /// Gets or sets the build logger.
  646. /// </summary>
  647. protected Logger BuildLogger
  648. {
  649. get { return buildLogger; }
  650. set
  651. {
  652. buildLogger = value;
  653. useProvidedLogger = true;
  654. }
  655. }
  656. /// <summary>
  657. /// Gets the taskprovider.
  658. /// </summary>
  659. protected TaskProvider TaskProvider
  660. {
  661. get { return taskProvider; }
  662. }
  663. /// <summary>
  664. /// Gets the project file name.
  665. /// </summary>
  666. protected string FileName
  667. {
  668. get { return filename; }
  669. }
  670. protected bool IsIdeInCommandLineMode
  671. {
  672. get
  673. {
  674. bool cmdline = false;
  675. var shell = site.GetService(typeof(SVsShell)) as IVsShell;
  676. if (shell != null)
  677. {
  678. object obj;
  679. Marshal.ThrowExceptionForHR(shell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode,
  680. out obj));
  681. cmdline = (bool)obj;
  682. }
  683. return cmdline;
  684. }
  685. }
  686. /// <summary>
  687. /// Gets the configuration provider.
  688. /// </summary>
  689. protected ConfigProvider ConfigProvider
  690. {
  691. get
  692. {
  693. if (configProvider == null)
  694. {
  695. configProvider = CreateConfigProvider();
  696. }
  697. return configProvider;
  698. }
  699. }
  700. /// <summary>
  701. /// Gets or sets whether or not source code control is disabled for this project.
  702. /// </summary>
  703. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
  704. protected bool IsSccDisabled
  705. {
  706. get { return disableScc; }
  707. set { disableScc = value; }
  708. }
  709. /// <summary>
  710. /// Gets or set whether items can be deleted for this project.
  711. /// Enabling this feature can have the potential destructive behavior such as deleting files from disk.
  712. /// </summary>
  713. protected internal bool CanProjectDeleteItems
  714. {
  715. get { return canProjectDeleteItems; }
  716. set { canProjectDeleteItems = value; }
  717. }
  718. /// <summary>
  719. /// Determines whether the project was fully opened. This is set when the OnAfterOpenProject has triggered.
  720. /// </summary>
  721. protected internal bool HasProjectOpened
  722. {
  723. get { return projectOpened; }
  724. }
  725. /// <summary>
  726. /// Gets or sets event triggering flags.
  727. /// </summary>
  728. internal EventTriggering EventTriggeringFlag
  729. {
  730. get { return eventTriggeringFlag; }
  731. set { eventTriggeringFlag = value; }
  732. }
  733. /// <summary>
  734. /// Defines the build project that has loaded the project file.
  735. /// </summary>
  736. protected internal MSBuild.Project BuildProject
  737. {
  738. get { return buildProject; }
  739. set { SetBuildProject(value); }
  740. }
  741. /// <summary>
  742. /// Gets the current config.
  743. /// </summary>
  744. /// <value>The current config.</value>
  745. protected internal ProjectInstance CurrentConfig
  746. {
  747. get { return currentConfig; }
  748. }
  749. /// <summary>
  750. /// Defines the build engine that is used to build the project file.
  751. /// </summary>
  752. internal ProjectCollection BuildEngine
  753. {
  754. get { return buildEngine; }
  755. set { buildEngine = value; }
  756. }
  757. /// <summary>
  758. /// The internal package implementation.
  759. /// </summary>
  760. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  761. internal ProjectPackage Package
  762. {
  763. get { return package; }
  764. set { package = value; }
  765. }
  766. #endregion
  767. #region ctor
  768. protected ProjectNode()
  769. {
  770. Initialize();
  771. }
  772. #endregion
  773. #region overridden methods
  774. protected override NodeProperties CreatePropertiesObject()
  775. {
  776. return new ProjectNodeProperties(this);
  777. }
  778. /// <summary>
  779. /// Sets the properties for the project node.
  780. /// </summary>
  781. /// <param name="propid">
  782. /// Identifier of the hierarchy property. For a list of propid values, <see cref="__VSHPROPID" />
  783. /// </param>
  784. /// <param name="value">The value to set. </param>
  785. /// <returns>A success or failure value.</returns>
  786. public override int SetProperty(int propid, object value)
  787. {
  788. __VSHPROPID id = (__VSHPROPID)propid;
  789. switch (id)
  790. {
  791. case __VSHPROPID.VSHPROPID_ShowProjInSolutionPage:
  792. ShowProjectInSolutionPage = (bool)value;
  793. return VSConstants.S_OK;
  794. }
  795. return base.SetProperty(propid, value);
  796. }
  797. /// <summary>
  798. /// Renames the project node.
  799. /// </summary>
  800. /// <param name="label">The new name</param>
  801. /// <returns>A success or failure value.</returns>
  802. public override int SetEditLabel(string label)
  803. {
  804. // Validate the filename.
  805. if (String.IsNullOrEmpty(label))
  806. {
  807. throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture));
  808. }
  809. else if (ProjectFolder.Length + label.Length + 1 > NativeMethods.MAX_PATH)
  810. {
  811. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
  812. SR.GetString(SR.PathTooLong,
  813. CultureInfo.CurrentUICulture), label));
  814. }
  815. else if (Utilities.IsFileNameInvalid(label))
  816. {
  817. throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture));
  818. }
  819. string fileName = Path.GetFileNameWithoutExtension(label);
  820. // if there is no filename or it starts with a leading dot issue an error message and quit.
  821. if (String.IsNullOrEmpty(fileName) || fileName[0] == '.')
  822. {
  823. throw new InvalidOperationException(SR.GetString(SR.FileNameCannotContainALeadingPeriod,
  824. CultureInfo.CurrentUICulture));
  825. }
  826. // Nothing to do if the name is the same
  827. string oldFileName = Path.GetFileNameWithoutExtension(Url);
  828. if (String.Compare(oldFileName, label, StringComparison.Ordinal) == 0)
  829. {
  830. return VSConstants.S_FALSE;
  831. }
  832. // Now check whether the original file is still there. It could have been renamed.
  833. if (!File.Exists(Url))
  834. {
  835. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
  836. SR.GetString(SR.FileOrFolderCannotBeFound,
  837. CultureInfo.CurrentUICulture),
  838. ProjectFile));
  839. }
  840. // Get the full file name and then rename the project file.
  841. string newFile = Path.Combine(ProjectFolder, label);
  842. string extension = Path.GetExtension(Url);
  843. // Make sure it has the correct extension
  844. if (String.Compare(Path.GetExtension(newFile), extension, StringComparison.OrdinalIgnoreCase) != 0)
  845. {
  846. newFile += extension;
  847. }
  848. RenameProjectFile(newFile);
  849. return VSConstants.S_OK;
  850. }
  851. /// <summary>
  852. /// Gets the automation object for the project node.
  853. /// </summary>
  854. /// <returns>An instance of an EnvDTE.Project implementation object representing the automation object for the project.</returns>
  855. public override object GetAutomationObject()
  856. {
  857. return new OAProject(this);
  858. }
  859. /// <summary>
  860. /// Closes the project node.
  861. /// </summary>
  862. /// <returns>A success or failure value.</returns>
  863. public override int Close()
  864. {
  865. int hr = VSConstants.S_OK;
  866. try
  867. {
  868. // Walk the tree and close all nodes.
  869. // This has to be done before the project closes, since we want still state available for the ProjectMgr on the nodes
  870. // when nodes are closing.
  871. try
  872. {
  873. CloseAllNodes(this);
  874. }
  875. finally
  876. {
  877. Dispose(true);
  878. }
  879. }
  880. catch (COMException e)
  881. {
  882. hr = e.ErrorCode;
  883. }
  884. finally
  885. {
  886. ErrorHandler.ThrowOnFailure(base.Close());
  887. }
  888. isClosed = true;
  889. return hr;
  890. }
  891. /// <summary>
  892. /// Sets the service provider from which to access the services.
  893. /// </summary>
  894. /// <param name="site">An instance to an Microsoft.VisualStudio.OLE.Interop object</param>
  895. /// <returns>A success or failure value.</returns>
  896. public override int SetSite(IOleServiceProvider site)
  897. {
  898. CCITracing.TraceCall();
  899. this.site = new ServiceProvider(site);
  900. ServiceProvider = this.site;
  901. if (taskProvider != null)
  902. {
  903. taskProvider.Dispose();
  904. }
  905. taskProvider = new TaskProvider(this.site);
  906. return VSConstants.S_OK;
  907. }
  908. /// <summary>
  909. /// Gets the properties of the project node.
  910. /// </summary>
  911. /// <param name="propId">The __VSHPROPID of the property.</param>
  912. /// <returns>
  913. /// A property dependent value. See: <see cref="__VSHPROPID" /> for details.
  914. /// </returns>
  915. public override object GetProperty(int propId)
  916. {
  917. switch ((__VSHPROPID)propId)
  918. {
  919. case __VSHPROPID.VSHPROPID_ConfigurationProvider:
  920. return ConfigProvider;
  921. case __VSHPROPID.VSHPROPID_ProjectName:
  922. return Caption;
  923. case __VSHPROPID.VSHPROPID_ProjectDir:
  924. return ProjectFolder;
  925. case __VSHPROPID.VSHPROPID_TypeName:
  926. return ProjectType;
  927. case __VSHPROPID.VSHPROPID_ShowProjInSolutionPage:
  928. return ShowProjectInSolutionPage;
  929. case __VSHPROPID.VSHPROPID_ExpandByDefault:
  930. return true;
  931. // Use the same icon as if the folder was closed
  932. case __VSHPROPID.VSHPROPID_OpenFolderIconIndex:
  933. return GetProperty((int)__VSHPROPID.VSHPROPID_IconIndex);
  934. }
  935. switch ((__VSHPROPID2)propId)
  936. {
  937. case __VSHPROPID2.VSHPROPID_SupportsProjectDesigner:
  938. return SupportsProjectDesigner;
  939. case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList:
  940. return
  941. Utilities.CreateSemicolonDelimitedListOfStringFromGuids(
  942. GetConfigurationIndependentPropertyPages());
  943. case __VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList:
  944. return
  945. Utilities.CreateSemicolonDelimitedListOfStringFromGuids(GetConfigurationDependentPropertyPages());
  946. case __VSHPROPID2.VSHPROPID_PriorityPropertyPagesCLSIDList:
  947. return Utilities.CreateSemicolonDelimitedListOfStringFromGuids(GetPriorityProjectDesignerPages());
  948. case __VSHPROPID2.VSHPROPID_Container:
  949. return true;
  950. default:
  951. break;
  952. }
  953. return base.GetProperty(propId);
  954. }
  955. /// <summary>
  956. /// Gets the GUID value of the node.
  957. /// </summary>
  958. /// <param name="propid">A __VSHPROPID or __VSHPROPID2 value of the guid property</param>
  959. /// <param name="guid">The guid to return for the property.</param>
  960. /// <returns>A success or failure value.</returns>
  961. public override int GetGuidProperty(int propid, out Guid guid)
  962. {
  963. guid = Guid.Empty;
  964. if ((__VSHPROPID)propid == __VSHPROPID.VSHPROPID_ProjectIDGuid)
  965. {
  966. guid = ProjectIDGuid;
  967. }
  968. else if (propid == (int)__VSHPROPID.VSHPROPID_CmdUIGuid)
  969. {
  970. guid = ProjectGuid;
  971. }
  972. else if ((__VSHPROPID2)propid == __VSHPROPID2.VSHPROPID_ProjectDesignerEditor && SupportsProjectDesigner)
  973. {
  974. guid = ProjectDesignerEditor;
  975. }
  976. else
  977. {
  978. base.GetGuidProperty(propid, out guid);
  979. }
  980. if (guid.CompareTo(Guid.Empty) == 0)
  981. {
  982. return VSConstants.DISP_E_MEMBERNOTFOUND;
  983. }
  984. return VSConstants.S_OK;
  985. }
  986. /// <summary>
  987. /// Sets Guid properties for the project node.
  988. /// </summary>
  989. /// <param name="propid">A __VSHPROPID or __VSHPROPID2 value of the guid property</param>
  990. /// <param name="guid">The guid value to set.</param>
  991. /// <returns>A success or failure value.</returns>
  992. public override int SetGuidProperty(int propid, ref Guid guid)
  993. {
  994. switch ((__VSHPROPID)propid)
  995. {
  996. case __VSHPROPID.VSHPROPID_ProjectIDGuid:
  997. ProjectIDGuid = guid;
  998. return VSConstants.S_OK;
  999. }
  1000. CCITracing.TraceCall(String.Format(CultureInfo.CurrentCulture, "Property {0} not found", propid));
  1001. return VSConstants.DISP_E_MEMBERNOTFOUND;
  1002. }
  1003. /// <summary>
  1004. /// Removes items from the hierarchy.
  1005. /// </summary>
  1006. /// <devdoc>Project overwrites this.</devdoc>
  1007. public override void Remove(bool removeFromStorage)
  1008. {
  1009. // the project will not be deleted from disk, just removed
  1010. if (removeFromStorage)
  1011. {
  1012. return;
  1013. }
  1014. // Remove the entire project from the solution
  1015. IVsSolution solution = Site.GetService(typeof(SVsSolution)) as IVsSolution;
  1016. uint iOption = 1; // SLNSAVEOPT_PromptSave
  1017. ErrorHandler.ThrowOnFailure(solution.CloseSolutionElement(iOption, InteropSafeIVsHierarchy, 0));
  1018. }
  1019. /// <summary>
  1020. /// Gets the moniker for the project node. That is the full path of the project file.
  1021. /// </summary>
  1022. /// <returns>The moniker for the project file.</returns>
  1023. public override string GetMkDocument()
  1024. {
  1025. Debug.Assert(!String.IsNullOrEmpty(filename));
  1026. Debug.Assert(BaseURI != null && !String.IsNullOrEmpty(BaseURI.AbsoluteUrl));
  1027. return Path.Combine(BaseURI.AbsoluteUrl, filename);
  1028. }
  1029. /// <summary>
  1030. /// Disposes the project node object.
  1031. /// </summary>
  1032. /// <param name="disposing">Flag determining ehether it was deterministic or non deterministic clean up.</param>
  1033. protected override void Dispose(bool disposing)
  1034. {
  1035. if (isDisposed)
  1036. {
  1037. return;
  1038. }
  1039. try
  1040. {
  1041. try
  1042. {
  1043. UnRegisterProject();
  1044. }
  1045. finally
  1046. {
  1047. try
  1048. {
  1049. RegisterClipboardNotifications(false);
  1050. }
  1051. finally
  1052. {
  1053. try
  1054. {
  1055. if (projectEventsProvider != null)
  1056. {
  1057. projectEventsProvider.AfterProjectFileOpened -= OnAfterProjectOpen;
  1058. }
  1059. if (taskProvider != null)
  1060. {
  1061. taskProvider.Tasks.Clear();
  1062. taskProvider.Dispose();
  1063. taskProvider = null;
  1064. }
  1065. if (buildLogger != null)
  1066. {
  1067. buildLogger.Shutdown();
  1068. buildLogger = null;
  1069. }
  1070. if (site != null)
  1071. {
  1072. site.Dispose();
  1073. }
  1074. }
  1075. finally
  1076. {
  1077. buildEngine = null;
  1078. }
  1079. }
  1080. }
  1081. if (buildProject != null)
  1082. {
  1083. buildProject.ProjectCollection.UnloadProject(buildProject);
  1084. buildProject.ProjectCollection.UnloadProject(buildProject.Xml);
  1085. buildProject = null;
  1086. }
  1087. if (null != imageHandler)
  1088. {
  1089. imageHandler.Close();
  1090. imageHandler = null;
  1091. }
  1092. }
  1093. finally
  1094. {
  1095. base.Dispose(disposing);
  1096. isDisposed = true;
  1097. }
  1098. }
  1099. /// <summary>
  1100. /// Handles command status on the project node. If a command cannot be handled then the base should be called.
  1101. /// </summary>
  1102. /// <param name="cmdGroup">A unique identifier of the command group. The pguidCmdGroup parameter can be NULL to specify the standard group.</param>
  1103. /// <param name="cmd">The command to query status for.</param>
  1104. /// <param name="pCmdText">Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information.</param>
  1105. /// <param name="result">An out parameter specifying the QueryStatusResult of the command.</param>
  1106. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1107. protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result)
  1108. {
  1109. if (cmdGroup == VsMenus.guidStandardCommandSet97)
  1110. {
  1111. switch ((VsCommands)cmd)
  1112. {
  1113. case VsCommands.Copy:
  1114. case VsCommands.Paste:
  1115. case VsCommands.Cut:
  1116. case VsCommands.Rename:
  1117. case VsCommands.Exit:
  1118. case VsCommands.ProjectSettings:
  1119. case VsCommands.BuildSln:
  1120. case VsCommands.UnloadProject:
  1121. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1122. return VSConstants.S_OK;
  1123. case VsCommands.ViewForm:
  1124. if (HasDesigner)
  1125. {
  1126. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1127. return VSConstants.S_OK;
  1128. }
  1129. break;
  1130. case VsCommands.CancelBuild:
  1131. result |= QueryStatusResult.SUPPORTED;
  1132. if (buildInProcess)
  1133. result |= QueryStatusResult.ENABLED;
  1134. else
  1135. result |= QueryStatusResult.INVISIBLE;
  1136. return VSConstants.S_OK;
  1137. case VsCommands.NewFolder:
  1138. case VsCommands.AddNewItem:
  1139. case VsCommands.AddExistingItem:
  1140. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1141. return VSConstants.S_OK;
  1142. case VsCommands.SetStartupProject:
  1143. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1144. return VSConstants.S_OK;
  1145. }
  1146. }
  1147. else if (cmdGroup == VsMenus.guidStandardCommandSet2K)
  1148. {
  1149. switch ((VsCommands2K)cmd)
  1150. {
  1151. case VsCommands2K.ADDREFERENCE:
  1152. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1153. return VSConstants.S_OK;
  1154. case VsCommands2K.EXCLUDEFROMPROJECT:
  1155. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
  1156. return VSConstants.S_OK;
  1157. case ExploreFolderInWindowsCommand:
  1158. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1159. return VSConstants.S_OK;
  1160. }
  1161. }
  1162. else
  1163. {
  1164. return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP;
  1165. }
  1166. return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result);
  1167. }
  1168. /// <summary>
  1169. /// Handles command execution.
  1170. /// </summary>
  1171. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1172. /// <param name="cmd">The command to be executed.</param>
  1173. /// <param name="nCmdexecopt">Values describe how the object should execute the command.</param>
  1174. /// <param name="pvaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
  1175. /// <param name="pvaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
  1176. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1177. protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
  1178. {
  1179. if (cmdGroup == VsMenus.guidStandardCommandSet97)
  1180. {
  1181. switch ((VsCommands)cmd)
  1182. {
  1183. case VsCommands.UnloadProject:
  1184. return UnloadProject();
  1185. case VsCommands.CleanSel:
  1186. case VsCommands.CleanCtx:
  1187. return CleanProject();
  1188. }
  1189. }
  1190. else if (cmdGroup == VsMenus.guidStandardCommandSet2K)
  1191. {
  1192. switch ((VsCommands2K)cmd)
  1193. {
  1194. case VsCommands2K.ADDREFERENCE:
  1195. return AddProjectReference();
  1196. case VsCommands2K.ADDWEBREFERENCE:
  1197. case VsCommands2K.ADDWEBREFERENCECTX:
  1198. return AddWebReference();
  1199. case ExploreFolderInWindowsCommand:
  1200. string explorerPath = Path.Combine(
  1201. Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
  1202. Process.Start(explorerPath, ProjectFolder);
  1203. return VSConstants.S_OK;
  1204. }
  1205. }
  1206. return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut);
  1207. }
  1208. /// <summary>
  1209. /// Get the boolean value for the deletion of a project item
  1210. /// </summary>
  1211. /// <param name="deleteOperation">A flag that specifies the type of delete operation (delete from storage or remove from project)</param>
  1212. /// <returns>true if item can be deleted from project</returns>
  1213. protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation)
  1214. {
  1215. if (deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject)
  1216. {
  1217. return true;
  1218. }
  1219. return false;
  1220. }
  1221. /// <summary>
  1222. /// Returns a specific Document manager to handle opening and closing of the Project(Application) Designer if projectdesigner is supported.
  1223. /// </summary>
  1224. /// <returns>Document manager object</returns>
  1225. protected internal override DocumentManager GetDocumentManager()
  1226. {
  1227. if (SupportsProjectDesigner)
  1228. {
  1229. return new ProjectDesignerDocumentManager(this);
  1230. }
  1231. return null;
  1232. }
  1233. #endregion
  1234. #region virtual methods
  1235. /// <summary>
  1236. /// Executes a wizard.
  1237. /// </summary>
  1238. /// <param name="parentNode">The node to which the wizard should add item(s).</param>
  1239. /// <param name="itemName">The name of the file that the user typed in.</param>
  1240. /// <param name="wizardToRun">The name of the wizard to run.</param>
  1241. /// <param name="dlgOwner">The owner of the dialog box.</param>
  1242. /// <returns>A VSADDRESULT enum value describing success or failure.</returns>
  1243. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"),
  1244. SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dlg")]
  1245. public virtual VSADDRESULT RunWizard(HierarchyNode parentNode, string itemName, string wizardToRun,
  1246. IntPtr dlgOwner)
  1247. {
  1248. Debug.Assert(!String.IsNullOrEmpty(itemName),
  1249. "The Add item dialog was passing in a null or empty item to be added to the hierrachy.");
  1250. Debug.Assert(!String.IsNullOrEmpty(ProjectFolder), "The Project Folder is not specified for this project.");
  1251. if (parentNode == null)
  1252. {
  1253. throw new ArgumentNullException("parentNode");
  1254. }
  1255. if (String.IsNullOrEmpty(itemName))
  1256. {
  1257. throw new ArgumentNullException("itemName");
  1258. }
  1259. // We just validate for length, since we assume other validation has been performed by the dlgOwner.
  1260. if (ProjectFolder.Length + itemName.Length + 1 > NativeMethods.MAX_PATH)
  1261. {
  1262. string errorMessage = String.Format(CultureInfo.CurrentCulture,
  1263. SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), itemName);
  1264. if (!Utilities.IsInAutomationFunction(Site))
  1265. {
  1266. string title = null;
  1267. OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
  1268. OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
  1269. OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
  1270. VsShellUtilities.ShowMessageBox(Site, title, errorMessage, icon, buttons, defaultButton);
  1271. return VSADDRESULT.ADDRESULT_Failure;
  1272. }
  1273. else
  1274. {
  1275. throw new InvalidOperationException(errorMessage);
  1276. }
  1277. }
  1278. // Build up the ContextParams safearray
  1279. // [0] = Wizard type guid (bstr)
  1280. // [1] = Project name (bstr)
  1281. // [2] = ProjectItems collection (bstr)
  1282. // [3] = Local Directory (bstr)
  1283. // [4] = Filename the user typed (bstr)
  1284. // [5] = Product install Directory (bstr)
  1285. // [6] = Run silent (bool)
  1286. object[] contextParams = new object[7];
  1287. contextParams[0] = Constants.vsWizardAddItem;
  1288. contextParams[1] = Caption;
  1289. object automationObject = parentNode.GetAutomationObject();
  1290. if (automationObject is EnvDTE.Project)
  1291. {
  1292. EnvDTE.Project project = (EnvDTE.Project)automationObject;
  1293. contextParams[2] = project.ProjectItems;
  1294. }
  1295. else
  1296. {
  1297. // This would normally be a folder unless it is an item with subitems
  1298. ProjectItem item = (ProjectItem)automationObject;
  1299. contextParams[2] = item.ProjectItems;
  1300. }
  1301. contextParams[3] = ProjectFolder;
  1302. contextParams[4] = itemName;
  1303. object objInstallationDir = null;
  1304. IVsShell shell = (IVsShell)GetService(typeof(IVsShell));
  1305. ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory,
  1306. out objInstallationDir));
  1307. string installDir = (string)objInstallationDir;
  1308. // append a '\' to the install dir to mimic what the shell does (though it doesn't add one to destination dir)
  1309. if (!installDir.EndsWith("\\", StringComparison.Ordinal))
  1310. {
  1311. installDir += "\\";
  1312. }
  1313. contextParams[5] = installDir;
  1314. contextParams[6] = true;
  1315. IVsExtensibility3 ivsExtensibility = GetService(typeof(IVsExtensibility)) as IVsExtensibility3;
  1316. Debug.Assert(ivsExtensibility != null, "Failed to get IVsExtensibility3 service");
  1317. if (ivsExtensibility == null)
  1318. {
  1319. return VSADDRESULT.ADDRESULT_Failure;
  1320. }
  1321. // Determine if we have the trust to run this wizard.
  1322. IVsDetermineWizardTrust wizardTrust = GetService(typeof(SVsDetermineWizardTrust)) as IVsDetermineWizardTrust;
  1323. if (wizardTrust != null)
  1324. {
  1325. Guid guidProjectAdding = Guid.Empty;
  1326. ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardInitiated(wizardToRun, ref guidProjectAdding));
  1327. }
  1328. int wizResultAsInt;
  1329. try
  1330. {
  1331. Array contextParamsAsArray = contextParams;
  1332. int result = ivsExtensibility.RunWizardFile(wizardToRun, (int)dlgOwner, ref contextParamsAsArray,
  1333. out wizResultAsInt);
  1334. if (!ErrorHandler.Succeeded(result) && result != VSConstants.OLE_E_PROMPTSAVECANCELLED)
  1335. {
  1336. ErrorHandler.ThrowOnFailure(result);
  1337. }
  1338. }
  1339. finally
  1340. {
  1341. if (wizardTrust != null)
  1342. {
  1343. ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardCompleted());
  1344. }
  1345. }
  1346. wizardResult wizardResult = (wizardResult)wizResultAsInt;
  1347. switch (wizardResult)
  1348. {
  1349. default:
  1350. return VSADDRESULT.ADDRESULT_Cancel;
  1351. case wizardResult.wizardResultSuccess:
  1352. return VSADDRESULT.ADDRESULT_Success;
  1353. case wizardResult.wizardResultFailure:
  1354. return VSADDRESULT.ADDRESULT_Failure;
  1355. }
  1356. }
  1357. /// <summary>
  1358. /// Override this method if you want to modify the behavior of the Add Reference dialog
  1359. /// By example you could change which pages are visible and which is visible by default.
  1360. /// </summary>
  1361. /// <returns></returns>
  1362. public virtual int AddProjectReference()
  1363. {
  1364. CCITracing.TraceCall();
  1365. IVsComponentSelectorDlg4 componentDialog;
  1366. string strBrowseLocations = Path.GetDirectoryName(BaseURI.Uri.LocalPath);
  1367. var tabInitList = new List<VSCOMPONENTSELECTORTABINIT>
  1368. {
  1369. new VSCOMPONENTSELECTORTABINIT
  1370. {
  1371. guidTab = VSConstants.GUID_COMPlusPage,
  1372. varTabInitInfo = GetComponentPickerDirectories(),
  1373. },
  1374. new VSCOMPONENTSELECTORTABINIT
  1375. {
  1376. guidTab = VSConstants.GUID_COMClassicPage,
  1377. },
  1378. new VSCOMPONENTSELECTORTABINIT
  1379. {
  1380. // Tell the Add Reference dialog to call hierarchies GetProperty with the following
  1381. // propID to enablefiltering out ourself from the Project to Project reference
  1382. varTabInitInfo = (int)__VSHPROPID.VSHPROPID_ShowProjInSolutionPage,
  1383. guidTab = VSConstants.GUID_SolutionPage,
  1384. },
  1385. // Add the Browse for file page
  1386. new VSCOMPONENTSELECTORTABINIT
  1387. {
  1388. varTabInitInfo = 0,
  1389. guidTab = VSConstants.GUID_BrowseFilePage,
  1390. },
  1391. new VSCOMPONENTSELECTORTABINIT
  1392. {
  1393. guidTab = GUID_MruPage,
  1394. },
  1395. };
  1396. tabInitList.ForEach(tab => tab.dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT)));
  1397. componentDialog = GetService(typeof(IVsComponentSelectorDlg)) as IVsComponentSelectorDlg4;
  1398. try
  1399. {
  1400. // call the container to open the add reference dialog.
  1401. if (componentDialog != null)
  1402. {
  1403. // Let the project know not to show itself in the Add Project Reference Dialog page
  1404. ShowProjectInSolutionPage = false;
  1405. // call the container to open the add reference dialog.
  1406. string browseFilters = "Component Files (*.exe;*.dll)\0*.exe;*.dll\0";
  1407. ErrorHandler.ThrowOnFailure(componentDialog.ComponentSelectorDlg5(
  1408. (UInt32)
  1409. (__VSCOMPSELFLAGS.VSCOMSEL_MultiSelectMode | __VSCOMPSELFLAGS.VSCOMSEL_IgnoreMachineName),
  1410. InteropSafeIVsComponentUser,
  1411. 0,
  1412. null,
  1413. SR.GetString(SR.AddReferenceDialogTitle, CultureInfo.CurrentUICulture), // Title
  1414. "VS.AddReference", // Help topic
  1415. addComponentDialogSizeX,
  1416. addComponentDialogSizeY,
  1417. (uint)tabInitList.Count,
  1418. tabInitList.ToArray(),
  1419. ref addComponentLastActiveTab,
  1420. browseFilters,
  1421. ref strBrowseLocations,
  1422. TargetFrameworkMoniker.FullName));
  1423. }
  1424. }
  1425. catch (COMException e)
  1426. {
  1427. Trace.WriteLine("Exception : " + e.Message);
  1428. return e.ErrorCode;
  1429. }
  1430. finally
  1431. {
  1432. // Let the project know it can show itself in the Add Project Reference Dialog page
  1433. ShowProjectInSolutionPage = true;
  1434. }
  1435. return VSConstants.S_OK;
  1436. }
  1437. /// <summary>
  1438. /// Returns the Compiler associated to the project
  1439. /// </summary>
  1440. /// <returns>Null</returns>
  1441. public virtual ICodeCompiler GetCompiler()
  1442. {
  1443. return null;
  1444. }
  1445. /// <summary>
  1446. /// Override this method if you have your own project specific
  1447. /// subclass of ProjectOptions
  1448. /// </summary>
  1449. /// <returns>This method returns a new instance of the ProjectOptions base class.</returns>
  1450. public virtual ProjectOptions CreateProjectOptions()
  1451. {
  1452. return new ProjectOptions();
  1453. }
  1454. /// <summary>
  1455. /// Loads a project file. Called from the factory CreateProject to load the project.
  1456. /// </summary>
  1457. /// <param name="fileName">File name of the project that will be created. </param>
  1458. /// <param name="location">Location where the project will be created.</param>
  1459. /// <param name="name">If applicable, the name of the template to use when cloning a new project.</param>
  1460. /// <param name="flags">Set of flag values taken from the VSCREATEPROJFLAGS enumeration.</param>
  1461. /// <param name="iidProject">Identifier of the interface that the caller wants returned. </param>
  1462. /// <param name="canceled">An out parameter specifying if the project creation was canceled</param>
  1463. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "iid")]
  1464. public virtual void Load(string fileName, string location, string name, uint flags, ref Guid iidProject,
  1465. out int canceled)
  1466. {
  1467. try
  1468. {
  1469. disableQueryEdit = true;
  1470. // set up internal members and icons
  1471. canceled = 0;
  1472. ProjectMgr = this;
  1473. isNewProject = false;
  1474. if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE)
  1475. {
  1476. // we need to generate a new guid for the project
  1477. projectIdGuid = Guid.NewGuid();
  1478. }
  1479. else
  1480. {
  1481. SetProjectGuidFromProjectFile();
  1482. }
  1483. // This is almost a No op if the engine has already been instantiated in the factory.
  1484. buildEngine = Utilities.InitializeMsBuildEngine(buildEngine, Site);
  1485. // based on the passed in flags, this either reloads/loads a project, or tries to create a new one
  1486. // now we create a new project... we do that by loading the template and then saving under a new name
  1487. // we also need to copy all the associated files with it.
  1488. if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE)
  1489. {
  1490. Debug.Assert(!String.IsNullOrEmpty(fileName) && File.Exists(fileName),
  1491. "Invalid filename passed to load the project. A valid filename is expected");
  1492. isNewProject = true;
  1493. // This should be a very fast operation if the build project is already initialized by the Factory.
  1494. SetBuildProject(Utilities.ReinitializeMsBuildProject(buildEngine, fileName, buildProject));
  1495. // Compute the file name
  1496. // We try to solve two problems here. When input comes from a wizzard in case of zipped based projects
  1497. // the parameters are different.
  1498. // In that case the filename has the new filename in a temporay path.
  1499. // First get the extension from the template.
  1500. // Then get the filename from the name.
  1501. // Then create the new full path of the project.
  1502. string extension = Path.GetExtension(fileName);
  1503. string tempName = String.Empty;
  1504. // We have to be sure that we are not going to loose data here. If the project name is a.b.c then for a project that was based on a zipped template(the wizzard calls us) GetFileNameWithoutExtension will suppress "c".
  1505. // We are going to check if the parameter "name" is extension based and the extension is the same as the one from the "filename" parameter.
  1506. string tempExtension = Path.GetExtension(name);
  1507. if (!String.IsNullOrEmpty(tempExtension))
  1508. {
  1509. bool isSameExtension =
  1510. (String.Compare(tempExtension, extension, StringComparison.OrdinalIgnoreCase) == 0);
  1511. if (isSameExtension)
  1512. {
  1513. tempName = Path.GetFileNameWithoutExtension(name);
  1514. }
  1515. // If the tempExtension is not the same as the extension that the project name comes from then assume that the project name is a dotted name.
  1516. else
  1517. {
  1518. tempName = Path.GetFileName(name);
  1519. }
  1520. }
  1521. else
  1522. {
  1523. tempName = Path.GetFileName(name);
  1524. }
  1525. Debug.Assert(!String.IsNullOrEmpty(tempName), "Could not compute project name");
  1526. string tempProjectFileName = tempName + extension;
  1527. filename = Path.Combine(location, tempProjectFileName);
  1528. // Initialize the common project properties.
  1529. InitializeProjectProperties();
  1530. ErrorHandler.ThrowOnFailure(Save(filename, 1, 0));
  1531. // now we do have the project file saved. we need to create embedded files.
  1532. foreach (MSBuild.ProjectItem item in BuildProject.Items)
  1533. {
  1534. // Ignore the item if it is a reference or folder
  1535. if (FilterItemTypeToBeAddedToHierarchy(item.ItemType))
  1536. {
  1537. continue;
  1538. }
  1539. // MSBuilds tasks/targets can create items (such as object files),
  1540. // such items are not part of the project per say, and should not be displayed.
  1541. // so ignore those items.
  1542. if (!IsItemTypeFileType(item.ItemType))
  1543. {
  1544. continue;
  1545. }
  1546. string strRelFilePath = item.EvaluatedInclude;
  1547. string basePath = Path.GetDirectoryName(fileName);
  1548. string strPathToFile;
  1549. string newFileName;
  1550. // taking the base name from the project template + the relative pathname,
  1551. // and you get the filename
  1552. strPathToFile = Path.Combine(basePath, strRelFilePath);
  1553. // the new path should be the base dir of the new project (location) + the rel path of the file
  1554. newFileName = Path.Combine(location, strRelFilePath);
  1555. // now the copy file
  1556. AddFileFromTemplate(strPathToFile, newFileName);
  1557. }
  1558. }
  1559. else
  1560. {
  1561. filename = fileName;
  1562. }
  1563. // now reload to fix up references
  1564. Reload();
  1565. }
  1566. finally
  1567. {
  1568. disableQueryEdit = false;
  1569. }
  1570. }
  1571. /// <summary>
  1572. /// Called to add a file to the project from a template.
  1573. /// Override to do it yourself if you want to customize the file
  1574. /// </summary>
  1575. /// <param name="source">Full path of template file</param>
  1576. /// <param name="target">Full path of file once added to the project</param>
  1577. public virtual void AddFileFromTemplate(string source, string target)
  1578. {
  1579. if (String.IsNullOrEmpty(source))
  1580. {
  1581. throw new ArgumentNullException("source");
  1582. }
  1583. if (String.IsNullOrEmpty(target))
  1584. {
  1585. throw new ArgumentNullException("target");
  1586. }
  1587. try
  1588. {
  1589. string directory = Path.GetDirectoryName(target);
  1590. if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory))
  1591. {
  1592. Directory.CreateDirectory(directory);
  1593. }
  1594. FileInfo fiOrg = new FileInfo(source);
  1595. FileInfo fiNew = fiOrg.CopyTo(target, true);
  1596. fiNew.Attributes = FileAttributes.Normal; // remove any read only attributes.
  1597. }
  1598. catch (IOException e)
  1599. {
  1600. Trace.WriteLine("Exception : " + e.Message);
  1601. }
  1602. catch (UnauthorizedAccessException e)
  1603. {
  1604. Trace.WriteLine("Exception : " + e.Message);
  1605. }
  1606. catch (ArgumentException e)
  1607. {
  1608. Trace.WriteLine("Exception : " + e.Message);
  1609. }
  1610. catch (NotSupportedException e)
  1611. {
  1612. Trace.WriteLine("Exception : " + e.Message);
  1613. }
  1614. }
  1615. /// <summary>
  1616. /// Called when the project opens an editor window for the given file
  1617. /// </summary>
  1618. public virtual void OnOpenItem(string fullPathToSourceFile)
  1619. {
  1620. }
  1621. /// <summary>
  1622. /// This add methos adds the "key" item to the hierarchy, potentially adding other subitems in the process
  1623. /// This method may recurse if the parent is an other subitem
  1624. /// </summary>
  1625. /// <param name="subitems">List of subitems not yet added to the hierarchy</param>
  1626. /// <param name="key">Key to retrieve the target item from the subitems list</param>
  1627. /// <returns>Newly added node</returns>
  1628. /// <remarks>If the parent node was found we add the dependent item to it otherwise we add the item ignoring the "DependentUpon" metatdata</remarks>
  1629. protected virtual HierarchyNode AddDependentFileNode(IDictionary<String, MSBuild.ProjectItem> subitems,
  1630. string key)
  1631. {
  1632. if (subitems == null)
  1633. {
  1634. throw new ArgumentNullException("subitems");
  1635. }
  1636. MSBuild.ProjectItem item = subitems[key];
  1637. subitems.Remove(key);
  1638. HierarchyNode newNode;
  1639. HierarchyNode parent = null;
  1640. string dependentOf = item.GetMetadataValue(ProjectFileConstants.DependentUpon);
  1641. Debug.Assert(String.Compare(dependentOf, key, StringComparison.OrdinalIgnoreCase) != 0,
  1642. "File dependent upon itself is not valid. Ignoring the DependentUpon metadata");
  1643. if (subitems.ContainsKey(dependentOf))
  1644. {
  1645. // The parent item is an other subitem, so recurse into this method to add the parent first
  1646. parent = AddDependentFileNode(subitems, dependentOf);
  1647. }
  1648. else
  1649. {
  1650. // See if the parent node already exist in the hierarchy
  1651. uint parentItemID;
  1652. string path = Path.Combine(ProjectFolder, dependentOf);
  1653. ErrorHandler.ThrowOnFailure(ParseCanonicalName(path, out parentItemID));
  1654. if (parentItemID != (uint)VSConstants.VSITEMID.Nil)
  1655. parent = NodeFromItemId(parentItemID);
  1656. Debug.Assert(parent != null,
  1657. "File dependent upon a non existing item or circular dependency. Ignoring the DependentUpon metadata");
  1658. }
  1659. // If the parent node was found we add the dependent item to it otherwise we add the item ignoring the "DependentUpon" metatdata
  1660. if (parent != null)
  1661. newNode = AddDependentFileNodeToNode(item, parent);
  1662. else
  1663. newNode = AddIndependentFileNode(item);
  1664. return newNode;
  1665. }
  1666. /// <summary>
  1667. /// This is called from the main thread before the background build starts.
  1668. /// cleanBuild is not part of the vsopts, but passed down as the callpath is differently
  1669. /// PrepareBuild mainly creates directories and cleans house if cleanBuild is true
  1670. /// </summary>
  1671. /// <param name="config"></param>
  1672. /// <param name="cleanBuild"></param>
  1673. public virtual void PrepareBuild(string config, bool cleanBuild)
  1674. {
  1675. if (buildIsPrepared && !cleanBuild)
  1676. return;
  1677. ProjectOptions options = GetProjectOptions(config);
  1678. string outputPath = Path.GetDirectoryName(options.OutputAssembly);
  1679. if (cleanBuild && currentConfig.Targets.ContainsKey(MsBuildTarget.Clean))
  1680. {
  1681. InvokeMsBuild(MsBuildTarget.Clean);
  1682. }
  1683. PackageUtilities.EnsureOutputPath(outputPath);
  1684. if (!String.IsNullOrEmpty(options.XmlDocFileName))
  1685. {
  1686. PackageUtilities.EnsureOutputPath(Path.GetDirectoryName(options.XmlDocFileName));
  1687. }
  1688. buildIsPrepared = true;
  1689. }
  1690. /// <summary>
  1691. /// Do the build by invoking msbuild
  1692. /// </summary>
  1693. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vsopts")]
  1694. public virtual MSBuildResult Build(uint vsopts, string config, IVsOutputWindowPane output, string target)
  1695. {
  1696. lock (BuildLock)
  1697. {
  1698. bool engineLogOnlyCritical = BuildPrelude(output);
  1699. MSBuildResult result = MSBuildResult.Failed;
  1700. try
  1701. {
  1702. SetBuildConfigurationProperties(config);
  1703. result = InvokeMsBuild(target);
  1704. }
  1705. finally
  1706. {
  1707. // Unless someone specifically request to use an output window pane, we should not output to it
  1708. if (null != output)
  1709. {
  1710. SetOutputLogger(null);
  1711. BuildEngine.OnlyLogCriticalEvents = engineLogOnlyCritical;
  1712. }
  1713. }
  1714. return result;
  1715. }
  1716. }
  1717. /// <summary>
  1718. /// Do the build by invoking msbuild
  1719. /// </summary>
  1720. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vsopts")]
  1721. internal virtual void BuildAsync(uint vsopts, string config, IVsOutputWindowPane output, string target,
  1722. Action<MSBuildResult, string> uiThreadCallback)
  1723. {
  1724. BuildPrelude(output);
  1725. SetBuildConfigurationProperties(config);
  1726. DoMSBuildSubmission(BuildKind.Async, target, uiThreadCallback);
  1727. }
  1728. /// <summary>
  1729. /// Return the value of a project property
  1730. /// </summary>
  1731. /// <param name="propertyName">Name of the property to get</param>
  1732. /// <param name="resetCache">True to avoid using the cache</param>
  1733. /// <returns>null if property does not exist, otherwise value of the property</returns>
  1734. public virtual string GetProjectProperty(string propertyName, bool resetCache)
  1735. {
  1736. ProjectPropertyInstance property = GetMsBuildProperty(propertyName, resetCache);
  1737. if (property == null)
  1738. return null;
  1739. return property.EvaluatedValue;
  1740. }
  1741. /// <summary>
  1742. /// Set value of project property
  1743. /// </summary>
  1744. /// <param name="propertyName">Name of property</param>
  1745. /// <param name="propertyValue">Value of property</param>
  1746. public virtual void SetProjectProperty(string propertyName, string propertyValue)
  1747. {
  1748. if (propertyName == null)
  1749. throw new ArgumentNullException("propertyName", "Cannot set a null project property");
  1750. string oldValue = null;
  1751. ProjectPropertyInstance oldProp = GetMsBuildProperty(propertyName, true);
  1752. if (oldProp != null)
  1753. oldValue = oldProp.EvaluatedValue;
  1754. if (propertyValue == null)
  1755. {
  1756. // if property already null, do nothing
  1757. if (oldValue == null)
  1758. return;
  1759. // otherwise, set it to empty
  1760. propertyValue = String.Empty;
  1761. }
  1762. // Only do the work if this is different to what we had before
  1763. if (String.Compare(oldValue, propertyValue, StringComparison.Ordinal) != 0)
  1764. {
  1765. // Check out the project file.
  1766. if (!ProjectMgr.QueryEditProjectFile(false))
  1767. {
  1768. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  1769. }
  1770. buildProject.SetProperty(propertyName, propertyValue);
  1771. RaiseProjectPropertyChanged(propertyName, oldValue, propertyValue);
  1772. // property cache will need to be updated
  1773. currentConfig = null;
  1774. SetProjectFileDirty(true);
  1775. }
  1776. return;
  1777. }
  1778. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"),
  1779. SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
  1780. public virtual ProjectOptions GetProjectOptions(string config = null)
  1781. {
  1782. if (string.IsNullOrEmpty(config))
  1783. {
  1784. EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project;
  1785. try
  1786. {
  1787. config = Utilities.GetActiveConfigurationName(automationObject);
  1788. }
  1789. catch (InvalidOperationException)
  1790. {
  1791. // Can't figure out the active configuration. Perhaps during solution load, or in a unit test.
  1792. }
  1793. catch (COMException)
  1794. {
  1795. // We may be in solution load and don't have an active config yet.
  1796. }
  1797. }
  1798. // Even though the options are the same, when this config was originally set, it may have been before
  1799. // the project system was ready to set up its configuration, so go ahead and call through to SetConfiguration
  1800. // anyway -- it should do effectively nothing if the config is the same and all the initialization has
  1801. // already occurred.
  1802. if (this.options != null && String.Equals(this.options.Config, config, StringComparison.OrdinalIgnoreCase))
  1803. {
  1804. if (config != null)
  1805. {
  1806. // Fancy dance with the options required because SetConfiguration nulls out this.options
  1807. // even if the configuration itself has not changed; whereas we're only calling SetConfiguration
  1808. // for purposes of initializing some other fields here; since we know the config is the same, it
  1809. // should be perfectly safe to keep the same options as before.
  1810. ProjectOptions currentOptions = this.options;
  1811. SetConfiguration(config);
  1812. this.options = currentOptions;
  1813. }
  1814. return this.options;
  1815. }
  1816. ProjectOptions options = CreateProjectOptions();
  1817. options.Config = config;
  1818. string targetFrameworkMoniker = GetProjectProperty("TargetFrameworkMoniker", false);
  1819. if (!string.IsNullOrEmpty(targetFrameworkMoniker))
  1820. {
  1821. try
  1822. {
  1823. options.TargetFrameworkMoniker = new FrameworkName(targetFrameworkMoniker);
  1824. }
  1825. catch (ArgumentException e)
  1826. {
  1827. Trace.WriteLine("Exception : " + e.Message);
  1828. }
  1829. }
  1830. if (config == null)
  1831. {
  1832. this.options = options;
  1833. return options;
  1834. }
  1835. options.GenerateExecutable = true;
  1836. SetConfiguration(config);
  1837. string outputPath = GetOutputPath(currentConfig);
  1838. if (!String.IsNullOrEmpty(outputPath))
  1839. {
  1840. // absolutize relative to project folder location
  1841. outputPath = Path.Combine(ProjectFolder, outputPath);
  1842. }
  1843. // Set some default values
  1844. options.OutputAssembly = outputPath + Caption + ".exe";
  1845. options.ModuleKind = ModuleKindFlags.ConsoleApplication;
  1846. options.RootNamespace = GetProjectProperty(ProjectFileConstants.RootNamespace, false);
  1847. options.OutputAssembly = outputPath + GetAssemblyName(config);
  1848. string outputtype = GetProjectProperty(ProjectFileConstants.OutputType, false);
  1849. if (!string.IsNullOrEmpty(outputtype))
  1850. {
  1851. outputtype = outputtype.ToLower(CultureInfo.InvariantCulture);
  1852. }
  1853. if (outputtype == "library")
  1854. {
  1855. options.ModuleKind = ModuleKindFlags.DynamicallyLinkedLibrary;
  1856. options.GenerateExecutable = false; // DLL's have no entry point.
  1857. }
  1858. else if (outputtype == "winexe")
  1859. options.ModuleKind = ModuleKindFlags.WindowsApplication;
  1860. else
  1861. options.ModuleKind = ModuleKindFlags.ConsoleApplication;
  1862. options.Win32Icon = GetProjectProperty("ApplicationIcon", false);
  1863. options.MainClass = GetProjectProperty("StartupObject", false);
  1864. // other settings from CSharp we may want to adopt at some point...
  1865. // AssemblyKeyContainerName = "" //This is the key file used to sign the interop assembly generated when importing a com object via add reference
  1866. // AssemblyOriginatorKeyFile = ""
  1867. // DelaySign = "false"
  1868. // DefaultClientScript = "JScript"
  1869. // DefaultHTMLPageLayout = "Grid"
  1870. // DefaultTargetSchema = "IE50"
  1871. // PreBuildEvent = ""
  1872. // PostBuildEvent = ""
  1873. // RunPostBuildEvent = "OnBuildSuccess"
  1874. // transfer all config build options...
  1875. if (GetBoolAttr(currentConfig, "AllowUnsafeBlocks"))
  1876. {
  1877. options.AllowUnsafeCode = true;
  1878. }
  1879. if (GetProjectProperty("BaseAddress", false) != null)
  1880. {
  1881. try
  1882. {
  1883. options.BaseAddress = Int64.Parse(GetProjectProperty("BaseAddress", false),
  1884. CultureInfo.InvariantCulture);
  1885. }
  1886. catch (ArgumentNullException e)
  1887. {
  1888. Trace.WriteLine("Exception : " + e.Message);
  1889. }
  1890. catch (ArgumentException e)
  1891. {
  1892. Trace.WriteLine("Exception : " + e.Message);
  1893. }
  1894. catch (FormatException e)
  1895. {
  1896. Trace.WriteLine("Exception : " + e.Message);
  1897. }
  1898. catch (OverflowException e)
  1899. {
  1900. Trace.WriteLine("Exception : " + e.Message);
  1901. }
  1902. }
  1903. if (GetBoolAttr(currentConfig, "CheckForOverflowUnderflow"))
  1904. {
  1905. options.CheckedArithmetic = true;
  1906. }
  1907. if (GetProjectProperty("DefineConstants", false) != null)
  1908. {
  1909. options.DefinedPreprocessorSymbols = new StringCollection();
  1910. foreach (string s in GetProjectProperty("DefineConstants", false).Replace(" \t\r\n", "").Split(';'))
  1911. {
  1912. options.DefinedPreprocessorSymbols.Add(s);
  1913. }
  1914. }
  1915. string docFile = GetProjectProperty("DocumentationFile", false);
  1916. if (!String.IsNullOrEmpty(docFile))
  1917. {
  1918. options.XmlDocFileName = Path.Combine(ProjectFolder, docFile);
  1919. }
  1920. if (GetBoolAttr(currentConfig, "DebugSymbols"))
  1921. {
  1922. options.IncludeDebugInformation = true;
  1923. }
  1924. if (GetProjectProperty("FileAlignment", false) != null)
  1925. {
  1926. try
  1927. {
  1928. options.FileAlignment = Int32.Parse(GetProjectProperty("FileAlignment", false),
  1929. CultureInfo.InvariantCulture);
  1930. }
  1931. catch (ArgumentNullException e)
  1932. {
  1933. Trace.WriteLine("Exception : " + e.Message);
  1934. }
  1935. catch (ArgumentException e)
  1936. {
  1937. Trace.WriteLine("Exception : " + e.Message);
  1938. }
  1939. catch (FormatException e)
  1940. {
  1941. Trace.WriteLine("Exception : " + e.Message);
  1942. }
  1943. catch (OverflowException e)
  1944. {
  1945. Trace.WriteLine("Exception : " + e.Message);
  1946. }
  1947. }
  1948. if (GetBoolAttr(currentConfig, "IncrementalBuild"))
  1949. {
  1950. options.IncrementalCompile = true;
  1951. }
  1952. if (GetBoolAttr(currentConfig, "Optimize"))
  1953. {
  1954. options.Optimize = true;
  1955. }
  1956. if (GetBoolAttr(currentConfig, "RegisterForComInterop"))
  1957. {
  1958. }
  1959. if (GetBoolAttr(currentConfig, "RemoveIntegerChecks"))
  1960. {
  1961. }
  1962. if (GetBoolAttr(currentConfig, "TreatWarningsAsErrors"))
  1963. {
  1964. options.TreatWarningsAsErrors = true;
  1965. }
  1966. if (GetProjectProperty("WarningLevel", false) != null)
  1967. {
  1968. try
  1969. {
  1970. options.WarningLevel = Int32.Parse(GetProjectProperty("WarningLevel", false),
  1971. CultureInfo.InvariantCulture);
  1972. }
  1973. catch (ArgumentNullException e)
  1974. {
  1975. Trace.WriteLine("Exception : " + e.Message);
  1976. }
  1977. catch (ArgumentException e)
  1978. {
  1979. Trace.WriteLine("Exception : " + e.Message);
  1980. }
  1981. catch (FormatException e)
  1982. {
  1983. Trace.WriteLine("Exception : " + e.Message);
  1984. }
  1985. catch (OverflowException e)
  1986. {
  1987. Trace.WriteLine("Exception : " + e.Message);
  1988. }
  1989. }
  1990. this.options = options; // do this AFTER setting configuration so it doesn't clear it.
  1991. return options;
  1992. }
  1993. public virtual void OnTargetFrameworkMonikerChanged(ProjectOptions options, FrameworkName currentTargetFramework,
  1994. FrameworkName newTargetFramework)
  1995. {
  1996. if (currentTargetFramework == null)
  1997. {
  1998. throw new ArgumentNullException("currentTargetFramework");
  1999. }
  2000. if (newTargetFramework == null)
  2001. {
  2002. throw new ArgumentNullException("newTargetFramework");
  2003. }
  2004. var retargetingService = site.GetService(typeof(SVsTrackProjectRetargeting)) as IVsTrackProjectRetargeting;
  2005. if (retargetingService == null)
  2006. {
  2007. // Probably in a unit test.
  2008. ////throw new InvalidOperationException("Unable to acquire the SVsTrackProjectRetargeting service.");
  2009. Marshal.ThrowExceptionForHR(UpdateTargetFramework(InteropSafeIVsHierarchy,
  2010. currentTargetFramework.FullName,
  2011. newTargetFramework.FullName));
  2012. }
  2013. else
  2014. {
  2015. Marshal.ThrowExceptionForHR(retargetingService.OnSetTargetFramework(InteropSafeIVsHierarchy,
  2016. currentTargetFramework.FullName,
  2017. newTargetFramework.FullName, this,
  2018. true));
  2019. }
  2020. }
  2021. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Attr")]
  2022. [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "bool")]
  2023. public virtual bool GetBoolAttr(string config, string name)
  2024. {
  2025. SetConfiguration(config);
  2026. return GetBoolAttr(currentConfig, name);
  2027. }
  2028. /// <summary>
  2029. /// Get the assembly name for a give configuration
  2030. /// </summary>
  2031. /// <param name="config">the matching configuration in the msbuild file</param>
  2032. /// <returns>assembly name</returns>
  2033. public virtual string GetAssemblyName(string config)
  2034. {
  2035. SetConfiguration(config);
  2036. return GetAssemblyName(currentConfig);
  2037. }
  2038. /// <summary>
  2039. /// Determines whether a file is a code file.
  2040. /// </summary>
  2041. /// <param name="fileName">Name of the file to be evaluated</param>
  2042. /// <returns>false by default for any fileName</returns>
  2043. public virtual bool IsCodeFile(string fileName)
  2044. {
  2045. return false;
  2046. }
  2047. /// <summary>
  2048. /// Determines whether the given file is a resource file (resx file).
  2049. /// </summary>
  2050. /// <param name="fileName">Name of the file to be evaluated.</param>
  2051. /// <returns>true if the file is a resx file, otherwise false.</returns>
  2052. public virtual bool IsEmbeddedResource(string fileName)
  2053. {
  2054. if (String.Compare(Path.GetExtension(fileName), ".ResX", StringComparison.OrdinalIgnoreCase) == 0)
  2055. return true;
  2056. return false;
  2057. }
  2058. /// <summary>
  2059. /// Create a file node based on an msbuild item.
  2060. /// </summary>
  2061. /// <param name="item">msbuild item</param>
  2062. /// <returns>FileNode added</returns>
  2063. public virtual FileNode CreateFileNode(ProjectElement item)
  2064. {
  2065. return new FileNode(this, item);
  2066. }
  2067. /// <summary>
  2068. /// Create a file node based on a string.
  2069. /// </summary>
  2070. /// <param name="file">filename of the new filenode</param>
  2071. /// <returns>File node added</returns>
  2072. public virtual FileNode CreateFileNode(string file)
  2073. {
  2074. ProjectElement item = AddFileToMsBuild(file);
  2075. return CreateFileNode(item);
  2076. }
  2077. /// <summary>
  2078. /// Create dependent file node based on an msbuild item
  2079. /// </summary>
  2080. /// <param name="item">msbuild item</param>
  2081. /// <returns>dependent file node</returns>
  2082. public virtual DependentFileNode CreateDependentFileNode(ProjectElement item)
  2083. {
  2084. return new DependentFileNode(this, item);
  2085. }
  2086. /// <summary>
  2087. /// Create a dependent file node based on a string.
  2088. /// </summary>
  2089. /// <param name="file">filename of the new dependent file node</param>
  2090. /// <returns>Dependent node added</returns>
  2091. public virtual DependentFileNode CreateDependentFileNode(string file)
  2092. {
  2093. ProjectElement item = AddFileToMsBuild(file);
  2094. return CreateDependentFileNode(item);
  2095. }
  2096. /// <summary>
  2097. /// Walks the subpaths of a project relative path and checks if the folder nodes hierarchy is already there, if not creates it.
  2098. /// </summary>
  2099. /// <param name="strPath">Path of the folder, can be relative to project or absolute</param>
  2100. public virtual HierarchyNode CreateFolderNodes(string path)
  2101. {
  2102. if (String.IsNullOrEmpty(path))
  2103. {
  2104. throw new ArgumentNullException("path");
  2105. }
  2106. if (Path.IsPathRooted(path))
  2107. {
  2108. // Ensure we are using a relative path
  2109. if (
  2110. String.Compare(ProjectFolder, 0, path, 0, ProjectFolder.Length, StringComparison.OrdinalIgnoreCase) !=
  2111. 0)
  2112. throw new ArgumentException("The path is not relative", "path");
  2113. path = path.Substring(ProjectFolder.Length);
  2114. }
  2115. string[] parts;
  2116. HierarchyNode curParent;
  2117. parts = path.Split(Path.DirectorySeparatorChar);
  2118. path = String.Empty;
  2119. curParent = this;
  2120. // now we have an array of subparts....
  2121. for (int i = 0; i < parts.Length; i++)
  2122. {
  2123. if (parts[i].Length > 0)
  2124. {
  2125. path += parts[i] + "\\";
  2126. curParent = VerifySubFolderExists(path, curParent);
  2127. }
  2128. }
  2129. return curParent;
  2130. }
  2131. /// <summary>
  2132. /// Defines if Node has Designer. By default we do not support designers for nodes
  2133. /// </summary>
  2134. /// <param name="itemPath">Path to item to query for designer support</param>
  2135. /// <returns>true if node has designer</returns>
  2136. public virtual bool NodeHasDesigner(string itemPath)
  2137. {
  2138. return false;
  2139. }
  2140. /// <summary>
  2141. /// List of Guids of the config independent property pages. It is called by the GetProperty for VSHPROPID_PropertyPagesCLSIDList property.
  2142. /// </summary>
  2143. /// <returns></returns>
  2144. protected virtual Guid[] GetConfigurationIndependentPropertyPages()
  2145. {
  2146. return new[] { Guid.Empty };
  2147. }
  2148. /// <summary>
  2149. /// Returns a list of Guids of the configuration dependent property pages. It is called by the GetProperty for VSHPROPID_CfgPropertyPagesCLSIDList property.
  2150. /// </summary>
  2151. /// <returns></returns>
  2152. protected virtual Guid[] GetConfigurationDependentPropertyPages()
  2153. {
  2154. return new Guid[0];
  2155. }
  2156. /// <summary>
  2157. /// An ordered list of guids of the prefered property pages. See
  2158. /// <see
  2159. /// cref="__VSHPROPID.VSHPROPID_PriorityPropertyPagesCLSIDList" />
  2160. /// </summary>
  2161. /// <returns>An array of guids.</returns>
  2162. protected virtual Guid[] GetPriorityProjectDesignerPages()
  2163. {
  2164. return new[] { Guid.Empty };
  2165. }
  2166. /// <summary>
  2167. /// Takes a path and verifies that we have a node with that name.
  2168. /// It is meant to be a helper method for CreateFolderNodes().
  2169. /// For some scenario it may be useful to override.
  2170. /// </summary>
  2171. /// <param name="path">full path to the subfolder we want to verify.</param>
  2172. /// <param name="parent">the parent node where to add the subfolder if it does not exist.</param>
  2173. /// <returns>the foldernode correcsponding to the path.</returns>
  2174. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubFolder")]
  2175. protected virtual FolderNode VerifySubFolderExists(string path, HierarchyNode parent)
  2176. {
  2177. FolderNode folderNode = null;
  2178. uint uiItemId;
  2179. Url url = new Url(BaseURI, path);
  2180. string strFullPath = url.AbsoluteUrl;
  2181. // Folders end in our storage with a backslash, so add one...
  2182. ParseCanonicalName(strFullPath, out uiItemId);
  2183. if (uiItemId != (uint)VSConstants.VSITEMID.Nil)
  2184. {
  2185. Debug.Assert(NodeFromItemId(uiItemId) is FolderNode, "Not a FolderNode");
  2186. folderNode = (FolderNode)NodeFromItemId(uiItemId);
  2187. }
  2188. if (folderNode == null && path != null && parent != null)
  2189. {
  2190. // folder does not exist yet...
  2191. // We could be in the process of loading so see if msbuild knows about it
  2192. ProjectElement item = null;
  2193. foreach (MSBuild.ProjectItem folder in buildProject.GetItems(ProjectFileConstants.Folder))
  2194. {
  2195. if (
  2196. String.Compare(folder.EvaluatedInclude.TrimEnd('\\'), path.TrimEnd('\\'),
  2197. StringComparison.OrdinalIgnoreCase) == 0)
  2198. {
  2199. item = new ProjectElement(this, folder, false);
  2200. break;
  2201. }
  2202. }
  2203. // If MSBuild did not know about it, create a new one
  2204. if (item == null)
  2205. item = AddFolderToMsBuild(path);
  2206. folderNode = CreateFolderNode(path, item);
  2207. parent.AddChild(folderNode);
  2208. }
  2209. return folderNode;
  2210. }
  2211. /// <summary>
  2212. /// To support virtual folders, override this method to return your own folder nodes
  2213. /// </summary>
  2214. /// <param name="path">Path to store for this folder</param>
  2215. /// <param name="element">Element corresponding to the folder</param>
  2216. /// <returns>A FolderNode that can then be added to the hierarchy</returns>
  2217. protected internal virtual FolderNode CreateFolderNode(string path, ProjectElement element)
  2218. {
  2219. FolderNode folderNode = new FolderNode(this, path, element);
  2220. return folderNode;
  2221. }
  2222. /// <summary>
  2223. /// Gets the list of selected HierarchyNode objects
  2224. /// </summary>
  2225. /// <returns>A list of HierarchyNode objects</returns>
  2226. protected internal virtual IList<HierarchyNode> GetSelectedNodes()
  2227. {
  2228. // Retrieve shell interface in order to get current selection
  2229. IVsMonitorSelection monitorSelection = GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection;
  2230. if (monitorSelection == null)
  2231. {
  2232. throw new InvalidOperationException();
  2233. }
  2234. List<HierarchyNode> selectedNodes = new List<HierarchyNode>();
  2235. IntPtr hierarchyPtr = IntPtr.Zero;
  2236. IntPtr selectionContainer = IntPtr.Zero;
  2237. try
  2238. {
  2239. // Get the current project hierarchy, project item, and selection container for the current selection
  2240. // If the selection spans multiple hierachies, hierarchyPtr is Zero
  2241. uint itemid;
  2242. IVsMultiItemSelect multiItemSelect = null;
  2243. ErrorHandler.ThrowOnFailure(monitorSelection.GetCurrentSelection(out hierarchyPtr, out itemid,
  2244. out multiItemSelect,
  2245. out selectionContainer));
  2246. // We only care if there are one ore more nodes selected in the tree
  2247. if (itemid != VSConstants.VSITEMID_NIL && hierarchyPtr != IntPtr.Zero)
  2248. {
  2249. IVsHierarchy hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy;
  2250. if (itemid != VSConstants.VSITEMID_SELECTION)
  2251. {
  2252. // This is a single selection. Compare hirarchy with our hierarchy and get node from itemid
  2253. if (Utilities.IsSameComObject(InteropSafeIVsHierarchy, hierarchy))
  2254. {
  2255. HierarchyNode node = NodeFromItemId(itemid);
  2256. if (node != null)
  2257. {
  2258. selectedNodes.Add(node);
  2259. }
  2260. }
  2261. else
  2262. {
  2263. NestedProjectNode node = GetNestedProjectForHierarchy(hierarchy);
  2264. if (node != null)
  2265. {
  2266. selectedNodes.Add(node);
  2267. }
  2268. }
  2269. }
  2270. else if (multiItemSelect != null)
  2271. {
  2272. // This is a multiple item selection.
  2273. //Get number of items selected and also determine if the items are located in more than one hierarchy
  2274. uint numberOfSelectedItems;
  2275. int isSingleHierarchyInt;
  2276. ErrorHandler.ThrowOnFailure(multiItemSelect.GetSelectionInfo(out numberOfSelectedItems,
  2277. out isSingleHierarchyInt));
  2278. bool isSingleHierarchy = (isSingleHierarchyInt != 0);
  2279. // Now loop all selected items and add to the list only those that are selected within this hierarchy
  2280. if (!isSingleHierarchy ||
  2281. (isSingleHierarchy && Utilities.IsSameComObject(InteropSafeIVsHierarchy, hierarchy)))
  2282. {
  2283. Debug.Assert(numberOfSelectedItems > 0, "Bad number of selected itemd");
  2284. VSITEMSELECTION[] vsItemSelections = new VSITEMSELECTION[numberOfSelectedItems];
  2285. uint flags = (isSingleHierarchy) ? (uint)__VSGSIFLAGS.GSI_fOmitHierPtrs : 0;
  2286. ErrorHandler.ThrowOnFailure(multiItemSelect.GetSelectedItems(flags, numberOfSelectedItems,
  2287. vsItemSelections));
  2288. foreach (VSITEMSELECTION vsItemSelection in vsItemSelections)
  2289. {
  2290. if (isSingleHierarchy ||
  2291. Utilities.IsSameComObject(InteropSafeIVsHierarchy, vsItemSelection.pHier))
  2292. {
  2293. HierarchyNode node = NodeFromItemId(vsItemSelection.itemid);
  2294. if (node != null)
  2295. {
  2296. selectedNodes.Add(node);
  2297. }
  2298. }
  2299. }
  2300. }
  2301. }
  2302. }
  2303. }
  2304. finally
  2305. {
  2306. if (hierarchyPtr != IntPtr.Zero)
  2307. {
  2308. Marshal.Release(hierarchyPtr);
  2309. }
  2310. if (selectionContainer != IntPtr.Zero)
  2311. {
  2312. Marshal.Release(selectionContainer);
  2313. }
  2314. }
  2315. return selectedNodes;
  2316. }
  2317. /// <summary>
  2318. /// Recursevily walks the hierarchy nodes and redraws the state icons
  2319. /// </summary>
  2320. protected internal override void UpdateSccStateIcons()
  2321. {
  2322. if (FirstChild == null)
  2323. {
  2324. return;
  2325. }
  2326. for (HierarchyNode n = FirstChild; n != null; n = n.NextSibling)
  2327. {
  2328. n.UpdateSccStateIcons();
  2329. }
  2330. }
  2331. /// <summary>
  2332. /// Handles the shows all objects command.
  2333. /// </summary>
  2334. /// <returns></returns>
  2335. protected internal virtual int ShowAllFiles()
  2336. {
  2337. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  2338. }
  2339. /// <summary>
  2340. /// Handles the Add web reference command.
  2341. /// </summary>
  2342. /// <returns></returns>
  2343. protected internal virtual int AddWebReference()
  2344. {
  2345. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  2346. }
  2347. /// <summary>
  2348. /// Unloads the project.
  2349. /// </summary>
  2350. /// <returns></returns>
  2351. protected internal virtual int UnloadProject()
  2352. {
  2353. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  2354. }
  2355. /// <summary>
  2356. /// Handles the clean project command.
  2357. /// </summary>
  2358. /// <returns></returns>
  2359. protected virtual int CleanProject()
  2360. {
  2361. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  2362. }
  2363. /// <summary>
  2364. /// Reload project from project file
  2365. /// </summary>
  2366. protected virtual void Reload()
  2367. {
  2368. Debug.Assert(buildEngine != null, "There is no build engine defined for this project");
  2369. try
  2370. {
  2371. disableQueryEdit = true;
  2372. isClosed = false;
  2373. eventTriggeringFlag = EventTriggering.DoNotTriggerHierarchyEvents |
  2374. EventTriggering.DoNotTriggerTrackerEvents;
  2375. SetBuildProject(Utilities.ReinitializeMsBuildProject(buildEngine, filename, buildProject));
  2376. // Load the guid
  2377. SetProjectGuidFromProjectFile();
  2378. ProcessReferences();
  2379. ProcessFiles();
  2380. ProcessFolders();
  2381. LoadNonBuildInformation();
  2382. InitSccInfo();
  2383. RegisterSccProject();
  2384. }
  2385. finally
  2386. {
  2387. SetProjectFileDirty(false);
  2388. eventTriggeringFlag = EventTriggering.TriggerAll;
  2389. disableQueryEdit = false;
  2390. }
  2391. }
  2392. /// <summary>
  2393. /// Renames the project file
  2394. /// </summary>
  2395. /// <param name="newFile">The full path of the new project file.</param>
  2396. protected virtual void RenameProjectFile(string newFile)
  2397. {
  2398. IVsUIShell shell = Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
  2399. Debug.Assert(shell != null, "Could not get the ui shell from the project");
  2400. if (shell == null)
  2401. {
  2402. throw new InvalidOperationException();
  2403. }
  2404. // Do some name validation
  2405. if (Utilities.ContainsInvalidFileNameChars(newFile))
  2406. {
  2407. throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidProjectName,
  2408. CultureInfo.CurrentUICulture));
  2409. }
  2410. // Figure out what the new full name is
  2411. string oldFile = Url;
  2412. int canContinue = 0;
  2413. IVsSolution vsSolution = (IVsSolution)GetService(typeof(SVsSolution));
  2414. if (ErrorHandler.Succeeded(vsSolution.QueryRenameProject(InteropSafeIVsProject3, oldFile, newFile, 0,
  2415. out canContinue))
  2416. && canContinue != 0)
  2417. {
  2418. bool isFileSame = NativeMethods.IsSamePath(oldFile, newFile);
  2419. // If file already exist and is not the same file with different casing
  2420. if (!isFileSame && File.Exists(newFile))
  2421. {
  2422. // Prompt the user for replace
  2423. string message = SR.GetString(SR.FileAlreadyExists, newFile);
  2424. if (!Utilities.IsInAutomationFunction(Site))
  2425. {
  2426. if (!VsShellUtilities.PromptYesNo(message, null, OLEMSGICON.OLEMSGICON_WARNING, shell))
  2427. {
  2428. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  2429. }
  2430. }
  2431. else
  2432. {
  2433. throw new InvalidOperationException(message);
  2434. }
  2435. // Delete the destination file after making sure it is not read only
  2436. File.SetAttributes(newFile, FileAttributes.Normal);
  2437. File.Delete(newFile);
  2438. }
  2439. SuspendFileChanges fileChanges = new SuspendFileChanges(Site, filename);
  2440. fileChanges.Suspend();
  2441. try
  2442. {
  2443. // Actual file rename
  2444. SaveMSBuildProjectFileAs(newFile);
  2445. SetProjectFileDirty(false);
  2446. if (!isFileSame)
  2447. {
  2448. // Now that the new file name has been created delete the old one.
  2449. // TODO: Handle source control issues.
  2450. File.SetAttributes(oldFile, FileAttributes.Normal);
  2451. File.Delete(oldFile);
  2452. }
  2453. OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0);
  2454. // Update solution
  2455. ErrorHandler.ThrowOnFailure(vsSolution.OnAfterRenameProject(InteropSafeIVsProject3, oldFile, newFile,
  2456. 0));
  2457. ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0));
  2458. }
  2459. finally
  2460. {
  2461. fileChanges.Resume();
  2462. }
  2463. }
  2464. else
  2465. {
  2466. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  2467. }
  2468. }
  2469. /// <summary>
  2470. /// Called by the project to know if the item is a file (that is part of the project)
  2471. /// or an intermediate file used by the MSBuild tasks/targets
  2472. /// Override this method if your project has more types or different ones
  2473. /// </summary>
  2474. /// <param name="type">Type name</param>
  2475. /// <returns>True = items of this type should be included in the project</returns>
  2476. protected virtual bool IsItemTypeFileType(string type)
  2477. {
  2478. if (String.Compare(type, BuildAction.Compile.ToString(), StringComparison.OrdinalIgnoreCase) == 0
  2479. || String.Compare(type, BuildAction.Content.ToString(), StringComparison.OrdinalIgnoreCase) == 0
  2480. ||
  2481. String.Compare(type, BuildAction.EmbeddedResource.ToString(), StringComparison.OrdinalIgnoreCase) == 0
  2482. || String.Compare(type, BuildAction.None.ToString(), StringComparison.OrdinalIgnoreCase) == 0)
  2483. return true;
  2484. // we don't know about this type, so ignore it.
  2485. return false;
  2486. }
  2487. /// <summary>
  2488. /// Filter items that should not be processed as file items. Example: Folders and References.
  2489. /// </summary>
  2490. protected virtual bool FilterItemTypeToBeAddedToHierarchy(string itemType)
  2491. {
  2492. return (String.Compare(itemType, ProjectFileConstants.Reference, StringComparison.OrdinalIgnoreCase) == 0
  2493. ||
  2494. String.Compare(itemType, ProjectFileConstants.ProjectReference, StringComparison.OrdinalIgnoreCase) ==
  2495. 0
  2496. ||
  2497. String.Compare(itemType, ProjectFileConstants.COMReference, StringComparison.OrdinalIgnoreCase) == 0
  2498. || String.Compare(itemType, ProjectFileConstants.Folder, StringComparison.OrdinalIgnoreCase) == 0
  2499. ||
  2500. String.Compare(itemType, ProjectFileConstants.WebReference, StringComparison.OrdinalIgnoreCase) == 0
  2501. ||
  2502. String.Compare(itemType, ProjectFileConstants.WebReferenceFolder, StringComparison.OrdinalIgnoreCase) ==
  2503. 0);
  2504. }
  2505. /// <summary>
  2506. /// Associate window output pane to the build logger
  2507. /// </summary>
  2508. /// <param name="output"></param>
  2509. protected virtual void SetOutputLogger(IVsOutputWindowPane output)
  2510. {
  2511. // Create our logger, if it was not specified
  2512. if (!useProvidedLogger || buildLogger == null)
  2513. {
  2514. // Because we may be aggregated, we need to make sure to get the outer IVsHierarchy
  2515. IntPtr unknown = IntPtr.Zero;
  2516. IVsHierarchy hierarchy = null;
  2517. try
  2518. {
  2519. unknown = Marshal.GetIUnknownForObject(this);
  2520. hierarchy = Marshal.GetTypedObjectForIUnknown(unknown, typeof(IVsHierarchy)) as IVsHierarchy;
  2521. }
  2522. finally
  2523. {
  2524. if (unknown != IntPtr.Zero)
  2525. Marshal.Release(unknown);
  2526. }
  2527. // Create the logger
  2528. BuildLogger = new IDEBuildLogger(output, TaskProvider, hierarchy);
  2529. // To retrive the verbosity level, the build logger depends on the registry root
  2530. // (otherwise it will used an hardcoded default)
  2531. ILocalRegistry2 registry = GetService(typeof(SLocalRegistry)) as ILocalRegistry2;
  2532. if (null != registry)
  2533. {
  2534. string registryRoot;
  2535. ErrorHandler.ThrowOnFailure(registry.GetLocalRegistryRoot(out registryRoot));
  2536. IDEBuildLogger logger = BuildLogger as IDEBuildLogger;
  2537. if (!String.IsNullOrEmpty(registryRoot) && (null != logger))
  2538. {
  2539. logger.BuildVerbosityRegistryRoot = registryRoot;
  2540. logger.ErrorString = ErrorString;
  2541. logger.WarningString = WarningString;
  2542. }
  2543. }
  2544. }
  2545. else
  2546. {
  2547. ((IDEBuildLogger)BuildLogger).OutputWindowPane = output;
  2548. }
  2549. }
  2550. /// <summary>
  2551. /// Set configuration properties for a specific configuration
  2552. /// </summary>
  2553. /// <param name="config">configuration name</param>
  2554. protected virtual void SetBuildConfigurationProperties(string config)
  2555. {
  2556. ProjectOptions options = null;
  2557. if (!String.IsNullOrEmpty(config))
  2558. {
  2559. options = GetProjectOptions(config);
  2560. }
  2561. if (options != null && buildProject != null)
  2562. {
  2563. // Make sure the project configuration is set properly
  2564. SetConfiguration(config);
  2565. }
  2566. }
  2567. /// <summary>
  2568. /// This execute an MSBuild target for a design-time build.
  2569. /// </summary>
  2570. /// <param name="target">Name of the MSBuild target to execute</param>
  2571. /// <returns>Result from executing the target (success/failure)</returns>
  2572. /// <remarks>
  2573. /// If you depend on the items/properties generated by the target
  2574. /// you should be aware that any call to BuildTarget on any project
  2575. /// will reset the list of generated items/properties
  2576. /// </remarks>
  2577. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
  2578. protected virtual MSBuildResult InvokeMsBuild(string target)
  2579. {
  2580. MSBuildResult result = MSBuildResult.Failed;
  2581. const bool designTime = true;
  2582. bool requiresUIThread = UIThread.Instance.IsUIThread;
  2583. // we don't run tasks that require calling the STA thread, so unless we're ON it, we don't need it.
  2584. IVsBuildManagerAccessor accessor =
  2585. Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
  2586. BuildSubmission submission = null;
  2587. if (buildProject != null)
  2588. {
  2589. if (!TryBeginBuild(designTime, requiresUIThread))
  2590. {
  2591. throw new InvalidOperationException("A build is already in progress.");
  2592. }
  2593. try
  2594. {
  2595. // Do the actual Build
  2596. string[] targetsToBuild = new string[target != null ? 1 : 0];
  2597. if (target != null)
  2598. {
  2599. targetsToBuild[0] = target;
  2600. }
  2601. currentConfig = BuildProject.CreateProjectInstance();
  2602. BuildRequestData requestData = new BuildRequestData(currentConfig, targetsToBuild,
  2603. BuildProject.ProjectCollection.HostServices,
  2604. BuildRequestDataFlags
  2605. .ReplaceExistingProjectInstance);
  2606. submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
  2607. if (accessor != null)
  2608. {
  2609. ErrorHandler.ThrowOnFailure(accessor.RegisterLogger(submission.SubmissionId, buildLogger));
  2610. }
  2611. BuildResult buildResult = submission.Execute();
  2612. result = (buildResult.OverallResult == BuildResultCode.Success)
  2613. ? MSBuildResult.Successful
  2614. : MSBuildResult.Failed;
  2615. }
  2616. finally
  2617. {
  2618. EndBuild(submission, designTime, requiresUIThread);
  2619. }
  2620. }
  2621. return result;
  2622. }
  2623. /// <summary>
  2624. /// Start MSBuild build submission
  2625. /// </summary>
  2626. /// If buildKind is ASync, this method starts the submission and returns. uiThreadCallback will be called on UI thread once submissions completes.
  2627. /// if buildKind is Sync, this method executes the submission and runs uiThreadCallback
  2628. /// <param name="buildKind">Is it a Sync or ASync build</param>
  2629. /// <param name="target">target to build</param>
  2630. /// <param name="projectInstance">project instance to build; if null, this.BuildProject.CreateProjectInstance() is used to populate</param>
  2631. /// <param name="uiThreadCallback">callback to be run UI thread </param>
  2632. /// <returns>A Build submission instance.</returns>
  2633. protected virtual BuildSubmission DoMSBuildSubmission(BuildKind buildKind, string target,
  2634. Action<MSBuildResult, string> uiThreadCallback)
  2635. {
  2636. const bool designTime = false;
  2637. bool requiresUIThread = buildKind == BuildKind.Sync && UIThread.Instance.IsUIThread;
  2638. // we don't run tasks that require calling the STA thread, so unless we're ON it, we don't need it.
  2639. IVsBuildManagerAccessor accessor = (IVsBuildManagerAccessor)Site.GetService(typeof(SVsBuildManagerAccessor));
  2640. if (accessor == null)
  2641. {
  2642. throw new InvalidOperationException();
  2643. }
  2644. if (!TryBeginBuild(designTime, requiresUIThread))
  2645. {
  2646. if (uiThreadCallback != null)
  2647. {
  2648. uiThreadCallback(MSBuildResult.Failed, target);
  2649. }
  2650. return null;
  2651. }
  2652. string[] targetsToBuild = new string[target != null ? 1 : 0];
  2653. if (target != null)
  2654. {
  2655. targetsToBuild[0] = target;
  2656. }
  2657. ProjectInstance projectInstance = BuildProject.CreateProjectInstance();
  2658. projectInstance.SetProperty(GlobalProperty.VisualStudioStyleErrors.ToString(), "true");
  2659. projectInstance.SetProperty("UTFOutput", "true");
  2660. projectInstance.SetProperty(GlobalProperty.BuildingInsideVisualStudio.ToString(), "true");
  2661. BuildProject.ProjectCollection.HostServices.SetNodeAffinity(projectInstance.FullPath, NodeAffinity.InProc);
  2662. BuildRequestData requestData = new BuildRequestData(projectInstance, targetsToBuild,
  2663. BuildProject.ProjectCollection.HostServices,
  2664. BuildRequestDataFlags.ReplaceExistingProjectInstance);
  2665. BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
  2666. try
  2667. {
  2668. if (useProvidedLogger && buildLogger != null)
  2669. {
  2670. ErrorHandler.ThrowOnFailure(accessor.RegisterLogger(submission.SubmissionId, buildLogger));
  2671. }
  2672. if (buildKind == BuildKind.Async)
  2673. {
  2674. submission.ExecuteAsync(sub =>
  2675. {
  2676. UIThread.Instance.Run(() =>
  2677. {
  2678. FlushBuildLoggerContent();
  2679. EndBuild(sub, designTime, requiresUIThread);
  2680. uiThreadCallback(
  2681. (sub.BuildResult.OverallResult == BuildResultCode.Success)
  2682. ? MSBuildResult.Successful
  2683. : MSBuildResult.Failed, target);
  2684. });
  2685. }, null);
  2686. }
  2687. else
  2688. {
  2689. submission.Execute();
  2690. EndBuild(submission, designTime, requiresUIThread);
  2691. MSBuildResult msbuildResult = (submission.BuildResult.OverallResult == BuildResultCode.Success)
  2692. ? MSBuildResult.Successful
  2693. : MSBuildResult.Failed;
  2694. if (uiThreadCallback != null)
  2695. {
  2696. uiThreadCallback(msbuildResult, target);
  2697. }
  2698. }
  2699. }
  2700. catch (Exception e)
  2701. {
  2702. Debug.Fail(e.ToString());
  2703. EndBuild(submission, designTime, requiresUIThread);
  2704. if (uiThreadCallback != null)
  2705. {
  2706. uiThreadCallback(MSBuildResult.Failed, target);
  2707. }
  2708. throw;
  2709. }
  2710. return submission;
  2711. }
  2712. /// <summary>
  2713. /// Initialize common project properties with default value if they are empty
  2714. /// </summary>
  2715. /// <remarks>
  2716. /// The following common project properties are defaulted to projectName (if empty):
  2717. /// AssemblyName, Name and RootNamespace.
  2718. /// If the project filename is not set then no properties are set
  2719. /// </remarks>
  2720. protected virtual void InitializeProjectProperties()
  2721. {
  2722. // Get projectName from project filename. Return if not set
  2723. string projectName = Path.GetFileNameWithoutExtension(filename);
  2724. if (String.IsNullOrEmpty(projectName))
  2725. {
  2726. return;
  2727. }
  2728. if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.AssemblyName)))
  2729. {
  2730. SetProjectProperty(ProjectFileConstants.AssemblyName, projectName);
  2731. }
  2732. if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.Name)))
  2733. {
  2734. SetProjectProperty(ProjectFileConstants.Name, projectName);
  2735. }
  2736. if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.RootNamespace)))
  2737. {
  2738. SetProjectProperty(ProjectFileConstants.RootNamespace, projectName);
  2739. }
  2740. }
  2741. /// <summary>
  2742. /// Factory method for configuration provider
  2743. /// </summary>
  2744. /// <returns>Configuration provider created</returns>
  2745. protected virtual ConfigProvider CreateConfigProvider()
  2746. {
  2747. return new ConfigProvider(this);
  2748. }
  2749. /// <summary>
  2750. /// Factory method for reference container node
  2751. /// </summary>
  2752. /// <returns>ReferenceContainerNode created</returns>
  2753. protected virtual ReferenceContainerNode CreateReferenceContainerNode()
  2754. {
  2755. return new ReferenceContainerNode(this);
  2756. }
  2757. /// <summary>
  2758. /// Saves the project file on a new name.
  2759. /// </summary>
  2760. /// <param name="newFileName">The new name of the project file.</param>
  2761. /// <returns>Success value or an error code.</returns>
  2762. protected virtual int SaveAs(string newFileName)
  2763. {
  2764. Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name");
  2765. if (String.IsNullOrEmpty(newFileName))
  2766. {
  2767. throw new ArgumentNullException("newFileName");
  2768. }
  2769. newFileName = newFileName.Trim();
  2770. string errorMessage = String.Empty;
  2771. if (newFileName.Length > NativeMethods.MAX_PATH)
  2772. {
  2773. errorMessage = String.Format(CultureInfo.CurrentCulture,
  2774. SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), newFileName);
  2775. }
  2776. else
  2777. {
  2778. string fileName = String.Empty;
  2779. try
  2780. {
  2781. fileName = Path.GetFileNameWithoutExtension(newFileName);
  2782. }
  2783. // 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.
  2784. catch (ArgumentException)
  2785. {
  2786. errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture);
  2787. }
  2788. if (errorMessage.Length == 0)
  2789. {
  2790. // If there is no filename or it starts with a leading dot issue an error message and quit.
  2791. // For some reason the save as dialog box allows to save files like "......ext"
  2792. if (String.IsNullOrEmpty(fileName) || fileName[0] == '.')
  2793. {
  2794. errorMessage = SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture);
  2795. }
  2796. else if (Utilities.ContainsInvalidFileNameChars(newFileName))
  2797. {
  2798. errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture);
  2799. }
  2800. else
  2801. {
  2802. string url = Path.GetDirectoryName(newFileName);
  2803. string oldUrl = Path.GetDirectoryName(Url);
  2804. if (!NativeMethods.IsSamePath(oldUrl, url))
  2805. {
  2806. errorMessage = String.Format(CultureInfo.CurrentCulture,
  2807. SR.GetString(SR.SaveOfProjectFileOutsideCurrentDirectory,
  2808. CultureInfo.CurrentUICulture), ProjectFolder);
  2809. }
  2810. }
  2811. }
  2812. }
  2813. if (errorMessage.Length > 0)
  2814. {
  2815. // If it is not called from an automation method show a dialog box.
  2816. if (!Utilities.IsInAutomationFunction(Site))
  2817. {
  2818. string title = null;
  2819. OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
  2820. OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
  2821. OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
  2822. VsShellUtilities.ShowMessageBox(Site, title, errorMessage, icon, buttons, defaultButton);
  2823. return VSConstants.OLE_E_PROMPTSAVECANCELLED;
  2824. }
  2825. throw new InvalidOperationException(errorMessage);
  2826. }
  2827. string oldName = filename;
  2828. IVsSolution solution = Site.GetService(typeof(IVsSolution)) as IVsSolution;
  2829. Debug.Assert(solution != null, "Could not retrieve the solution form the service provider");
  2830. if (solution == null)
  2831. {
  2832. throw new InvalidOperationException();
  2833. }
  2834. int canRenameContinue = 0;
  2835. ErrorHandler.ThrowOnFailure(solution.QueryRenameProject(InteropSafeIVsProject3, filename, newFileName, 0,
  2836. out canRenameContinue));
  2837. if (canRenameContinue == 0)
  2838. {
  2839. return VSConstants.OLE_E_PROMPTSAVECANCELLED;
  2840. }
  2841. SuspendFileChanges fileChanges = new SuspendFileChanges(Site, oldName);
  2842. fileChanges.Suspend();
  2843. try
  2844. {
  2845. // Save the project file and project file related properties.
  2846. SaveMSBuildProjectFileAs(newFileName);
  2847. SetProjectFileDirty(false);
  2848. // TODO: If source control is enabled check out the project file.
  2849. //Redraw.
  2850. OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0);
  2851. ErrorHandler.ThrowOnFailure(solution.OnAfterRenameProject(InteropSafeIVsProject3, oldName, filename, 0));
  2852. IVsUIShell shell = Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
  2853. Debug.Assert(shell != null, "Could not get the ui shell from the project");
  2854. if (shell == null)
  2855. {
  2856. throw new InvalidOperationException();
  2857. }
  2858. ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0));
  2859. }
  2860. finally
  2861. {
  2862. fileChanges.Resume();
  2863. }
  2864. return VSConstants.S_OK;
  2865. }
  2866. /// <summary>
  2867. /// Saves project file related information to the new file name. It also calls msbuild API to save the project file.
  2868. /// It is called by the SaveAs method and the SetEditLabel before the project file rename related events are triggered.
  2869. /// An implementer can override this method to provide specialized semantics on how the project file is renamed in the msbuild file.
  2870. /// </summary>
  2871. /// <param name="newFileName">The new full path of the project file</param>
  2872. protected virtual void SaveMSBuildProjectFileAs(string newFileName)
  2873. {
  2874. Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name");
  2875. buildProject.FullPath = newFileName;
  2876. filename = newFileName;
  2877. string newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(newFileName);
  2878. // Refresh solution explorer
  2879. SetProjectProperty(ProjectFileConstants.Name, newFileNameWithoutExtension);
  2880. // Saves the project file on disk.
  2881. buildProject.Save(newFileName);
  2882. }
  2883. /// <summary>
  2884. /// Adds a file to the msbuild project.
  2885. /// </summary>
  2886. /// <param name="file">The file to be added.</param>
  2887. /// <returns>A ProjectElement describing the newly added file.</returns>
  2888. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMs")]
  2889. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
  2890. protected virtual ProjectElement AddFileToMsBuild(string file)
  2891. {
  2892. ProjectElement newItem;
  2893. string itemPath = PackageUtilities.MakeRelativeIfRooted(file, BaseURI);
  2894. Debug.Assert(!Path.IsPathRooted(itemPath), "Cannot add item with full path.");
  2895. if (IsCodeFile(itemPath))
  2896. {
  2897. newItem = CreateMsBuildFileItem(itemPath, ProjectFileConstants.Compile);
  2898. newItem.SetMetadata(ProjectFileConstants.SubType, ProjectFileAttributeValue.Code);
  2899. }
  2900. else if (IsEmbeddedResource(itemPath))
  2901. {
  2902. newItem = CreateMsBuildFileItem(itemPath, ProjectFileConstants.EmbeddedResource);
  2903. }
  2904. else
  2905. {
  2906. newItem = CreateMsBuildFileItem(itemPath, ProjectFileConstants.Content);
  2907. newItem.SetMetadata(ProjectFileConstants.SubType, ProjectFileConstants.Content);
  2908. }
  2909. return newItem;
  2910. }
  2911. /// <summary>
  2912. /// Adds a folder to the msbuild project.
  2913. /// </summary>
  2914. /// <param name="folder">The folder to be added.</param>
  2915. /// <returns>A Projectelement describing the newly added folder.</returns>
  2916. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMs")]
  2917. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
  2918. protected virtual ProjectElement AddFolderToMsBuild(string folder)
  2919. {
  2920. ProjectElement newItem;
  2921. string itemPath = PackageUtilities.MakeRelativeIfRooted(folder, BaseURI);
  2922. Debug.Assert(!Path.IsPathRooted(itemPath), "Cannot add item with full path.");
  2923. newItem = CreateMsBuildFileItem(itemPath, ProjectFileConstants.Folder);
  2924. return newItem;
  2925. }
  2926. /// <summary>
  2927. /// Determines whether an item can be owerwritten in the hierarchy.
  2928. /// </summary>
  2929. /// <param name="originalFileName">The orginal filname.</param>
  2930. /// <param name="computedNewFileName">The computed new file name, that will be copied to the project directory or into the folder .</param>
  2931. /// <returns>S_OK for success, or an error message</returns>
  2932. protected virtual int CanOverwriteExistingItem(string originalFileName, string computedNewFileName)
  2933. {
  2934. if (String.IsNullOrEmpty(originalFileName) || String.IsNullOrEmpty(computedNewFileName))
  2935. {
  2936. return VSConstants.E_INVALIDARG;
  2937. }
  2938. string message = String.Empty;
  2939. string title = String.Empty;
  2940. OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
  2941. OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
  2942. OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
  2943. // If the document is open then return error message.
  2944. IVsUIHierarchy hier;
  2945. IVsWindowFrame windowFrame;
  2946. uint itemid = VSConstants.VSITEMID_NIL;
  2947. bool isOpen = VsShellUtilities.IsDocumentOpen(Site, computedNewFileName, Guid.Empty, out hier, out itemid,
  2948. out windowFrame);
  2949. if (isOpen)
  2950. {
  2951. message = String.Format(CultureInfo.CurrentCulture,
  2952. SR.GetString(SR.CannotAddFileThatIsOpenInEditor, CultureInfo.CurrentUICulture),
  2953. Path.GetFileName(computedNewFileName));
  2954. VsShellUtilities.ShowMessageBox(Site, title, message, icon, buttons, defaultButton);
  2955. return VSConstants.E_ABORT;
  2956. }
  2957. // File already exists in project... message box
  2958. message = SR.GetString(SR.FileAlreadyInProject, CultureInfo.CurrentUICulture);
  2959. icon = OLEMSGICON.OLEMSGICON_QUERY;
  2960. buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO;
  2961. int msgboxResult = VsShellUtilities.ShowMessageBox(Site, title, message, icon, buttons, defaultButton);
  2962. if (msgboxResult != NativeMethods.IDYES)
  2963. {
  2964. return (int)OleConstants.OLECMDERR_E_CANCELED;
  2965. }
  2966. return VSConstants.S_OK;
  2967. }
  2968. /// <summary>
  2969. /// Handle owerwriting of an existing item in the hierarchy.
  2970. /// </summary>
  2971. /// <param name="existingNode">The node that exists.</param>
  2972. protected virtual void OverwriteExistingItem(HierarchyNode existingNode)
  2973. {
  2974. }
  2975. /// <summary>
  2976. /// Adds a new file node to the hierarchy.
  2977. /// </summary>
  2978. /// <param name="parentNode">The parent of the new fileNode</param>
  2979. /// <param name="fileName">The file name</param>
  2980. protected virtual void AddNewFileNodeToHierarchy(HierarchyNode parentNode, string fileName)
  2981. {
  2982. if (parentNode == null)
  2983. {
  2984. throw new ArgumentNullException("parentNode");
  2985. }
  2986. HierarchyNode child;
  2987. // In the case of subitem, we want to create dependent file node
  2988. // and set the DependentUpon property
  2989. if (canFileNodesHaveChilds && (parentNode is FileNode || parentNode is DependentFileNode))
  2990. {
  2991. child = CreateDependentFileNode(fileName);
  2992. child.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon,
  2993. parentNode.ItemNode.GetMetadata(ProjectFileConstants.Include));
  2994. // Make sure to set the HasNameRelation flag on the dependent node if it is related to the parent by name
  2995. if (!child.HasParentNodeNameRelation &&
  2996. string.Compare(child.GetRelationalName(), parentNode.GetRelationalName(),
  2997. StringComparison.OrdinalIgnoreCase) == 0)
  2998. {
  2999. child.HasParentNodeNameRelation = true;
  3000. }
  3001. }
  3002. else
  3003. {
  3004. //Create and add new filenode to the project
  3005. child = CreateFileNode(fileName);
  3006. }
  3007. parentNode.AddChild(child);
  3008. // TODO : Revisit the VSADDFILEFLAGS here. Can it be a nested project?
  3009. tracker.OnItemAdded(fileName, VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags);
  3010. }
  3011. /// <summary>
  3012. /// Defines whther the current mode of the project is in a supress command mode.
  3013. /// </summary>
  3014. /// <returns></returns>
  3015. protected internal virtual bool IsCurrentStateASuppressCommandsMode()
  3016. {
  3017. if (VsShellUtilities.IsSolutionBuilding(Site))
  3018. {
  3019. return true;
  3020. }
  3021. DBGMODE dbgMode = VsShellUtilities.GetDebugMode(Site) & ~DBGMODE.DBGMODE_EncMask;
  3022. if (dbgMode == DBGMODE.DBGMODE_Run || dbgMode == DBGMODE.DBGMODE_Break)
  3023. {
  3024. return true;
  3025. }
  3026. return false;
  3027. }
  3028. /// <summary>
  3029. /// This is the list of output groups that the configuration object should
  3030. /// provide.
  3031. /// The first string is the name of the group.
  3032. /// The second string is the target name (MSBuild) for that group.
  3033. /// To add/remove OutputGroups, simply override this method and edit the list.
  3034. /// To get nice display names and description for your groups, override:
  3035. /// - GetOutputGroupDisplayName
  3036. /// - GetOutputGroupDescription
  3037. /// </summary>
  3038. /// <returns>List of output group name and corresponding MSBuild target</returns>
  3039. protected internal virtual IList<KeyValuePair<string, string>> GetOutputGroupNames()
  3040. {
  3041. return new List<KeyValuePair<string, string>>(outputGroupNames);
  3042. }
  3043. /// <summary>
  3044. /// Get the display name of the given output group.
  3045. /// </summary>
  3046. /// <param name="canonicalName">Canonical name of the output group</param>
  3047. /// <returns>Display name</returns>
  3048. protected internal virtual string GetOutputGroupDisplayName(string canonicalName)
  3049. {
  3050. string result = SR.GetString(String.Format(CultureInfo.InvariantCulture, "Output{0}", canonicalName),
  3051. CultureInfo.CurrentUICulture);
  3052. if (String.IsNullOrEmpty(result))
  3053. result = canonicalName;
  3054. return result;
  3055. }
  3056. /// <summary>
  3057. /// Get the description of the given output group.
  3058. /// </summary>
  3059. /// <param name="canonicalName">Canonical name of the output group</param>
  3060. /// <returns>Description</returns>
  3061. protected internal virtual string GetOutputGroupDescription(string canonicalName)
  3062. {
  3063. string result =
  3064. SR.GetString(String.Format(CultureInfo.InvariantCulture, "Output{0}Description", canonicalName),
  3065. CultureInfo.CurrentUICulture);
  3066. if (String.IsNullOrEmpty(result))
  3067. result = canonicalName;
  3068. return result;
  3069. }
  3070. /// <summary>
  3071. /// Set the configuration in MSBuild.
  3072. /// This does not get persisted and is used to evaluate msbuild conditions
  3073. /// which are based on the $(Configuration) property.
  3074. /// </summary>
  3075. protected internal virtual void SetCurrentConfiguration()
  3076. {
  3077. if (BuildInProgress)
  3078. {
  3079. // we are building so this should already be the current configuration
  3080. return;
  3081. }
  3082. // Can't ask for the active config until the project is opened, so do nothing in that scenario
  3083. if (!projectOpened)
  3084. return;
  3085. EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project;
  3086. SetConfiguration(Utilities.GetActiveConfigurationName(automationObject));
  3087. }
  3088. /// <summary>
  3089. /// Set the configuration property in MSBuild.
  3090. /// This does not get persisted and is used to evaluate msbuild conditions
  3091. /// which are based on the $(Configuration) property.
  3092. /// </summary>
  3093. /// <param name="config">Configuration name</param>
  3094. protected internal virtual void SetConfiguration(string config)
  3095. {
  3096. if (config == null)
  3097. {
  3098. throw new ArgumentNullException("config");
  3099. }
  3100. // Can't ask for the active config until the project is opened, so do nothing in that scenario
  3101. if (!projectOpened)
  3102. return;
  3103. // We cannot change properties during the build so if the config
  3104. // we want to set is the current, we do nothing otherwise we fail.
  3105. if (BuildInProgress)
  3106. {
  3107. EnvDTE.Project automationObject = GetAutomationObject() as EnvDTE.Project;
  3108. string currentConfigName = Utilities.GetActiveConfigurationName(automationObject);
  3109. bool configsAreEqual = String.Compare(currentConfigName, config, StringComparison.OrdinalIgnoreCase) ==
  3110. 0;
  3111. if (configsAreEqual)
  3112. {
  3113. return;
  3114. }
  3115. throw new InvalidOperationException();
  3116. }
  3117. bool propertiesChanged = buildProject.SetGlobalProperty(ProjectFileConstants.Configuration, config);
  3118. if (currentConfig == null || propertiesChanged)
  3119. {
  3120. currentConfig = buildProject.CreateProjectInstance();
  3121. }
  3122. if (propertiesChanged || designTimeAssemblyResolution == null)
  3123. {
  3124. if (designTimeAssemblyResolution == null)
  3125. {
  3126. designTimeAssemblyResolution = new DesignTimeAssemblyResolution();
  3127. }
  3128. designTimeAssemblyResolution.Initialize(this);
  3129. }
  3130. options = null;
  3131. }
  3132. /// <summary>
  3133. /// Loads reference items from the project file into the hierarchy.
  3134. /// </summary>
  3135. protected internal virtual void ProcessReferences()
  3136. {
  3137. IReferenceContainer container = GetReferenceContainer();
  3138. if (null == container)
  3139. {
  3140. // Process References
  3141. ReferenceContainerNode referencesFolder = CreateReferenceContainerNode();
  3142. if (null == referencesFolder)
  3143. {
  3144. // This project type does not support references or there is a problem
  3145. // creating the reference container node.
  3146. // In both cases there is no point to try to process references, so exit.
  3147. return;
  3148. }
  3149. AddChild(referencesFolder);
  3150. container = referencesFolder;
  3151. }
  3152. // Load the referernces.
  3153. container.LoadReferencesFromBuildProject(buildProject);
  3154. }
  3155. /// <summary>
  3156. /// Loads folders from the project file into the hierarchy.
  3157. /// </summary>
  3158. protected internal virtual void ProcessFolders()
  3159. {
  3160. // Process Folders (useful to persist empty folder)
  3161. foreach (MSBuild.ProjectItem folder in buildProject.GetItems(ProjectFileConstants.Folder))
  3162. {
  3163. string strPath = folder.EvaluatedInclude;
  3164. // We do not need any special logic for assuring that a folder is only added once to the ui hierarchy.
  3165. // The below method will only add once the folder to the ui hierarchy
  3166. CreateFolderNodes(strPath);
  3167. }
  3168. }
  3169. /// <summary>
  3170. /// Loads file items from the project file into the hierarchy.
  3171. /// </summary>
  3172. protected internal virtual void ProcessFiles()
  3173. {
  3174. List<String> subitemsKeys = new List<String>();
  3175. Dictionary<String, MSBuild.ProjectItem> subitems = new Dictionary<String, MSBuild.ProjectItem>();
  3176. // Define a set for our build items. The value does not really matter here.
  3177. Dictionary<String, MSBuild.ProjectItem> items = new Dictionary<String, MSBuild.ProjectItem>();
  3178. // Process Files
  3179. for (int i = 0; i < buildProject.Items.Count; i++)
  3180. {
  3181. MSBuild.ProjectItem item = buildProject.Items.ElementAt(i);
  3182. // Ignore the item if it is a reference or folder
  3183. if (FilterItemTypeToBeAddedToHierarchy(item.ItemType))
  3184. continue;
  3185. // MSBuilds tasks/targets can create items (such as object files),
  3186. // such items are not part of the project per say, and should not be displayed.
  3187. // so ignore those items.
  3188. if (!IsItemTypeFileType(item.ItemType))
  3189. continue;
  3190. // If the item is already contained do nothing.
  3191. // TODO: possibly report in the error list that the the item is already contained in the project file similar to Language projects.
  3192. if (items.ContainsKey(item.EvaluatedInclude.ToUpperInvariant()))
  3193. continue;
  3194. // Make sure that we do not want to add the item, dependent, or independent twice to the ui hierarchy
  3195. items.Add(item.EvaluatedInclude.ToUpperInvariant(), item);
  3196. string dependentOf = item.GetMetadataValue(ProjectFileConstants.DependentUpon);
  3197. if (!CanFileNodesHaveChilds || String.IsNullOrEmpty(dependentOf))
  3198. {
  3199. AddIndependentFileNode(item);
  3200. }
  3201. else
  3202. {
  3203. // We will process dependent items later.
  3204. // Note that we use 2 lists as we want to remove elements from
  3205. // the collection as we loop through it
  3206. subitemsKeys.Add(item.EvaluatedInclude);
  3207. subitems.Add(item.EvaluatedInclude, item);
  3208. }
  3209. }
  3210. // Now process the dependent items.
  3211. if (CanFileNodesHaveChilds)
  3212. {
  3213. ProcessDependentFileNodes(subitemsKeys, subitems);
  3214. }
  3215. }
  3216. /// <summary>
  3217. /// Processes dependent filenodes from list of subitems. Multi level supported, but not circular dependencies.
  3218. /// </summary>
  3219. /// <param name="subitemsKeys">List of sub item keys </param>
  3220. /// <param name="subitems"></param>
  3221. protected internal virtual void ProcessDependentFileNodes(IList<String> subitemsKeys,
  3222. Dictionary<String, MSBuild.ProjectItem> subitems)
  3223. {
  3224. if (subitemsKeys == null || subitems == null)
  3225. {
  3226. return;
  3227. }
  3228. foreach (string key in subitemsKeys)
  3229. {
  3230. // A previous pass could have removed the key so make sure it still needs to be added
  3231. if (!subitems.ContainsKey(key))
  3232. continue;
  3233. AddDependentFileNode(subitems, key);
  3234. }
  3235. }
  3236. /// <summary>
  3237. /// For flavored projects which implement IPersistXMLFragment, load the information now
  3238. /// </summary>
  3239. protected internal virtual void LoadNonBuildInformation()
  3240. {
  3241. IPersistXMLFragment outerHierarchy = InteropSafeIVsHierarchy as IPersistXMLFragment;
  3242. if (outerHierarchy != null)
  3243. {
  3244. LoadXmlFragment(outerHierarchy, null);
  3245. }
  3246. }
  3247. /// <summary>
  3248. /// Used to sort nodes in the hierarchy.
  3249. /// </summary>
  3250. protected internal virtual int CompareNodes(HierarchyNode node1, HierarchyNode node2)
  3251. {
  3252. Debug.Assert(node1 != null && node2 != null);
  3253. if (node1 == null)
  3254. {
  3255. throw new ArgumentNullException("node1");
  3256. }
  3257. if (node2 == null)
  3258. {
  3259. throw new ArgumentNullException("node2");
  3260. }
  3261. if (node1.SortPriority == node2.SortPriority)
  3262. {
  3263. return String.Compare(node2.Caption, node1.Caption, true, CultureInfo.CurrentCulture);
  3264. }
  3265. else
  3266. {
  3267. return node2.SortPriority - node1.SortPriority;
  3268. }
  3269. }
  3270. /// <summary>
  3271. /// Handles global properties related to configuration and platform changes invoked by a change in the active configuration.
  3272. /// </summary>
  3273. /// <param name="sender">The sender of the event.</param>
  3274. /// <param name="eventArgs">The event args</param>
  3275. [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers",
  3276. Justification =
  3277. "This method will give the opportunity to update global properties based on active configuration change. " +
  3278. "There is no security threat that could otherwise not be reached by listening to configuration chnage events."
  3279. )]
  3280. protected virtual void OnHandleConfigurationRelatedGlobalProperties(object sender,
  3281. ActiveConfigurationChangedEventArgs
  3282. eventArgs)
  3283. {
  3284. Debug.Assert(eventArgs != null, "Wrong hierarchy passed as event arg for the configuration change listener.");
  3285. // If (eventArgs.Hierarchy == NULL) then we received this event because the solution configuration
  3286. // was changed.
  3287. // If it is not null we got the event because a project in teh configuration manager has changed its active configuration.
  3288. // We care only about our project in the default implementation.
  3289. if (eventArgs == null || eventArgs.Hierarchy == null ||
  3290. !Utilities.IsSameComObject(eventArgs.Hierarchy, InteropSafeIVsHierarchy))
  3291. {
  3292. return;
  3293. }
  3294. string name, platform;
  3295. if (!Utilities.TryGetActiveConfigurationAndPlatform(Site, InteropSafeIVsHierarchy, out name, out platform))
  3296. {
  3297. throw new InvalidOperationException();
  3298. }
  3299. buildProject.SetGlobalProperty(GlobalProperty.Configuration.ToString(), name);
  3300. // If the platform is "Any CPU" then it should be set to AnyCPU, since that is the property value in MsBuild terms.
  3301. if (String.Compare(platform, ConfigProvider.AnyCPUPlatform, StringComparison.Ordinal) == 0)
  3302. {
  3303. platform = ProjectFileValues.AnyCPU;
  3304. }
  3305. buildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), platform);
  3306. }
  3307. /// <summary>
  3308. /// Flush any remaining content from build logger.
  3309. /// This method is called as part of the callback method passed to the buildsubmission during async build
  3310. /// so that results can be printed the the build is finished.
  3311. /// </summary>
  3312. protected virtual void FlushBuildLoggerContent()
  3313. {
  3314. }
  3315. #endregion
  3316. #region non-virtual methods
  3317. /// <summary>
  3318. /// Suspends MSBuild
  3319. /// </summary>
  3320. public void SuspendMSBuild()
  3321. {
  3322. suspendMSBuildCounter++;
  3323. }
  3324. /// <summary>
  3325. /// Resumes MSBuild.
  3326. /// </summary>
  3327. public void ResumeMSBuild(string config, IVsOutputWindowPane output, string target)
  3328. {
  3329. suspendMSBuildCounter--;
  3330. if (suspendMSBuildCounter == 0 && invokeMSBuildWhenResumed)
  3331. {
  3332. try
  3333. {
  3334. Build(config, output, target);
  3335. }
  3336. finally
  3337. {
  3338. invokeMSBuildWhenResumed = false;
  3339. }
  3340. }
  3341. }
  3342. /// <summary>
  3343. /// Resumes MSBuild.
  3344. /// </summary>
  3345. public void ResumeMSBuild(string config, string target)
  3346. {
  3347. ResumeMSBuild(config, null, target);
  3348. }
  3349. /// <summary>
  3350. /// Resumes MSBuild.
  3351. /// </summary>
  3352. public void ResumeMSBuild(string target)
  3353. {
  3354. ResumeMSBuild(String.Empty, null, target);
  3355. }
  3356. /// <summary>
  3357. /// Calls MSBuild if it is not suspended. If it is suspended then it will remember to call when msbuild is resumed.
  3358. /// </summary>
  3359. public MSBuildResult CallMSBuild(string config, IVsOutputWindowPane output, string target)
  3360. {
  3361. if (suspendMSBuildCounter > 0)
  3362. {
  3363. // remember to invoke MSBuild
  3364. invokeMSBuildWhenResumed = true;
  3365. return MSBuildResult.Suspended;
  3366. }
  3367. else
  3368. {
  3369. return Build(config, output, target);
  3370. }
  3371. }
  3372. /// <summary>
  3373. /// Overloaded method. Calls MSBuild if it is not suspended. Does not log on the outputwindow. If it is suspended then it will remeber to call when msbuild is resumed.
  3374. /// </summary>
  3375. public MSBuildResult CallMSBuild(string config, string target)
  3376. {
  3377. return CallMSBuild(config, null, target);
  3378. }
  3379. /// <summary>
  3380. /// Calls MSBuild if it is not suspended. Does not log and uses current configuration. If it is suspended then it will remeber to call when msbuild is resumed.
  3381. /// </summary>
  3382. public MSBuildResult CallMSBuild(string target)
  3383. {
  3384. return CallMSBuild(String.Empty, null, target);
  3385. }
  3386. /// <summary>
  3387. /// Calls MSBuild if it is not suspended. Uses current configuration. If it is suspended then it will remeber to call when msbuild is resumed.
  3388. /// </summary>
  3389. public MSBuildResult CallMSBuild(string target, IVsOutputWindowPane output)
  3390. {
  3391. return CallMSBuild(String.Empty, output, target);
  3392. }
  3393. /// <summary>
  3394. /// Overloaded method to invoke MSBuild
  3395. /// </summary>
  3396. public MSBuildResult Build(string config, IVsOutputWindowPane output, string target)
  3397. {
  3398. return Build(0, config, output, target);
  3399. }
  3400. /// <summary>
  3401. /// Overloaded method to invoke MSBuild. Does not log build results to the output window pane.
  3402. /// </summary>
  3403. public MSBuildResult Build(string config, string target)
  3404. {
  3405. return Build(0, config, null, target);
  3406. }
  3407. /// <summary>
  3408. /// Overloaded method. Invokes MSBuild using the default configuration and does without logging on the output window pane.
  3409. /// </summary>
  3410. public MSBuildResult Build(string target)
  3411. {
  3412. return Build(0, String.Empty, null, target);
  3413. }
  3414. /// <summary>
  3415. /// Overloaded method. Invokes MSBuild using the default configuration.
  3416. /// </summary>
  3417. public MSBuildResult Build(string target, IVsOutputWindowPane output)
  3418. {
  3419. return Build(0, String.Empty, output, target);
  3420. }
  3421. /// <summary>
  3422. /// Get the output path for a specific configuration name
  3423. /// </summary>
  3424. /// <param name="config">name of configuration</param>
  3425. /// <returns>Output path</returns>
  3426. public string GetOutputPath(string config)
  3427. {
  3428. SetConfiguration(config);
  3429. return GetOutputPath(currentConfig);
  3430. }
  3431. /// <summary>
  3432. /// Get value of Project property
  3433. /// </summary>
  3434. /// <param name="propertyName">Name of Property to retrieve</param>
  3435. /// <returns>Evaluated value of property.</returns>
  3436. public string GetProjectProperty(string propertyName)
  3437. {
  3438. return GetProjectProperty(propertyName, true);
  3439. }
  3440. /// <summary>
  3441. /// Gets the unevaluated value of a project property.
  3442. /// </summary>
  3443. /// <param name="propertyName">The name of the property to retrieve.</param>
  3444. /// <returns>Unevaluated value of the property.</returns>
  3445. public string GetProjectPropertyUnevaluated(string propertyName)
  3446. {
  3447. return buildProject.GetProperty(propertyName).UnevaluatedValue;
  3448. }
  3449. /// <summary>
  3450. /// Set dirty state of project
  3451. /// </summary>
  3452. /// <param name="value">boolean value indicating dirty state</param>
  3453. public void SetProjectFileDirty(bool value)
  3454. {
  3455. isDirty = value;
  3456. if (isDirty)
  3457. {
  3458. lastModifiedTime = DateTime.Now;
  3459. buildIsPrepared = false;
  3460. }
  3461. }
  3462. /// <summary>
  3463. /// Get output assembly for a specific configuration name
  3464. /// </summary>
  3465. /// <param name="config">Name of configuration</param>
  3466. /// <returns>Name of output assembly</returns>
  3467. public string GetOutputAssembly(string config)
  3468. {
  3469. ProjectOptions options = GetProjectOptions(config);
  3470. return options.OutputAssembly;
  3471. }
  3472. /// <summary>
  3473. /// Get Node from ItemID.
  3474. /// </summary>
  3475. /// <param name="itemId">ItemID for the requested node</param>
  3476. /// <returns>Node if found</returns>
  3477. public HierarchyNode NodeFromItemId(uint itemId)
  3478. {
  3479. if (VSConstants.VSITEMID_ROOT == itemId)
  3480. {
  3481. return this;
  3482. }
  3483. else if (VSConstants.VSITEMID_NIL == itemId)
  3484. {
  3485. return null;
  3486. }
  3487. else if (VSConstants.VSITEMID_SELECTION == itemId)
  3488. {
  3489. throw new NotImplementedException();
  3490. }
  3491. return (HierarchyNode)ItemIdMap[itemId];
  3492. }
  3493. /// <summary>
  3494. /// This method return new project element, and add new MSBuild item to the project/build hierarchy
  3495. /// </summary>
  3496. /// <param name="file">file name</param>
  3497. /// <param name="itemType">MSBuild item type</param>
  3498. /// <returns>new project element</returns>
  3499. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
  3500. public ProjectElement CreateMsBuildFileItem(string file, string itemType)
  3501. {
  3502. return new ProjectElement(this, file, itemType);
  3503. }
  3504. /// <summary>
  3505. /// This method returns new project element based on existing MSBuild item. It does not modify/add project/build hierarchy at all.
  3506. /// </summary>
  3507. /// <param name="item">MSBuild item instance</param>
  3508. /// <returns>wrapping project element</returns>
  3509. public ProjectElement GetProjectElement(MSBuild.ProjectItem item)
  3510. {
  3511. return new ProjectElement(this, item, false);
  3512. }
  3513. /// <summary>
  3514. /// Create FolderNode from Path
  3515. /// </summary>
  3516. /// <param name="path">Path to folder</param>
  3517. /// <returns>FolderNode created that can be added to the hierarchy</returns>
  3518. protected internal FolderNode CreateFolderNode(string path)
  3519. {
  3520. ProjectElement item = AddFolderToMsBuild(path);
  3521. FolderNode folderNode = CreateFolderNode(path, item);
  3522. return folderNode;
  3523. }
  3524. /// <summary>
  3525. /// Verify if the file can be written to.
  3526. /// Return false if the file is read only and/or not checked out
  3527. /// and the user did not give permission to change it.
  3528. /// Note that exact behavior can also be affected based on the SCC
  3529. /// settings under Tools->Options.
  3530. /// </summary>
  3531. internal bool QueryEditProjectFile(bool suppressUI)
  3532. {
  3533. bool result = true;
  3534. if (site == null)
  3535. {
  3536. // We're already zombied. Better return FALSE.
  3537. result = false;
  3538. }
  3539. else if (disableQueryEdit)
  3540. {
  3541. return true;
  3542. }
  3543. else
  3544. {
  3545. IVsQueryEditQuerySave2 queryEditQuerySave =
  3546. GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2;
  3547. if (queryEditQuerySave != null)
  3548. {
  3549. // Project path dependends on server/client project
  3550. string path = filename;
  3551. tagVSQueryEditFlags qef = tagVSQueryEditFlags.QEF_AllowInMemoryEdits;
  3552. if (suppressUI)
  3553. qef |= tagVSQueryEditFlags.QEF_SilentMode;
  3554. // If we are debugging, we want to prevent our project from being reloaded. To
  3555. // do this, we pass the QEF_NoReload flag
  3556. if (!Utilities.IsVisualStudioInDesignMode(Site))
  3557. qef |= tagVSQueryEditFlags.QEF_NoReload;
  3558. uint verdict;
  3559. uint moreInfo;
  3560. string[] files = new string[1];
  3561. files[0] = path;
  3562. uint[] flags = new uint[1];
  3563. VSQEQS_FILE_ATTRIBUTE_DATA[] attributes = new VSQEQS_FILE_ATTRIBUTE_DATA[1];
  3564. int hr = queryEditQuerySave.QueryEditFiles(
  3565. (uint)qef,
  3566. 1, // 1 file
  3567. files, // array of files
  3568. flags, // no per file flags
  3569. attributes, // no per file file attributes
  3570. out verdict,
  3571. out moreInfo /* ignore additional results */);
  3572. tagVSQueryEditResult qer = (tagVSQueryEditResult)verdict;
  3573. if (ErrorHandler.Failed(hr) || (qer != tagVSQueryEditResult.QER_EditOK))
  3574. {
  3575. if (!suppressUI && !Utilities.IsInAutomationFunction(Site))
  3576. {
  3577. string message = SR.GetString(SR.CancelQueryEdit, path);
  3578. string title = string.Empty;
  3579. OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
  3580. OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
  3581. OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
  3582. VsShellUtilities.ShowMessageBox(Site, title, message, icon, buttons, defaultButton);
  3583. }
  3584. result = false;
  3585. }
  3586. }
  3587. }
  3588. return result;
  3589. }
  3590. /// <summary>
  3591. /// Checks whether a hierarchy is a nested project.
  3592. /// </summary>
  3593. /// <param name="hierarchy"></param>
  3594. /// <returns></returns>
  3595. internal NestedProjectNode GetNestedProjectForHierarchy(IVsHierarchy hierarchy)
  3596. {
  3597. IVsProject3 project = hierarchy as IVsProject3;
  3598. if (project != null)
  3599. {
  3600. string mkDocument = String.Empty;
  3601. ErrorHandler.ThrowOnFailure(project.GetMkDocument(VSConstants.VSITEMID_ROOT, out mkDocument));
  3602. if (!String.IsNullOrEmpty(mkDocument))
  3603. {
  3604. HierarchyNode node = FindChild(mkDocument);
  3605. return node as NestedProjectNode;
  3606. }
  3607. }
  3608. return null;
  3609. }
  3610. /// <summary>
  3611. /// Given a node determines what is the directory that can accept files.
  3612. /// If the node is a FoldeNode than it is the Url of the Folder.
  3613. /// If the node is a ProjectNode it is the project folder.
  3614. /// Otherwise (such as FileNode subitem) it delegate the resolution to the parent node.
  3615. /// </summary>
  3616. internal string GetBaseDirectoryForAddingFiles(HierarchyNode nodeToAddFile)
  3617. {
  3618. string baseDir = String.Empty;
  3619. if (nodeToAddFile is FolderNode)
  3620. {
  3621. baseDir = nodeToAddFile.Url;
  3622. }
  3623. else if (nodeToAddFile is ProjectNode)
  3624. {
  3625. baseDir = ProjectFolder;
  3626. }
  3627. else if (nodeToAddFile != null)
  3628. {
  3629. baseDir = GetBaseDirectoryForAddingFiles(nodeToAddFile.Parent);
  3630. }
  3631. return baseDir;
  3632. }
  3633. /// <summary>
  3634. /// For internal use only.
  3635. /// This creates a copy of an existing configuration and add it to the project.
  3636. /// Caller should change the condition on the PropertyGroup.
  3637. /// If derived class want to accomplish this, they should call ConfigProvider.AddCfgsOfCfgName()
  3638. /// It is expected that in the future MSBuild will have support for this so we don't have to
  3639. /// do it manually.
  3640. /// </summary>
  3641. /// <param name="group">PropertyGroup to clone</param>
  3642. /// <returns></returns>
  3643. internal MSBuildConstruction.ProjectPropertyGroupElement ClonePropertyGroup(
  3644. MSBuildConstruction.ProjectPropertyGroupElement group)
  3645. {
  3646. // Create a new (empty) PropertyGroup
  3647. MSBuildConstruction.ProjectPropertyGroupElement newPropertyGroup = buildProject.Xml.AddPropertyGroup();
  3648. // Now copy everything from the group we are trying to clone to the group we are creating
  3649. if (!String.IsNullOrEmpty(group.Condition))
  3650. newPropertyGroup.Condition = group.Condition;
  3651. foreach (MSBuildConstruction.ProjectPropertyElement prop in group.Properties)
  3652. {
  3653. MSBuildConstruction.ProjectPropertyElement newProperty = newPropertyGroup.AddProperty(prop.Name,
  3654. prop.Value);
  3655. if (!String.IsNullOrEmpty(prop.Condition))
  3656. newProperty.Condition = prop.Condition;
  3657. }
  3658. return newPropertyGroup;
  3659. }
  3660. /// <summary>
  3661. /// Get the project extensions
  3662. /// </summary>
  3663. /// <returns></returns>
  3664. internal MSBuildConstruction.ProjectExtensionsElement GetProjectExtensions()
  3665. {
  3666. var extensionsElement =
  3667. buildProject.Xml.ChildrenReversed.OfType<MSBuildConstruction.ProjectExtensionsElement>()
  3668. .FirstOrDefault();
  3669. if (extensionsElement == null)
  3670. {
  3671. extensionsElement = buildProject.Xml.CreateProjectExtensionsElement();
  3672. buildProject.Xml.AppendChild(extensionsElement);
  3673. }
  3674. return extensionsElement;
  3675. }
  3676. /// <summary>
  3677. /// Set the xmlText as a project extension element with the id passed.
  3678. /// </summary>
  3679. /// <param name="id">The id of the project extension element.</param>
  3680. /// <param name="xmlText">The value to set for a project extension.</param>
  3681. internal void SetProjectExtensions(string id, string xmlText)
  3682. {
  3683. MSBuildConstruction.ProjectExtensionsElement element = GetProjectExtensions();
  3684. // If it doesn't already have a value and we're asked to set it to
  3685. // nothing, don't do anything. Same as old OM. Keeps project neat.
  3686. if (element == null)
  3687. {
  3688. if (xmlText.Length == 0)
  3689. {
  3690. return;
  3691. }
  3692. element = buildProject.Xml.CreateProjectExtensionsElement();
  3693. buildProject.Xml.AppendChild(element);
  3694. }
  3695. element[id] = xmlText;
  3696. }
  3697. /// <summary>
  3698. /// Register the project with the Scc manager.
  3699. /// </summary>
  3700. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
  3701. protected void RegisterSccProject()
  3702. {
  3703. if (IsSccDisabled || isRegisteredWithScc || String.IsNullOrEmpty(sccProjectName))
  3704. {
  3705. return;
  3706. }
  3707. IVsSccManager2 sccManager = Site.GetService(typeof(SVsSccManager)) as IVsSccManager2;
  3708. if (sccManager != null)
  3709. {
  3710. ErrorHandler.ThrowOnFailure(sccManager.RegisterSccProject(InteropSafeIVsSccProject2, sccProjectName,
  3711. sccAuxPath, sccLocalPath, sccProvider));
  3712. isRegisteredWithScc = true;
  3713. }
  3714. }
  3715. /// <summary>
  3716. /// Unregisters us from the SCC manager
  3717. /// </summary>
  3718. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "UnRegister")]
  3719. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Un")]
  3720. protected void UnRegisterProject()
  3721. {
  3722. if (IsSccDisabled || !isRegisteredWithScc)
  3723. {
  3724. return;
  3725. }
  3726. IVsSccManager2 sccManager = Site.GetService(typeof(SVsSccManager)) as IVsSccManager2;
  3727. if (sccManager != null)
  3728. {
  3729. ErrorHandler.ThrowOnFailure(sccManager.UnregisterSccProject(InteropSafeIVsSccProject2));
  3730. isRegisteredWithScc = false;
  3731. }
  3732. }
  3733. /// <summary>
  3734. /// Get the CATID corresponding to the specified type.
  3735. /// </summary>
  3736. /// <param name="type">Type of the object for which you want the CATID</param>
  3737. /// <returns>CATID</returns>
  3738. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")]
  3739. protected internal Guid GetCATIDForType(Type type)
  3740. {
  3741. if (type == null)
  3742. throw new ArgumentNullException("type");
  3743. if (catidMapping.ContainsKey(type))
  3744. return catidMapping[type];
  3745. // If you get here and you want your object to be extensible, then add a call to AddCATIDMapping() in your project constructor
  3746. return Guid.Empty;
  3747. }
  3748. /// <summary>
  3749. /// This is used to specify a CATID corresponding to a BrowseObject or an ExtObject.
  3750. /// The CATID can be any GUID you choose. For types which are your owns, you could use
  3751. /// their type GUID, while for other types (such as those provided in the MPF) you should
  3752. /// provide a different GUID.
  3753. /// </summary>
  3754. /// <param name="type">Type of the extensible object</param>
  3755. /// <param name="catid">GUID that extender can use to uniquely identify your object type</param>
  3756. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "catid")]
  3757. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")]
  3758. protected void AddCATIDMapping(Type type, Guid catid)
  3759. {
  3760. catidMapping.Add(type, catid);
  3761. }
  3762. /// <summary>
  3763. /// Initialize an object with an XML fragment.
  3764. /// </summary>
  3765. /// <param name="iPersistXMLFragment">Object that support being initialized with an XML fragment</param>
  3766. /// <param name="configName">Name of the configuration being initialized, null if it is the project</param>
  3767. protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName)
  3768. {
  3769. if (persistXmlFragment == null)
  3770. {
  3771. throw new ArgumentNullException("persistXmlFragment");
  3772. }
  3773. if (xmlFragments == null)
  3774. {
  3775. // Retrieve the xml fragments from MSBuild
  3776. xmlFragments = new XmlDocument();
  3777. string fragments = GetProjectExtensions()[ProjectFileConstants.VisualStudio];
  3778. fragments = String.Format(CultureInfo.InvariantCulture, "<root>{0}</root>", fragments);
  3779. xmlFragments.LoadXml(fragments);
  3780. }
  3781. // We need to loop through all the flavors
  3782. string flavorsGuid;
  3783. ErrorHandler.ThrowOnFailure(((IVsAggregatableProject)this).GetAggregateProjectTypeGuids(out flavorsGuid));
  3784. foreach (Guid flavor in Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(flavorsGuid))
  3785. {
  3786. // Look for a matching fragment
  3787. string flavorGuidString = flavor.ToString("B");
  3788. string fragment = null;
  3789. XmlNode node = null;
  3790. foreach (XmlNode child in xmlFragments.FirstChild.ChildNodes)
  3791. {
  3792. if (child.Attributes.Count > 0)
  3793. {
  3794. string guid = String.Empty;
  3795. string configuration = String.Empty;
  3796. if (child.Attributes[ProjectFileConstants.Guid] != null)
  3797. guid = child.Attributes[ProjectFileConstants.Guid].Value;
  3798. if (child.Attributes[ProjectFileConstants.Configuration] != null)
  3799. configuration = child.Attributes[ProjectFileConstants.Configuration].Value;
  3800. if (
  3801. String.Compare(child.Name, ProjectFileConstants.FlavorProperties,
  3802. StringComparison.OrdinalIgnoreCase) == 0
  3803. && String.Compare(guid, flavorGuidString, StringComparison.OrdinalIgnoreCase) == 0
  3804. && ((String.IsNullOrEmpty(configName) && String.IsNullOrEmpty(configuration))
  3805. || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0)))
  3806. {
  3807. // we found the matching fragment
  3808. fragment = child.InnerXml;
  3809. node = child;
  3810. break;
  3811. }
  3812. }
  3813. }
  3814. Guid flavorGuid = flavor;
  3815. if (String.IsNullOrEmpty(fragment))
  3816. {
  3817. // the fragment was not found so init with default values
  3818. ErrorHandler.ThrowOnFailure(persistXmlFragment.InitNew(ref flavorGuid,
  3819. (uint)_PersistStorageType.PST_PROJECT_FILE));
  3820. // While we don't yet support user files, our flavors might, so we will store that in the project file until then
  3821. // TODO: Refactor this code when we support user files
  3822. ErrorHandler.ThrowOnFailure(persistXmlFragment.InitNew(ref flavorGuid,
  3823. (uint)_PersistStorageType.PST_USER_FILE));
  3824. }
  3825. else
  3826. {
  3827. ErrorHandler.ThrowOnFailure(persistXmlFragment.Load(ref flavorGuid,
  3828. (uint)_PersistStorageType.PST_PROJECT_FILE,
  3829. fragment));
  3830. // While we don't yet support user files, our flavors might, so we will store that in the project file until then
  3831. // TODO: Refactor this code when we support user files
  3832. if (node.NextSibling != null && node.NextSibling.Attributes[ProjectFileConstants.User] != null)
  3833. ErrorHandler.ThrowOnFailure(persistXmlFragment.Load(ref flavorGuid,
  3834. (uint)_PersistStorageType.PST_USER_FILE,
  3835. node.NextSibling.InnerXml));
  3836. }
  3837. }
  3838. }
  3839. /// <summary>
  3840. /// Retrieve all XML fragments that need to be saved from the flavors and store the information in msbuild.
  3841. /// </summary>
  3842. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")]
  3843. protected void PersistXMLFragments()
  3844. {
  3845. if (IsFlavorDirty() != 0)
  3846. {
  3847. XmlDocument doc = new XmlDocument();
  3848. XmlElement root = doc.CreateElement("ROOT");
  3849. // We will need the list of configuration inside the loop, so get it before entering the loop
  3850. uint[] count = new uint[1];
  3851. IVsCfg[] configs = null;
  3852. int hr = ConfigProvider.GetCfgs(0, null, count, null);
  3853. if (ErrorHandler.Succeeded(hr) && count[0] > 0)
  3854. {
  3855. configs = new IVsCfg[count[0]];
  3856. hr = ConfigProvider.GetCfgs((uint)configs.Length, configs, count, null);
  3857. if (ErrorHandler.Failed(hr))
  3858. count[0] = 0;
  3859. }
  3860. if (count[0] == 0)
  3861. configs = new IVsCfg[0];
  3862. // We need to loop through all the flavors
  3863. string flavorsGuid;
  3864. ErrorHandler.ThrowOnFailure(((IVsAggregatableProject)this).GetAggregateProjectTypeGuids(out flavorsGuid));
  3865. foreach (Guid flavor in Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(flavorsGuid))
  3866. {
  3867. IPersistXMLFragment outerHierarchy = InteropSafeIVsHierarchy as IPersistXMLFragment;
  3868. // First check the project
  3869. if (outerHierarchy != null)
  3870. {
  3871. // Retrieve the XML fragment
  3872. string fragment = string.Empty;
  3873. Guid flavorGuid = flavor;
  3874. ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid,
  3875. (uint)_PersistStorageType.PST_PROJECT_FILE,
  3876. out fragment, 1));
  3877. if (!String.IsNullOrEmpty(fragment))
  3878. {
  3879. // Add the fragment to our XML
  3880. WrapXmlFragment(doc, root, flavor, null, fragment);
  3881. }
  3882. // While we don't yet support user files, our flavors might, so we will store that in the project file until then
  3883. // TODO: Refactor this code when we support user files
  3884. fragment = String.Empty;
  3885. ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid,
  3886. (uint)_PersistStorageType.PST_USER_FILE,
  3887. out fragment, 1));
  3888. if (!String.IsNullOrEmpty(fragment))
  3889. {
  3890. // Add the fragment to our XML
  3891. XmlElement node = WrapXmlFragment(doc, root, flavor, null, fragment);
  3892. node.Attributes.Append(doc.CreateAttribute(ProjectFileConstants.User));
  3893. }
  3894. }
  3895. // Then look at the configurations
  3896. foreach (IVsCfg config in configs)
  3897. {
  3898. // Get the fragment for this flavor/config pair
  3899. string fragment;
  3900. ErrorHandler.ThrowOnFailure(((ProjectConfig)config).GetXmlFragment(flavor,
  3901. _PersistStorageType
  3902. .PST_PROJECT_FILE,
  3903. out fragment));
  3904. if (!String.IsNullOrEmpty(fragment))
  3905. {
  3906. string configName;
  3907. ErrorHandler.ThrowOnFailure(config.get_DisplayName(out configName));
  3908. WrapXmlFragment(doc, root, flavor, configName, fragment);
  3909. }
  3910. }
  3911. }
  3912. if (root.ChildNodes != null && root.ChildNodes.Count > 0)
  3913. {
  3914. // Save our XML (this is only the non-build information for each flavor) in msbuild
  3915. SetProjectExtensions(ProjectFileConstants.VisualStudio, root.InnerXml);
  3916. }
  3917. }
  3918. }
  3919. #endregion
  3920. #region IVsGetCfgProvider Members
  3921. //=================================================================================
  3922. public virtual int GetCfgProvider(out IVsCfgProvider p)
  3923. {
  3924. CCITracing.TraceCall();
  3925. // Be sure to call the property here since that is doing a polymorhic ProjectConfig creation.
  3926. p = ConfigProvider;
  3927. return (p == null ? VSConstants.E_NOTIMPL : VSConstants.S_OK);
  3928. }
  3929. #endregion
  3930. #region IPersist Members
  3931. public int GetClassID(out Guid clsid)
  3932. {
  3933. clsid = ProjectGuid;
  3934. return VSConstants.S_OK;
  3935. }
  3936. #endregion
  3937. #region IPersistFileFormat Members
  3938. int IPersistFileFormat.GetClassID(out Guid clsid)
  3939. {
  3940. clsid = ProjectGuid;
  3941. return VSConstants.S_OK;
  3942. }
  3943. public virtual int GetCurFile(out string name, out uint formatIndex)
  3944. {
  3945. name = filename;
  3946. formatIndex = 0;
  3947. return VSConstants.S_OK;
  3948. }
  3949. public virtual int GetFormatList(out string formatlist)
  3950. {
  3951. formatlist = String.Empty;
  3952. return VSConstants.S_OK;
  3953. }
  3954. public virtual int InitNew(uint formatIndex)
  3955. {
  3956. return VSConstants.S_OK;
  3957. }
  3958. public virtual int IsDirty(out int isDirty)
  3959. {
  3960. isDirty = 0;
  3961. if (buildProject.Xml.HasUnsavedChanges || IsProjectFileDirty)
  3962. {
  3963. isDirty = 1;
  3964. return VSConstants.S_OK;
  3965. }
  3966. isDirty = IsFlavorDirty();
  3967. return VSConstants.S_OK;
  3968. }
  3969. public virtual int Load(string fileName, uint mode, int readOnly)
  3970. {
  3971. filename = fileName;
  3972. Reload();
  3973. return VSConstants.S_OK;
  3974. }
  3975. public virtual int Save(string fileToBeSaved, int remember, uint formatIndex)
  3976. {
  3977. // The file name can be null. Then try to use the Url.
  3978. string tempFileToBeSaved = fileToBeSaved;
  3979. if (String.IsNullOrEmpty(tempFileToBeSaved) && !String.IsNullOrEmpty(Url))
  3980. {
  3981. tempFileToBeSaved = Url;
  3982. }
  3983. if (String.IsNullOrEmpty(tempFileToBeSaved))
  3984. {
  3985. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture),
  3986. "fileToBeSaved");
  3987. }
  3988. bool setProjectFileDirtyAfterSave = false;
  3989. if (remember == 0)
  3990. {
  3991. setProjectFileDirtyAfterSave = IsProjectFileDirty;
  3992. }
  3993. // Update the project with the latest flavor data (if needed)
  3994. PersistXMLFragments();
  3995. int result = VSConstants.S_OK;
  3996. bool saveAs = true;
  3997. if (NativeMethods.IsSamePath(tempFileToBeSaved, filename))
  3998. {
  3999. saveAs = false;
  4000. }
  4001. if (!saveAs)
  4002. {
  4003. SuspendFileChanges fileChanges = new SuspendFileChanges(Site, filename);
  4004. fileChanges.Suspend();
  4005. try
  4006. {
  4007. // Ensure the directory exist
  4008. string saveFolder = Path.GetDirectoryName(tempFileToBeSaved);
  4009. if (!Directory.Exists(saveFolder))
  4010. Directory.CreateDirectory(saveFolder);
  4011. // Save the project
  4012. buildProject.Save(tempFileToBeSaved);
  4013. SetProjectFileDirty(false);
  4014. }
  4015. finally
  4016. {
  4017. fileChanges.Resume();
  4018. }
  4019. }
  4020. else
  4021. {
  4022. result = SaveAs(tempFileToBeSaved);
  4023. if (result != VSConstants.OLE_E_PROMPTSAVECANCELLED)
  4024. {
  4025. ErrorHandler.ThrowOnFailure(result);
  4026. }
  4027. }
  4028. if (setProjectFileDirtyAfterSave)
  4029. {
  4030. SetProjectFileDirty(true);
  4031. }
  4032. return result;
  4033. }
  4034. public virtual int SaveCompleted(string filename)
  4035. {
  4036. // TODO: turn file watcher back on.
  4037. return VSConstants.S_OK;
  4038. }
  4039. protected int IsFlavorDirty()
  4040. {
  4041. int isDirty = 0;
  4042. // See if one of our flavor consider us dirty
  4043. IPersistXMLFragment outerHierarchy = InteropSafeIVsHierarchy as IPersistXMLFragment;
  4044. if (outerHierarchy != null)
  4045. {
  4046. // First check the project
  4047. ErrorHandler.ThrowOnFailure(outerHierarchy.IsFragmentDirty((uint)_PersistStorageType.PST_PROJECT_FILE,
  4048. out isDirty));
  4049. // While we don't yet support user files, our flavors might, so we will store that in the project file until then
  4050. // TODO: Refactor this code when we support user files
  4051. if (isDirty == 0)
  4052. ErrorHandler.ThrowOnFailure(outerHierarchy.IsFragmentDirty((uint)_PersistStorageType.PST_USER_FILE,
  4053. out isDirty));
  4054. }
  4055. if (isDirty == 0)
  4056. {
  4057. // Then look at the configurations
  4058. uint[] count = new uint[1];
  4059. int hr = ConfigProvider.GetCfgs(0, null, count, null);
  4060. if (ErrorHandler.Succeeded(hr) && count[0] > 0)
  4061. {
  4062. // We need to loop through the configurations
  4063. IVsCfg[] configs = new IVsCfg[count[0]];
  4064. hr = ConfigProvider.GetCfgs((uint)configs.Length, configs, count, null);
  4065. Debug.Assert(ErrorHandler.Succeeded(hr), "failed to retrieve configurations");
  4066. foreach (IVsCfg config in configs)
  4067. {
  4068. isDirty = ((ProjectConfig)config).IsFlavorDirty(_PersistStorageType.PST_PROJECT_FILE);
  4069. if (isDirty != 0)
  4070. break;
  4071. }
  4072. }
  4073. }
  4074. return isDirty;
  4075. }
  4076. #endregion
  4077. #region IVsProject3 Members
  4078. /// <summary>
  4079. /// Callback from the additem dialog. Deals with adding new and existing items
  4080. /// </summary>
  4081. public virtual int GetMkDocument(uint itemId, out string mkDoc)
  4082. {
  4083. mkDoc = null;
  4084. if (itemId == VSConstants.VSITEMID_SELECTION)
  4085. {
  4086. return VSConstants.E_UNEXPECTED;
  4087. }
  4088. HierarchyNode n = NodeFromItemId(itemId);
  4089. if (n == null)
  4090. {
  4091. return VSConstants.E_INVALIDARG;
  4092. }
  4093. mkDoc = n.GetMkDocument();
  4094. if (String.IsNullOrEmpty(mkDoc))
  4095. {
  4096. return VSConstants.E_FAIL;
  4097. }
  4098. return VSConstants.S_OK;
  4099. }
  4100. public virtual int AddItem(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen,
  4101. string[] files, IntPtr dlgOwner, VSADDRESULT[] result)
  4102. {
  4103. Guid empty = Guid.Empty;
  4104. return AddItemWithSpecific(itemIdLoc, op, itemName, filesToOpen, files, dlgOwner, 0, ref empty, null,
  4105. ref empty, result);
  4106. }
  4107. /// <summary>
  4108. /// Creates new items in a project, adds existing files to a project, or causes Add Item wizards to be run
  4109. /// </summary>
  4110. /// <param name="itemIdLoc"></param>
  4111. /// <param name="op"></param>
  4112. /// <param name="itemName"></param>
  4113. /// <param name="filesToOpen"></param>
  4114. /// <param name="files">
  4115. /// Array of file names.
  4116. /// If dwAddItemOperation is VSADDITEMOP_CLONEFILE the first item in the array is the name of the file to clone.
  4117. /// If dwAddItemOperation is VSADDITEMOP_OPENDIRECTORY, the first item in the array is the directory to open.
  4118. /// If dwAddItemOperation is VSADDITEMOP_RUNWIZARD, the first item is the name of the wizard to run,
  4119. /// and the second item is the file name the user supplied (same as itemName).
  4120. /// </param>
  4121. /// <param name="dlgOwner"></param>
  4122. /// <param name="editorFlags"></param>
  4123. /// <param name="editorType"></param>
  4124. /// <param name="physicalView"></param>
  4125. /// <param name="logicalView"></param>
  4126. /// <param name="result"></param>
  4127. /// <returns>S_OK if it succeeds </returns>
  4128. /// <remarks>The result array is initalized to failure.</remarks>
  4129. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"),
  4130. SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
  4131. public virtual int AddItemWithSpecific(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen,
  4132. string[] files, IntPtr dlgOwner, uint editorFlags, ref Guid editorType,
  4133. string physicalView, ref Guid logicalView, VSADDRESULT[] result)
  4134. {
  4135. if (files == null || result == null || files.Length == 0 || result.Length == 0)
  4136. {
  4137. return VSConstants.E_INVALIDARG;
  4138. }
  4139. // Locate the node to be the container node for the file(s) being added
  4140. // only projectnode or foldernode and file nodes are valid container nodes
  4141. // We need to locate the parent since the item wizard expects the parent to be passed.
  4142. HierarchyNode n = NodeFromItemId(itemIdLoc);
  4143. if (n == null)
  4144. {
  4145. return VSConstants.E_INVALIDARG;
  4146. }
  4147. while ((!(n is ProjectNode)) && (!(n is FolderNode)) && (!CanFileNodesHaveChilds || !(n is FileNode)))
  4148. {
  4149. n = n.Parent;
  4150. }
  4151. Debug.Assert(n != null,
  4152. "We should at this point have either a ProjectNode or FolderNode or a FileNode as a container for the new filenodes");
  4153. // handle link and runwizard operations at this point
  4154. switch (op)
  4155. {
  4156. case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE:
  4157. // we do not support this right now
  4158. throw new NotImplementedException("VSADDITEMOP_LINKTOFILE");
  4159. case VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD:
  4160. result[0] = RunWizard(n, itemName, files[0], dlgOwner);
  4161. return VSConstants.S_OK;
  4162. }
  4163. string[] actualFiles = new string[files.Length];
  4164. VSQUERYADDFILEFLAGS[] flags = GetQueryAddFileFlags(files);
  4165. string baseDir = GetBaseDirectoryForAddingFiles(n);
  4166. // If we did not get a directory for node that is the parent of the item then fail.
  4167. if (String.IsNullOrEmpty(baseDir))
  4168. {
  4169. return VSConstants.E_FAIL;
  4170. }
  4171. // Pre-calculates some paths that we can use when calling CanAddItems
  4172. List<string> filesToAdd = new List<string>();
  4173. for (int index = 0; index < files.Length; index++)
  4174. {
  4175. string newFileName = String.Empty;
  4176. string file = files[index];
  4177. switch (op)
  4178. {
  4179. case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE:
  4180. {
  4181. string fileName = Path.GetFileName(itemName);
  4182. newFileName = Path.Combine(baseDir, fileName);
  4183. }
  4184. break;
  4185. case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE:
  4186. {
  4187. string fileName = Path.GetFileName(file);
  4188. newFileName = Path.Combine(baseDir, fileName);
  4189. }
  4190. break;
  4191. }
  4192. filesToAdd.Add(newFileName);
  4193. }
  4194. // Ask tracker objects if we can add files
  4195. if (!tracker.CanAddItems(filesToAdd.ToArray(), flags))
  4196. {
  4197. // We were not allowed to add the files
  4198. return VSConstants.E_FAIL;
  4199. }
  4200. if (!ProjectMgr.QueryEditProjectFile(false))
  4201. {
  4202. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  4203. }
  4204. // Add the files to the hierarchy
  4205. int actualFilesAddedIndex = 0;
  4206. for (int index = 0; index < filesToAdd.Count; index++)
  4207. {
  4208. HierarchyNode child;
  4209. bool overwrite = false;
  4210. string newFileName = filesToAdd[index];
  4211. string file = files[index];
  4212. result[0] = VSADDRESULT.ADDRESULT_Failure;
  4213. child = FindChild(newFileName);
  4214. if (child != null)
  4215. {
  4216. // If the file to be added is an existing file part of the hierarchy then continue.
  4217. if (NativeMethods.IsSamePath(file, newFileName))
  4218. {
  4219. result[0] = VSADDRESULT.ADDRESULT_Cancel;
  4220. continue;
  4221. }
  4222. int canOverWriteExistingItem = CanOverwriteExistingItem(file, newFileName);
  4223. if (canOverWriteExistingItem == (int)OleConstants.OLECMDERR_E_CANCELED)
  4224. {
  4225. result[0] = VSADDRESULT.ADDRESULT_Cancel;
  4226. return canOverWriteExistingItem;
  4227. }
  4228. else if (canOverWriteExistingItem == VSConstants.S_OK)
  4229. {
  4230. overwrite = true;
  4231. }
  4232. else
  4233. {
  4234. return canOverWriteExistingItem;
  4235. }
  4236. }
  4237. // If the file to be added is not in the same path copy it.
  4238. if (NativeMethods.IsSamePath(file, newFileName) == false)
  4239. {
  4240. if (!overwrite && File.Exists(newFileName))
  4241. {
  4242. string message = String.Format(CultureInfo.CurrentCulture,
  4243. SR.GetString(SR.FileAlreadyExists, CultureInfo.CurrentUICulture),
  4244. newFileName);
  4245. string title = string.Empty;
  4246. OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
  4247. OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO;
  4248. OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
  4249. int messageboxResult = VsShellUtilities.ShowMessageBox(Site, title, message, icon, buttons,
  4250. defaultButton);
  4251. if (messageboxResult == NativeMethods.IDNO)
  4252. {
  4253. result[0] = VSADDRESULT.ADDRESULT_Cancel;
  4254. return (int)OleConstants.OLECMDERR_E_CANCELED;
  4255. }
  4256. }
  4257. // Copy the file to the correct location.
  4258. // We will suppress the file change events to be triggered to this item, since we are going to copy over the existing file and thus we will trigger a file change event.
  4259. // We do not want the filechange event to ocur in this case, similar that we do not want a file change event to occur when saving a file.
  4260. IVsFileChangeEx fileChange = site.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx;
  4261. if (fileChange == null)
  4262. {
  4263. throw new InvalidOperationException();
  4264. }
  4265. try
  4266. {
  4267. ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 1));
  4268. if (op == VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE)
  4269. {
  4270. AddFileFromTemplate(file, newFileName);
  4271. }
  4272. else
  4273. {
  4274. PackageUtilities.CopyUrlToLocal(new Uri(file), newFileName);
  4275. }
  4276. }
  4277. finally
  4278. {
  4279. ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 0));
  4280. }
  4281. }
  4282. if (overwrite)
  4283. {
  4284. OverwriteExistingItem(child);
  4285. }
  4286. else
  4287. {
  4288. //Add new filenode/dependentfilenode
  4289. AddNewFileNodeToHierarchy(n, newFileName);
  4290. }
  4291. result[0] = VSADDRESULT.ADDRESULT_Success;
  4292. actualFiles[actualFilesAddedIndex++] = newFileName;
  4293. }
  4294. // Notify listeners that items were appended.
  4295. if (actualFilesAddedIndex > 0)
  4296. n.OnItemsAppended(n);
  4297. //Open files if this was requested through the editorFlags
  4298. bool openFiles = (editorFlags & (uint)__VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_DoOpen) != 0;
  4299. if (openFiles && actualFiles.Length <= filesToOpen)
  4300. {
  4301. for (int i = 0; i < filesToOpen; i++)
  4302. {
  4303. if (!String.IsNullOrEmpty(actualFiles[i]))
  4304. {
  4305. string name = actualFiles[i];
  4306. HierarchyNode child = FindChild(name);
  4307. Debug.Assert(child != null, "We should have been able to find the new element in the hierarchy");
  4308. if (child != null)
  4309. {
  4310. IVsWindowFrame frame;
  4311. if (editorType == Guid.Empty)
  4312. {
  4313. Guid view = Guid.Empty;
  4314. ErrorHandler.ThrowOnFailure(OpenItem(child.ID, ref view, IntPtr.Zero, out frame));
  4315. }
  4316. else
  4317. {
  4318. ErrorHandler.ThrowOnFailure(OpenItemWithSpecific(child.ID, editorFlags, ref editorType,
  4319. physicalView, ref logicalView,
  4320. IntPtr.Zero, out frame));
  4321. }
  4322. // Show the window frame in the UI and make it the active window
  4323. if (frame != null)
  4324. {
  4325. ErrorHandler.ThrowOnFailure(frame.Show());
  4326. }
  4327. }
  4328. }
  4329. }
  4330. }
  4331. return VSConstants.S_OK;
  4332. }
  4333. /// <summary>
  4334. /// for now used by add folder. Called on the ROOT, as only the project should need
  4335. /// to implement this.
  4336. /// for folders, called with parent folder, blank extension and blank suggested root
  4337. /// </summary>
  4338. public virtual int GenerateUniqueItemName(uint itemIdLoc, string ext, string suggestedRoot, out string itemName)
  4339. {
  4340. string rootName = String.Empty;
  4341. string extToUse = String.Empty;
  4342. itemName = String.Empty;
  4343. //force new items to have a number
  4344. int cb = 1;
  4345. bool found = false;
  4346. bool fFolderCase = false;
  4347. HierarchyNode parent = NodeFromItemId(itemIdLoc);
  4348. if (!String.IsNullOrEmpty(ext))
  4349. {
  4350. extToUse = ext.Trim();
  4351. }
  4352. if (!String.IsNullOrEmpty(suggestedRoot))
  4353. {
  4354. suggestedRoot = suggestedRoot.Trim();
  4355. }
  4356. if (suggestedRoot == null || suggestedRoot.Length == 0)
  4357. {
  4358. // foldercase, we assume...
  4359. suggestedRoot = "NewFolder";
  4360. fFolderCase = true;
  4361. }
  4362. while (!found)
  4363. {
  4364. rootName = suggestedRoot;
  4365. if (cb > 0)
  4366. rootName += cb.ToString(CultureInfo.CurrentCulture);
  4367. if (extToUse.Length > 0)
  4368. {
  4369. rootName += extToUse;
  4370. }
  4371. cb++;
  4372. found = true;
  4373. for (HierarchyNode n = parent.FirstChild; n != null; n = n.NextSibling)
  4374. {
  4375. if (rootName == n.GetEditLabel())
  4376. {
  4377. found = false;
  4378. break;
  4379. }
  4380. //if parent is a folder, we need the whole url
  4381. string parentFolder = parent.Url;
  4382. if (parent is ProjectNode)
  4383. parentFolder = Path.GetDirectoryName(parent.Url);
  4384. string checkFile = Path.Combine(parentFolder, rootName);
  4385. if (fFolderCase)
  4386. {
  4387. if (Directory.Exists(checkFile))
  4388. {
  4389. found = false;
  4390. break;
  4391. }
  4392. }
  4393. else
  4394. {
  4395. if (File.Exists(checkFile))
  4396. {
  4397. found = false;
  4398. break;
  4399. }
  4400. }
  4401. }
  4402. }
  4403. itemName = rootName;
  4404. return VSConstants.S_OK;
  4405. }
  4406. public virtual int GetItemContext(uint itemId, out IOleServiceProvider psp)
  4407. {
  4408. CCITracing.TraceCall();
  4409. psp = null;
  4410. HierarchyNode child = NodeFromItemId(itemId);
  4411. if (child != null)
  4412. {
  4413. psp = child.OleServiceProvider;
  4414. }
  4415. return VSConstants.S_OK;
  4416. }
  4417. public virtual int IsDocumentInProject(string mkDoc, out int found, VSDOCUMENTPRIORITY[] pri, out uint itemId)
  4418. {
  4419. CCITracing.TraceCall();
  4420. if (pri != null && pri.Length >= 1)
  4421. {
  4422. pri[0] = VSDOCUMENTPRIORITY.DP_Unsupported;
  4423. }
  4424. found = 0;
  4425. itemId = 0;
  4426. // If it is the project file just return.
  4427. if (NativeMethods.IsSamePath(mkDoc, GetMkDocument()))
  4428. {
  4429. found = 1;
  4430. itemId = VSConstants.VSITEMID_ROOT;
  4431. }
  4432. else
  4433. {
  4434. HierarchyNode child = FindChild(mkDoc);
  4435. if (child != null)
  4436. {
  4437. found = 1;
  4438. itemId = child.ID;
  4439. }
  4440. }
  4441. if (found == 1)
  4442. {
  4443. if (pri != null && pri.Length >= 1)
  4444. {
  4445. pri[0] = VSDOCUMENTPRIORITY.DP_Standard;
  4446. }
  4447. }
  4448. return VSConstants.S_OK;
  4449. }
  4450. public virtual int OpenItem(uint itemId, ref Guid logicalView, IntPtr punkDocDataExisting,
  4451. out IVsWindowFrame frame)
  4452. {
  4453. // Init output params
  4454. frame = null;
  4455. HierarchyNode n = NodeFromItemId(itemId);
  4456. if (n == null)
  4457. {
  4458. throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture),
  4459. "itemId");
  4460. }
  4461. // Delegate to the document manager object that knows how to open the item
  4462. DocumentManager documentManager = n.GetDocumentManager();
  4463. if (documentManager != null)
  4464. {
  4465. return documentManager.Open(ref logicalView, punkDocDataExisting, out frame,
  4466. WindowFrameShowAction.DoNotShow);
  4467. }
  4468. // This node does not have an associated document manager and we must fail
  4469. return VSConstants.E_FAIL;
  4470. }
  4471. public virtual int OpenItemWithSpecific(uint itemId, uint editorFlags, ref Guid editorType, string physicalView,
  4472. ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame)
  4473. {
  4474. // Init output params
  4475. frame = null;
  4476. HierarchyNode n = NodeFromItemId(itemId);
  4477. if (n == null)
  4478. {
  4479. throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture),
  4480. "itemId");
  4481. }
  4482. // Delegate to the document manager object that knows how to open the item
  4483. DocumentManager documentManager = n.GetDocumentManager();
  4484. if (documentManager != null)
  4485. {
  4486. return documentManager.OpenWithSpecific(editorFlags, ref editorType, physicalView, ref logicalView,
  4487. docDataExisting, out frame, WindowFrameShowAction.DoNotShow);
  4488. }
  4489. // This node does not have an associated document manager and we must fail
  4490. return VSConstants.E_FAIL;
  4491. }
  4492. public virtual int RemoveItem(uint reserved, uint itemId, out int result)
  4493. {
  4494. HierarchyNode n = NodeFromItemId(itemId);
  4495. if (n == null)
  4496. {
  4497. throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture),
  4498. "itemId");
  4499. }
  4500. n.Remove(true);
  4501. result = 1;
  4502. return VSConstants.S_OK;
  4503. }
  4504. public virtual int ReopenItem(uint itemId, ref Guid editorType, string physicalView, ref Guid logicalView,
  4505. IntPtr docDataExisting, out IVsWindowFrame frame)
  4506. {
  4507. // Init output params
  4508. frame = null;
  4509. HierarchyNode n = NodeFromItemId(itemId);
  4510. if (n == null)
  4511. {
  4512. throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture),
  4513. "itemId");
  4514. }
  4515. // Delegate to the document manager object that knows how to open the item
  4516. DocumentManager documentManager = n.GetDocumentManager();
  4517. if (documentManager != null)
  4518. {
  4519. return documentManager.OpenWithSpecific(0, ref editorType, physicalView, ref logicalView,
  4520. docDataExisting, out frame, WindowFrameShowAction.DoNotShow);
  4521. }
  4522. // This node does not have an associated document manager and we must fail
  4523. return VSConstants.E_FAIL;
  4524. }
  4525. /// <summary>
  4526. /// Implements IVsProject3::TransferItem
  4527. /// This function is called when an open miscellaneous file is being transferred
  4528. /// to our project. The sequence is for the shell to call AddItemWithSpecific and
  4529. /// then use TransferItem to transfer the open document to our project.
  4530. /// </summary>
  4531. /// <param name="oldMkDoc">Old document name</param>
  4532. /// <param name="newMkDoc">New document name</param>
  4533. /// <param name="frame">Optional frame if the document is open</param>
  4534. /// <returns></returns>
  4535. public virtual int TransferItem(string oldMkDoc, string newMkDoc, IVsWindowFrame frame)
  4536. {
  4537. // Fail if hierarchy already closed
  4538. if (ProjectMgr == null || ProjectMgr.IsClosed)
  4539. {
  4540. return VSConstants.E_FAIL;
  4541. }
  4542. //Fail if the document names passed are null.
  4543. if (oldMkDoc == null || newMkDoc == null)
  4544. return VSConstants.E_INVALIDARG;
  4545. int hr = VSConstants.S_OK;
  4546. VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1];
  4547. uint itemid = VSConstants.VSITEMID_NIL;
  4548. uint cookie = 0;
  4549. uint grfFlags = 0;
  4550. IVsRunningDocumentTable pRdt = GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable;
  4551. if (pRdt == null)
  4552. return VSConstants.E_ABORT;
  4553. string doc;
  4554. int found;
  4555. IVsHierarchy pHier;
  4556. uint id, readLocks, editLocks;
  4557. IntPtr docdataForCookiePtr = IntPtr.Zero;
  4558. IntPtr docDataPtr = IntPtr.Zero;
  4559. IntPtr hierPtr = IntPtr.Zero;
  4560. // We get the document from the running doc table so that we can see if it is transient
  4561. try
  4562. {
  4563. ErrorHandler.ThrowOnFailure(pRdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldMkDoc, out pHier,
  4564. out id, out docdataForCookiePtr, out cookie));
  4565. }
  4566. finally
  4567. {
  4568. if (docdataForCookiePtr != IntPtr.Zero)
  4569. Marshal.Release(docdataForCookiePtr);
  4570. }
  4571. //Get the document info
  4572. try
  4573. {
  4574. ErrorHandler.ThrowOnFailure(pRdt.GetDocumentInfo(cookie, out grfFlags, out readLocks, out editLocks,
  4575. out doc, out pHier, out id, out docDataPtr));
  4576. }
  4577. finally
  4578. {
  4579. if (docDataPtr != IntPtr.Zero)
  4580. Marshal.Release(docDataPtr);
  4581. }
  4582. // Now see if the document is in the project. If not, we fail
  4583. try
  4584. {
  4585. ErrorHandler.ThrowOnFailure(IsDocumentInProject(newMkDoc, out found, priority, out itemid));
  4586. Debug.Assert(itemid != VSConstants.VSITEMID_NIL && itemid != VSConstants.VSITEMID_ROOT);
  4587. hierPtr = Marshal.GetComInterfaceForObject(this, typeof(IVsUIHierarchy));
  4588. // Now rename the document
  4589. ErrorHandler.ThrowOnFailure(pRdt.RenameDocument(oldMkDoc, newMkDoc, hierPtr, itemid));
  4590. }
  4591. finally
  4592. {
  4593. if (hierPtr != IntPtr.Zero)
  4594. Marshal.Release(hierPtr);
  4595. }
  4596. //Change the caption if we are passed a window frame
  4597. if (frame != null)
  4598. {
  4599. string caption = "%2";
  4600. hr = frame.SetProperty((int)(__VSFPROPID.VSFPROPID_OwnerCaption), caption);
  4601. }
  4602. return hr;
  4603. }
  4604. #endregion
  4605. #region IVsProjectBuidSystem Members
  4606. public virtual int SetHostObject(string targetName, string taskName, object hostObject)
  4607. {
  4608. Debug.Assert(targetName != null && taskName != null && buildProject != null && buildProject.Targets != null);
  4609. if (targetName == null || taskName == null || buildProject == null || buildProject.Targets == null)
  4610. {
  4611. return VSConstants.E_INVALIDARG;
  4612. }
  4613. buildProject.ProjectCollection.HostServices.RegisterHostObject(buildProject.FullPath, targetName, taskName,
  4614. (ITaskHost)hostObject);
  4615. return VSConstants.S_OK;
  4616. }
  4617. public int BuildTarget(string targetName, out bool success)
  4618. {
  4619. success = false;
  4620. MSBuildResult result = Build(targetName);
  4621. if (result == MSBuildResult.Successful)
  4622. {
  4623. success = true;
  4624. }
  4625. return VSConstants.S_OK;
  4626. }
  4627. public virtual int CancelBatchEdit()
  4628. {
  4629. return VSConstants.E_NOTIMPL;
  4630. }
  4631. public virtual int EndBatchEdit()
  4632. {
  4633. return VSConstants.E_NOTIMPL;
  4634. }
  4635. public virtual int StartBatchEdit()
  4636. {
  4637. return VSConstants.E_NOTIMPL;
  4638. }
  4639. /// <summary>
  4640. /// Used to determine the kind of build system, in VS 2005 there's only one defined kind: MSBuild
  4641. /// </summary>
  4642. /// <param name="kind"></param>
  4643. /// <returns></returns>
  4644. public virtual int GetBuildSystemKind(out uint kind)
  4645. {
  4646. kind = (uint)_BuildSystemKindFlags2.BSK_MSBUILD_VS10;
  4647. return VSConstants.S_OK;
  4648. }
  4649. #endregion
  4650. #region IVsComponentUser methods
  4651. /// <summary>
  4652. /// Add Components to the Project.
  4653. /// Used by the environment to add components specified by the user in the Component Selector dialog
  4654. /// to the specified project
  4655. /// </summary>
  4656. /// <param name="dwAddCompOperation">The component operation to be performed.</param>
  4657. /// <param name="cComponents">Number of components to be added</param>
  4658. /// <param name="rgpcsdComponents">array of component selector data</param>
  4659. /// <param name="hwndDialog">Handle to the component picker dialog</param>
  4660. /// <param name="pResult">Result to be returned to the caller</param>
  4661. public virtual int AddComponent(VSADDCOMPOPERATION dwAddCompOperation, uint cComponents,
  4662. IntPtr[] rgpcsdComponents, IntPtr hwndDialog, VSADDCOMPRESULT[] pResult)
  4663. {
  4664. if (rgpcsdComponents == null || pResult == null)
  4665. {
  4666. return VSConstants.E_FAIL;
  4667. }
  4668. //initalize the out parameter
  4669. pResult[0] = VSADDCOMPRESULT.ADDCOMPRESULT_Success;
  4670. IReferenceContainer references = GetReferenceContainer();
  4671. if (null == references)
  4672. {
  4673. // This project does not support references or the reference container was not created.
  4674. // In both cases this operation is not supported.
  4675. return VSConstants.E_NOTIMPL;
  4676. }
  4677. for (int cCount = 0; cCount < cComponents; cCount++)
  4678. {
  4679. VSCOMPONENTSELECTORDATA selectorData = new VSCOMPONENTSELECTORDATA();
  4680. IntPtr ptr = rgpcsdComponents[cCount];
  4681. selectorData = (VSCOMPONENTSELECTORDATA)Marshal.PtrToStructure(ptr, typeof(VSCOMPONENTSELECTORDATA));
  4682. if (null == references.AddReferenceFromSelectorData(selectorData))
  4683. {
  4684. //Skip further proccessing since a reference has to be added
  4685. pResult[0] = VSADDCOMPRESULT.ADDCOMPRESULT_Failure;
  4686. return VSConstants.S_OK;
  4687. }
  4688. }
  4689. return VSConstants.S_OK;
  4690. }
  4691. #endregion
  4692. #region IVsDependencyProvider Members
  4693. public int EnumDependencies(out IVsEnumDependencies enumDependencies)
  4694. {
  4695. enumDependencies = new EnumDependencies(buildDependencyList);
  4696. return VSConstants.S_OK;
  4697. }
  4698. public int OpenDependency(string szDependencyCanonicalName, out IVsDependency dependency)
  4699. {
  4700. dependency = null;
  4701. return VSConstants.S_OK;
  4702. }
  4703. #endregion
  4704. #region IVsSccProject2 Members
  4705. /// <summary>
  4706. /// This method is called to determine which files should be placed under source control for a given VSITEMID within this hierarchy.
  4707. /// </summary>
  4708. /// <param name="itemid">Identifier for the VSITEMID being queried.</param>
  4709. /// <param name="stringsOut">Pointer to an array of CALPOLESTR strings containing the file names for this item.</param>
  4710. /// <param name="flagsOut">Pointer to a CADWORD array of flags stored in DWORDs indicating that some of the files have special behaviors.</param>
  4711. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
  4712. public virtual int GetSccFiles(uint itemid, CALPOLESTR[] stringsOut, CADWORD[] flagsOut)
  4713. {
  4714. if (itemid == VSConstants.VSITEMID_SELECTION)
  4715. {
  4716. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
  4717. }
  4718. HierarchyNode n = NodeFromItemId(itemid);
  4719. if (n == null)
  4720. {
  4721. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
  4722. }
  4723. List<string> files = new List<string>();
  4724. List<tagVsSccFilesFlags> flags = new List<tagVsSccFilesFlags>();
  4725. n.GetSccFiles(files, flags);
  4726. if (stringsOut != null && stringsOut.Length > 0)
  4727. {
  4728. stringsOut[0] = Utilities.CreateCALPOLESTR(files);
  4729. }
  4730. if (flagsOut != null && flagsOut.Length > 0)
  4731. {
  4732. flagsOut[0] = Utilities.CreateCADWORD(flags);
  4733. }
  4734. return VSConstants.S_OK;
  4735. }
  4736. /// <summary>
  4737. /// This method is called to discover special (hidden files) associated with a given VSITEMID within this hierarchy.
  4738. /// </summary>
  4739. /// <param name="itemid">Identifier for the VSITEMID being queried.</param>
  4740. /// <param name="sccFile">One of the files associated with the node</param>
  4741. /// <param name="stringsOut">Pointer to an array of CALPOLESTR strings containing the file names for this item.</param>
  4742. /// <param name="flagsOut">Pointer to a CADWORD array of flags stored in DWORDs indicating that some of the files have special behaviors.</param>
  4743. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
  4744. /// <remarks>This method is called to discover any special or hidden files associated with an item in the project hierarchy. It is called when GetSccFiles returns with the SFF_HasSpecialFiles flag set for any of the files associated with the node.</remarks>
  4745. public virtual int GetSccSpecialFiles(uint itemid, string sccFile, CALPOLESTR[] stringsOut, CADWORD[] flagsOut)
  4746. {
  4747. if (itemid == VSConstants.VSITEMID_SELECTION)
  4748. {
  4749. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
  4750. }
  4751. HierarchyNode n = NodeFromItemId(itemid);
  4752. if (n == null)
  4753. {
  4754. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
  4755. }
  4756. List<string> files = new List<string>();
  4757. List<tagVsSccFilesFlags> flags = new List<tagVsSccFilesFlags>();
  4758. n.GetSccSpecialFiles(sccFile, files, flags);
  4759. if (stringsOut != null && stringsOut.Length > 0)
  4760. {
  4761. stringsOut[0] = Utilities.CreateCALPOLESTR(files);
  4762. }
  4763. if (flagsOut != null && flagsOut.Length > 0)
  4764. {
  4765. flagsOut[0] = Utilities.CreateCADWORD(flags);
  4766. }
  4767. return VSConstants.S_OK;
  4768. }
  4769. /// <summary>
  4770. /// This method is called by the source control portion of the environment to inform the project of changes to the source control glyph on various nodes.
  4771. /// </summary>
  4772. /// <param name="affectedNodes">Count of changed nodes.</param>
  4773. /// <param name="itemidAffectedNodes">An array of VSITEMID identifiers of the changed nodes.</param>
  4774. /// <param name="newGlyphs">An array of VsStateIcon glyphs representing the new state of the corresponding item in rgitemidAffectedNodes.</param>
  4775. /// <param name="newSccStatus">An array of status flags from SccStatus corresponding to rgitemidAffectedNodes. </param>
  4776. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
  4777. public virtual int SccGlyphChanged(int affectedNodes, uint[] itemidAffectedNodes, VsStateIcon[] newGlyphs,
  4778. uint[] newSccStatus)
  4779. {
  4780. // if all the paramaters are null adn the count is 0, it means scc wants us to updated everything
  4781. if (affectedNodes == 0 && itemidAffectedNodes == null && newGlyphs == null && newSccStatus == null)
  4782. {
  4783. ReDraw(UIHierarchyElement.SccState);
  4784. UpdateSccStateIcons();
  4785. }
  4786. else if (affectedNodes > 0 && itemidAffectedNodes != null && newGlyphs != null && newSccStatus != null)
  4787. {
  4788. for (int i = 0; i < affectedNodes; i++)
  4789. {
  4790. HierarchyNode n = NodeFromItemId(itemidAffectedNodes[i]);
  4791. if (n == null)
  4792. {
  4793. throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture),
  4794. "itemidAffectedNodes");
  4795. }
  4796. n.ReDraw(UIHierarchyElement.SccState);
  4797. }
  4798. }
  4799. return VSConstants.S_OK;
  4800. }
  4801. /// <summary>
  4802. /// This method is called by the source control portion of the environment when a project is initially added to source control, or to change some of the project's settings.
  4803. /// </summary>
  4804. /// <param name="sccProjectName">String, opaque to the project, that identifies the project location on the server. Persist this string in the project file. </param>
  4805. /// <param name="sccLocalPath">String, opaque to the project, that identifies the path to the server. Persist this string in the project file.</param>
  4806. /// <param name="sccAuxPath">String, opaque to the project, that identifies the local path to the project. Persist this string in the project file.</param>
  4807. /// <param name="sccProvider">String, opaque to the project, that identifies the source control package. Persist this string in the project file.</param>
  4808. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  4809. public virtual int SetSccLocation(string sccProjectName, string sccAuxPath, string sccLocalPath,
  4810. string sccProvider)
  4811. {
  4812. if (IsSccDisabled)
  4813. {
  4814. throw new NotImplementedException();
  4815. }
  4816. if (sccProjectName == null)
  4817. {
  4818. throw new ArgumentNullException("sccProjectName");
  4819. }
  4820. if (sccAuxPath == null)
  4821. {
  4822. throw new ArgumentNullException("sccAuxPath");
  4823. }
  4824. if (sccLocalPath == null)
  4825. {
  4826. throw new ArgumentNullException("sccLocalPath");
  4827. }
  4828. if (sccProvider == null)
  4829. {
  4830. throw new ArgumentNullException("sccProvider");
  4831. }
  4832. // Save our settings (returns true if something changed)
  4833. if (!SetSccSettings(sccProjectName, sccLocalPath, sccAuxPath, sccProvider))
  4834. {
  4835. return VSConstants.S_OK;
  4836. }
  4837. bool unbinding = (sccProjectName.Length == 0 && sccProvider.Length == 0);
  4838. if (unbinding || QueryEditProjectFile(false))
  4839. {
  4840. buildProject.SetProperty(ProjectFileConstants.SccProjectName, sccProjectName);
  4841. buildProject.SetProperty(ProjectFileConstants.SccProvider, sccProvider);
  4842. buildProject.SetProperty(ProjectFileConstants.SccAuxPath, sccAuxPath);
  4843. buildProject.SetProperty(ProjectFileConstants.SccLocalPath, sccLocalPath);
  4844. }
  4845. isRegisteredWithScc = true;
  4846. return VSConstants.S_OK;
  4847. }
  4848. #endregion
  4849. #region IVsProjectSpecialFiles Members
  4850. /// <summary>
  4851. /// Allows you to query the project for special files and optionally create them.
  4852. /// </summary>
  4853. /// <param name="fileId">__PSFFILEID of the file</param>
  4854. /// <param name="flags">__PSFFLAGS flags for the file</param>
  4855. /// <param name="itemid">The itemid of the node in the hierarchy</param>
  4856. /// <param name="fileName">The file name of the special file.</param>
  4857. /// <returns></returns>
  4858. public virtual int GetFile(int fileId, uint flags, out uint itemid, out string fileName)
  4859. {
  4860. itemid = VSConstants.VSITEMID_NIL;
  4861. fileName = String.Empty;
  4862. // We need to return S_OK, otherwise the property page tabs will not be shown.
  4863. return VSConstants.E_NOTIMPL;
  4864. }
  4865. #endregion
  4866. #region IAggregatedHierarchy Members
  4867. /// <summary>
  4868. /// Get the inner object of an aggregated hierarchy
  4869. /// </summary>
  4870. /// <returns>A HierarchyNode</returns>
  4871. public virtual HierarchyNode GetInner()
  4872. {
  4873. return this;
  4874. }
  4875. #endregion
  4876. #region IBuildDependencyUpdate Members
  4877. public virtual IVsBuildDependency[] BuildDependencies
  4878. {
  4879. get { return buildDependencyList.ToArray(); }
  4880. }
  4881. public virtual void AddBuildDependency(IVsBuildDependency dependency)
  4882. {
  4883. if (isClosed || dependency == null)
  4884. {
  4885. return;
  4886. }
  4887. if (!buildDependencyList.Contains(dependency))
  4888. {
  4889. buildDependencyList.Add(dependency);
  4890. }
  4891. }
  4892. public virtual void RemoveBuildDependency(IVsBuildDependency dependency)
  4893. {
  4894. if (isClosed || dependency == null)
  4895. {
  4896. return;
  4897. }
  4898. if (buildDependencyList.Contains(dependency))
  4899. {
  4900. buildDependencyList.Remove(dependency);
  4901. }
  4902. }
  4903. #endregion
  4904. #region IReferenceDataProvider Members
  4905. /// <summary>
  4906. /// Returns the reference container node.
  4907. /// </summary>
  4908. /// <returns></returns>
  4909. public IReferenceContainer GetReferenceContainer()
  4910. {
  4911. return FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as IReferenceContainer;
  4912. }
  4913. #endregion
  4914. #region IProjectEventsListener Members
  4915. public bool IsProjectEventsListener
  4916. {
  4917. get { return isProjectEventsListener; }
  4918. set { isProjectEventsListener = value; }
  4919. }
  4920. #endregion
  4921. #region IProjectEventsProvider Members
  4922. /// <summary>
  4923. /// Defines the provider for the project events
  4924. /// </summary>
  4925. IProjectEvents IProjectEventsProvider.ProjectEventsProvider
  4926. {
  4927. get { return projectEventsProvider; }
  4928. set
  4929. {
  4930. if (null != projectEventsProvider)
  4931. {
  4932. projectEventsProvider.AfterProjectFileOpened -= OnAfterProjectOpen;
  4933. }
  4934. projectEventsProvider = value;
  4935. if (null != projectEventsProvider)
  4936. {
  4937. projectEventsProvider.AfterProjectFileOpened += OnAfterProjectOpen;
  4938. }
  4939. }
  4940. }
  4941. #endregion
  4942. #region IVsAggregatableProject Members
  4943. /// <summary>
  4944. /// Retrieve the list of project GUIDs that are aggregated together to make this project.
  4945. /// </summary>
  4946. /// <param name="projectTypeGuids">Semi colon separated list of Guids. Typically, the last GUID would be the GUID of the base project factory</param>
  4947. /// <returns>HResult</returns>
  4948. public int GetAggregateProjectTypeGuids(out string projectTypeGuids)
  4949. {
  4950. projectTypeGuids = GetProjectProperty(ProjectFileConstants.ProjectTypeGuids);
  4951. // In case someone manually removed this from our project file, default to our project without flavors
  4952. if (String.IsNullOrEmpty(projectTypeGuids))
  4953. projectTypeGuids = ProjectGuid.ToString("B");
  4954. return VSConstants.S_OK;
  4955. }
  4956. /// <summary>
  4957. /// This is where the initialization occurs.
  4958. /// </summary>
  4959. public virtual int InitializeForOuter(string filename, string location, string name, uint flags, ref Guid iid,
  4960. out IntPtr projectPointer, out int canceled)
  4961. {
  4962. canceled = 0;
  4963. projectPointer = IntPtr.Zero;
  4964. // Initialize the interop-safe versions of this node's implementations of various VS interfaces,
  4965. // which point to the outer object. The project node itself should never be passed to unmanaged
  4966. // code -- we should always use these properties instead.
  4967. InteropSafeIVsHierarchy = Utilities.GetOuterAs<IVsHierarchy>(this);
  4968. InteropSafeIVsUIHierarchy = Utilities.GetOuterAs<IVsUIHierarchy>(this);
  4969. InteropSafeIVsProject3 = Utilities.GetOuterAs<IVsProject3>(this);
  4970. InteropSafeIVsSccProject2 = Utilities.GetOuterAs<IVsSccProject2>(this);
  4971. InteropSafeIVsUIHierWinClipboardHelperEvents = Utilities.GetOuterAs<IVsUIHierWinClipboardHelperEvents>(this);
  4972. InteropSafeIVsComponentUser = Utilities.GetOuterAs<IVsComponentUser>(this);
  4973. // Initialize the project
  4974. Load(filename, location, name, flags, ref iid, out canceled);
  4975. if (canceled != 1)
  4976. {
  4977. // Set ourself as the project
  4978. return Marshal.QueryInterface(Marshal.GetIUnknownForObject(this), ref iid, out projectPointer);
  4979. }
  4980. return VSConstants.OLE_E_PROMPTSAVECANCELLED;
  4981. }
  4982. /// <summary>
  4983. /// This is called after the project is done initializing the different layer of the aggregations
  4984. /// </summary>
  4985. /// <returns>HResult</returns>
  4986. public virtual int OnAggregationComplete()
  4987. {
  4988. return VSConstants.S_OK;
  4989. }
  4990. /// <summary>
  4991. /// Set the list of GUIDs that are aggregated together to create this project.
  4992. /// </summary>
  4993. /// <param name="projectTypeGuids">Semi-colon separated list of GUIDs, the last one is usually the project factory of the base project factory</param>
  4994. /// <returns>HResult</returns>
  4995. public int SetAggregateProjectTypeGuids(string projectTypeGuids)
  4996. {
  4997. SetProjectProperty(ProjectFileConstants.ProjectTypeGuids, projectTypeGuids);
  4998. return VSConstants.S_OK;
  4999. }
  5000. /// <summary>
  5001. /// We are always the inner most part of the aggregation
  5002. /// and as such we don't support setting an inner project
  5003. /// </summary>
  5004. public int SetInnerProject(object innerProject)
  5005. {
  5006. return VSConstants.E_NOTIMPL;
  5007. }
  5008. #endregion
  5009. #region IVsProjectFlavorCfgProvider Members
  5010. int IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg,
  5011. out IVsProjectFlavorCfg ppFlavorCfg)
  5012. {
  5013. // Our config object is also our IVsProjectFlavorCfg object
  5014. ppFlavorCfg = pBaseProjectCfg as IVsProjectFlavorCfg;
  5015. return VSConstants.S_OK;
  5016. }
  5017. #endregion
  5018. #region IVsBuildPropertyStorage Members
  5019. /// <summary>
  5020. /// Get the property of an item
  5021. /// </summary>
  5022. /// <param name="item">ItemID</param>
  5023. /// <param name="attributeName">Name of the property</param>
  5024. /// <param name="attributeValue">Value of the property (out parameter)</param>
  5025. /// <returns>HRESULT</returns>
  5026. int IVsBuildPropertyStorage.GetItemAttribute(uint item, string attributeName, out string attributeValue)
  5027. {
  5028. attributeValue = null;
  5029. HierarchyNode node = NodeFromItemId(item);
  5030. if (node == null)
  5031. throw new ArgumentException("Invalid item id", "item");
  5032. attributeValue = node.ItemNode.GetMetadata(attributeName);
  5033. return VSConstants.S_OK;
  5034. }
  5035. /// <summary>
  5036. /// Get the value of the property in the project file
  5037. /// </summary>
  5038. /// <param name="propertyName">Name of the property to remove</param>
  5039. /// <param name="configName">Configuration for which to remove the property</param>
  5040. /// <param name="storage">Project or user file (_PersistStorageType)</param>
  5041. /// <param name="propertyValue">Value of the property (out parameter)</param>
  5042. /// <returns>HRESULT</returns>
  5043. int IVsBuildPropertyStorage.GetPropertyValue(string propertyName, string configName, uint storage,
  5044. out string propertyValue)
  5045. {
  5046. // TODO: when adding support for User files, we need to update this method
  5047. propertyValue = null;
  5048. if (string.IsNullOrEmpty(configName))
  5049. {
  5050. propertyValue = GetProjectProperty(propertyName);
  5051. }
  5052. else
  5053. {
  5054. IVsCfg configurationInterface;
  5055. ErrorHandler.ThrowOnFailure(ConfigProvider.GetCfgOfName(configName, string.Empty,
  5056. out configurationInterface));
  5057. ProjectConfig config = (ProjectConfig)configurationInterface;
  5058. propertyValue = config.GetConfigurationProperty(propertyName, true);
  5059. }
  5060. return VSConstants.S_OK;
  5061. }
  5062. /// <summary>
  5063. /// Delete a property
  5064. /// In our case this simply mean defining it as null
  5065. /// </summary>
  5066. /// <param name="propertyName">Name of the property to remove</param>
  5067. /// <param name="configName">Configuration for which to remove the property</param>
  5068. /// <param name="storage">Project or user file (_PersistStorageType)</param>
  5069. /// <returns>HRESULT</returns>
  5070. int IVsBuildPropertyStorage.RemoveProperty(string propertyName, string configName, uint storage)
  5071. {
  5072. return ((IVsBuildPropertyStorage)this).SetPropertyValue(propertyName, configName, storage, null);
  5073. }
  5074. /// <summary>
  5075. /// Set a property on an item
  5076. /// </summary>
  5077. /// <param name="item">ItemID</param>
  5078. /// <param name="attributeName">Name of the property</param>
  5079. /// <param name="attributeValue">New value for the property</param>
  5080. /// <returns>HRESULT</returns>
  5081. int IVsBuildPropertyStorage.SetItemAttribute(uint item, string attributeName, string attributeValue)
  5082. {
  5083. HierarchyNode node = NodeFromItemId(item);
  5084. if (node == null)
  5085. throw new ArgumentException("Invalid item id", "item");
  5086. node.ItemNode.SetMetadata(attributeName, attributeValue);
  5087. return VSConstants.S_OK;
  5088. }
  5089. /// <summary>
  5090. /// Set a project property
  5091. /// </summary>
  5092. /// <param name="propertyName">Name of the property to set</param>
  5093. /// <param name="configName">Configuration for which to set the property</param>
  5094. /// <param name="storage">Project file or user file (_PersistStorageType)</param>
  5095. /// <param name="propertyValue">New value for that property</param>
  5096. /// <returns>HRESULT</returns>
  5097. int IVsBuildPropertyStorage.SetPropertyValue(string propertyName, string configName, uint storage,
  5098. string propertyValue)
  5099. {
  5100. // TODO: when adding support for User files, we need to update this method
  5101. if (string.IsNullOrEmpty(configName))
  5102. {
  5103. SetProjectProperty(propertyName, propertyValue);
  5104. }
  5105. else
  5106. {
  5107. IVsCfg configurationInterface;
  5108. ErrorHandler.ThrowOnFailure(ConfigProvider.GetCfgOfName(configName, string.Empty,
  5109. out configurationInterface));
  5110. ProjectConfig config = (ProjectConfig)configurationInterface;
  5111. config.SetConfigurationProperty(propertyName, propertyValue);
  5112. }
  5113. return VSConstants.S_OK;
  5114. }
  5115. #endregion
  5116. #region IVsDesignTimeAssemblyResolution methods
  5117. public int GetTargetFramework(out string ppTargetFramework)
  5118. {
  5119. ppTargetFramework = ProjectMgr.TargetFrameworkMoniker.FullName;
  5120. return VSConstants.S_OK;
  5121. }
  5122. public int ResolveAssemblyPathInTargetFx(string[] prgAssemblySpecs, uint cAssembliesToResolve,
  5123. VsResolvedAssemblyPath[] prgResolvedAssemblyPaths,
  5124. out uint pcResolvedAssemblyPaths)
  5125. {
  5126. if (prgAssemblySpecs == null || cAssembliesToResolve == 0 || prgResolvedAssemblyPaths == null)
  5127. {
  5128. throw new ArgumentException("One or more of the arguments are invalid.");
  5129. }
  5130. pcResolvedAssemblyPaths = 0;
  5131. try
  5132. {
  5133. var results = designTimeAssemblyResolution.Resolve(prgAssemblySpecs.Take((int)cAssembliesToResolve));
  5134. results.CopyTo(prgResolvedAssemblyPaths, 0);
  5135. pcResolvedAssemblyPaths = (uint)results.Length;
  5136. }
  5137. catch (Exception ex)
  5138. {
  5139. return Marshal.GetHRForException(ex);
  5140. }
  5141. return VSConstants.S_OK;
  5142. }
  5143. #endregion
  5144. #region private helper methods
  5145. /// <summary>
  5146. /// Initialize projectNode
  5147. /// </summary>
  5148. private void Initialize()
  5149. {
  5150. ID = VSConstants.VSITEMID_ROOT;
  5151. tracker = new TrackDocumentsHelper(this);
  5152. }
  5153. /// <summary>
  5154. /// Add an item to the hierarchy based on the item path
  5155. /// </summary>
  5156. /// <param name="item">Item to add</param>
  5157. /// <returns>Added node</returns>
  5158. private HierarchyNode AddIndependentFileNode(MSBuild.ProjectItem item)
  5159. {
  5160. HierarchyNode currentParent = GetItemParentNode(item);
  5161. return AddFileNodeToNode(item, currentParent);
  5162. }
  5163. /// <summary>
  5164. /// Add a dependent file node to the hierarchy
  5165. /// </summary>
  5166. /// <param name="item">msbuild item to add</param>
  5167. /// <param name="parentNode">Parent Node</param>
  5168. /// <returns>Added node</returns>
  5169. private HierarchyNode AddDependentFileNodeToNode(MSBuild.ProjectItem item, HierarchyNode parentNode)
  5170. {
  5171. FileNode node = CreateDependentFileNode(new ProjectElement(this, item, false));
  5172. parentNode.AddChild(node);
  5173. // Make sure to set the HasNameRelation flag on the dependent node if it is related to the parent by name
  5174. if (!node.HasParentNodeNameRelation &&
  5175. string.Compare(node.GetRelationalName(), parentNode.GetRelationalName(),
  5176. StringComparison.OrdinalIgnoreCase) == 0)
  5177. {
  5178. node.HasParentNodeNameRelation = true;
  5179. }
  5180. return node;
  5181. }
  5182. /// <summary>
  5183. /// Add a file node to the hierarchy
  5184. /// </summary>
  5185. /// <param name="item">msbuild item to add</param>
  5186. /// <param name="parentNode">Parent Node</param>
  5187. /// <returns>Added node</returns>
  5188. private HierarchyNode AddFileNodeToNode(MSBuild.ProjectItem item, HierarchyNode parentNode)
  5189. {
  5190. FileNode node = CreateFileNode(new ProjectElement(this, item, false));
  5191. parentNode.AddChild(node);
  5192. return node;
  5193. }
  5194. /// <summary>
  5195. /// Get the parent node of an msbuild item
  5196. /// </summary>
  5197. /// <param name="item">msbuild item</param>
  5198. /// <returns>parent node</returns>
  5199. private HierarchyNode GetItemParentNode(MSBuild.ProjectItem item)
  5200. {
  5201. HierarchyNode currentParent = this;
  5202. string strPath = item.EvaluatedInclude;
  5203. strPath = Path.GetDirectoryName(strPath);
  5204. if (strPath.Length > 0)
  5205. {
  5206. // Use the relative to verify the folders...
  5207. currentParent = CreateFolderNodes(strPath);
  5208. }
  5209. return currentParent;
  5210. }
  5211. private ProjectPropertyInstance GetMsBuildProperty(string propertyName, bool resetCache)
  5212. {
  5213. if (resetCache || currentConfig == null)
  5214. {
  5215. // Get properties from project file and cache it
  5216. SetCurrentConfiguration();
  5217. currentConfig = buildProject.CreateProjectInstance();
  5218. }
  5219. if (currentConfig == null)
  5220. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
  5221. SR.GetString(SR.FailedToRetrieveProperties,
  5222. CultureInfo.CurrentUICulture),
  5223. propertyName));
  5224. // return property asked for
  5225. return currentConfig.GetProperty(propertyName);
  5226. }
  5227. private string GetOutputPath(ProjectInstance properties)
  5228. {
  5229. currentConfig = properties;
  5230. string outputPath = GetProjectProperty("OutputPath");
  5231. if (!String.IsNullOrEmpty(outputPath))
  5232. {
  5233. outputPath = outputPath.Replace('/', Path.DirectorySeparatorChar);
  5234. if (outputPath[outputPath.Length - 1] != Path.DirectorySeparatorChar)
  5235. outputPath += Path.DirectorySeparatorChar;
  5236. }
  5237. return outputPath;
  5238. }
  5239. private bool GetBoolAttr(ProjectInstance properties, string name)
  5240. {
  5241. currentConfig = properties;
  5242. string s = GetProjectProperty(name);
  5243. return (s != null && s.ToUpperInvariant().Trim() == "TRUE");
  5244. }
  5245. [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
  5246. private string GetAssemblyName(ProjectInstance properties)
  5247. {
  5248. currentConfig = properties;
  5249. string name = null;
  5250. name = GetProjectProperty(ProjectFileConstants.AssemblyName);
  5251. if (name == null)
  5252. name = Caption;
  5253. string outputtype = GetProjectProperty(ProjectFileConstants.OutputType, false);
  5254. if (outputtype == "library")
  5255. {
  5256. outputtype = outputtype.ToLowerInvariant();
  5257. name += ".dll";
  5258. }
  5259. else
  5260. {
  5261. name += ".exe";
  5262. }
  5263. return name;
  5264. }
  5265. /// <summary>
  5266. /// Updates our scc project settings.
  5267. /// </summary>
  5268. /// <param name="sccProjectName">String, opaque to the project, that identifies the project location on the server. Persist this string in the project file. </param>
  5269. /// <param name="sccLocalPath">String, opaque to the project, that identifies the path to the server. Persist this string in the project file.</param>
  5270. /// <param name="sccAuxPath">String, opaque to the project, that identifies the local path to the project. Persist this string in the project file.</param>
  5271. /// <param name="sccProvider">String, opaque to the project, that identifies the source control package. Persist this string in the project file.</param>
  5272. /// <returns>Returns true if something changed.</returns>
  5273. private bool SetSccSettings(string sccProjectName, string sccLocalPath, string sccAuxPath, string sccProvider)
  5274. {
  5275. bool changed = false;
  5276. Debug.Assert(sccProjectName != null && sccLocalPath != null && sccAuxPath != null && sccProvider != null);
  5277. if (String.Compare(sccProjectName, this.sccProjectName, StringComparison.OrdinalIgnoreCase) != 0 ||
  5278. String.Compare(sccLocalPath, this.sccLocalPath, StringComparison.OrdinalIgnoreCase) != 0 ||
  5279. String.Compare(sccAuxPath, this.sccAuxPath, StringComparison.OrdinalIgnoreCase) != 0 ||
  5280. String.Compare(sccProvider, this.sccProvider, StringComparison.OrdinalIgnoreCase) != 0)
  5281. {
  5282. changed = true;
  5283. this.sccProjectName = sccProjectName;
  5284. this.sccLocalPath = sccLocalPath;
  5285. this.sccAuxPath = sccAuxPath;
  5286. this.sccProvider = sccProvider;
  5287. }
  5288. return changed;
  5289. }
  5290. /// <summary>
  5291. /// Sets the scc info from the project file.
  5292. /// </summary>
  5293. private void InitSccInfo()
  5294. {
  5295. sccProjectName = GetProjectProperty(ProjectFileConstants.SccProjectName);
  5296. sccLocalPath = GetProjectProperty(ProjectFileConstants.SccLocalPath);
  5297. sccProvider = GetProjectProperty(ProjectFileConstants.SccProvider);
  5298. sccAuxPath = GetProjectProperty(ProjectFileConstants.SccAuxPath);
  5299. }
  5300. private void OnAfterProjectOpen(object sender, AfterProjectFileOpenedEventArgs e)
  5301. {
  5302. projectOpened = true;
  5303. }
  5304. private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor,
  5305. string configuration, string fragment)
  5306. {
  5307. XmlElement node = document.CreateElement(ProjectFileConstants.FlavorProperties);
  5308. XmlAttribute attribute = document.CreateAttribute(ProjectFileConstants.Guid);
  5309. attribute.Value = flavor.ToString("B");
  5310. node.Attributes.Append(attribute);
  5311. if (!String.IsNullOrEmpty(configuration))
  5312. {
  5313. attribute = document.CreateAttribute(ProjectFileConstants.Configuration);
  5314. attribute.Value = configuration;
  5315. node.Attributes.Append(attribute);
  5316. }
  5317. node.InnerXml = fragment;
  5318. root.AppendChild(node);
  5319. return node;
  5320. }
  5321. /// <summary>
  5322. /// Sets the project guid from the project file. If no guid is found a new one is created and assigne for the instance project guid.
  5323. /// </summary>
  5324. private void SetProjectGuidFromProjectFile()
  5325. {
  5326. string projectGuid = GetProjectProperty(ProjectFileConstants.ProjectGuid);
  5327. if (String.IsNullOrEmpty(projectGuid))
  5328. {
  5329. projectIdGuid = Guid.NewGuid();
  5330. }
  5331. else
  5332. {
  5333. Guid guid = new Guid(projectGuid);
  5334. if (guid != projectIdGuid)
  5335. {
  5336. projectIdGuid = guid;
  5337. }
  5338. }
  5339. }
  5340. /// <summary>
  5341. /// Helper for sharing common code between Build() and BuildAsync()
  5342. /// </summary>
  5343. /// <param name="output"></param>
  5344. /// <returns></returns>
  5345. private bool BuildPrelude(IVsOutputWindowPane output)
  5346. {
  5347. bool engineLogOnlyCritical = false;
  5348. // If there is some output, then we can ask the build engine to log more than
  5349. // just the critical events.
  5350. if (null != output)
  5351. {
  5352. engineLogOnlyCritical = BuildEngine.OnlyLogCriticalEvents;
  5353. BuildEngine.OnlyLogCriticalEvents = false;
  5354. }
  5355. SetOutputLogger(output);
  5356. return engineLogOnlyCritical;
  5357. }
  5358. /// <summary>
  5359. /// Recusively parses the tree and closes all nodes.
  5360. /// </summary>
  5361. /// <param name="node">The subtree to close.</param>
  5362. private static void CloseAllNodes(HierarchyNode node)
  5363. {
  5364. for (HierarchyNode n = node.FirstChild; n != null; n = n.NextSibling)
  5365. {
  5366. if (n.FirstChild != null)
  5367. {
  5368. CloseAllNodes(n);
  5369. }
  5370. n.Close();
  5371. }
  5372. }
  5373. /// <summary>
  5374. /// Set the build project with the new project instance value
  5375. /// </summary>
  5376. /// <param name="project">The new build project instance</param>
  5377. private void SetBuildProject(MSBuild.Project project)
  5378. {
  5379. buildProject = project;
  5380. if (buildProject != null)
  5381. {
  5382. SetupProjectGlobalPropertiesThatAllProjectSystemsMustSet();
  5383. }
  5384. }
  5385. /// <summary>
  5386. /// Setup the global properties for project instance.
  5387. /// </summary>
  5388. private void SetupProjectGlobalPropertiesThatAllProjectSystemsMustSet()
  5389. {
  5390. string solutionDirectory = null;
  5391. string solutionFile = null;
  5392. string userOptionsFile = null;
  5393. IVsSolution solution = Site.GetService(typeof(SVsSolution)) as IVsSolution;
  5394. if (solution != null)
  5395. {
  5396. // We do not want to throw. If we cannot set the solution related constants we set them to empty string.
  5397. solution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptionsFile);
  5398. }
  5399. if (solutionDirectory == null)
  5400. {
  5401. solutionDirectory = String.Empty;
  5402. }
  5403. if (solutionFile == null)
  5404. {
  5405. solutionFile = String.Empty;
  5406. }
  5407. string solutionFileName = (solutionFile.Length == 0) ? String.Empty : Path.GetFileName(solutionFile);
  5408. string solutionName = (solutionFile.Length == 0)
  5409. ? String.Empty
  5410. : Path.GetFileNameWithoutExtension(solutionFile);
  5411. string solutionExtension = String.Empty;
  5412. if (solutionFile.Length > 0 && Path.HasExtension(solutionFile))
  5413. {
  5414. solutionExtension = Path.GetExtension(solutionFile);
  5415. }
  5416. buildProject.SetGlobalProperty(GlobalProperty.SolutionDir.ToString(), solutionDirectory);
  5417. buildProject.SetGlobalProperty(GlobalProperty.SolutionPath.ToString(), solutionFile);
  5418. buildProject.SetGlobalProperty(GlobalProperty.SolutionFileName.ToString(), solutionFileName);
  5419. buildProject.SetGlobalProperty(GlobalProperty.SolutionName.ToString(), solutionName);
  5420. buildProject.SetGlobalProperty(GlobalProperty.SolutionExt.ToString(), solutionExtension);
  5421. // Other misc properties
  5422. buildProject.SetGlobalProperty(GlobalProperty.BuildingInsideVisualStudio.ToString(), "true");
  5423. buildProject.SetGlobalProperty(GlobalProperty.Configuration.ToString(), ProjectConfig.Debug);
  5424. buildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), ProjectConfig.AnyCPU);
  5425. // DevEnvDir property
  5426. object installDirAsObject = null;
  5427. IVsShell shell = Site.GetService(typeof(SVsShell)) as IVsShell;
  5428. if (shell != null)
  5429. {
  5430. // We do not want to throw. If we cannot set the solution related constants we set them to empty string.
  5431. shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out installDirAsObject);
  5432. }
  5433. string installDir = ((string)installDirAsObject);
  5434. if (String.IsNullOrEmpty(installDir))
  5435. {
  5436. installDir = String.Empty;
  5437. }
  5438. else
  5439. {
  5440. // Ensure that we have traimnling backslash as this is done for the langproj macros too.
  5441. if (installDir[installDir.Length - 1] != Path.DirectorySeparatorChar)
  5442. {
  5443. installDir += Path.DirectorySeparatorChar;
  5444. }
  5445. }
  5446. buildProject.SetGlobalProperty(GlobalProperty.DevEnvDir.ToString(), installDir);
  5447. }
  5448. /// <summary>
  5449. /// Attempts to lock in the privilege of running a build in Visual Studio.
  5450. /// </summary>
  5451. /// <param name="designTime">
  5452. /// <c>false</c> if this build was called for by the Solution Build Manager; <c>true</c> otherwise.
  5453. /// </param>
  5454. /// <param name="requiresUIThread">
  5455. /// Need to claim the UI thread for build under the following conditions:
  5456. /// 1. The build must use a resource that uses the UI thread, such as
  5457. /// - you set HostServices and you have a host object which requires (even indirectly) the UI thread (VB and C# compilers do this for instance.)
  5458. /// or,
  5459. /// 2. The build requires the in-proc node AND waits on the UI thread for the build to complete, such as:
  5460. /// - you use a ProjectInstance to build, or
  5461. /// - you have specified a host object, whether or not it requires the UI thread, or
  5462. /// - you set HostServices and you have specified a node affinity.
  5463. /// - In addition to the above you also call submission.Execute(), or you call submission.ExecuteAsync() and then also submission.WaitHandle.Wait*().
  5464. /// </param>
  5465. /// <returns>A value indicating whether a build may proceed.</returns>
  5466. /// <remarks>
  5467. /// This method must be called on the UI thread.
  5468. /// </remarks>
  5469. private bool TryBeginBuild(bool designTime, bool requiresUIThread = false)
  5470. {
  5471. IVsBuildManagerAccessor accessor = null;
  5472. if (Site != null)
  5473. {
  5474. accessor = Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
  5475. }
  5476. bool releaseUIThread = false;
  5477. try
  5478. {
  5479. // If the SVsBuildManagerAccessor service is absent, we're not running within Visual Studio.
  5480. if (accessor != null)
  5481. {
  5482. if (requiresUIThread)
  5483. {
  5484. int result = accessor.ClaimUIThreadForBuild();
  5485. if (result < 0)
  5486. {
  5487. // Not allowed to claim the UI thread right now. Try again later.
  5488. return false;
  5489. }
  5490. releaseUIThread = true;
  5491. // assume we need to release this immediately until we get through the whole gauntlet.
  5492. }
  5493. if (designTime)
  5494. {
  5495. int result = accessor.BeginDesignTimeBuild();
  5496. if (result < 0)
  5497. {
  5498. // Not allowed to begin a design-time build at this time. Try again later.
  5499. return false;
  5500. }
  5501. }
  5502. // We obtained all the resources we need. So don't release the UI thread until after the build is finished.
  5503. releaseUIThread = false;
  5504. }
  5505. else
  5506. {
  5507. BuildParameters buildParameters =
  5508. new BuildParameters(buildEngine ?? ProjectCollection.GlobalProjectCollection);
  5509. BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
  5510. }
  5511. buildInProcess = true;
  5512. return true;
  5513. }
  5514. finally
  5515. {
  5516. // If we were denied the privilege of starting a design-time build,
  5517. // we need to release the UI thread.
  5518. if (releaseUIThread)
  5519. {
  5520. Debug.Assert(accessor != null,
  5521. "We think we need to release the UI thread for an accessor we don't have!");
  5522. Marshal.ThrowExceptionForHR(accessor.ReleaseUIThreadForBuild());
  5523. }
  5524. }
  5525. }
  5526. /// <summary>
  5527. /// Lets Visual Studio know that we're done with our design-time build so others can use the build manager.
  5528. /// </summary>
  5529. /// <param name="submission">The build submission that built, if any.</param>
  5530. /// <param name="designTime">
  5531. /// This must be the same value as the one passed to <see cref="TryBeginBuild" />.
  5532. /// </param>
  5533. /// <param name="requiresUIThread">
  5534. /// This must be the same value as the one passed to <see cref="TryBeginBuild" />.
  5535. /// </param>
  5536. /// <remarks>
  5537. /// This method must be called on the UI thread.
  5538. /// </remarks>
  5539. private void EndBuild(BuildSubmission submission, bool designTime, bool requiresUIThread = false)
  5540. {
  5541. IVsBuildManagerAccessor accessor = null;
  5542. if (Site != null)
  5543. {
  5544. accessor = Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
  5545. }
  5546. if (accessor != null)
  5547. {
  5548. // It's very important that we try executing all three end-build steps, even if errors occur partway through.
  5549. try
  5550. {
  5551. if (submission != null)
  5552. {
  5553. Marshal.ThrowExceptionForHR(accessor.UnregisterLoggers(submission.SubmissionId));
  5554. }
  5555. }
  5556. catch (Exception ex)
  5557. {
  5558. if (ErrorHandler.IsCriticalException(ex))
  5559. {
  5560. throw;
  5561. }
  5562. Trace.TraceError(ex.ToString());
  5563. }
  5564. try
  5565. {
  5566. if (designTime)
  5567. {
  5568. Marshal.ThrowExceptionForHR(accessor.EndDesignTimeBuild());
  5569. }
  5570. }
  5571. catch (Exception ex)
  5572. {
  5573. if (ErrorHandler.IsCriticalException(ex))
  5574. {
  5575. throw;
  5576. }
  5577. Trace.TraceError(ex.ToString());
  5578. }
  5579. try
  5580. {
  5581. if (requiresUIThread)
  5582. {
  5583. Marshal.ThrowExceptionForHR(accessor.ReleaseUIThreadForBuild());
  5584. }
  5585. }
  5586. catch (Exception ex)
  5587. {
  5588. if (ErrorHandler.IsCriticalException(ex))
  5589. {
  5590. throw;
  5591. }
  5592. Trace.TraceError(ex.ToString());
  5593. }
  5594. }
  5595. else
  5596. {
  5597. BuildManager.DefaultBuildManager.EndBuild();
  5598. }
  5599. buildInProcess = false;
  5600. }
  5601. private string GetComponentPickerDirectories()
  5602. {
  5603. IVsComponentEnumeratorFactory4 enumFactory =
  5604. site.GetService(typeof(SCompEnumService)) as IVsComponentEnumeratorFactory4;
  5605. if (enumFactory == null)
  5606. {
  5607. throw new InvalidOperationException("Missing the SCompEnumService service.");
  5608. }
  5609. IEnumComponents enumerator;
  5610. Marshal.ThrowExceptionForHR(enumFactory.GetReferencePathsForTargetFramework(
  5611. TargetFrameworkMoniker.FullName, out enumerator));
  5612. if (enumerator == null)
  5613. {
  5614. throw new ApplicationException(
  5615. "IVsComponentEnumeratorFactory4.GetReferencePathsForTargetFramework returned null.");
  5616. }
  5617. StringBuilder paths = new StringBuilder();
  5618. VSCOMPONENTSELECTORDATA[] data = new VSCOMPONENTSELECTORDATA[1];
  5619. uint fetchedCount;
  5620. while (enumerator.Next(1, data, out fetchedCount) == VSConstants.S_OK && fetchedCount == 1)
  5621. {
  5622. Debug.Assert(data[0].type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_Path);
  5623. paths.Append(data[0].bstrFile);
  5624. paths.Append(";");
  5625. }
  5626. // Trim off the last semicolon.
  5627. if (paths.Length > 0)
  5628. {
  5629. paths.Length -= 1;
  5630. }
  5631. return paths.ToString();
  5632. }
  5633. #endregion
  5634. public int UpgradeProject(uint grfUpgradeFlags)
  5635. {
  5636. int hr = VSConstants.S_OK;
  5637. if (!PerformTargetFrameworkCheck())
  5638. {
  5639. // Just return OLE_E_PROMPTSAVECANCELLED here which will cause the shell
  5640. // to leave the project in an unloaded state.
  5641. hr = VSConstants.OLE_E_PROMPTSAVECANCELLED;
  5642. }
  5643. return hr;
  5644. }
  5645. public int UpdateTargetFramework(IVsHierarchy pHier, string currentTargetFramework, string newTargetFramework)
  5646. {
  5647. FrameworkName moniker = new FrameworkName(newTargetFramework);
  5648. SetProjectProperty("TargetFrameworkIdentifier", moniker.Identifier);
  5649. SetProjectProperty("TargetFrameworkVersion", "v" + moniker.Version);
  5650. SetProjectProperty("TargetFrameworkProfile", moniker.Profile);
  5651. return VSConstants.S_OK;
  5652. }
  5653. private bool PerformTargetFrameworkCheck()
  5654. {
  5655. if (IsFrameworkOnMachine())
  5656. {
  5657. // Nothing to do since the framework is present.
  5658. return true;
  5659. }
  5660. return ShowRetargetingDialog();
  5661. }
  5662. /// <summary>
  5663. /// </summary>
  5664. /// <returns>
  5665. /// <c>true</c> if the project will be retargeted. <c>false</c> to load project in unloaded state.
  5666. /// </returns>
  5667. private bool ShowRetargetingDialog()
  5668. {
  5669. var retargetDialog = site.GetService(typeof(SVsFrameworkRetargetingDlg)) as IVsFrameworkRetargetingDlg;
  5670. if (retargetDialog == null)
  5671. {
  5672. throw new InvalidOperationException("Missing SVsFrameworkRetargetingDlg service.");
  5673. }
  5674. // We can only display the retargeting dialog if the IDE is not in command-line mode.
  5675. if (IsIdeInCommandLineMode)
  5676. {
  5677. string message = SR.GetString(SR.CannotLoadUnknownTargetFrameworkProject, FileName,
  5678. TargetFrameworkMoniker);
  5679. var outputWindow = site.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
  5680. if (outputWindow != null)
  5681. {
  5682. IVsOutputWindowPane outputPane;
  5683. Guid outputPaneGuid = VSConstants.GUID_BuildOutputWindowPane;
  5684. if (outputWindow.GetPane(ref outputPaneGuid, out outputPane) >= 0 && outputPane != null)
  5685. {
  5686. Marshal.ThrowExceptionForHR(outputPane.OutputString(message));
  5687. }
  5688. }
  5689. throw new InvalidOperationException(message);
  5690. }
  5691. else
  5692. {
  5693. uint outcome;
  5694. int dontShowAgain;
  5695. Marshal.ThrowExceptionForHR(retargetDialog.ShowFrameworkRetargetingDlg(
  5696. Package.ProductUserContext,
  5697. FileName,
  5698. TargetFrameworkMoniker.FullName,
  5699. (uint)__FRD_FLAGS.FRDF_DEFAULT,
  5700. out outcome,
  5701. out dontShowAgain));
  5702. switch ((__FRD_OUTCOME)outcome)
  5703. {
  5704. case __FRD_OUTCOME.FRDO_GOTO_DOWNLOAD_SITE:
  5705. Marshal.ThrowExceptionForHR(retargetDialog.NavigateToFrameworkDownloadUrl());
  5706. return false;
  5707. case __FRD_OUTCOME.FRDO_LEAVE_UNLOADED:
  5708. return false;
  5709. case __FRD_OUTCOME.FRDO_RETARGET_TO_40:
  5710. // If we are retargeting to 4.0, then set the flag to set the appropriate Target Framework.
  5711. // This will dirty the project file, so we check it out of source control now -- so that
  5712. // the user can associate getting the checkout prompt with the "No Framework" dialog.
  5713. if (QueryEditProjectFile(false /* bSuppressUI */))
  5714. {
  5715. var retargetingService =
  5716. site.GetService(typeof(SVsTrackProjectRetargeting)) as IVsTrackProjectRetargeting;
  5717. if (retargetingService != null)
  5718. {
  5719. // We surround our batch retargeting request with begin/end because in individual project load
  5720. // scenarios the solution load context hasn't done it for us.
  5721. Marshal.ThrowExceptionForHR(retargetingService.BeginRetargetingBatch());
  5722. Marshal.ThrowExceptionForHR(
  5723. retargetingService.BatchRetargetProject(InteropSafeIVsHierarchy,
  5724. DefaultTargetFrameworkMoniker.FullName, true));
  5725. Marshal.ThrowExceptionForHR(retargetingService.EndRetargetingBatch());
  5726. }
  5727. else
  5728. {
  5729. // Just setting the moniker to null will allow the default framework (.NETFX 4.0) to assert itself.
  5730. TargetFrameworkMoniker = null;
  5731. }
  5732. return true;
  5733. }
  5734. else
  5735. {
  5736. return false;
  5737. }
  5738. default:
  5739. throw new ArgumentException("Unexpected outcome from retargeting dialog.");
  5740. }
  5741. }
  5742. }
  5743. private bool IsFrameworkOnMachine()
  5744. {
  5745. var multiTargeting = site.GetService(typeof(SVsFrameworkMultiTargeting)) as IVsFrameworkMultiTargeting;
  5746. Array frameworks;
  5747. Marshal.ThrowExceptionForHR(multiTargeting.GetSupportedFrameworks(out frameworks));
  5748. foreach (string fx in frameworks)
  5749. {
  5750. uint compat;
  5751. int hr = multiTargeting.CheckFrameworkCompatibility(TargetFrameworkMoniker.FullName, fx, out compat);
  5752. if (hr < 0)
  5753. {
  5754. break;
  5755. }
  5756. if ((__VSFRAMEWORKCOMPATIBILITY)compat == __VSFRAMEWORKCOMPATIBILITY.VSFRAMEWORKCOMPATIBILITY_COMPATIBLE)
  5757. {
  5758. return true;
  5759. }
  5760. }
  5761. return false;
  5762. }
  5763. }
  5764. }