views:

316

answers:

1

I am successfully instantiating/automating Visual Studio using the following code:

System.Type t = System.Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
object obj = Activator.CreateInstance(t, true);
dte = (DTE)obj;
Solution sln = dte.Solution;
sln.Open(SolutionFile);
System.Threading.Thread.Sleep(1000);
//Do stuff with the solution

Notice the Thread.Sleep(1000) call? If I don't include that, the code tries to bug the instance before it's ready and I get an exception:

the message filter indicated that the application is busy.

Rather than wait exactly n seconds, is there a way to poll this object for readiness?

+2  A: 

As a solution to this issue you can register to an event that notifies when the solution load is done.

This is a sample of class that lets you listen to events on solution loading:

public class SolutionEventsListener : IVsSolutionEvents, IDisposable
{
    private IVsSolution solution;
    private uint solutionEventsCookie;

    public event Action AfterSolutionLoaded;
    public event Action BeforeSolutionClosed;

    public SolutionEventsListener(IServiceProvider serviceProvider)
    {
        InitNullEvents();

        solution = serviceProvider.GetService(typeof (SVsSolution)) as IVsSolution;
        if (solution != null)
        {
            solution.AdviseSolutionEvents(this, out solutionEventsCookie);
        }
    }

    private void InitNullEvents()
    {
        AfterSolutionLoaded += () => { };
        BeforeSolutionClosed += () => { };
    }

    #region IVsSolutionEvents Members

    int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
    {
        AfterSolutionLoaded();
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)
    {
        BeforeSolutionClosed();
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)
    {
        return VSConstants.S_OK;
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        if (solution != null && solutionEventsCookie != 0)
        {
            GC.SuppressFinalize(this);
            solution.UnadviseSolutionEvents(solutionEventsCookie);
            AfterSolutionLoaded = null;
            BeforeSolutionClosed = null;
            solutionEventsCookie = 0;
            solution = null;
        }
    }

    #endregion
}

Usage example:

DTE2 applicationObject = dte;
var serviceProvider = new ServiceProvider(applicationObject as IServiceProvider);
solutionEventsListener = new SolutionEventsListener(serviceProvider);
solutionEventsListener.AfterSolutionLoaded += () => /* logic here */ ;
Elisha
This is a great answer and worked perfectly. Incidentally, I found a cheat that worked as well- check the Solution.IsOpen property right after opening the solution with Solution.Open. This is apparently a blocking call that forces a wait until the instance of devenv.exe is ready.
Dave Swersky
@Dave Swersky, I like the Solution.IsOpen, it saves lots of code :)
Elisha