@@ -440,6 +440,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
440440let mutable updateSolnEventsHandle = 0 u
441441let mutable updateSolnEventsHandle2 = 0 u
442442let mutable updateSolnEventsHandle3 = 0 u
443+ let mutable updateSolnEventsHandle4 = 0 u
443444
444445let mutable trackProjectRetargetingCookie = 0 u
445446
@@ -586,14 +587,26 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
586587let listener = new SolutionEventsListener( this)
587588
588589let buildMgr = this.Site.GetService( typeof< SVsSolutionBuildManager>) :?> IVsSolutionBuildManager
590+ if updateSolnEventsHandle<> 0 uthen
591+ buildMgr.UnadviseUpdateSolutionEvents( updateSolnEventsHandle) |> ignore
589592 buildMgr.AdviseUpdateSolutionEvents(( listener:> IVsUpdateSolutionEvents), & updateSolnEventsHandle) |> ignore
590593let buildMgr2 = this.Site.GetService( typeof< SVsSolutionBuildManager>) :?> IVsSolutionBuildManager2
594+ if updateSolnEventsHandle2<> 0 uthen
595+ buildMgr2.UnadviseUpdateSolutionEvents( updateSolnEventsHandle2) |> ignore
591596 buildMgr2.AdviseUpdateSolutionEvents(( listener:> IVsUpdateSolutionEvents2), & updateSolnEventsHandle2) |> ignore
592597let buildMgr3 = this.Site.GetService( typeof< SVsSolutionBuildManager>) :?> IVsSolutionBuildManager3
598+ if updateSolnEventsHandle3<> 0 uthen
599+ buildMgr3.UnadviseUpdateSolutionEvents3( updateSolnEventsHandle3) |> ignore
593600 buildMgr3.AdviseUpdateSolutionEvents3(( listener:> IVsUpdateSolutionEvents3), & updateSolnEventsHandle3) |> ignore
601+ let buildMgr5 = this.Site.GetService( typeof< SVsSolutionBuildManager>) :?> IVsSolutionBuildManager5
602+ if updateSolnEventsHandle4<> 0 uthen
603+ buildMgr5.UnadviseUpdateSolutionEvents4( updateSolnEventsHandle4) |> ignore
604+ buildMgr5.AdviseUpdateSolutionEvents4(( listener:> IVsUpdateSolutionEvents4), & updateSolnEventsHandle4) |> ignore
594605
595606// Register for project retargeting events
596607let sTrackProjectRetargeting = this.Site.GetService( typeof< SVsTrackProjectRetargeting>) :?> IVsTrackProjectRetargeting
608+ if trackProjectRetargetingCookie<> 0 uthen
609+ sTrackProjectRetargeting.UnadviseTrackProjectRetargetingEvents( trackProjectRetargetingCookie) |> ignore
597610 sTrackProjectRetargeting.AdviseTrackProjectRetargetingEvents(( listener:> IVsTrackProjectRetargetingEvents), & trackProjectRetargetingCookie) |> ignore
598611
599612 isInCommandLineMode<-
@@ -616,6 +629,8 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
616629 buildMgr2.UnadviseUpdateSolutionEvents( updateSolnEventsHandle2) |> ignore
617630let buildMgr3 = this.Site.GetService( typeof< SVsSolutionBuildManager>) :?> IVsSolutionBuildManager3
618631 buildMgr3.UnadviseUpdateSolutionEvents3( updateSolnEventsHandle3) |> ignore
632+ let buildMgr5 = this.Site.GetService( typeof< SVsSolutionBuildManager>) :?> IVsSolutionBuildManager5
633+ buildMgr5.UnadviseUpdateSolutionEvents4( updateSolnEventsHandle4) |> ignore
619634
620635let documentTracker = this.Site.GetService( typeof< SVsTrackProjectDocuments>) :?> IVsTrackProjectDocuments2
621636 documentTracker.UnadviseTrackProjectDocumentsEvents( trackDocumentsHandle) |> ignore
@@ -1328,12 +1343,27 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
13281343yield System.IO.Path.GetFullPath( System.IO.Path.Combine( projectFolder, i.EvaluatedInclude))
13291344|]
13301345member x.GetCompileItems () = let sources , _ = sourcesAndFlags.Valuein sources
1331- member x.GetCompileFlags () = let _ , flags = sourcesAndFlags.Valuein flags
1346+ member x.GetCompileFlags () = let _ , flags = sourcesAndFlags.Valuein flags
13321347
13331348override x.ComputeSourcesAndFlags () =
1349+
13341350if x.IsInBatchUpdate|| box x.BuildProject= null then ()
13351351else
13361352if not ( inMidstOfReloading) && not ( VsBuildManagerAccessorExtensionMethods.IsInProgress( accessor)) then
1353+
1354+ use waitDialog=
1355+ {
1356+ WaitCaption= FSharpSR.GetString FSharpSR.ProductName
1357+ WaitMessage= FSharpSR.GetString FSharpSR.ComputingSourcesAndFlags
1358+ ProgressText= Some x.ProjectFile
1359+ StatusBmpAnim= null
1360+ StatusBarText= None
1361+ DelayToShowDialogSecs= 1
1362+ IsCancelable= false
1363+ ShowMarqueeProgress= true
1364+ }
1365+ |> WaitDialog.start x.Site
1366+
13371367// REVIEW CompilerFlags will be stale since last 'save' of MSBuild .fsproj file - can we do better?
13381368try
13391369 actuallyBuild<- false
@@ -1357,10 +1387,11 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
13571387// If property is not set - msbuild will resolve only primary dependencies,
13581388// and compiler will be very unhappy when during processing of referenced assembly it will discover that all fundamental types should be
13591389// taken from System.Runtime that is not supplied
1360- let _ = x.InvokeMsBuild( " Compile" , isBeingCalledByComputeSourcesAndFlags= true , extraProperties= [ KeyValuePair( " _ResolveReferenceDependencies" , " true" )])
1390+
1391+ let _ = x.InvokeMsBuild( " Compile" , extraProperties= [ KeyValuePair( " _ResolveReferenceDependencies" , " true" )])
13611392 sourcesAndFlagsNotifier.Notify()
13621393finally
1363- actuallyBuild<- true
1394+ actuallyBuild<- true
13641395
13651396member internal x.DetermineRuntimeAndSKU ( targetFrameworkMoniker : string ) =
13661397let frameworkName = new System.Runtime.Versioning.FrameworkName( targetFrameworkMoniker)
@@ -1439,7 +1470,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
14391470member ips.DescriptionOfProject () =
14401471let sources , flags = sourcesAndFlags.Value
14411472 sprintf" Project System: flags(%A ) sources:\n %A " flags sources
1442- member ips.CompilerFlags () = let _ , flags = sourcesAndFlags.Value in flags
1473+ member ips.CompilerFlags () = x.GetCompileFlags ()
14431474member ips.ProjectFileName () = MSBuildProject.GetFullPath( x.BuildProject)
14441475member ips.ErrorListTaskProvider () = Some( x.TaskProvider)
14451476member ips.ErrorListTaskReporter () = Some( x.TaskReporter)
@@ -1595,6 +1626,11 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
15951626member x.SetSpecificEditorProperty ( _mkDocument : string , _propid : int , _value : obj ) =
15961627 VSConstants.E_ NOTIMPL
15971628end
1629+
1630+ type internal ActiveCfgBatchUpdateState =
1631+ | NonBatch
1632+ | BatchWaiting
1633+ | BatchDone
15981634
15991635// Why is this a separate class, rather than an interface implemented on
16001636// FSharpProjectNode? Because, at the time of initial registration of this
@@ -1605,7 +1641,14 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
16051641// class means we have a separate object to CCW wrap, avoiding the problematic
16061642// "double CCW-wrapping" of the same object.
16071643type internal SolutionEventsListener ( projNode ) =
1608- let mutable queuedWork : option < list < FSharpProjectNode >> = None
1644+
1645+ let mutable waitDialog : IDisposable option = None
1646+
1647+ // During batch active project configuration changes, make sure we only run CSAF once
1648+ // per batch. Before this change, OnActiveProjectCfgChange was being called twice per
1649+ // batch per project.
1650+ let mutable batchState = NonBatch
1651+
16091652// The CCW wrapper seems to prevent an object-identity test, so we determine whether
16101653// two IVsHierarchy objects are equal by comparing their captions. (It's ok if this
16111654// occasionally yields false positives, as this just means we may do a little extra
@@ -1620,14 +1663,16 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
16201663 o:?> System.String
16211664else
16221665null : System.String
1666+
16231667let OnActiveProjectCfgChange ( pIVsHierarchy ) =
1624- if GetCaption( pIVsHierarchy) = GetCaption( projNode.InteropSafeIVsHierarchy) then
1668+ if GetCaption( pIVsHierarchy) = GetCaption( projNode.InteropSafeIVsHierarchy) && batchState <> BatchDone then
16251669 projNode.SetProjectFileDirty( projNode.IsProjectFileDirty)
1626- projNode.ComputeSourcesAndFlags() // REVIEW: It looks like ComputeSourcesAndFlags is called twice. Once on this line and then again because it is added to 'queuedWork' below.
1627- match queuedWork with
1628- | Some ( l ) -> queuedWork <- Some ( projNode :: l )
1629- | None -> ()
1670+ projNode.ComputeSourcesAndFlags()
1671+
1672+ if batchState = BatchWaiting then
1673+ batchState <- BatchDone
16301674 VSConstants.S_ OK
1675+
16311676let UpdateConfig ( pHierProj ) =
16321677// By default, the F# project system keeps its own internal Configuration and Platform in sync with the current active
16331678// Configuration and Platform by listening for OnActiveProjectCfgChange events. However there is one case where the
@@ -1640,6 +1685,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
16401685 MSBuildProject.SetGlobalProperty( projNode.BuildProject, ProjectFileConstants.Configuration, currentConfigName.ConfigName)
16411686 MSBuildProject.SetGlobalProperty( projNode.BuildProject, ProjectFileConstants.Platform, currentConfigName.MSBuildPlatform)
16421687 projNode.UpdateMSBuildState()
1688+
16431689interface IVsUpdateSolutionEventswith
16441690member x.UpdateSolution_Begin ( pfCancelUpdate ) =
16451691 VSConstants.S_ OK
@@ -1651,6 +1697,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
16511697 VSConstants.S_ OK
16521698member x.OnActiveProjectCfgChange ( pIVsHierarchy ) =
16531699 OnActiveProjectCfgChange( pIVsHierarchy)
1700+
16541701interface IVsUpdateSolutionEvents2with
16551702member x.UpdateSolution_Begin ( pfCancelUpdate ) =
16561703 VSConstants.S_ OK
@@ -1668,17 +1715,51 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem
16681715member x.UpdateProjectCfg_Done ( pHierProj , _pCfgProj , _pCfgSln , _dwAction , _fSuccess , _fCancel ) =
16691716 UpdateConfig( pHierProj)
16701717 VSConstants.S_ OK
1718+
16711719interface IVsUpdateSolutionEvents3with
16721720member x.OnBeforeActiveSolutionCfgChange ( _oldCfg , _newCfg ) =
1673- queuedWork<- Some( [] )
1721+ // this will be called for each project, but wait dialogs cannot 'stack'
1722+ // i.e. if a wait dialog is already open, subsequent calls to StartWaitDialog
1723+ // will not override the current open dialog
1724+ waitDialog<-
1725+ {
1726+ WaitCaption= FSharpSR.GetString FSharpSR.ProductName
1727+ WaitMessage= FSharpSR.GetString FSharpSR.UpdatingSolutionConfiguration
1728+ ProgressText= None
1729+ StatusBmpAnim= null
1730+ StatusBarText= None
1731+ DelayToShowDialogSecs= 1
1732+ IsCancelable= false
1733+ ShowMarqueeProgress= true
1734+ }
1735+ |> WaitDialog.start projNode.Site
1736+ |> Some
1737+
16741738 VSConstants.S_ OK
1739+
16751740member x.OnAfterActiveSolutionCfgChange ( _oldCfg , _newCfg ) =
1676- match queuedWorkwith
1677- | Some( l) -> l|> List.iter( fun projNode -> projNode.ComputeSourcesAndFlags())
1741+ match waitDialogwith
1742+ | Some x->
1743+ x.Dispose()
1744+ waitDialog<- None
16781745| None-> ()
1679- queuedWork<- None
16801746 VSConstants.S_ OK
1681-
1747+
1748+ interface IVsUpdateSolutionEvents4with
1749+ member x.OnActiveProjectCfgChangeBatchBegin () =
1750+ batchState<- BatchWaiting
1751+ member x.OnActiveProjectCfgChangeBatchEnd () =
1752+ batchState<- NonBatch
1753+ member x.UpdateSolution_BeginFirstUpdateAction () =
1754+ ()
1755+ member x.UpdateSolution_BeginUpdateAction ( _dwAction ) =
1756+ ()
1757+ member x.UpdateSolution_EndLastUpdateAction () =
1758+ ()
1759+ member x.UpdateSolution_EndUpdateAction ( _dwAction ) =
1760+ ()
1761+ member x.UpdateSolution_QueryDelayFirstUpdateAction ( _pfDelay ) =
1762+ ()
16821763
16831764interface IVsTrackProjectRetargetingEventswith
16841765override this.OnRetargetingBeforeChange