Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Automating a specific instance of Visual Studio .NET using C#

0.00/5 (No votes)
14 Aug 2004 1  
Get a list of running Visual Studio instances, and a reference to the automation server.

Screenshot - MsdevManager test app

Introduction

This article shows you how to get a list of the currently running Visual Studio IDE instances. For each instance, you can get a reference to the DTE object - the top-level object in the Visual Studio automation object model. Once you have a reference to this object, you can query for information about a solution, projects in a solution, start builds, etc.

MsdevManager is a very simple test application which exercises the provided functionality: it displays the running instances, and allows you to double-click to bring a selected IDE to the foreground. It does this by grabbing the HWnd for the IDE's MainWindow from the DTE object, and using that to raise the IDE to the foreground. While there are obviously other ways to accomplish this particular bit of functionality, the point is to demonstrate it using the DTE object. For more detailed automation examples, you can look at the MSDN documentation, but this article should help you get started with a reference to the object model.

Running Object Table

Many applications, such as Visual Studio and Microsoft Office, register a COM automation server in the Running Object Table (ROT) on startup. Automation clients such as MsdevManager can then get a pointer to this object to manipulate the server. Visual Studio .NET 2003 registers itself in the ROT as "!VisualStudio.DTE.7.1:pid", where "pid" would be replaced by the process ID of the corresponding devenv.exe process. Visual Studio .NET 2002 registers itself as "!VisualStudio.DTE.7:pid".

The DTE Object

DTE stands for "Development Tools Extensibility" and is the object's coclass. It implements the interface "_DTE". You can find more information about the DTE object in the Visual Studio documentation under "DTE object", and in the document "Referencing the DTE object". The O'Reilly book "Mastering Visual Studio .NET" also has a chapter with some useful information.

Using the code

The file MsdevManager.cs contains a class Msdev which defines a few of the relevant methods. The first thing we need is to be able to get at the system's Running Object Table. To do this, we'll need access to a couple of functions that live in ole32.dll:

 public class Msdev
 {
    [DllImport("ole32.dll")]  
    public static extern int GetRunningObjectTable(int reserved, 
                              out UCOMIRunningObjectTable prot); 
 
    [DllImport("ole32.dll")]  
    public static extern int  CreateBindCtx(int reserved, 
                                  out UCOMIBindCtx ppbc);

Now that we can get at these two functions, let's write a method to enumerate the objects in the ROT, giving us a snapshot of the contents:

/// <summary>

/// Get a snapshot of the running object table (ROT).

/// </summary>

/// <returns>A hashtable mapping the name of the object

//     in the ROT to the corresponding object</returns>


public static Hashtable GetRunningObjectTable()
{
    Hashtable result = new Hashtable();

    int numFetched;
    UCOMIRunningObjectTable runningObjectTable;   
    UCOMIEnumMoniker monikerEnumerator;
    UCOMIMoniker[] monikers = new UCOMIMoniker[1];

    GetRunningObjectTable(0, out runningObjectTable);    
    runningObjectTable.EnumRunning(out monikerEnumerator);
    monikerEnumerator.Reset();          
    
    while (monikerEnumerator.Next(1, monikers, out numFetched) == 0)
    {     
        UCOMIBindCtx ctx;
        CreateBindCtx(0, out ctx);     
            
        string runningObjectName;
        monikers[0].GetDisplayName(ctx, null, out runningObjectName);

        object runningObjectVal;  
        runningObjectTable.GetObject( monikers[0], out runningObjectVal); 

        result[ runningObjectName ] = runningObjectVal;
    } 

    return result;
}

From here, it's easy to write another method that just gets us a list of the instances of the Visual Studio IDE. We'll also include a parameter that allows us to specify that we only want IDE instances that have open solutions:

/// <summary>

/// Get a table of the currently running instances of the Visual Studio .NET IDE.

/// </summary>

/// <param name="openSolutionsOnly">Only return instances

///                   that have opened a solution</param>

/// <returns>A hashtable mapping the name of the IDE

///       in the running object table to the corresponding

///                                  DTE object</returns>


public static Hashtable GetIDEInstances( bool openSolutionsOnly )
{
    Hashtable runningIDEInstances = new Hashtable();
    Hashtable runningObjects = GetRunningObjectTable();

    IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
    while ( rotEnumerator.MoveNext() )
    {
        string candidateName = (string) rotEnumerator.Key;
        if (!candidateName.StartsWith("!VisualStudio.DTE"))
            continue;

        _DTE ide = rotEnumerator.Value as _DTE;
        if (ide == null)
            continue;

        if (openSolutionsOnly)
        {
            try
            {
                string solutionFile = ide.Solution.FullName;
                if (solutionFile != String.Empty)
                {
                    runningIDEInstances[ candidateName ] = ide;
                }
            } 
            catch {}
        }
        else
        {
            runningIDEInstances[ candidateName ] = ide;
        }                       
    }
    return runningIDEInstances;
}

Finally, once we've got a reference to the DTE for the IDE we would like to automate, we can do something useful:

[DllImport("user32.dll")] 
private static extern bool SetForegroundWindow(IntPtr hWnd);

private const int SW_RESTORE = 9;
[DllImport("user32.dll")] 
private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);

[DllImport("user32.dll")] 
private static extern bool IsIconic(IntPtr hWnd);       

/// <summary>

/// Raises an instance of the Visual Studio IDE to the foreground.

/// </summary>

/// <param name="ide">The DTE object for the IDE you

///    would like to raise to the foreground</param>


public static void ShowIDE( EnvDTE.DTE ide )
{
    // To show an existing IDE, we get the HWND for the MainWindow

    // and do a little interop to bring the desired IDE to the

    // foreground.  I tried some of the following other potentially

    // promising approaches but could only succeed in getting the

    // IDE's taskbar button to flash (this is as designed).  Ex:

    //

    //   ide.MainWindow.Activate();

    //   ide.MainWindow.SetFocus();

    //   ide.MainWindow.Visible = true;

    //   ide.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMinimize;

    //   ide.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMaximize;


    System.IntPtr hWnd = (System.IntPtr) ide.MainWindow.HWnd;
    if (IsIconic(hWnd))
    {
        ShowWindowAsync(hWnd,SW_RESTORE);
    }
    SetForegroundWindow(hWnd);
    ide.MainWindow.Visible = true;
}

History

  • 8/14/04 - Initial version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here