65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 自动化 Visual Studio .NET 的特定实例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (20投票s)

2004 年 8 月 15 日

2分钟阅读

viewsIcon

83682

downloadIcon

2216

获取正在运行的 Visual Studio 实例列表,以及对自动化服务器的引用。

Screenshot - MsdevManager test app

引言

本文向您展示如何获取当前正在运行的 Visual Studio IDE 实例的列表。对于每个实例,您可以获取对 DTE 对象的引用 - Visual Studio 自动化对象模型中的顶级对象。一旦您获得了对该对象的引用,您就可以查询有关解决方案、解决方案中的项目、启动构建等信息。

MsdevManager 是一个非常简单的测试应用程序,它使用了所提供的功能:它显示正在运行的实例,并允许您双击以将选定的 IDE 调到前台。它通过从 DTE 对象中获取 IDE 的 MainWindow 的 HWnd,并使用它将 IDE 调到前台来实现这一点。虽然显然还有其他方法可以实现此特定功能,但重点是使用 DTE 对象演示它。有关更详细的自动化示例,您可以查看 MSDN 文档,但本文应该可以帮助您开始使用对对象模型的引用。

运行对象表

许多应用程序,例如 Visual Studio 和 Microsoft Office,在启动时在运行对象表 (ROT) 中注册一个 COM 自动化服务器。 MsdevManager 等自动化客户端然后可以获取指向此对象的指针以操作服务器。 Visual Studio .NET 2003 将自身注册到 ROT 中,注册为 "!VisualStudio.DTE.7.1:pid",其中 "pid" 将被相应的 devenv.exe 进程的进程 ID 替换。 Visual Studio .NET 2002 将自身注册为 "!VisualStudio.DTE.7:pid"。

DTE 对象

DTE 代表“开发工具扩展性”,是对象的 coclass。它实现了接口 "_DTE"。您可以在 Visual Studio 文档的“DTE 对象”下以及文档“引用 DTE 对象”中找到有关 DTE 对象的更多信息。 O'Reilly 著作“精通 Visual Studio .NET”也有一章包含一些有用的信息。

使用代码

文件 MsdevManager.cs 包含一个名为 Msdev 的类,该类定义了几个相关的方法。我们需要做的第一件事是能够访问系统的运行对象表。为此,我们需要访问 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);

现在我们可以访问这两个函数了,让我们编写一个方法来枚举 ROT 中的对象,从而获得其内容的快照

/// <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;
}

从这里,很容易编写另一个方法,该方法只为我们提供 Visual Studio IDE 实例的列表。我们还将包含一个参数,允许我们指定我们只想包含具有打开的解决方案的 IDE 实例

/// <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;
}

最后,一旦我们获得了对要自动化的 IDE 的 DTE 的引用,我们就可以做一些有用的事情

[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;
}

历史

  • 04 年 8 月 14 日 - 初始版本。
© . All rights reserved.