Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: obsolete/Microsoft.VisualStudio.Project/ProjectNode.cs

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

Powered by Google App Engine
This is Rietveld 408576698