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

Side by Side Diff: experimental/visual_studio_plugin/third_party/Microsoft.VisualStudio.Project/NestedProjectNode.cs

Issue 10928195: First round of dead file removal (Closed) Base URL: https://github.com/samclegg/nativeclient-sdk.git@master
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /// Copyright (c) Microsoft Corporation. All rights reserved.
2
3 using System;
4 using System.Diagnostics;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Globalization;
7 using System.IO;
8 using System.Runtime.InteropServices;
9 using Microsoft.VisualStudio;
10 using Microsoft.VisualStudio.OLE.Interop;
11 using Microsoft.VisualStudio.Shell;
12 using Microsoft.VisualStudio.Shell.Interop;
13 using ErrorHandler = Microsoft.VisualStudio.ErrorHandler;
14 using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants;
15
16 namespace Microsoft.VisualStudio.Project
17 {
18 [CLSCompliant(false), ComVisible(true)]
19 public class NestedProjectNode : HierarchyNode, IPropertyNotifySink
20 {
21 #region fields
22 private IVsHierarchy nestedHierarchy;
23
24 Guid projectInstanceGuid = Guid.Empty;
25
26 private string projectName = String.Empty;
27
28 private string projectPath = String.Empty;
29
30 private ImageHandler imageHandler;
31
32 /// <summary>
33 /// Defines an object that will be a mutex for this object for s ynchronizing thread calls.
34 /// </summary>
35 private static volatile object Mutex = new object();
36
37 /// <summary>
38 /// Sets the dispose flag on the object.
39 /// </summary>
40 private bool isDisposed;
41
42 // A cooike retrieved when advising on property chnanged events.
43 private uint projectPropertyNotifySinkCookie;
44 #endregion
45
46 #region properties
47 internal IVsHierarchy NestedHierarchy
48 {
49 get
50 {
51 return this.nestedHierarchy;
52 }
53 }
54 #endregion
55
56 #region virtual properties
57 /// <summary>
58 /// Returns the __VSADDVPFLAGS that will be passed in when calli ng AddVirtualProjectEx
59 /// </summary>
60 protected virtual uint VirtualProjectFlags
61 {
62 get { return 0; }
63 }
64 #endregion
65
66 #region overridden properties
67 /// <summary>
68 /// The path of the nested project.
69 /// </summary>
70 public override string Url
71 {
72 get
73 {
74 return this.projectPath;
75 }
76 }
77
78 /// <summary>
79 /// The Caption of the nested project.
80 /// </summary>
81 public override string Caption
82 {
83 get
84 {
85 return Path.GetFileNameWithoutExtension(this.pro jectName);
86 }
87 }
88
89 public override Guid ItemTypeGuid
90 {
91 get
92 {
93 return VSConstants.GUID_ItemType_SubProject;
94 }
95 }
96
97 /// <summary>
98 /// Defines whether a node can execute a command if in selection .
99 /// We do this in order to let the nested project to handle the execution of its own commands.
100 /// </summary>
101 public override bool CanExecuteCommand
102 {
103 get
104 {
105 return false;
106 }
107 }
108
109 public override int SortPriority
110 {
111 get { return DefaultSortOrderNode.NestedProjectNode; }
112 }
113
114 protected bool IsDisposed
115 {
116 get { return this.isDisposed; }
117 set { this.isDisposed = value; }
118 }
119
120
121
122 #endregion
123
124 #region ctor
125
126 protected NestedProjectNode()
127 {
128 }
129
130 public NestedProjectNode(ProjectNode root, ProjectElement elemen t)
131 : base(root, element)
132 {
133 this.IsExpanded = true;
134 }
135 #endregion
136
137 #region IPropertyNotifySink Members
138 /// <summary>
139 /// Notifies a sink that the [bindable] property specified by di spID has changed.
140 /// If dispID is DISPID_UNKNOWN, then multiple properties have c hanged together.
141 /// The client (owner of the sink) should then retrieve the curr ent value of each property of interest from the object that generated the notifi cation.
142 /// In our case we will care about the VSLangProj80.VsProjPropI d.VBPROJPROPID_FileName and update the changes in the parent project file.
143 /// </summary>
144 /// <param name="dispid">Dispatch identifier of the property tha t is about to change or DISPID_UNKNOWN if multiple properties are about to chang e.</param>
145 public virtual void OnChanged(int dispid)
146 {
147 if(dispid == (int)VSLangProj80.VsProjPropId.VBPROJPROPID _FileName)
148 {
149 // Get the filename of the nested project. Inete ad of asking the label on the nested we ask the filename, since the label might not yet been set.
150 IVsProject3 nestedProject = this.nestedHierarchy as IVsProject3;
151
152 if(nestedProject != null)
153 {
154 string document;
155 ErrorHandler.ThrowOnFailure(nestedProjec t.GetMkDocument(VSConstants.VSITEMID_ROOT, out document));
156 this.RenameNestedProjectInParentProject( Path.GetFileNameWithoutExtension(document));
157
158 // We need to redraw the caption since f or some reason, by intervining to the OnChanged event the Caption is not updated .
159 this.ReDraw(UIHierarchyElement.Caption);
160 }
161 }
162 }
163
164 /// <summary>
165 /// Notifies a sink that a [requestedit] property is about to ch ange and that the object is asking the sink how to proceed.
166 /// </summary>
167 /// <param name="dispid">Dispatch identifier of the property tha t is about to change or DISPID_UNKNOWN if multiple properties are about to chang e.</param>
168 public virtual void OnRequestEdit(int dispid)
169 {
170
171 }
172
173 #endregion
174
175 #region public methods
176 #endregion
177
178 #region overridden methods
179
180 /// <summary>
181 /// Get the automation object for the NestedProjectNode
182 /// </summary>
183 /// <returns>An instance of the Automation.OANestedProjectItem t ype if succeded</returns>
184 public override object GetAutomationObject()
185 {
186 //Validate that we are not disposed or the project is cl osing
187 if(this.isDisposed || this.ProjectMgr == null || this.Pr ojectMgr.IsClosed)
188 {
189 return null;
190 }
191
192 return new Automation.OANestedProjectItem(this.ProjectMg r.GetAutomationObject() as Automation.OAProject, this);
193 }
194
195 /// <summary>
196 /// Gets properties of a given node or of the hierarchy.
197 /// </summary>
198 /// <param name="propId">Identifier of the hierarchy property</p aram>
199 /// <returns>It return an object which type is dependent on the propid.</returns>
200 public override object GetProperty(int propId)
201 {
202 __VSHPROPID vshPropId = (__VSHPROPID)propId;
203 switch(vshPropId)
204 {
205 default:
206 return base.GetProperty(propId);
207
208 case __VSHPROPID.VSHPROPID_Expandable:
209 return true;
210
211 case __VSHPROPID.VSHPROPID_BrowseObject:
212 case __VSHPROPID.VSHPROPID_HandlesOwnReload:
213 return this.DelegateGetPropertyToNested( propId);
214 }
215 }
216
217
218 /// <summary>
219 /// Gets properties whose values are GUIDs.
220 /// </summary>
221 /// <param name="propid">Identifier of the hierarchy property</p aram>
222 /// <param name="guid"> Pointer to a GUID property specified in propid</param>
223 /// <returns>If the method succeeds, it returns S_OK. If it fail s, it returns an error code.</returns>
224 public override int GetGuidProperty(int propid, out Guid guid)
225 {
226 guid = Guid.Empty;
227 switch((__VSHPROPID)propid)
228 {
229 case __VSHPROPID.VSHPROPID_ProjectIDGuid:
230 guid = this.projectInstanceGuid;
231 break;
232
233 default:
234 return base.GetGuidProperty(propid, out guid);
235 }
236
237 CCITracing.TraceCall(String.Format(CultureInfo.CurrentCu lture, "Guid for {0} property", propid));
238 if(guid.CompareTo(Guid.Empty) == 0)
239 {
240 return VSConstants.DISP_E_MEMBERNOTFOUND;
241 }
242
243 return VSConstants.S_OK;
244 }
245
246
247 /// <summary>
248 /// Determines whether the hierarchy item changed.
249 /// </summary>
250 /// <param name="itemId">Item identifier of the hierarchy item c ontained in VSITEMID</param>
251 /// <param name="punkDocData">Pointer to the IUnknown interface of the hierarchy item. </param>
252 /// <param name="pfDirty">TRUE if the hierarchy item changed.</p aram>
253 /// <returns>If the method succeeds, it returns S_OK. If it fail s, it returns an error code.</returns>
254 public override int IsItemDirty(uint itemId, IntPtr punkDocData, out int pfDirty)
255 {
256 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
257 Debug.Assert(punkDocData != IntPtr.Zero, "docData intptr was zero");
258
259 // Get an IPersistFileFormat object from docData object
260 IPersistFileFormat persistFileFormat = Marshal.GetTypedO bjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFileFormat;
261 Debug.Assert(persistFileFormat != null, "The docData obj ect does not implement the IPersistFileFormat interface");
262
263 // Call IsDirty on the IPersistFileFormat interface
264 ErrorHandler.ThrowOnFailure(persistFileFormat.IsDirty(ou t pfDirty));
265
266 return VSConstants.S_OK;
267 }
268
269 /// <summary>
270 /// Saves the hierarchy item to disk.
271 /// </summary>
272 /// <param name="dwSave">Flags whose values are taken from the V SSAVEFLAGS enumeration.</param>
273 /// <param name="silentSaveAsName">File name to be applied when dwSave is set to VSSAVE_SilentSave. </param>
274 /// <param name="itemid">Item identifier of the hierarchy item s aved from VSITEMID. </param>
275 /// <param name="punkDocData">Pointer to the IUnknown interface of the hierarchy item saved.</param>
276 /// <param name="pfCancelled">TRUE if the save action was cancel ed. </param>
277 /// <returns>If the method succeeds, it returns S_OK. If it fail s, it returns an error code.</returns>
278 public override int SaveItem(VSSAVEFLAGS dwSave, string silentSa veAsName, uint itemid, IntPtr punkDocData, out int pfCancelled)
279 {
280 // Don't ignore/unignore file changes
281 // Use Advise/Unadvise to work around rename situations
282 try
283 {
284 this.StopObservingNestedProjectFile();
285 Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
286 Debug.Assert(punkDocData != IntPtr.Zero, "docDat a intptr was zero");
287
288 // Get an IPersistFileFormat object from docData object (we don't call release on punkDocData since did not increment its ref co unt)
289 IPersistFileFormat persistFileFormat = Marshal.G etTypedObjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFil eFormat;
290 Debug.Assert(persistFileFormat != null, "The doc Data object does not implement the IPersistFileFormat interface");
291
292 IVsUIShell uiShell = this.GetService(typeof(SVsU IShell)) as IVsUIShell;
293 string newName;
294 ErrorHandler.ThrowOnFailure(uiShell.SaveDocDataT oFile(dwSave, persistFileFormat, silentSaveAsName, out newName, out pfCancelled) );
295
296 // When supported do a rename of the nested proj ect here
297 }
298 finally
299 {
300 // Succeeded or not we must hook to the file cha nge events
301 // Don't ignore/unignore file changes
302 // Use Advise/Unadvise to work around rename sit uations
303 this.ObserveNestedProjectFile();
304 }
305
306 return VSConstants.S_OK;
307 }
308
309 /// <summary>
310 /// Gets the icon handle. It tries first the nested to get the i con handle. If that is not supported it will get it from
311 /// the image list of the nested if that is supported. If neithe r of these is supported a default image will be shown.
312 /// </summary>
313 /// <returns>An object representing the icon.</returns>
314 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usag e", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.VisualStudio.Shell .Interop.IVsHierarchy.GetProperty(System.UInt32,System.Int32,System.Object@)")]
315 public override object GetIconHandle(bool open)
316 {
317 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
318
319 object iconHandle = null;
320 this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_RO OT, (int)__VSHPROPID.VSHPROPID_IconHandle, out iconHandle);
321 if(iconHandle == null)
322 {
323 if(null == imageHandler)
324 {
325 InitImageHandler();
326 }
327 // Try to get an icon from the nested hierrachy image list.
328 if(imageHandler.ImageList != null)
329 {
330 object imageIndexAsObject = null;
331 if(this.nestedHierarchy.GetProperty(VSCo nstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconIndex, out imageIndexAsObj ect) == VSConstants.S_OK &&
332 imageIndexAsObject != null)
333 {
334 int imageIndex = (int)imageIndex AsObject;
335 if(imageIndex < imageHandler.Ima geList.Images.Count)
336 {
337 iconHandle = imageHandle r.GetIconHandle(imageIndex);
338 }
339 }
340 }
341
342 if(null == iconHandle)
343 {
344 iconHandle = this.ProjectMgr.ImageHandle r.GetIconHandle((int)ProjectNode.ImageName.Application);
345 }
346 }
347
348 return iconHandle;
349 }
350
351 /// <summary>
352 /// Return S_OK. Implementation of Closing a nested project is d one in CloseNestedProject which is called by CloseChildren.
353 /// </summary>
354 /// <returns>S_OK</returns>
355 public override int Close()
356 {
357 return VSConstants.S_OK;
358 }
359
360
361 /// <summary>
362 /// Returns the moniker of the nested project.
363 /// </summary>
364 /// <returns></returns>
365 public override string GetMkDocument()
366 {
367 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
368 if(this.isDisposed || this.ProjectMgr == null || this.Pr ojectMgr.IsClosed)
369 {
370 return String.Empty;
371 }
372
373 return this.projectPath;
374 }
375
376 /// <summary>
377 /// Called by the shell when a node has been renamed from the GU I
378 /// </summary>
379 /// <param name="label">The name of the new label.</param>
380 /// <returns>A success or failure value.</returns>
381 public override int SetEditLabel(string label)
382 {
383 int result = this.DelegateSetPropertyToNested((int)__VSH PROPID.VSHPROPID_EditLabel, label);
384 if(ErrorHandler.Succeeded(result))
385 {
386 this.RenameNestedProjectInParentProject(label);
387 }
388
389 return result;
390 }
391
392 /// <summary>
393 /// Called by the shell to get the node caption when the user tr ies to rename from the GUI
394 /// </summary>
395 /// <returns>the node cation</returns>
396 public override string GetEditLabel()
397 {
398 return (string)this.DelegateGetPropertyToNested((int)__V SHPROPID.VSHPROPID_EditLabel);
399 }
400
401 /// <summary>
402 /// This is temporary until we have support for re-adding a nest ed item
403 /// </summary>
404 protected override bool CanDeleteItem(__VSDELETEITEMOPERATION de leteOperation)
405 {
406 return false;
407 }
408
409 /// <summary>
410 /// Delegates the call to the inner hierarchy.
411 /// </summary>
412 /// <param name="reserved">Reserved parameter defined at the IVs PersistHierarchyItem2::ReloadItem parameter.</param>
413 protected internal override void ReloadItem(uint reserved)
414 {
415 #region precondition
416 if(this.isDisposed || this.ProjectMgr == null || this.Pr ojectMgr.IsClosed)
417 {
418 throw new InvalidOperationException();
419 }
420
421 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
422 #endregion
423
424 IVsPersistHierarchyItem2 persistHierachyItem = this.nest edHierarchy as IVsPersistHierarchyItem2;
425
426 // We are expecting that if we get called then the neste dhierarchy supports IVsPersistHierarchyItem2, since then hierrachy should suppor t handling its own reload.
427 // There should be no errormessage to the user since thi s is an internal error, that it cannot be fixed at user level.
428 if(persistHierachyItem == null)
429 {
430 throw new InvalidOperationException();
431 }
432
433 ErrorHandler.ThrowOnFailure(persistHierachyItem.ReloadIt em(VSConstants.VSITEMID_ROOT, reserved));
434 }
435
436 /// <summary>
437 /// Flag indicating that changes to a file can be ignored when i tem is saved or reloaded.
438 /// </summary>
439 /// <param name="ignoreFlag">Flag indicating whether or not to i gnore changes (1 to ignore, 0 to stop ignoring).</param>
440 protected internal override void IgnoreItemFileChanges(bool igno reFlag)
441 {
442 #region precondition
443 if(this.isDisposed || this.ProjectMgr == null || this.Pr ojectMgr.IsClosed)
444 {
445 throw new InvalidOperationException();
446 }
447
448 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
449 #endregion
450
451 this.IgnoreNestedProjectFile(ignoreFlag);
452
453 IVsPersistHierarchyItem2 persistHierachyItem = this.nest edHierarchy as IVsPersistHierarchyItem2;
454
455 // If the IVsPersistHierarchyItem2 is not implemented by the nested just return
456 if(persistHierachyItem == null)
457 {
458 return;
459 }
460
461 ErrorHandler.ThrowOnFailure(persistHierachyItem.IgnoreIt emFileChanges(VSConstants.VSITEMID_ROOT, ignoreFlag ? 1 : 0));
462 }
463
464 /// <summary>
465 /// Sets the VSADDFILEFLAGS that will be used to call the IVsTr ackProjectDocumentsEvents2 OnAddFiles
466 /// </summary>
467 /// <param name="files">The files to which an array of VSADDFILE FLAGS has to be specified.</param>
468 /// <returns></returns>
469 protected internal override VSADDFILEFLAGS[] GetAddFileFlags(str ing[] files)
470 {
471 if(files == null || files.Length == 0)
472 {
473 return new VSADDFILEFLAGS[1] { VSADDFILEFLAGS.VS ADDFILEFLAGS_NoFlags };
474 }
475
476 VSADDFILEFLAGS[] addFileFlags = new VSADDFILEFLAGS[files .Length];
477
478 for(int i = 0; i < files.Length; i++)
479 {
480 addFileFlags[i] = VSADDFILEFLAGS.VSADDFILEFLAGS_ IsNestedProjectFile;
481 }
482
483 return addFileFlags;
484 }
485
486 /// <summary>
487 /// Sets the VSQUERYADDFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryAddFiles
488 /// </summary>
489 /// <param name="files">The files to which an array of VSADDFILE FLAGS has to be specified.</param>
490 /// <returns></returns>
491 protected internal override VSQUERYADDFILEFLAGS[] GetQueryAddFil eFlags(string[] files)
492 {
493 if(files == null || files.Length == 0)
494 {
495 return new VSQUERYADDFILEFLAGS[1] { VSQUERYADDFI LEFLAGS.VSQUERYADDFILEFLAGS_NoFlags };
496 }
497
498 VSQUERYADDFILEFLAGS[] queryAddFileFlags = new VSQUERYADD FILEFLAGS[files.Length];
499
500 for(int i = 0; i < files.Length; i++)
501 {
502 queryAddFileFlags[i] = VSQUERYADDFILEFLAGS.VSQUE RYADDFILEFLAGS_IsNestedProjectFile;
503 }
504
505 return queryAddFileFlags;
506 }
507
508 /// <summary>
509 /// Sets the VSREMOVEFILEFLAGS that will be used to call the IV sTrackProjectDocumentsEvents2 OnRemoveFiles
510 /// </summary>
511 /// <param name="files">The files to which an array of VSREMOVEF ILEFLAGS has to be specified.</param>
512 /// <returns></returns>
513 protected internal override VSREMOVEFILEFLAGS[] GetRemoveFileFla gs(string[] files)
514 {
515 if(files == null || files.Length == 0)
516 {
517 return new VSREMOVEFILEFLAGS[1] { VSREMOVEFILEFL AGS.VSREMOVEFILEFLAGS_NoFlags };
518 }
519
520 VSREMOVEFILEFLAGS[] removeFileFlags = new VSREMOVEFILEFL AGS[files.Length];
521
522 for(int i = 0; i < files.Length; i++)
523 {
524 removeFileFlags[i] = VSREMOVEFILEFLAGS.VSREMOVEF ILEFLAGS_IsNestedProjectFile;
525 }
526
527 return removeFileFlags;
528 }
529
530 /// <summary>
531 /// Sets the VSQUERYREMOVEFILEFLAGS that will be used to call th e IVsTrackProjectDocumentsEvents2 OnQueryRemoveFiles
532 /// </summary>
533 /// <param name="files">The files to which an array of VSQUERYRE MOVEFILEFLAGS has to be specified.</param>
534 /// <returns></returns>
535 protected internal override VSQUERYREMOVEFILEFLAGS[] GetQueryRem oveFileFlags(string[] files)
536 {
537 if(files == null || files.Length == 0)
538 {
539 return new VSQUERYREMOVEFILEFLAGS[1] { VSQUERYRE MOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags };
540 }
541
542 VSQUERYREMOVEFILEFLAGS[] queryRemoveFileFlags = new VSQU ERYREMOVEFILEFLAGS[files.Length];
543
544 for(int i = 0; i < files.Length; i++)
545 {
546 queryRemoveFileFlags[i] = VSQUERYREMOVEFILEFLAGS .VSQUERYREMOVEFILEFLAGS_IsNestedProjectFile;
547 }
548
549 return queryRemoveFileFlags;
550 }
551 #endregion
552
553 #region virtual methods
554 /// <summary>
555 /// Initialize the nested hierarhy node.
556 /// </summary>
557 /// <param name="fileName">The file name of the nested project.< /param>
558 /// <param name="destination">The location of the nested project .</param>
559 /// <param name="projectName">The name of the project.</param>
560 /// <param name="createFlags">The nested project creation flags </param>
561 /// <remarks>This methos should be called just after a NestedPro jectNode object is created.</remarks>
562 public virtual void Init(string fileName, string destination, st ring projectName, __VSCREATEPROJFLAGS createFlags)
563 {
564 if(String.IsNullOrEmpty(fileName))
565 {
566 throw new ArgumentException(SR.GetString(SR.Para meterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "fileName");
567 }
568
569 if(String.IsNullOrEmpty(destination))
570 {
571 throw new ArgumentException(SR.GetString(SR.Para meterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "destination");
572 }
573
574 this.projectName = Path.GetFileName(fileName);
575 this.projectPath = Path.Combine(destination, this.projec tName);
576
577 // get the IVsSolution interface from the global service provider
578 IVsSolution solution = this.GetService(typeof(IVsSolutio n)) as IVsSolution;
579 Debug.Assert(solution != null, "Could not get the IVsSol ution object from the services exposed by this project");
580 if(solution == null)
581 {
582 throw new InvalidOperationException();
583 }
584
585 // Get the project type guid from project element
586 string typeGuidString = this.ItemNode.GetMetadataAndThro w(ProjectFileConstants.TypeGuid, new InvalidOperationException());
587 Guid projectFactoryGuid = Guid.Empty;
588 if(!String.IsNullOrEmpty(typeGuidString))
589 {
590 projectFactoryGuid = new Guid(typeGuidString);
591 }
592
593 // Get the project factory.
594 IVsProjectFactory projectFactory;
595 ErrorHandler.ThrowOnFailure(solution.GetProjectFactory(( uint)0, new Guid[] { projectFactoryGuid }, fileName, out projectFactory));
596
597 this.CreateProjectDirectory();
598
599 //Create new project using factory
600 int cancelled;
601 Guid refiid = NativeMethods.IID_IUnknown;
602 IntPtr projectPtr = IntPtr.Zero;
603
604 // For a nested project the creation at unsafe location is governed by the parent project since the nested project will end up in the co ne of the parent project.
605 bool dontShowAgain = DontShowAgainDialog.ReadDontShowAga inValue(ProjectFactory.DontShowProjectSecurityWarningAgain);
606
607 try
608 {
609 DontShowAgainDialog.WriteDontShowAgainValue(Proj ectFactory.DontShowProjectSecurityWarningAgain, 1);
610 ErrorHandler.ThrowOnFailure(projectFactory.Creat eProject(fileName, destination, projectName, (uint)createFlags, ref refiid, out projectPtr, out cancelled));
611
612 if(projectPtr != IntPtr.Zero)
613 {
614 this.nestedHierarchy = Marshal.GetTypedO bjectForIUnknown(projectPtr, typeof(IVsHierarchy)) as IVsHierarchy;
615 Debug.Assert(this.nestedHierarchy != nul l, "Nested hierarchy could not be created");
616 Debug.Assert(cancelled == 0);
617 }
618 }
619 finally
620 {
621 if(projectPtr != IntPtr.Zero)
622 {
623 // We created a new instance of the proj ect, we need to call release to decrement the ref count
624 // the RCW (this.nestedHierarchy) still has a reference to it which will keep it alive
625 Marshal.Release(projectPtr);
626 }
627
628 // Revert back the old value that security quest ions about unsafe location are stil asked if that was the value.
629 if(!dontShowAgain)
630 {
631 DontShowAgainDialog.WriteDontShowAgainVa lue(ProjectFactory.DontShowProjectSecurityWarningAgain, 0);
632 }
633 }
634
635 if(cancelled != 0 && this.nestedHierarchy == null)
636 {
637 ErrorHandler.ThrowOnFailure(VSConstants.OLE_E_PR OMPTSAVECANCELLED);
638 }
639
640 // Link into the nested VS hierarchy.
641 ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProp erty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchy, this .ProjectMgr));
642 ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProp erty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchyItemid , (object)(int)this.ID));
643
644 this.LockRDTEntry();
645
646 this.ConnectPropertyNotifySink();
647 }
648
649 /// <summary>
650 /// Links a nested project as a virtual project to the solution.
651 /// </summary>
652 protected internal virtual void AddVirtualProject()
653 {
654 // This is the second step in creating and adding a nest ed project. The inner hierarchy must have been
655 // already initialized at this point.
656 #region precondition
657 if(this.nestedHierarchy == null)
658 {
659 throw new InvalidOperationException();
660 }
661 #endregion
662 // get the IVsSolution interface from the global service provider
663 IVsSolution solution = this.GetService(typeof(IVsSolutio n)) as IVsSolution;
664 Debug.Assert(solution != null, "Could not get the IVsSol ution object from the services exposed by this project");
665 if(solution == null)
666 {
667 throw new InvalidOperationException();
668 }
669
670 this.InitializeInstanceGuid();
671
672 // Add virtual project to solution.
673 ErrorHandler.ThrowOnFailure(solution.AddVirtualProjectEx (this.nestedHierarchy, this.VirtualProjectFlags, ref this.projectInstanceGuid));
674
675 // Now set up to listen on file changes on the nested pr oject node.
676 this.ObserveNestedProjectFile();
677 }
678
679 /// <summary>
680 /// The method that does the cleanup.
681 /// </summary>
682 /// <param name="disposing"></param>
683 protected override void Dispose(bool disposing)
684 {
685 // Everybody can go here.
686 if(!this.isDisposed)
687 {
688 try
689 {
690 // Synchronize calls to the Dispose simu lteniously.
691 lock(Mutex)
692 {
693 if(disposing)
694 {
695 this.DisconnectPropertyN otifySink();
696 this.StopObservingNested ProjectFile();
697 this.imageHandler.Close( );
698 }
699 }
700 }
701 finally
702 {
703 base.Dispose(disposing);
704 this.isDisposed = true;
705 }
706 }
707 }
708
709 /// <summary>
710 /// Creates the project directory if it does not exist.
711 /// </summary>
712 /// <returns></returns>
713 protected virtual void CreateProjectDirectory()
714 {
715 string directoryName = Path.GetDirectoryName(this.projec tPath);
716
717 if(!Directory.Exists(directoryName))
718 {
719 Directory.CreateDirectory(directoryName);
720 }
721 }
722
723
724 /// <summary>
725 /// Lock the RDT Entry for the nested project.
726 /// By default this document is marked as "Dont Save as". That m eans the menu File->SaveAs is disabled for the
727 /// nested project node.
728 /// </summary>
729 [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBe CasedCorrectly", MessageId = "RDT")]
730 protected virtual void LockRDTEntry()
731 {
732 // Define flags for the nested project document
733 _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_VirtualDocument | _V SRDTFLAGS.RDT_ProjSlnDocument; ;
734
735 // Request the RDT service
736 IVsRunningDocumentTable rdt = this.GetService(typeof(SVs RunningDocumentTable)) as IVsRunningDocumentTable;
737 Debug.Assert(rdt != null, " Could not get running docume nt table from the services exposed by this project");
738 if(rdt == null)
739 {
740 throw new InvalidOperationException();
741 }
742
743 // First we see if someone else has opened the requested view of the file.
744 uint itemid;
745 IntPtr docData = IntPtr.Zero;
746 IVsHierarchy ivsHierarchy;
747 uint docCookie;
748 IntPtr projectPtr = IntPtr.Zero;
749
750 try
751 {
752 ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocum ent((uint)flags, this.projectPath, out ivsHierarchy, out itemid, out docData, ou t docCookie));
753 flags |= _VSRDTFLAGS.RDT_EditLock;
754
755 if(ivsHierarchy != null && docCookie != (uint)Sh ellConstants.VSDOCCOOKIE_NIL)
756 {
757 if(docCookie != this.DocCookie)
758 {
759 this.DocCookie = docCookie;
760 }
761 }
762 else
763 {
764
765 // get inptr for hierarchy
766 projectPtr = Marshal.GetIUnknownForObjec t(this.nestedHierarchy);
767 Debug.Assert(projectPtr != IntPtr.Zero, " Project pointer for the nested hierarchy has not been initialized");
768 ErrorHandler.ThrowOnFailure(rdt.Register AndLockDocument((uint)flags, this.projectPath, this.ProjectMgr, this.ID, project Ptr, out docCookie));
769
770 this.DocCookie = docCookie;
771 Debug.Assert(this.DocCookie != (uint)She llConstants.VSDOCCOOKIE_NIL, "Invalid cookie when registering document in the ru nning document table.");
772
773 //we must also set the doc cookie on the nested hier
774 this.SetDocCookieOnNestedHier(this.DocCo okie);
775 }
776 }
777 finally
778 {
779 // Release all Inptr's that that were given as o ut pointers
780 if(docData != IntPtr.Zero)
781 {
782 Marshal.Release(docData);
783 }
784 if(projectPtr != IntPtr.Zero)
785 {
786 Marshal.Release(projectPtr);
787 }
788 }
789
790 }
791
792 /// <summary>
793 /// Unlock the RDT entry for the nested project
794 /// </summary>
795 [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBe CasedCorrectly", MessageId = "RDT")]
796 protected virtual void UnlockRDTEntry()
797 {
798 if(this.isDisposed || this.ProjectMgr == null || this.Pr ojectMgr.IsClosed)
799 {
800 return;
801 }
802 // First we see if someone else has opened the requested view of the file.
803 IVsRunningDocumentTable rdt = this.GetService(typeof(SVs RunningDocumentTable)) as IVsRunningDocumentTable;
804 if(rdt != null && this.DocCookie != (int)ShellConstants. VSDOCCOOKIE_NIL)
805 {
806 _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_EditLock;
807
808 ErrorHandler.ThrowOnFailure(rdt.UnlockDocument(( uint)flags, (uint)this.DocCookie));
809 }
810
811 this.DocCookie = (int)ShellConstants.VSDOCCOOKIE_NIL;
812 }
813
814 /// <summary>
815 /// Renames the project file in the parent project structure.
816 /// </summary>
817 /// <param name="label">The new label.</param>
818 protected virtual void RenameNestedProjectInParentProject(string label)
819 {
820 string existingLabel = this.Caption;
821
822 if(String.Compare(existingLabel, label, StringComparison .Ordinal) == 0)
823 {
824 return;
825 }
826
827 string oldFileName = this.projectPath;
828 string oldPath = this.Url;
829
830 try
831 {
832 this.StopObservingNestedProjectFile();
833 this.ProjectMgr.SuspendMSBuild();
834
835 // Check out the project file if necessary.
836 if(!this.ProjectMgr.QueryEditProjectFile(false))
837 {
838 throw Marshal.GetExceptionForHR(VSConsta nts.OLE_E_PROMPTSAVECANCELLED);
839 }
840
841
842 string newFileName = label + Path.GetExtension(o ldFileName);
843 this.SaveNestedProjectItemInProjectFile(newFileN ame);
844
845 string projectDirectory = Path.GetDirectoryName( oldFileName);
846
847 // update state.
848 this.projectName = newFileName;
849 this.projectPath = Path.Combine(projectDirectory , this.projectName);
850
851 // Unload and lock the RDT entries
852 this.UnlockRDTEntry();
853 this.LockRDTEntry();
854
855 // Since actually this is a rename in our hierar chy notify the tracker that a rename has happened.
856 this.ProjectMgr.Tracker.OnItemRenamed(oldPath, t his.projectPath, VSRENAMEFILEFLAGS.VSRENAMEFILEFLAGS_IsNestedProjectFile);
857 }
858 finally
859 {
860 this.ObserveNestedProjectFile();
861 this.ProjectMgr.ResumeMSBuild(this.ProjectMgr.Re EvaluateProjectFileTargetName);
862 }
863 }
864 /// <summary>
865 /// Saves the nested project information in the project file.
866 /// </summary>
867 /// <param name="newFileName"></param>
868 protected virtual void SaveNestedProjectItemInProjectFile(string newFileName)
869 {
870 string existingInclude = this.ItemNode.Item.Include;
871 string existingRelativePath = Path.GetDirectoryName(exis tingInclude);
872 string newRelativePath = Path.Combine(existingRelativePa th, newFileName);
873 this.ItemNode.Rename(newRelativePath);
874 }
875 #endregion
876
877 #region helper methods
878 /// <summary>
879 /// Closes a nested project and releases the nested hierrachy po inter.
880 /// </summary>
881 internal void CloseNestedProjectNode()
882 {
883 if(this.isDisposed || this.ProjectMgr == null || this.Pr ojectMgr.IsClosed)
884 {
885 return;
886 }
887
888 uint itemid = VSConstants.VSITEMID_NIL;
889 try
890 {
891 this.DisconnectPropertyNotifySink();
892
893 IVsUIHierarchy hier;
894
895 IVsWindowFrame windowFrame;
896 VsShellUtilities.IsDocumentOpen(this.ProjectMgr. Site, this.projectPath, Guid.Empty, out hier, out itemid, out windowFrame);
897
898
899 if(itemid == VSConstants.VSITEMID_NIL)
900 {
901 this.UnlockRDTEntry();
902 }
903
904 IVsSolution solution = this.GetService(typeof(IV sSolution)) as IVsSolution;
905 if(solution == null)
906 {
907 throw new InvalidOperationException();
908 }
909
910 ErrorHandler.ThrowOnFailure(solution.RemoveVirtu alProject(this.nestedHierarchy, 0));
911
912 }
913 finally
914 {
915 this.StopObservingNestedProjectFile();
916
917 // if we haven't already release the RDT cookie, do so now.
918 if(itemid == VSConstants.VSITEMID_NIL)
919 {
920 this.UnlockRDTEntry();
921 }
922
923 this.Dispose(true);
924 }
925 }
926
927 private void InitializeInstanceGuid()
928 {
929 if(this.projectInstanceGuid != Guid.Empty)
930 {
931 return;
932 }
933
934 Guid instanceGuid = Guid.Empty;
935
936 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
937
938 // This method should be called from the open children m ethod, then we can safely use the IsNewProject property
939 if(this.ProjectMgr.IsNewProject)
940 {
941 instanceGuid = Guid.NewGuid();
942 this.ItemNode.SetMetadata(ProjectFileConstants.I nstanceGuid, instanceGuid.ToString("B"));
943 ErrorHandler.ThrowOnFailure(this.nestedHierarchy .SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectID Guid, ref instanceGuid));
944 }
945 else
946 {
947 // Get a guid from the nested hiererachy.
948 Guid nestedHiererachyInstanceGuid;
949 ErrorHandler.ThrowOnFailure(this.nestedHierarchy .GetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectID Guid, out nestedHiererachyInstanceGuid));
950
951 // Get instance guid from the project file. If i t does not exist then we create one.
952 string instanceGuidAsString = this.ItemNode.GetM etadata(ProjectFileConstants.InstanceGuid);
953
954 // 1. nestedHiererachyInstanceGuid is empty and instanceGuidAsString is empty then create a new one.
955 // 2. nestedHiererachyInstanceGuid is empty and instanceGuidAsString not empty use instanceGuidAsString and update the nested pr oject object by calling SetGuidProperty.
956 // 3. nestedHiererachyInstanceGuid is not empty instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update t he outer project element.
957 // 4. nestedHiererachyInstanceGuid is not empty instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update t he outer project element.
958
959 if(nestedHiererachyInstanceGuid == Guid.Empty && String.IsNullOrEmpty(instanceGuidAsString))
960 {
961 instanceGuid = Guid.NewGuid();
962 }
963 else if(nestedHiererachyInstanceGuid == Guid.Emp ty && !String.IsNullOrEmpty(instanceGuidAsString))
964 {
965 instanceGuid = new Guid(instanceGuidAsSt ring);
966
967 ErrorHandler.ThrowOnFailure(this.nestedH ierarchy.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_P rojectIDGuid, ref instanceGuid));
968 }
969 else if(nestedHiererachyInstanceGuid != Guid.Emp ty)
970 {
971 instanceGuid = nestedHiererachyInstanceG uid;
972
973 // If the instanceGuidAsString is empty then creating a guid out of it would throw an exception.
974 if(String.IsNullOrEmpty(instanceGuidAsSt ring) || nestedHiererachyInstanceGuid != new Guid(instanceGuidAsString))
975 {
976 this.ItemNode.SetMetadata(Projec tFileConstants.InstanceGuid, instanceGuid.ToString("B"));
977 }
978 }
979 }
980
981 this.projectInstanceGuid = instanceGuid;
982 }
983
984 private void SetDocCookieOnNestedHier(uint itemDocCookie)
985 {
986 object docCookie = (int)itemDocCookie;
987
988 try
989 {
990 ErrorHandler.ThrowOnFailure(this.nestedHierarchy .SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ItemDocCookie , docCookie));
991 }
992 catch(NotImplementedException)
993 {
994 //we swallow this exception on purpose
995 }
996 }
997
998 private void InitImageHandler()
999 {
1000 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
1001
1002 if(null == imageHandler)
1003 {
1004 imageHandler = new ImageHandler();
1005 }
1006 object imageListAsPointer = null;
1007 ErrorHandler.ThrowOnFailure(this.nestedHierarchy.GetProp erty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconImgList, out imag eListAsPointer));
1008 if(imageListAsPointer != null)
1009 {
1010 this.imageHandler.ImageList = Utilities.GetImage List(imageListAsPointer);
1011 }
1012 }
1013
1014 /// <summary>
1015 /// Delegates Getproperty calls to the inner nested.
1016 /// </summary>
1017 /// <param name="propID">The property to delegate.</param>
1018 /// <returns>The return of the GetProperty from nested.</returns >
1019 private object DelegateGetPropertyToNested(int propID)
1020 {
1021 object returnValue = null;
1022 if(!this.ProjectMgr.IsClosed)
1023 {
1024 Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method");
1025
1026 // Do not throw since some project types will re turn E_FAIL if they do not support a property.
1027 ErrorHandler.ThrowOnFailure(this.nestedHierarchy .GetProperty(VSConstants.VSITEMID_ROOT, propID, out returnValue));
1028 }
1029 return returnValue;
1030 }
1031
1032 /// <summary>
1033 /// Delegates Setproperty calls to the inner nested.
1034 /// </summary>
1035 /// <param name="propID">The property to delegate.</param>
1036 /// <param name="value">The property to set.</param>
1037 /// <returns>The return of the SetProperty from nested.</returns >
1038 private int DelegateSetPropertyToNested(int propID, object value )
1039 {
1040 if(this.ProjectMgr.IsClosed)
1041 {
1042 return VSConstants.E_FAIL;
1043 }
1044
1045 Debug.Assert(this.nestedHierarchy != null, "The nested h ierarchy object must be created before calling this method");
1046
1047 // Do not throw since some project types will return E_F AIL if they do not support a property.
1048 return this.nestedHierarchy.SetProperty(VSConstants.VSIT EMID_ROOT, propID, value);
1049 }
1050
1051 /// <summary>
1052 /// Starts observing changes on this file.
1053 /// </summary>
1054 private void ObserveNestedProjectFile()
1055 {
1056 ProjectContainerNode parent = this.ProjectMgr as Project ContainerNode;
1057 Debug.Assert(parent != null, "The parent project for nes ted projects should be subclassed from ProjectContainerNode");
1058 parent.NestedProjectNodeReloader.ObserveItem(this.GetMkD ocument(), this.ID);
1059 }
1060
1061 /// <summary>
1062 /// Stops observing changes on this file.
1063 /// </summary>
1064 private void StopObservingNestedProjectFile()
1065 {
1066 ProjectContainerNode parent = this.ProjectMgr as Project ContainerNode;
1067 Debug.Assert(parent != null, "The parent project for nes ted projects should be subclassed from ProjectContainerNode");
1068 parent.NestedProjectNodeReloader.StopObservingItem(this. GetMkDocument());
1069 }
1070
1071 /// <summary>
1072 /// Ignores observing changes on this file depending on the bool ean flag.
1073 /// </summary>
1074 /// <param name="ignoreFlag">Flag indicating whether or not to i gnore changes (1 to ignore, 0 to stop ignoring).</param>
1075 private void IgnoreNestedProjectFile(bool ignoreFlag)
1076 {
1077 ProjectContainerNode parent = this.ProjectMgr as Project ContainerNode;
1078 Debug.Assert(parent != null, "The parent project for nes ted projects should be subclassed from ProjectContainerNode");
1079 parent.NestedProjectNodeReloader.IgnoreItemChanges(this. GetMkDocument(), ignoreFlag);
1080 }
1081
1082 /// <summary>
1083 /// We need to advise property notify sink on project properties so that
1084 /// we know when the project file is renamed through a property.
1085 /// </summary>
1086 private void ConnectPropertyNotifySink()
1087 {
1088 if(this.projectPropertyNotifySinkCookie != (uint)ShellCo nstants.VSCOOKIE_NIL)
1089 {
1090 return;
1091 }
1092
1093 IConnectionPoint connectionPoint = this.GetConnectionPoi ntFromPropertySink();
1094 if(connectionPoint != null)
1095 {
1096 connectionPoint.Advise(this, out this.projectPro pertyNotifySinkCookie);
1097 }
1098 }
1099
1100 /// <summary>
1101 /// Disconnects the propertynotify sink
1102 /// </summary>
1103 private void DisconnectPropertyNotifySink()
1104 {
1105 if(this.projectPropertyNotifySinkCookie == (uint)ShellCo nstants.VSCOOKIE_NIL)
1106 {
1107 return;
1108 }
1109
1110 IConnectionPoint connectionPoint = this.GetConnectionPoi ntFromPropertySink();
1111 if(connectionPoint != null)
1112 {
1113 connectionPoint.Unadvise(this.projectPropertyNot ifySinkCookie);
1114 this.projectPropertyNotifySinkCookie = (uint)She llConstants.VSCOOKIE_NIL;
1115 }
1116 }
1117
1118 /// <summary>
1119 /// Gets a ConnectionPoint for the IPropertyNotifySink interface .
1120 /// </summary>
1121 /// <returns></returns>
1122 private IConnectionPoint GetConnectionPointFromPropertySink()
1123 {
1124 IConnectionPoint connectionPoint = null;
1125 object browseObject = this.GetProperty((int)__VSHPROPID. VSHPROPID_BrowseObject);
1126 IConnectionPointContainer connectionPointContainer = bro wseObject as IConnectionPointContainer;
1127
1128 if(connectionPointContainer != null)
1129 {
1130 Guid guid = typeof(IPropertyNotifySink).GUID;
1131 connectionPointContainer.FindConnectionPoint(ref guid, out connectionPoint);
1132 }
1133
1134 return connectionPoint;
1135 }
1136 #endregion
1137 }
1138 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698