PageRenderTime 118ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 1ms

/Dependencies/MPFProjectBase/ProjectNode.cs

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