OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections.Generic; | |
5 using System.Diagnostics; | |
6 using System.Diagnostics.CodeAnalysis; | |
7 using System.Globalization; | |
8 using System.IO; | |
9 using System.Runtime.InteropServices; | |
10 using Microsoft.VisualStudio.Project.Automation; | |
11 using Microsoft.VisualStudio.Shell; | |
12 using Microsoft.VisualStudio.Shell.Interop; | |
13 using MSBuild = Microsoft.Build.BuildEngine; | |
14 | |
15 namespace Microsoft.VisualStudio.Project | |
16 { | |
17 [CLSCompliant(false), ComVisible(true)] | |
18 public abstract class ProjectContainerNode : ProjectNode, | |
19 IVsParentProject, | |
20 IBuildDependencyOnProjectContainer | |
21 { | |
22 #region fields | |
23 | |
24 /// <summary> | |
25 /// Setting this flag to true will build all nested project when
building this project | |
26 /// </summary> | |
27 private bool buildNestedProjectsOnBuild = true; | |
28 | |
29 private ProjectElement nestedProjectElement; | |
30 | |
31 /// <summary> | |
32 /// Defines the listener that would listen on file changes on th
e nested project node. | |
33 /// </summary> | |
34 ///<devremark> | |
35 ///This might need a refactoring when nested projects can be add
ed and removed by demand. | |
36 /// </devremark> | |
37 private FileChangeManager nestedProjectNodeReloader; | |
38 #endregion | |
39 | |
40 #region ctors | |
41 protected ProjectContainerNode() | |
42 { | |
43 } | |
44 #endregion | |
45 | |
46 #region properties | |
47 /// <summary> | |
48 /// Returns teh object that handles listening to file changes on
the nested project files. | |
49 /// </summary> | |
50 internal FileChangeManager NestedProjectNodeReloader | |
51 { | |
52 get | |
53 { | |
54 if(this.nestedProjectNodeReloader == null) | |
55 { | |
56 this.nestedProjectNodeReloader = new Fil
eChangeManager(this.Site); | |
57 this.nestedProjectNodeReloader.FileChang
edOnDisk += this.OnNestedProjectFileChangedOnDisk; | |
58 } | |
59 | |
60 return this.nestedProjectNodeReloader; | |
61 } | |
62 } | |
63 #endregion | |
64 | |
65 #region overridden properties | |
66 /// <summary> | |
67 /// This is the object that will be returned by EnvDTE.Project.O
bject for this project | |
68 /// </summary> | |
69 internal override object Object | |
70 { | |
71 get { return new OASolutionFolder<ProjectContainerNode>(
this); } | |
72 } | |
73 | |
74 #endregion | |
75 | |
76 #region public overridden methods | |
77 /// <summary> | |
78 /// Gets the nested hierarchy. | |
79 /// </summary> | |
80 /// <param name="itemId">The item id.</param> | |
81 /// <param name="iidHierarchyNested">Identifier of the interface
to be returned in ppHierarchyNested.</param> | |
82 /// <param name="ppHierarchyNested">Pointer to the interface who
se identifier was passed in iidHierarchyNested.</param> | |
83 /// <param name="pItemId">Pointer to an item identifier of the r
oot node of the nested hierarchy.</param> | |
84 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. If ITEMID is not a nested hierarchy, this method re
turns E_FAIL.</returns> | |
85 [CLSCompliant(false)] | |
86 public override int GetNestedHierarchy(UInt32 itemId, ref Guid i
idHierarchyNested, out IntPtr ppHierarchyNested, out uint pItemId) | |
87 { | |
88 pItemId = VSConstants.VSITEMID_ROOT; | |
89 ppHierarchyNested = IntPtr.Zero; | |
90 if(this.FirstChild != null) | |
91 { | |
92 for(HierarchyNode n = this.FirstChild; n != null
; n = n.NextSibling) | |
93 { | |
94 NestedProjectNode p = n as NestedProject
Node; | |
95 | |
96 if(p != null && p.ID == itemId) | |
97 { | |
98 if(p.NestedHierarchy != null) | |
99 { | |
100 IntPtr iunknownPtr = Int
Ptr.Zero; | |
101 int returnValue = VSCons
tants.S_OK; | |
102 try | |
103 { | |
104 iunknownPtr = Ma
rshal.GetIUnknownForObject(p.NestedHierarchy); | |
105 Marshal.QueryInt
erface(iunknownPtr, ref iidHierarchyNested, out ppHierarchyNested); | |
106 } | |
107 catch(COMException e) | |
108 { | |
109 returnValue = e.
ErrorCode; | |
110 } | |
111 finally | |
112 { | |
113 if(iunknownPtr !
= IntPtr.Zero) | |
114 { | |
115 Marshal.
Release(iunknownPtr); | |
116 } | |
117 } | |
118 | |
119 return returnValue; | |
120 } | |
121 break; | |
122 } | |
123 } | |
124 } | |
125 | |
126 return VSConstants.E_FAIL; | |
127 } | |
128 | |
129 public override int IsItemDirty(uint itemId, IntPtr punkDocData,
out int pfDirty) | |
130 { | |
131 HierarchyNode hierNode = this.NodeFromItemId(itemId); | |
132 Debug.Assert(hierNode != null, "Hierarchy node not found
"); | |
133 if(hierNode != this) | |
134 { | |
135 return ErrorHandler.ThrowOnFailure(hierNode.IsIt
emDirty(itemId, punkDocData, out pfDirty)); | |
136 } | |
137 else | |
138 { | |
139 return ErrorHandler.ThrowOnFailure(base.IsItemDi
rty(itemId, punkDocData, out pfDirty)); | |
140 } | |
141 } | |
142 | |
143 public override int SaveItem(VSSAVEFLAGS dwSave, string silentSa
veAsName, uint itemid, IntPtr punkDocData, out int pfCancelled) | |
144 { | |
145 HierarchyNode hierNode = this.NodeFromItemId(itemid); | |
146 Debug.Assert(hierNode != null, "Hierarchy node not found
"); | |
147 if(hierNode != this) | |
148 { | |
149 return ErrorHandler.ThrowOnFailure(hierNode.Save
Item(dwSave, silentSaveAsName, itemid, punkDocData, out pfCancelled)); | |
150 } | |
151 else | |
152 { | |
153 return ErrorHandler.ThrowOnFailure(base.SaveItem
(dwSave, silentSaveAsName, itemid, punkDocData, out pfCancelled)); | |
154 } | |
155 } | |
156 | |
157 protected override bool FilterItemTypeToBeAddedToHierarchy(strin
g itemType) | |
158 { | |
159 if(String.Compare(itemType, ProjectFileConstants.SubProj
ect, StringComparison.OrdinalIgnoreCase) == 0) | |
160 { | |
161 return true; | |
162 } | |
163 return base.FilterItemTypeToBeAddedToHierarchy(itemType)
; | |
164 } | |
165 | |
166 /// <summary> | |
167 /// Called to reload a project item. | |
168 /// Reloads a project and its nested project nodes. | |
169 /// </summary> | |
170 /// <param name="itemId">Specifies itemid from VSITEMID.</param> | |
171 /// <param name="reserved">Reserved.</param> | |
172 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
173 public override int ReloadItem(uint itemId, uint reserved) | |
174 { | |
175 #region precondition | |
176 if(this.IsClosed) | |
177 { | |
178 return VSConstants.E_FAIL; | |
179 } | |
180 #endregion | |
181 | |
182 NestedProjectNode node = this.NodeFromItemId(itemId) as
NestedProjectNode; | |
183 | |
184 if(node != null) | |
185 { | |
186 object propertyAsObject = node.GetProperty((int)
__VSHPROPID.VSHPROPID_HandlesOwnReload); | |
187 | |
188 if(propertyAsObject != null && (bool)propertyAsO
bject) | |
189 { | |
190 node.ReloadItem(reserved); | |
191 } | |
192 else | |
193 { | |
194 this.ReloadNestedProjectNode(node); | |
195 } | |
196 | |
197 return VSConstants.S_OK; | |
198 } | |
199 | |
200 return base.ReloadItem(itemId, reserved); | |
201 } | |
202 | |
203 /// <summary> | |
204 /// Reloads a project and its nested project nodes. | |
205 /// </summary> | |
206 protected override void Reload() | |
207 { | |
208 base.Reload(); | |
209 this.CreateNestedProjectNodes(); | |
210 } | |
211 #endregion | |
212 | |
213 #region IVsParentProject | |
214 public virtual int OpenChildren() | |
215 { | |
216 IVsSolution solution = this.GetService(typeof(IVsSolutio
n)) as IVsSolution; | |
217 | |
218 Debug.Assert(solution != null, "Could not retrieve the s
olution from the services provided by this project"); | |
219 if(solution == null) | |
220 { | |
221 return VSConstants.E_FAIL; | |
222 } | |
223 | |
224 IntPtr iUnKnownForSolution = IntPtr.Zero; | |
225 int returnValue = VSConstants.S_OK; // be optimistic. | |
226 | |
227 try | |
228 { | |
229 this.DisableQueryEdit = true; | |
230 this.EventTriggeringFlag = ProjectNode.EventTrig
gering.DoNotTriggerHierarchyEvents | ProjectNode.EventTriggering.DoNotTriggerTra
ckerEvents; | |
231 iUnKnownForSolution = Marshal.GetIUnknownForObje
ct(solution); | |
232 | |
233 // notify SolutionEvents listeners that we are a
bout to add children | |
234 IVsFireSolutionEvents fireSolutionEvents = Marsh
al.GetTypedObjectForIUnknown(iUnKnownForSolution, typeof(IVsFireSolutionEvents))
as IVsFireSolutionEvents; | |
235 ErrorHandler.ThrowOnFailure(fireSolutionEvents.F
ireOnBeforeOpeningChildren(this)); | |
236 | |
237 this.AddVirtualProjects(); | |
238 | |
239 ErrorHandler.ThrowOnFailure(fireSolutionEvents.F
ireOnAfterOpeningChildren(this)); | |
240 } | |
241 catch(Exception e) | |
242 { | |
243 // Exceptions are digested by the caller but we
want then to be shown if not a ComException and if not in automation. | |
244 if(!(e is COMException) && !Utilities.IsInAutoma
tionFunction(this.Site)) | |
245 { | |
246 string title = null; | |
247 OLEMSGICON icon = OLEMSGICON.OLEMSGICON_
CRITICAL; | |
248 OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEM
SGBUTTON_OK; | |
249 OLEMSGDEFBUTTON defaultButton = OLEMSGDE
FBUTTON.OLEMSGDEFBUTTON_FIRST; | |
250 VsShellUtilities.ShowMessageBox(this.Sit
e, title, e.Message, icon, buttons, defaultButton); | |
251 } | |
252 | |
253 Trace.WriteLine("Exception : " + e.Message); | |
254 throw; | |
255 } | |
256 finally | |
257 { | |
258 this.DisableQueryEdit = false; | |
259 | |
260 if(iUnKnownForSolution != IntPtr.Zero) | |
261 { | |
262 Marshal.Release(iUnKnownForSolution); | |
263 } | |
264 | |
265 this.EventTriggeringFlag = ProjectNode.EventTrig
gering.TriggerAll; | |
266 } | |
267 | |
268 return returnValue; | |
269 } | |
270 | |
271 public virtual int CloseChildren() | |
272 { | |
273 int returnValue = VSConstants.S_OK; // be optimistic. | |
274 | |
275 IVsSolution solution = this.GetService(typeof(IVsSolutio
n)) as IVsSolution; | |
276 Debug.Assert(solution != null, "Could not retrieve the s
olution from the services provided by this project"); | |
277 | |
278 if(solution == null) | |
279 { | |
280 return VSConstants.E_FAIL; | |
281 } | |
282 | |
283 IntPtr iUnKnownForSolution = IntPtr.Zero; | |
284 | |
285 try | |
286 { | |
287 iUnKnownForSolution = Marshal.GetIUnknownForObje
ct(solution); | |
288 | |
289 // notify SolutionEvents listeners that we are a
bout to close children | |
290 IVsFireSolutionEvents fireSolutionEvents = Marsh
al.GetTypedObjectForIUnknown(iUnKnownForSolution, typeof(IVsFireSolutionEvents))
as IVsFireSolutionEvents; | |
291 ErrorHandler.ThrowOnFailure(fireSolutionEvents.F
ireOnBeforeClosingChildren(this)); | |
292 | |
293 // If the removal crashes we never fire the clos
e children event. IS that a problem? | |
294 this.RemoveNestedProjectNodes(); | |
295 | |
296 ErrorHandler.ThrowOnFailure(fireSolutionEvents.F
ireOnAfterClosingChildren(this)); | |
297 } | |
298 finally | |
299 { | |
300 if(iUnKnownForSolution != IntPtr.Zero) | |
301 { | |
302 Marshal.Release(iUnKnownForSolution); | |
303 } | |
304 } | |
305 | |
306 return returnValue; | |
307 } | |
308 #endregion | |
309 | |
310 #region IBuildDependencyOnProjectContainerNode | |
311 /// <summary> | |
312 /// Defines whether nested projects should be build with the par
ent project | |
313 /// </summary> | |
314 public virtual bool BuildNestedProjectsOnBuild | |
315 { | |
316 get { return this.buildNestedProjectsOnBuild; } | |
317 set { this.buildNestedProjectsOnBuild = value; } | |
318 } | |
319 | |
320 /// <summary> | |
321 /// Enumerates the nested hierachies that should be added to the
build dependency list. | |
322 /// </summary> | |
323 /// <returns></returns> | |
324 public virtual IVsHierarchy[] EnumNestedHierachiesForBuildDepend
ency() | |
325 { | |
326 List<IVsHierarchy> nestedProjectList = new List<IVsHiera
rchy>(); | |
327 // Add all nested project among projectContainer child n
odes | |
328 for(HierarchyNode child = this.FirstChild; child != null
; child = child.NextSibling) | |
329 { | |
330 NestedProjectNode nestedProjectNode = child as N
estedProjectNode; | |
331 if(nestedProjectNode != null) | |
332 { | |
333 nestedProjectList.Add(nestedProjectNode.
NestedHierarchy); | |
334 } | |
335 } | |
336 | |
337 return nestedProjectList.ToArray(); | |
338 } | |
339 #endregion | |
340 | |
341 #region helper methods | |
342 | |
343 internal protected void RemoveNestedProjectNodes() | |
344 { | |
345 for(HierarchyNode n = this.FirstChild; n != null; n = n.
NextSibling) | |
346 { | |
347 NestedProjectNode p = n as NestedProjectNode; | |
348 if(p != null) | |
349 { | |
350 p.CloseNestedProjectNode(); | |
351 } | |
352 } | |
353 | |
354 // We do not care of file changes after this. | |
355 this.NestedProjectNodeReloader.FileChangedOnDisk -= this
.OnNestedProjectFileChangedOnDisk; | |
356 this.NestedProjectNodeReloader.Dispose(); | |
357 } | |
358 | |
359 /// <summary> | |
360 /// This is used when loading the project to loop through all th
e items | |
361 /// and for each SubProject it finds, it create the project and
a node | |
362 /// in our Hierarchy to hold the project. | |
363 /// </summary> | |
364 internal protected void CreateNestedProjectNodes() | |
365 { | |
366 // 1. Create a ProjectElement with the found item and th
en Instantiate a new Nested project with this ProjectElement. | |
367 // 2. Link into the hierarchy. | |
368 // Read subprojects from from msbuildmodel | |
369 __VSCREATEPROJFLAGS creationFlags = __VSCREATEPROJFLAGS.
CPF_NOTINSLNEXPLR | __VSCREATEPROJFLAGS.CPF_SILENT; | |
370 | |
371 if(this.IsNewProject) | |
372 { | |
373 creationFlags |= __VSCREATEPROJFLAGS.CPF_CLONEFI
LE; | |
374 } | |
375 else | |
376 { | |
377 creationFlags |= __VSCREATEPROJFLAGS.CPF_OPENFIL
E; | |
378 } | |
379 | |
380 foreach(MSBuild.BuildItem item in this.BuildProject.Eval
uatedItems) | |
381 { | |
382 if(String.Compare(item.Name, ProjectFileConstant
s.SubProject, StringComparison.OrdinalIgnoreCase) == 0) | |
383 { | |
384 this.nestedProjectElement = new ProjectE
lement(this, item, false); | |
385 | |
386 if(!this.IsNewProject) | |
387 { | |
388 AddExistingNestedProject(null, c
reationFlags); | |
389 } | |
390 else | |
391 { | |
392 // If we are creating the subpro
ject from a vstemplate/vsz file | |
393 bool isVsTemplate = Utilities.Is
TemplateFile(GetProjectTemplatePath(null)); | |
394 if(isVsTemplate) | |
395 { | |
396 RunVsTemplateWizard(null
, true); | |
397 } | |
398 else | |
399 { | |
400 // We are cloning the sp
ecified project file | |
401 AddNestedProjectFromTemp
late(null, creationFlags); | |
402 } | |
403 } | |
404 } | |
405 } | |
406 | |
407 this.nestedProjectElement = null; | |
408 } | |
409 /// <summary> | |
410 /// Add an existing project as a nested node of our hierarchy. | |
411 /// This is used while loading the project and can also be used | |
412 /// to add an existing project to our hierarchy. | |
413 /// </summary> | |
414 protected internal virtual NestedProjectNode AddExistingNestedPr
oject(ProjectElement element, __VSCREATEPROJFLAGS creationFlags) | |
415 { | |
416 ProjectElement elementToUse = (element == null) ? this.n
estedProjectElement : element; | |
417 | |
418 if(elementToUse == null) | |
419 { | |
420 throw new ArgumentNullException("element"); | |
421 } | |
422 | |
423 string filename = elementToUse.GetFullPathForElement(); | |
424 // Delegate to AddNestedProjectFromTemplate. Because we
pass flags that specify open project rather then clone, this will works. | |
425 Debug.Assert((creationFlags & __VSCREATEPROJFLAGS.CPF_OP
ENFILE) == __VSCREATEPROJFLAGS.CPF_OPENFILE, "__VSCREATEPROJFLAGS.CPF_OPENFILE s
hould have been specified, did you mean to call AddNestedProjectFromTemplate?"); | |
426 return AddNestedProjectFromTemplate(filename, Path.GetDi
rectoryName(filename), Path.GetFileName(filename), elementToUse, creationFlags); | |
427 } | |
428 | |
429 /// <summary> | |
430 /// Let the wizard code execute and provide us the information w
e need. | |
431 /// Our SolutionFolder automation object should be called back w
ith the | |
432 /// details at which point it will call back one of our method t
o add | |
433 /// a nested project. | |
434 /// If you are trying to add a new subproject this is probably t
he | |
435 /// method you want to call. If you are just trying to clone a t
emplate | |
436 /// project file, then AddNestedProjectFromTemplate is what you
want. | |
437 /// </summary> | |
438 /// <param name="element">The project item to use as the base of
the nested project.</param> | |
439 /// <param name="silent">true if the wizard should run silently,
otherwise false.</param> | |
440 [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBe
CasedCorrectly", MessageId = "Vs")] | |
441 protected internal void RunVsTemplateWizard(ProjectElement eleme
nt, bool silent) | |
442 { | |
443 ProjectElement elementToUse = (element == null) ? this.n
estedProjectElement : element; | |
444 | |
445 if(elementToUse == null) | |
446 { | |
447 throw new ArgumentNullException("element"); | |
448 } | |
449 this.nestedProjectElement = elementToUse; | |
450 | |
451 Automation.OAProject oaProject = GetAutomationObject() a
s Automation.OAProject; | |
452 if(oaProject == null || oaProject.ProjectItems == null) | |
453 throw new System.InvalidOperationException(SR.Ge
tString(SR.InvalidAutomationObject, CultureInfo.CurrentUICulture)); | |
454 Debug.Assert(oaProject.Object != null, "The project auto
mation object should have set the Object to the SolutionFolder"); | |
455 Automation.OASolutionFolder<ProjectContainerNode> folder
= oaProject.Object as Automation.OASolutionFolder<ProjectContainerNode>; | |
456 | |
457 // Prepare the parameters to pass to RunWizardFile | |
458 string destination = elementToUse.GetFullPathForElement(
); | |
459 string template = this.GetProjectTemplatePath(elementToU
se); | |
460 | |
461 object[] wizParams = new object[7]; | |
462 wizParams[0] = EnvDTE.Constants.vsWizardAddSubProject; | |
463 wizParams[1] = Path.GetFileNameWithoutExtension(destinat
ion); | |
464 wizParams[2] = oaProject.ProjectItems; | |
465 wizParams[3] = Path.GetDirectoryName(destination); | |
466 wizParams[4] = Path.GetFileNameWithoutExtension(destinat
ion); | |
467 wizParams[5] = Path.GetDirectoryName(folder.DTE.FullName
); //VS install dir | |
468 wizParams[6] = silent; | |
469 | |
470 IVsDetermineWizardTrust wizardTrust = this.GetService(ty
peof(SVsDetermineWizardTrust)) as IVsDetermineWizardTrust; | |
471 if(wizardTrust != null) | |
472 { | |
473 Guid guidProjectAdding = Guid.Empty; | |
474 | |
475 // In case of a project template an empty guid s
hould be added as the guid parameter. See env\msenv\core\newtree.h IsTrustedTemp
late method definition. | |
476 ErrorHandler.ThrowOnFailure(wizardTrust.OnWizard
Initiated(template, ref guidProjectAdding)); | |
477 } | |
478 | |
479 try | |
480 { | |
481 // Make the call to execute the wizard. This sho
uld cause AddNestedProjectFromTemplate to be | |
482 // called back with the correct set of parameter
s. | |
483 EnvDTE.IVsExtensibility extensibilityService = (
EnvDTE.IVsExtensibility)GetService(typeof(EnvDTE.IVsExtensibility)); | |
484 EnvDTE.wizardResult result = extensibilityServic
e.RunWizardFile(template, 0, ref wizParams); | |
485 if(result == EnvDTE.wizardResult.wizardResultFai
lure) | |
486 throw new COMException(); | |
487 } | |
488 finally | |
489 { | |
490 if(wizardTrust != null) | |
491 { | |
492 ErrorHandler.ThrowOnFailure(wizardTrust.
OnWizardCompleted()); | |
493 } | |
494 } | |
495 } | |
496 | |
497 /// <summary> | |
498 /// This will clone a template project file and add it as a | |
499 /// subproject to our hierarchy. | |
500 /// If you want to create a project for which there exist a | |
501 /// vstemplate, consider using RunVsTemplateWizard instead. | |
502 /// </summary> | |
503 protected internal virtual NestedProjectNode AddNestedProjectFro
mTemplate(ProjectElement element, __VSCREATEPROJFLAGS creationFlags) | |
504 { | |
505 ProjectElement elementToUse = (element == null) ? this.n
estedProjectElement : element; | |
506 | |
507 if(elementToUse == null) | |
508 { | |
509 throw new ArgumentNullException("element"); | |
510 } | |
511 string destination = elementToUse.GetFullPathForElement(
); | |
512 string template = this.GetProjectTemplatePath(elementToU
se); | |
513 | |
514 return this.AddNestedProjectFromTemplate(template, Path.
GetDirectoryName(destination), Path.GetFileName(destination), elementToUse, crea
tionFlags); | |
515 } | |
516 | |
517 /// <summary> | |
518 /// This can be called directly or through RunVsTemplateWizard. | |
519 /// This will clone a template project file and add it as a | |
520 /// subproject to our hierarchy. | |
521 /// If you want to create a project for which there exist a | |
522 /// vstemplate, consider using RunVsTemplateWizard instead. | |
523 /// </summary> | |
524 protected internal virtual NestedProjectNode AddNestedProjectFro
mTemplate(string fileName, string destination, string projectName, ProjectElemen
t element, __VSCREATEPROJFLAGS creationFlags) | |
525 { | |
526 // If this is project creation and the template specifie
d a subproject in its project file, this.nestedProjectElement will be used | |
527 ProjectElement elementToUse = (element == null) ? this.n
estedProjectElement : element; | |
528 | |
529 if(elementToUse == null) | |
530 { | |
531 // If this is null, this means MSBuild does not
know anything about our subproject so add an MSBuild item for it | |
532 elementToUse = new ProjectElement(this, fileName
, ProjectFileConstants.SubProject); | |
533 } | |
534 | |
535 NestedProjectNode node = CreateNestedProjectNode(element
ToUse); | |
536 node.Init(fileName, destination, projectName, creationFl
ags); | |
537 | |
538 // In case that with did not have an existing element, o
r the nestedProjectelement was null | |
539 // and since Init computes the final path, set the MSBu
ild item to that path | |
540 if(this.nestedProjectElement == null) | |
541 { | |
542 string relativePath = node.Url; | |
543 if(Path.IsPathRooted(relativePath)) | |
544 { | |
545 relativePath = this.ProjectFolder; | |
546 if(!relativePath.EndsWith("/\\", StringC
omparison.Ordinal)) | |
547 { | |
548 relativePath += Path.DirectorySe
paratorChar; | |
549 } | |
550 | |
551 relativePath = new Url(relativePath).Mak
eRelative(new Url(node.Url)); | |
552 } | |
553 | |
554 elementToUse.Rename(relativePath); | |
555 } | |
556 | |
557 this.AddChild(node); | |
558 return node; | |
559 } | |
560 | |
561 /// <summary> | |
562 /// Override this method if you want to provide your own type of
nodes. | |
563 /// This would be the case if you derive a class from NestedProj
ectNode | |
564 /// </summary> | |
565 protected virtual NestedProjectNode CreateNestedProjectNode(Proj
ectElement element) | |
566 { | |
567 return new NestedProjectNode(this, element); | |
568 } | |
569 | |
570 /// <summary> | |
571 /// Links the nested project nodes to the solution. The default
implementation parses all nested project nodes and calles AddVirtualProjectEx on
them. | |
572 /// </summary> | |
573 protected virtual void AddVirtualProjects() | |
574 { | |
575 for(HierarchyNode child = this.FirstChild; child != null
; child = child.NextSibling) | |
576 { | |
577 NestedProjectNode nestedProjectNode = child as N
estedProjectNode; | |
578 if(nestedProjectNode != null) | |
579 { | |
580 nestedProjectNode.AddVirtualProject(); | |
581 } | |
582 } | |
583 } | |
584 | |
585 /// <summary> | |
586 /// Based on the Template and TypeGuid properties of the | |
587 /// element, generate the full template path. | |
588 /// | |
589 /// TypeGuid should be the Guid of a registered project factory. | |
590 /// Template can be a full path, a project template (for project
s | |
591 /// that support VsTemplates) or a relative path (for other proj
ects). | |
592 /// </summary> | |
593 protected virtual string GetProjectTemplatePath(ProjectElement e
lement) | |
594 { | |
595 ProjectElement elementToUse = (element == null) ? this.n
estedProjectElement : element; | |
596 | |
597 if(elementToUse == null) | |
598 { | |
599 throw new ArgumentNullException("element"); | |
600 } | |
601 | |
602 string templateFile = elementToUse.GetMetadata(ProjectFi
leConstants.Template); | |
603 Debug.Assert(!String.IsNullOrEmpty(templateFile), "No te
mplate file has been specified in the template attribute in the project file"); | |
604 | |
605 string fullPath = templateFile; | |
606 if(!Path.IsPathRooted(templateFile)) | |
607 { | |
608 RegisteredProjectType registeredProjectType = th
is.GetRegisteredProject(elementToUse); | |
609 | |
610 // This is not a full path | |
611 Debug.Assert(registeredProjectType != null && (!
String.IsNullOrEmpty(registeredProjectType.DefaultProjectExtensionValue) || !Str
ing.IsNullOrEmpty(registeredProjectType.WizardTemplatesDirValue)), " Registered
wizard directory value not set in the registry."); | |
612 | |
613 // See if this specify a VsTemplate file | |
614 fullPath = registeredProjectType.GetVsTemplateFi
le(templateFile); | |
615 if(String.IsNullOrEmpty(fullPath)) | |
616 { | |
617 // Default to using the WizardTemplateDi
r to calculate the absolute path | |
618 fullPath = Path.Combine(registeredProjec
tType.WizardTemplatesDirValue, templateFile); | |
619 } | |
620 } | |
621 | |
622 return fullPath; | |
623 } | |
624 | |
625 /// <summary> | |
626 /// Get information from the registry based for the project | |
627 /// factory corresponding to the TypeGuid of the element | |
628 /// </summary> | |
629 private RegisteredProjectType GetRegisteredProject(ProjectElemen
t element) | |
630 { | |
631 ProjectElement elementToUse = (element == null) ? this.n
estedProjectElement : element; | |
632 | |
633 if(elementToUse == null) | |
634 { | |
635 throw new ArgumentNullException("element"); | |
636 } | |
637 | |
638 // Get the project type guid from project elementToUse
| |
639 string typeGuidString = elementToUse.GetMetadataAndThrow
(ProjectFileConstants.TypeGuid, new Exception()); | |
640 Guid projectFactoryGuid = new Guid(typeGuidString); | |
641 | |
642 EnvDTE.DTE dte = this.ProjectMgr.Site.GetService(typeof(
EnvDTE.DTE)) as EnvDTE.DTE; | |
643 Debug.Assert(dte != null, "Could not get the automation
object from the services exposed by this project"); | |
644 | |
645 if(dte == null) | |
646 throw new InvalidOperationException(); | |
647 | |
648 RegisteredProjectType registeredProjectType = Registered
ProjectType.CreateRegisteredProjectType(projectFactoryGuid); | |
649 Debug.Assert(registeredProjectType != null, "Could not r
ead the registry setting associated to this project."); | |
650 if(registeredProjectType == null) | |
651 { | |
652 throw new InvalidOperationException(); | |
653 } | |
654 return registeredProjectType; | |
655 } | |
656 | |
657 /// <summary> | |
658 /// Reloads a nested project node by deleting it and readding it
. | |
659 /// </summary> | |
660 /// <param name="node">The node to reload.</param> | |
661 protected virtual void ReloadNestedProjectNode(NestedProjectNode
node) | |
662 { | |
663 if(node == null) | |
664 { | |
665 throw new ArgumentNullException("node"); | |
666 } | |
667 | |
668 IVsSolution solution = this.GetService(typeof(IVsSolutio
n)) as IVsSolution; | |
669 | |
670 if(solution == null) | |
671 { | |
672 throw new InvalidOperationException(); | |
673 } | |
674 | |
675 NestedProjectNode newNode = null; | |
676 try | |
677 { | |
678 // (VS 2005 UPDATE) When deleting and re-adding
the nested project, | |
679 // we do not want SCC to see this as a delete an
d add operation. | |
680 this.EventTriggeringFlag = ProjectNode.EventTrig
gering.DoNotTriggerTrackerEvents; | |
681 | |
682 // notify SolutionEvents listeners that we are a
bout to add children | |
683 IVsFireSolutionEvents fireSolutionEvents = solut
ion as IVsFireSolutionEvents; | |
684 | |
685 if(fireSolutionEvents == null) | |
686 { | |
687 throw new InvalidOperationException(); | |
688 } | |
689 | |
690 ErrorHandler.ThrowOnFailure(fireSolutionEvents.F
ireOnBeforeUnloadProject(node.NestedHierarchy)); | |
691 | |
692 int isDirtyAsInt = 0; | |
693 this.IsDirty(out isDirtyAsInt); | |
694 | |
695 bool isDirty = (isDirtyAsInt == 0) ? false : tru
e; | |
696 | |
697 ProjectElement element = node.ItemNode; | |
698 node.CloseNestedProjectNode(); | |
699 | |
700 // Remove from the solution | |
701 this.RemoveChild(node); | |
702 | |
703 // Now readd it | |
704 try | |
705 { | |
706 __VSCREATEPROJFLAGS flags = __VSCREATEPR
OJFLAGS.CPF_NOTINSLNEXPLR | __VSCREATEPROJFLAGS.CPF_SILENT | __VSCREATEPROJFLAGS
.CPF_OPENFILE; | |
707 newNode = this.AddExistingNestedProject(
element, flags); | |
708 newNode.AddVirtualProject(); | |
709 } | |
710 catch(Exception e) | |
711 { | |
712 // We get a System.Exception if anything
failed, thus we have no choice but catch it. | |
713 // Exceptions are digested by VS. Show t
he error if not in automation. | |
714 if(!Utilities.IsInAutomationFunction(thi
s.Site)) | |
715 { | |
716 string message = (String.IsNullO
rEmpty(e.Message)) ? SR.GetString(SR.NestedProjectFailedToReload, CultureInfo.Cu
rrentUICulture) : e.Message; | |
717 string title = string.Empty; | |
718 OLEMSGICON icon = OLEMSGICON.OLE
MSGICON_CRITICAL; | |
719 OLEMSGBUTTON buttons = OLEMSGBUT
TON.OLEMSGBUTTON_OK; | |
720 OLEMSGDEFBUTTON defaultButton =
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; | |
721 VsShellUtilities.ShowMessageBox(
this.Site, title, message, icon, buttons, defaultButton); | |
722 } | |
723 | |
724 // Do not digest exception. let the call
er handle it. If in a later stage this exception is not digested then the above
messagebox is not needed. | |
725 throw; | |
726 } | |
727 | |
728 #if DEBUG | |
729 IVsHierarchy nestedHierarchy; | |
730 ErrorHandler.ThrowOnFailure(solution.GetProjectO
fUniqueName(newNode.GetMkDocument(), out nestedHierarchy)); | |
731 Debug.Assert(nestedHierarchy != null && Utilitie
s.IsSameComObject(nestedHierarchy, newNode.NestedHierarchy), "The nested hierrac
hy was not reloaded correctly."); | |
732 #endif | |
733 this.SetProjectFileDirty(isDirty); | |
734 | |
735 ErrorHandler.ThrowOnFailure(fireSolutionEvents.F
ireOnAfterLoadProject(newNode.NestedHierarchy)); | |
736 } | |
737 finally | |
738 { | |
739 // In this scenario the nested project failed to
unload or reload the nested project. We will unload the whole project, otherwis
e the nested project is lost. | |
740 // This is similar to the scenario when one want
s to open a project and the nested project cannot be loaded because for example
the project file has xml errors. | |
741 // We should note that we rely here that if the
unload fails then exceptions are not digested and are shown to the user. | |
742 if(newNode == null || newNode.NestedHierarchy ==
null) | |
743 { | |
744 ErrorHandler.ThrowOnFailure(solution.Clo
seSolutionElement((uint)__VSSLNCLOSEOPTIONS.SLNCLOSEOPT_UnloadProject | (uint)__
VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, this, 0)); | |
745 } | |
746 else | |
747 { | |
748 this.EventTriggeringFlag = ProjectNode.E
ventTriggering.TriggerAll; | |
749 } | |
750 } | |
751 } | |
752 | |
753 /// <summary> | |
754 /// Event callback. Called when one of the nested project files
is changed. | |
755 /// </summary> | |
756 /// <param name="sender">The FileChangeManager object.</param> | |
757 /// <param name="e">Event args containing the file name that was
updated.</param> | |
758 private void OnNestedProjectFileChangedOnDisk(object sender, Fil
eChangedOnDiskEventArgs e) | |
759 { | |
760 #region Pre-condition validation | |
761 Debug.Assert(e != null, "No event args specified for the
FileChangedOnDisk event"); | |
762 | |
763 // We care only about time change for reload. | |
764 if((e.FileChangeFlag & _VSFILECHANGEFLAGS.VSFILECHG_Time
) == 0) | |
765 { | |
766 return; | |
767 } | |
768 | |
769 // test if we actually have a document for this id. | |
770 string moniker; | |
771 this.GetMkDocument(e.ItemID, out moniker); | |
772 Debug.Assert(NativeMethods.IsSamePath(moniker, e.FileNam
e), " The file + " + e.FileName + " has changed but we could not retrieve the pa
th for the item id associated to the path."); | |
773 #endregion | |
774 | |
775 bool reload = true; | |
776 if(!Utilities.IsInAutomationFunction(this.Site)) | |
777 { | |
778 // Prompt to reload the nested project file. We
use the moniker here since the filename from the event arg is canonicalized. | |
779 string message = String.Format(CultureInfo.Curre
ntCulture, SR.GetString(SR.QueryReloadNestedProject, CultureInfo.CurrentUICultur
e), moniker); | |
780 string title = string.Empty; | |
781 OLEMSGICON icon = OLEMSGICON.OLEMSGICON_INFO; | |
782 OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON
_YESNO; | |
783 OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.
OLEMSGDEFBUTTON_FIRST; | |
784 reload = (VsShellUtilities.ShowMessageBox(this.S
ite, message, title, icon, buttons, defaultButton) == NativeMethods.IDYES); | |
785 } | |
786 | |
787 if(reload) | |
788 { | |
789 // We have to use here the interface method call
, since it might be that specialized project nodes like the project container it
em | |
790 // is owerwriting the default functionality. | |
791 this.ReloadItem(e.ItemID, 0); | |
792 } | |
793 } | |
794 #endregion | |
795 } | |
796 } | |
OLD | NEW |