PageRenderTime 94ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/source2/VSIP/MPF/10.0_original/Src/CSharp/ProjectNode.cs

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