1 /***************************************************************************
3 Copyright (c) Microsoft Corporation. All rights reserved.
4 This code is licensed under the Visual Studio SDK license terms.
5 THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
6 ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
7 IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
8 PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
10 ***************************************************************************/
12 namespace Microsoft
.VisualStudio
.Shell
.Flavor
15 using System
.Diagnostics
;
16 using System
.Diagnostics
.CodeAnalysis
;
17 using System
.Globalization
;
18 using System
.Runtime
.InteropServices
;
19 using Microsoft
.VisualStudio
.Shell
.Interop
;
20 using Microsoft
.VisualStudio
.OLE
.Interop
;
21 using Microsoft
.VisualStudio
.Shell
;
22 using ErrorHandler
= Microsoft
.VisualStudio
.ErrorHandler
;
24 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject"]/*' />
26 /// A project that is a subtype/flavor of an inner project.
27 /// The default behavior of all methods is to delegate to the
28 /// inner project. For any behavior you want to change, simply
29 /// handle the request yourself.
32 public abstract class FlavoredProjectBase
:
33 IVsAggregatableProjectCorrected
,
34 System
.IServiceProvider
,
39 // Keep interface reference for all interface we override
41 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject._innerVsAggregatableProject"]/*' />
42 protected IVsAggregatableProjectCorrected _innerVsAggregatableProject
;
43 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject._innerVsHierarchy"]/*' />
44 protected IVsHierarchy _innerVsHierarchy
;
45 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject._innerVsUIHierarchy"]/*' />
46 protected IVsUIHierarchy _innerVsUIHierarchy
;
47 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject._innerOleCommandTarget"]/*' />
48 protected IOleCommandTarget _innerOleCommandTarget
;
50 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.serviceProvider"]/*' />
51 protected System
.IServiceProvider serviceProvider
;
53 private OleMenuCommandService _menuService
;
54 private DocumentsEventsSink _documentsEventsSink
;
55 private bool _hierarchyClosed
= false;
56 private int _inExecCommand
= 0;
60 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.FlavoredProject"]/*' />
61 public FlavoredProjectBase()
63 _documentsEventsSink
= new FlavoredProjectBase
.DocumentsEventsSink(this);
66 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.GetComInterface"]/*' />
68 /// A project derived from this base class will be aggregated with a native COM component (the ProjectAggregator object)
69 /// that can also aggregate an inner project in case of flavoring.
70 /// Because of this structure, all the request for interfaces exposed to COM must be handled by the external object that
71 /// has a special implementation of QueryInterface that handles both inner and outer projects.
72 /// If you don’t use this helper method when requesting an interface you can get unexpected InvalidCast exceptions.
73 /// Note that if you want to get the implementation of an interface implemented by your FlavoredProjectBase-derived
74 /// object, then you must use the standard cast operator.
76 public Interface_T GetComInterface
<Interface_T
>() where Interface_T
: class
78 IntPtr thisUnknown
= IntPtr
.Zero
;
79 IntPtr interfacePtr
= IntPtr
.Zero
;
82 thisUnknown
= Marshal
.GetIUnknownForObject(this);
83 Guid iid
= typeof(Interface_T
).GUID
;
84 if (ErrorHandler
.Failed(Marshal
.QueryInterface(thisUnknown
, ref iid
, out interfacePtr
)) || (IntPtr
.Zero
== interfacePtr
))
88 return Marshal
.GetObjectForIUnknown(interfacePtr
) as Interface_T
;
92 if (IntPtr
.Zero
!= thisUnknown
)
94 Marshal
.Release(thisUnknown
);
96 if (IntPtr
.Zero
!= interfacePtr
)
98 Marshal
.Release(interfacePtr
);
103 #region IVsAggregatableProjectCorrected
106 /// This is where all QI for interface on the inner object should happen
107 /// Then set the inner project
108 /// wait for InitializeForOuter to be called to do the real initialization
110 int IVsAggregatableProjectCorrected
.SetInnerProject(IntPtr innerIUnknown
)
112 // delegate to the protected method
113 this.SetInnerProject(innerIUnknown
);
115 return VSConstants
.S_OK
;
118 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.SetInnerProject"]/*' />
120 /// This is were all QI for interface on the inner object should happen
121 /// Then set the inner project
122 /// wait for InitializeForOuter to be called to do the real initialization
124 protected virtual void SetInnerProject(IntPtr innerIUnknown
)
128 inner
= Marshal
.GetObjectForIUnknown(innerIUnknown
);
130 // Keep a reference to each interface we want to call on the inner project
131 // we must do it now as once we call SetInner the AddRef would be forwarded to ourselves
132 _innerVsAggregatableProject
= inner
as IVsAggregatableProjectCorrected
;
133 Debug
.Assert(inner
!= null, "Failed to retrieve IVsAggregatableProjectCorrected from inner Project");
134 _innerVsHierarchy
= (IVsHierarchy
)inner
;
135 _innerVsUIHierarchy
= (IVsUIHierarchy
)inner
;
136 // "As" should return null without throwing in the event the base project does not implement the interface
137 _innerOleCommandTarget
= inner
as IOleCommandTarget
;
139 // Setup our menu command service
140 if (this.serviceProvider
== null)
141 throw new NotSupportedException("serviceProvider should have been set before SetInnerProject gets called.");
142 _menuService
= new OleMenuCommandService(this, _innerOleCommandTarget
);
144 // Pass the inner project pointer to the VisualStudio.ProjectAggregator2 object. This native object
145 // has a special implementation of QueryInterface that delegates first to our managed FlavoredProjectBase
146 // derived object and then to the inner project (either the base project or the next project flavor down).
147 IntPtr thisIUnknown
= IntPtr
.Zero
;
148 IVsProjectAggregator2 vsProjectAggregator2
= null;
151 thisIUnknown
= Marshal
.GetIUnknownForObject(this);
152 vsProjectAggregator2
= (IVsProjectAggregator2
)Marshal
.GetObjectForIUnknown(thisIUnknown
);
153 if (vsProjectAggregator2
!= null)
154 vsProjectAggregator2
.SetInner(innerIUnknown
);
158 if (thisIUnknown
!= IntPtr
.Zero
)
159 Marshal
.Release(thisIUnknown
);
164 /// Do the initialization here (such as loading flavor specific
165 /// information from the project)
167 int IVsAggregatableProjectCorrected
.InitializeForOuter(string fileName
, string location
, string name
,
168 uint flags
, ref Guid guidProject
, out IntPtr project
, out int canceled
)
170 int hr
= VSConstants
.S_OK
;
171 project
= IntPtr
.Zero
;
174 if (_innerVsAggregatableProject
== null || guidProject
!= VSConstants
.IID_IUnknown
)
175 throw new NotSupportedException();
177 IntPtr thisIUnknown
= IntPtr
.Zero
;
180 thisIUnknown
= Marshal
.GetIUnknownForObject(this);
181 if (thisIUnknown
!= IntPtr
.Zero
)
182 hr
= Marshal
.QueryInterface(thisIUnknown
, ref guidProject
, out project
);
186 if (thisIUnknown
!= IntPtr
.Zero
)
187 Marshal
.Release(thisIUnknown
);
191 this.InitializeForOuter(fileName
, location
, name
, flags
, ref guidProject
, out cancel
);
198 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.InitializeForOuter"]/*' />
200 /// Allow the project to initialize itself.
201 /// At this point it possible to call the inner project
202 /// Also allow canceling the project creation
204 /// <returns>Return true to cancel the project creation</returns>
205 protected virtual void InitializeForOuter(string fileName
, string location
, string name
, uint flags
, ref Guid guidProject
, out bool cancel
)
210 /// This is called when all object in aggregation have received InitializeForOuter calls.
211 /// At this point the aggregation is complete and fully functional.
213 int IVsAggregatableProjectCorrected
.OnAggregationComplete()
215 this.OnAggregationComplete();
216 if (_innerVsAggregatableProject
!= null)
217 return _innerVsAggregatableProject
.OnAggregationComplete();
218 return VSConstants
.S_OK
;
221 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.OnAggregationComplete"]/*' />
223 /// This is called when all object in aggregation have received InitializeForOuter calls.
224 /// At this point the aggregation is complete and fully functional.
226 protected virtual void OnAggregationComplete()
228 // This will subscribe to the IVsTrackProjectDocumentsEvents.
229 // This is not required to flavor a project but makes it easier for derived class
230 // to subscribe to these events.
231 IVsTrackProjectDocuments2 trackDocuments
= GetTrackProjectDocuments();
232 ErrorHandler
.ThrowOnFailure(trackDocuments
.AdviseTrackProjectDocumentsEvents(_documentsEventsSink
, out cookie
));
236 /// This must be delegetated to the inner most project
238 int IVsAggregatableProjectCorrected
.SetAggregateProjectTypeGuids(string projectTypeGuids
)
240 if (_innerVsAggregatableProject
== null)
241 throw new NotSupportedException();
243 return _innerVsAggregatableProject
.SetAggregateProjectTypeGuids(projectTypeGuids
);
247 /// This must be delegetated to the inner most project
249 int IVsAggregatableProjectCorrected
.GetAggregateProjectTypeGuids(out string projectTypeGuids
)
251 if (_innerVsAggregatableProject
== null)
252 throw new NotSupportedException();
254 return _innerVsAggregatableProject
.GetAggregateProjectTypeGuids(out projectTypeGuids
);
261 // Most methods call protected virtual methods which delegate to the inner project.
262 // Derived classes should override those protected method if they want to change the
266 int IVsHierarchy
.AdviseHierarchyEvents(Microsoft
.VisualStudio
.Shell
.Interop
.IVsHierarchyEvents eventSink
, out uint cookie
)
268 cookie
= this.AdviseHierarchyEvents(eventSink
);
269 return VSConstants
.S_OK
;
272 int IVsHierarchy
.Close()
274 _hierarchyClosed
= true;
279 // Unsubscribe to events
280 IVsTrackProjectDocuments2 trackDocuments
= GetTrackProjectDocuments();
281 trackDocuments
.UnadviseTrackProjectDocumentsEvents(cookie
);
285 if (this._menuService
!= null)
287 OleMenuCommandService tempService
= this._menuService
;
288 this._menuService
= null;
289 tempService
.Dispose();
292 if (_inExecCommand
== 0)
295 return VSConstants
.S_OK
;
298 public virtual void FreeInterfaces()
300 if (this._menuService
!= null)
302 OleMenuCommandService tempService
= this._menuService
;
303 this._menuService
= null;
304 tempService
.Dispose();
307 _innerOleCommandTarget
= null;
308 _innerVsAggregatableProject
= null;
309 _innerVsUIHierarchy
= null;
310 _innerVsHierarchy
= null;
315 int IVsHierarchy
.GetCanonicalName(uint itemId
, out string name
)
317 return this.GetCanonicalName(itemId
, out name
);
320 int IVsHierarchy
.GetGuidProperty(uint itemId
, int propId
, out System
.Guid guid
)
322 guid
= this.GetGuidProperty(itemId
, propId
);
323 return VSConstants
.S_OK
;
326 int IVsHierarchy
.GetNestedHierarchy(uint itemId
, ref System
.Guid guidHierarchyNested
, out System
.IntPtr hierarchyNested
, out uint itemIdNested
)
328 return this.GetNestedHierarchy(itemId
, ref guidHierarchyNested
, out hierarchyNested
, out itemIdNested
);
331 int IVsHierarchy
.GetProperty(uint itemId
, int propId
, out System
.Object property
)
333 // While other methods expect the protected method to throw, for GetProperty
334 // we break this pattern as it is called much more often and it is legitimate to
335 // return not implemented. Therefore it can help perf and debugging experience
336 return this.GetProperty(itemId
, propId
, out property
);
339 int IVsHierarchy
.GetSite(out Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider serviceProvider
)
341 serviceProvider
= this.GetSite();
342 return VSConstants
.S_OK
;
345 int IVsHierarchy
.ParseCanonicalName(string name
, out uint itemId
)
347 return this.ParseCanonicalName(name
, out itemId
);
350 int IVsHierarchy
.QueryClose(out int canClose
)
353 if (this.QueryClose())
355 return VSConstants
.S_OK
;
358 int IVsHierarchy
.SetGuidProperty(uint itemId
, int propId
, ref System
.Guid guid
)
360 this.SetGuidProperty(itemId
, propId
, ref guid
);
361 return VSConstants
.S_OK
;
364 int IVsHierarchy
.SetSite(Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider serviceProvider
)
366 this.serviceProvider
= (System
.IServiceProvider
)new ServiceProvider(serviceProvider
);
367 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.SetSite(serviceProvider
));
368 return VSConstants
.S_OK
;
371 int IVsHierarchy
.UnadviseHierarchyEvents(uint cookie
)
373 this.UnadviseHierarchyEvents(cookie
);
374 return VSConstants
.S_OK
;
377 int IVsHierarchy
.SetProperty(uint itemId
, int propId
, System
.Object property
)
379 return this.SetProperty(itemId
, propId
, property
);
382 int IVsHierarchy
.Unused0()
385 return VSConstants
.S_OK
;
388 int IVsHierarchy
.Unused1()
391 return VSConstants
.S_OK
;
394 int IVsHierarchy
.Unused2()
397 return VSConstants
.S_OK
;
400 int IVsHierarchy
.Unused3()
403 return VSConstants
.S_OK
;
406 int IVsHierarchy
.Unused4()
409 return VSConstants
.S_OK
;
412 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.AdviseHierarchyEvents"]/*' />
413 protected virtual uint AdviseHierarchyEvents(Microsoft
.VisualStudio
.Shell
.Interop
.IVsHierarchyEvents eventSink
)
416 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.AdviseHierarchyEvents(eventSink
, out cookie
));
420 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.Close"]/*' />
421 protected virtual void Close()
423 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.Close());
426 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.GetCanonicalName"]/*' />
427 protected virtual int GetCanonicalName(uint itemId
, out string name
)
429 return _innerVsHierarchy
.GetCanonicalName(itemId
, out name
);
432 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.GetGuidProperty"]/*' />
433 protected virtual Guid
GetGuidProperty(uint itemId
, int propId
)
435 Guid property
= Guid
.Empty
;
436 if (_innerVsHierarchy
!= null)
437 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.GetGuidProperty(itemId
, propId
, out property
));
443 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.GetNestedHierarchy"]/*' />
444 protected virtual int GetNestedHierarchy(uint itemId
, ref System
.Guid guidHierarchyNested
, out System
.IntPtr hierarchyNested
, out uint itemIdNested
)
446 if (_innerVsHierarchy
!= null)
447 return _innerVsHierarchy
.GetNestedHierarchy(itemId
, ref guidHierarchyNested
, out hierarchyNested
, out itemIdNested
);
450 hierarchyNested
= IntPtr
.Zero
;
451 itemIdNested
= VSConstants
.VSITEMID_NIL
;
452 return VSConstants
.E_NOINTERFACE
;
456 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.GetProperty"]/*' />
457 protected virtual int GetProperty(uint itemId
, int propId
, out Object property
)
459 if (_innerVsHierarchy
!= null)
460 return _innerVsHierarchy
.GetProperty(itemId
, propId
, out property
);
464 return VSConstants
.E_UNEXPECTED
;
468 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.GetSite"]/*' />
469 protected virtual Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider
GetSite()
471 Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider serviceProvider
;
472 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.GetSite(out serviceProvider
));
473 return serviceProvider
;
476 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.ParseCanonicalName"]/*' />
477 protected virtual int ParseCanonicalName(string name
, out uint itemId
)
479 return _innerVsHierarchy
.ParseCanonicalName(name
, out itemId
);
482 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.QueryClose"]/*' />
483 protected virtual bool QueryClose()
486 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.QueryClose(out canClose
));
487 return (canClose
!= 0);
490 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.SetGuidProperty"]/*' />
491 protected virtual void SetGuidProperty(uint itemId
, int propId
, ref System
.Guid guid
)
493 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.SetGuidProperty(itemId
, propId
, ref guid
));
496 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.UnadviseHierarchyEvents"]/*' />
497 protected virtual void UnadviseHierarchyEvents(uint cookie
)
499 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.UnadviseHierarchyEvents(cookie
));
502 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.SetProperty"]/*' />
503 protected virtual int SetProperty(uint itemId
, int propId
, System
.Object property
)
505 return _innerVsHierarchy
.SetProperty(itemId
, propId
, property
);
508 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.Unused0"]/*' />
509 protected virtual void Unused0()
511 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.Unused0());
514 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.Unused1"]/*' />
515 protected virtual void Unused1()
517 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.Unused1());
520 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.Unused2"]/*' />
521 protected virtual void Unused2()
523 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.Unused2());
526 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.Unused3"]/*' />
527 protected virtual void Unused3()
529 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.Unused3());
532 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.Unused4"]/*' />
533 protected virtual void Unused4()
535 ErrorHandler
.ThrowOnFailure(_innerVsHierarchy
.Unused4());
539 #region IVsUIHierarchy Members
541 // All methods (except for QueryStatusCommand and ExecCommand) call the IVsHierarchy implementation.
542 // QueryStatusCommand and ExecCommand call a protected virtual method that the base class can override.
543 // Note that we QI for IVsUIHierarchy on this so that if we are flavored we call the outer IVsHierarchy.
546 int IVsUIHierarchy
.QueryStatusCommand(uint itemid
, ref Guid pguidCmdGroup
, uint cCmds
, OLECMD
[] prgCmds
, IntPtr pCmdText
)
548 return this.QueryStatusCommand(itemid
, ref pguidCmdGroup
, cCmds
, prgCmds
, pCmdText
);
550 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.QueryStatusCommand"]/*' />
551 protected virtual int QueryStatusCommand(uint itemid
, ref Guid pguidCmdGroup
, uint cCmds
, OLECMD
[] prgCmds
, IntPtr pCmdText
)
553 return _innerVsUIHierarchy
.QueryStatusCommand(itemid
, ref pguidCmdGroup
, cCmds
, prgCmds
, pCmdText
);
556 int IVsUIHierarchy
.ExecCommand(uint itemid
, ref Guid pguidCmdGroup
, uint nCmdID
, uint nCmdexecopt
, IntPtr pvaIn
, IntPtr pvaOut
)
558 int hr
= VSConstants
.S_OK
;
562 hr
= this.ExecCommand(itemid
, ref pguidCmdGroup
, nCmdID
, nCmdexecopt
, pvaIn
, pvaOut
);
567 Debug
.Assert(_inExecCommand
>= 0);
568 if (_hierarchyClosed
&& _inExecCommand
== 0)
575 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.ExecCommand"]/*' />
576 protected virtual int ExecCommand(uint itemid
, ref Guid pguidCmdGroup
, uint nCmdID
, uint nCmdexecopt
, IntPtr pvaIn
, IntPtr pvaOut
)
578 return _innerVsUIHierarchy
.ExecCommand(itemid
, ref pguidCmdGroup
, nCmdID
, nCmdexecopt
, pvaIn
, pvaOut
);
581 int IVsUIHierarchy
.AdviseHierarchyEvents(IVsHierarchyEvents pEventSink
, out uint pdwCookie
)
583 return ((IVsHierarchy
)this).AdviseHierarchyEvents(pEventSink
, out pdwCookie
);
586 int IVsUIHierarchy
.Close()
588 return ((IVsHierarchy
)this).Close();
591 int IVsUIHierarchy
.GetCanonicalName(uint itemid
, out string pbstrName
)
593 return ((IVsHierarchy
)this).GetCanonicalName(itemid
, out pbstrName
);
596 int IVsUIHierarchy
.GetGuidProperty(uint itemid
, int propid
, out Guid pguid
)
598 return ((IVsHierarchy
)this).GetGuidProperty(itemid
, propid
, out pguid
);
601 int IVsUIHierarchy
.GetNestedHierarchy(uint itemid
, ref Guid iidHierarchyNested
, out IntPtr ppHierarchyNested
, out uint pitemidNested
)
603 return ((IVsHierarchy
)this).GetNestedHierarchy(itemid
, ref iidHierarchyNested
, out ppHierarchyNested
, out pitemidNested
);
606 int IVsUIHierarchy
.GetProperty(uint itemid
, int propid
, out object pvar
)
608 return ((IVsHierarchy
)this).GetProperty(itemid
, propid
, out pvar
);
611 int IVsUIHierarchy
.GetSite(out Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider ppSP
)
613 return ((IVsHierarchy
)this).GetSite(out ppSP
);
616 int IVsUIHierarchy
.ParseCanonicalName(string pszName
, out uint pitemid
)
618 return ((IVsHierarchy
)this).ParseCanonicalName(pszName
, out pitemid
);
621 int IVsUIHierarchy
.QueryClose(out int pfCanClose
)
623 return ((IVsHierarchy
)this).QueryClose(out pfCanClose
);
626 int IVsUIHierarchy
.SetGuidProperty(uint itemid
, int propid
, ref Guid rguid
)
628 return ((IVsHierarchy
)this).SetGuidProperty(itemid
, propid
, ref rguid
);
631 int IVsUIHierarchy
.SetProperty(uint itemid
, int propid
, object var)
633 return ((IVsHierarchy
)this).SetProperty(itemid
, propid
, var);
636 int IVsUIHierarchy
.SetSite(Microsoft
.VisualStudio
.OLE
.Interop
.IServiceProvider psp
)
638 return ((IVsHierarchy
)this).SetSite(psp
);
641 int IVsUIHierarchy
.UnadviseHierarchyEvents(uint dwCookie
)
643 return ((IVsHierarchy
)this).UnadviseHierarchyEvents(dwCookie
);
646 int IVsUIHierarchy
.Unused0()
648 return ((IVsHierarchy
)this).Unused0();
651 int IVsUIHierarchy
.Unused1()
653 return ((IVsHierarchy
)this).Unused1();
656 int IVsUIHierarchy
.Unused2()
658 return ((IVsHierarchy
)this).Unused2();
661 int IVsUIHierarchy
.Unused3()
663 return ((IVsHierarchy
)this).Unused3();
666 int IVsUIHierarchy
.Unused4()
668 return ((IVsHierarchy
)this).Unused4();
674 #region IOleCommandTarget Members
676 int IOleCommandTarget
.Exec(ref Guid pguidCmdGroup
, uint nCmdID
, uint nCmdexecopt
, IntPtr pvaIn
, IntPtr pvaOut
)
678 int hr
= ((IOleCommandTarget
)_menuService
).Exec(ref pguidCmdGroup
, nCmdID
, nCmdexecopt
, pvaIn
, pvaOut
);
682 int IOleCommandTarget
.QueryStatus(ref Guid pguidCmdGroup
, uint cCmds
, OLECMD
[] prgCmds
, IntPtr pCmdText
)
684 int hr
= ((IOleCommandTarget
)_menuService
).QueryStatus(ref pguidCmdGroup
, cCmds
, prgCmds
, pCmdText
);
690 #region IServiceProvider Members
692 object System
.IServiceProvider
.GetService(Type serviceType
)
694 if (serviceType
.IsEquivalentTo(typeof(IOleCommandTarget
)))
695 return ((IOleCommandTarget
)_menuService
);
696 else if (serviceType
.IsEquivalentTo(typeof(System
.ComponentModel
.Design
.IMenuCommandService
)))
697 return ((System
.ComponentModel
.Design
.IMenuCommandService
)_menuService
);
699 return this.serviceProvider
.GetService(serviceType
);
705 #region Events (subset of IVsTrackProjectDocumentsEvents)
706 // This makes it easier for the derived class to subscribe to only the events it
707 // is really interested in and get one event per file. This also filter events
708 // and only send events that have to do with this project
710 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.EventHandler"]/*' />
711 public delegate void EventHandler
<ProjectDocumentsChangeEventArgs
>(object sender
, ProjectDocumentsChangeEventArgs e
);
713 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.FileAdded"]/*' />
715 /// Called after a file was added to this project.
717 public event EventHandler
<ProjectDocumentsChangeEventArgs
> FileAdded
;
718 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.FileRemoved"]/*' />
720 /// Called after a file was remove from this project.
722 public event EventHandler
<ProjectDocumentsChangeEventArgs
> FileRemoved
;
723 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.FileRenamed"]/*' />
725 /// Called after a file was renamed in this project.
727 public event EventHandler
<ProjectDocumentsChangeEventArgs
> FileRenamed
;
729 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.DirectoryAdded"]/*' />
731 /// Called after a directory was added to this project.
733 public event EventHandler
<ProjectDocumentsChangeEventArgs
> DirectoryAdded
;
734 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.DirectoryRemoved"]/*' />
736 /// Called after a directory was remove from this project.
738 public event EventHandler
<ProjectDocumentsChangeEventArgs
> DirectoryRemoved
;
739 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.DirectoryRenamed"]/*' />
741 /// Called after a directory was renamed in this project.
743 public event EventHandler
<ProjectDocumentsChangeEventArgs
> DirectoryRenamed
;
745 /// <include file='doc\FlavoredProject.uex' path='docs/doc[@for="FlavoredProject.SccStatusChanged"]/*' />
747 /// Called after the source code control status of a file in this project changed.
749 // Below suppression cannot be added to the fxcop baseline file as the code analysis phase just ignores it.
750 [SuppressMessage("Microsoft.Naming","CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId
="Scc", Justification
="VSIP Shell MPF")]
751 public event EventHandler
<ProjectDocumentsChangeEventArgs
> SccStatusChanged
;
754 #region IVsTrackProjectDocumentsEvents2 Members
755 internal class DocumentsEventsSink
: IVsTrackProjectDocumentsEvents2
757 private FlavoredProjectBase _flavoredProjectBase
;
759 internal DocumentsEventsSink(FlavoredProjectBase flavoredProjectBase
)
761 _flavoredProjectBase
= flavoredProjectBase
;
764 /// We subscribes to IVsTrackProjectDocumentsEvents and trigger the
765 /// corresponding event once per file per event.
766 /// We filters the events to only reports those related to our project.
767 /// This is NOT required for flavoring, but simplify the work the
768 /// derived classes have to do when subscribing to these events
770 int IVsTrackProjectDocumentsEvents2
.OnAfterAddDirectoriesEx(int cProjects
, int cDirectories
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgpszMkDocuments
, VSADDDIRECTORYFLAGS
[] rgFlags
)
772 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgpszMkDocuments
, _flavoredProjectBase
.DirectoryAdded
, new ProjectDocumentsChangeEventArgs());
773 return VSConstants
.S_OK
;
776 int IVsTrackProjectDocumentsEvents2
.OnAfterAddFilesEx(int cProjects
, int cFiles
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgpszMkDocuments
, VSADDFILEFLAGS
[] rgFlags
)
778 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgpszMkDocuments
, _flavoredProjectBase
.FileAdded
, new ProjectDocumentsChangeEventArgs());
779 return VSConstants
.S_OK
;
782 int IVsTrackProjectDocumentsEvents2
.OnAfterRemoveDirectories(int cProjects
, int cDirectories
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgpszMkDocuments
, VSREMOVEDIRECTORYFLAGS
[] rgFlags
)
784 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgpszMkDocuments
, _flavoredProjectBase
.DirectoryRemoved
, new ProjectDocumentsChangeEventArgs());
785 return VSConstants
.S_OK
;
788 int IVsTrackProjectDocumentsEvents2
.OnAfterRemoveFiles(int cProjects
, int cFiles
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgpszMkDocuments
, VSREMOVEFILEFLAGS
[] rgFlags
)
790 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgpszMkDocuments
, _flavoredProjectBase
.FileRemoved
, new ProjectDocumentsChangeEventArgs());
791 return VSConstants
.S_OK
;
794 int IVsTrackProjectDocumentsEvents2
.OnAfterRenameDirectories(int cProjects
, int cDirs
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgszMkOldNames
, string[] rgszMkNewNames
, VSRENAMEDIRECTORYFLAGS
[] rgFlags
)
796 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgszMkNewNames
, _flavoredProjectBase
.DirectoryRenamed
, new ProjectDocumentsChangeEventArgs());
797 return VSConstants
.S_OK
;
800 int IVsTrackProjectDocumentsEvents2
.OnAfterRenameFiles(int cProjects
, int cFiles
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgszMkOldNames
, string[] rgszMkNewNames
, VSRENAMEFILEFLAGS
[] rgFlags
)
802 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgszMkNewNames
, _flavoredProjectBase
.FileRenamed
, new ProjectDocumentsChangeEventArgs());
803 return VSConstants
.S_OK
;
806 int IVsTrackProjectDocumentsEvents2
.OnAfterSccStatusChanged(int cProjects
, int cFiles
, IVsProject
[] rgpProjects
, int[] rgFirstIndices
, string[] rgpszMkDocuments
, uint[] rgdwSccStatus
)
808 _flavoredProjectBase
.GenerateEvents(rgpProjects
, rgFirstIndices
, rgpszMkDocuments
, _flavoredProjectBase
.SccStatusChanged
, new ProjectDocumentsChangeEventArgs());
809 return VSConstants
.S_OK
;
812 int IVsTrackProjectDocumentsEvents2
.OnQueryAddDirectories(IVsProject pProject
, int cDirectories
, string[] rgpszMkDocuments
, VSQUERYADDDIRECTORYFLAGS
[] rgFlags
, VSQUERYADDDIRECTORYRESULTS
[] pSummaryResult
, VSQUERYADDDIRECTORYRESULTS
[] rgResults
)
814 return VSConstants
.S_OK
; // We are not interested in this one
817 int IVsTrackProjectDocumentsEvents2
.OnQueryAddFiles(IVsProject pProject
, int cFiles
, string[] rgpszMkDocuments
, VSQUERYADDFILEFLAGS
[] rgFlags
, VSQUERYADDFILERESULTS
[] pSummaryResult
, VSQUERYADDFILERESULTS
[] rgResults
)
819 return VSConstants
.S_OK
; // We are not interested in this one
822 int IVsTrackProjectDocumentsEvents2
.OnQueryRemoveDirectories(IVsProject pProject
, int cDirectories
, string[] rgpszMkDocuments
, VSQUERYREMOVEDIRECTORYFLAGS
[] rgFlags
, VSQUERYREMOVEDIRECTORYRESULTS
[] pSummaryResult
, VSQUERYREMOVEDIRECTORYRESULTS
[] rgResults
)
824 return VSConstants
.S_OK
; // We are not interested in this one
827 int IVsTrackProjectDocumentsEvents2
.OnQueryRemoveFiles(IVsProject pProject
, int cFiles
, string[] rgpszMkDocuments
, VSQUERYREMOVEFILEFLAGS
[] rgFlags
, VSQUERYREMOVEFILERESULTS
[] pSummaryResult
, VSQUERYREMOVEFILERESULTS
[] rgResults
)
829 return VSConstants
.S_OK
; // We are not interested in this one
832 int IVsTrackProjectDocumentsEvents2
.OnQueryRenameDirectories(IVsProject pProject
, int cDirs
, string[] rgszMkOldNames
, string[] rgszMkNewNames
, VSQUERYRENAMEDIRECTORYFLAGS
[] rgFlags
, VSQUERYRENAMEDIRECTORYRESULTS
[] pSummaryResult
, VSQUERYRENAMEDIRECTORYRESULTS
[] rgResults
)
834 return VSConstants
.S_OK
; // We are not interested in this one
837 int IVsTrackProjectDocumentsEvents2
.OnQueryRenameFiles(IVsProject pProject
, int cFiles
, string[] rgszMkOldNames
, string[] rgszMkNewNames
, VSQUERYRENAMEFILEFLAGS
[] rgFlags
, VSQUERYRENAMEFILERESULTS
[] pSummaryResult
, VSQUERYRENAMEFILERESULTS
[] rgResults
)
839 return VSConstants
.S_OK
; // We are not interested in this one
845 #region Helpers for IVsTrackProjectDocumentsEvents2
848 /// Used to subscribe/unsubscribe to those events
850 private IVsTrackProjectDocuments2
GetTrackProjectDocuments()
852 IVsTrackProjectDocuments2 trackDocuments
= ((System
.IServiceProvider
)this).GetService(typeof(SVsTrackProjectDocuments
)) as IVsTrackProjectDocuments2
;
853 Debug
.Assert(trackDocuments
!= null, "Could not get the IVsTrackProjectDocuments2 object");
854 if (trackDocuments
== null)
856 throw new InvalidOperationException();
858 return trackDocuments
;
862 /// Look at the list of projects and files and for each file that is part of this
863 /// project, set the MkDocument on the event argument and trigger the event.
865 private void GenerateEvents(
866 IVsProject
[] projects
,
868 string[] mkDocuments
,
869 EventHandler
<ProjectDocumentsChangeEventArgs
> eventToGenerate
,
870 ProjectDocumentsChangeEventArgs e
)
872 if (eventToGenerate
== null)
873 return; // no event = nothing to do
875 if (projects
== null || firstFiles
== null || mkDocuments
== null)
876 throw new ArgumentNullException();
877 if (projects
.Length
!= firstFiles
.Length
)
878 throw new ArgumentException();
880 // First find out which range of the array (if any) include the files that belong to this project
882 int last
= mkDocuments
.Length
- 1; // default to the last document
883 for (int i
= 0; i
< projects
.Length
; ++i
)
887 // We get here if there is 1 or more project(s) after ours in the list
888 last
= firstFiles
[i
] - 1;
891 if (IsThisProject(projects
[i
]))
892 first
= firstFiles
[i
];
894 if (last
>= mkDocuments
.Length
)
895 throw new ArgumentException();
896 // See if we have any documents
898 return; // Nothing that belongs to this project
900 // For each file, generate the event
901 for (int i
= first
; i
<= last
; ++i
)
905 e
.MkDocument
= mkDocuments
[i
];
906 eventToGenerate(this, e
);
908 catch(Exception error
)
910 Debug
.Fail(error
.Message
);
915 private bool IsThisProject(IVsProject prj
)
917 bool areSame
= false;
918 Guid IID_IUnknown
= VSConstants
.IID_IUnknown
;
919 IntPtr otherPtr
= IntPtr
.Zero
;
920 IntPtr otherIUnk
= IntPtr
.Zero
;
921 IntPtr thisPtr
= IntPtr
.Zero
;
922 IntPtr thisIUnk
= IntPtr
.Zero
;
925 otherPtr
= Marshal
.GetIUnknownForObject(prj
);
926 Marshal
.QueryInterface(otherPtr
, ref IID_IUnknown
, out otherIUnk
);
928 thisPtr
= Marshal
.GetIUnknownForObject(this);
929 Marshal
.QueryInterface(thisPtr
, ref IID_IUnknown
, out thisIUnk
);
930 areSame
= (otherIUnk
== thisIUnk
);
934 if (IntPtr
.Zero
!= otherPtr
)
936 Marshal
.Release(otherPtr
);
938 if (IntPtr
.Zero
!= otherIUnk
)
940 Marshal
.Release(otherIUnk
);
942 if (IntPtr
.Zero
!= thisPtr
)
944 Marshal
.Release(thisPtr
);
946 if (IntPtr
.Zero
!= thisIUnk
)
948 Marshal
.Release(thisIUnk
);