OLD | NEW |
| (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 } | |
OLD | NEW |