在 Visual C# 2010 中获取 DTE2 对象的引用

发布于 2024-10-12 17:31:23 字数 678 浏览 10 评论 0原文

我想在 Visual Studio 2010 中使用带有 C# 的 DTE2 对象来获取对当前解决方案的引用。

我首先尝试了以下代码:

var dte = Marshal.GetActiveObject("VisualStudio.DTE.10.0") as EnvDTE80.DTE2;

但是当我打开 2 个解决方案,并且此代码位于第一个解决方案中时,我没有得到引用当前的解决方案,而是对我加载的最后一个解决方案的引用。我需要当前的解决方案...

在互联网上搜索,我在 如何从 VSPackage 获取当前解决方案目录?

// Get an instance of the currently running Visual Studio IDE
DTE dte = (DTE)GetService(typeof(DTE));

但是当我使用它时,我的 dte 对象始终为 NULL。

那么如何在 .net Framework 4.0 上使用 C# 获取 VS2010 中当前的解决方案对象呢?

I want to get a reference to the current solution, using the DTE2 object with C# in Visual Studio 2010.

I first tried the following code:

var dte = Marshal.GetActiveObject("VisualStudio.DTE.10.0") as EnvDTE80.DTE2;

But when I open 2 solutions, and this code is in the first solution, I get NOT a reference to the current solution, but a reference to the last solution I loaded. I need the current solution...

Searching on the internet, I found the following solution in How do you get the current solution directory from a VSPackage?:

// Get an instance of the currently running Visual Studio IDE
DTE dte = (DTE)GetService(typeof(DTE));

But when I use this, my dte object is always NULL.

So how do I get to my current solution object in VS2010 using C# on .net framework 4.0?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

霓裳挽歌倾城醉 2024-10-19 17:31:24

我觉得以下几点令人不安,所以我解决了它们并找到了适合我的解决方案:

  • GetActiveObject("VisualStudio.DTE.10.0") 仅适用于第一个打开的(我想)Visual Studio
  • Dennis 答案的内部静态 DTE2 GetCurrent() 方法需要 Visual Studio 进程 ID。如果您从加载项运行代码(我认为),那很好,但在单元测试中不起作用。
  • 调试模式下的问题

我还从 GetCurrent 方法开始,取自 此处。问题是,我不知道如何获取正确的 VisualStudio 进程的 ProcessId(通常有多个实例正在运行)。因此,我采取的方法是获取所有 VisualStudio ROT 条目及其 DTE2,然后将 DTE2.Solution.FullName 与执行程序集位置进行比较(您是否看到更好的选择?)。虽然我承认这不是非常精确的科学,但如果您没有相当特殊的输出路径配置,它应该可以工作。
然后我发现在调试模式下运行代码并访问 DTE2 COM 对象会引发以下异常: System.Runtime.InteropServices.COMException:调用被被调用者拒绝。 (HRESULT 异常:0x80010001 (RPC_E_CALL_REJECTED))。不过,有一种补救措施,称为 MessageFilter< /a>.为了完整起见,我已将代码放在底部。

包含测试方法(使用示例)、调整后的 GetCurrent 方法和用于字符串比较的辅助方法的测试类:

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EnvDTE80;
using EnvDTE;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

[TestClass]
public class ProjectSettingsTest
{
    /// <summary>
    /// Tests that the platform for Mixed Platforms and Any CPU configurations 
    /// is Any CPU for all projects of this solution
    /// </summary>
    [TestMethod]
    public void TestReleaseBuildIsAnyCPU()
    {
        MessageFilter.Register();

        DTE2 dte2 = GetCurrent();
        Assert.IsNotNull(dte2);

        foreach (SolutionConfiguration2 config in dte2.Solution.SolutionBuild.SolutionConfigurations)
        {
            if (config.PlatformName.Contains("Mixed Platforms") || config.PlatformName.Contains("Any CPU"))
            {
                foreach (SolutionContext context in config.SolutionContexts)
                    Assert.AreEqual("Any CPU", context.PlatformName, string.Format("{0} is configured {1} in {2}/{3}", context.ProjectName, context.PlatformName, config.PlatformName, config.Name));
            }
        }

        MessageFilter.Revoke();
    }


    [DllImport("ole32.dll")]
    private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
    [DllImport("ole32.dll")]
    private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

    /// <summary>
    /// Gets the current visual studio's solution DTE2
    /// </summary>
    public static DTE2 GetCurrent()
    {
        List<DTE2> dte2s = new List<DTE2>();

        IRunningObjectTable rot;
        GetRunningObjectTable(0, out rot);
        IEnumMoniker enumMoniker;
        rot.EnumRunning(out enumMoniker);
        enumMoniker.Reset();
        IntPtr fetched = IntPtr.Zero;
        IMoniker[] moniker = new IMoniker[1];
        while (enumMoniker.Next(1, moniker, fetched) == 0)
        {
            IBindCtx bindCtx;
            CreateBindCtx(0, out bindCtx);
            string displayName;
            moniker[0].GetDisplayName(bindCtx, null, out displayName);
            // add all VisualStudio ROT entries to list
            if (displayName.StartsWith("!VisualStudio"))
            {
                object comObject;
                rot.GetObject(moniker[0], out comObject);
                dte2s.Add((DTE2)comObject);
            }
        }

        // get path of the executing assembly (assembly that holds this code) - you may need to adapt that to your setup
        string thisPath = System.Reflection.Assembly.GetExecutingAssembly().Location;

        // compare dte solution paths to find best match
        KeyValuePair<DTE2, int> maxMatch = new KeyValuePair<DTE2, int>(null, 0);
        foreach (DTE2 dte2 in dte2s)
        {
            int matching = GetMatchingCharsFromStart(thisPath, dte2.Solution.FullName);
            if (matching > maxMatch.Value)
                maxMatch = new KeyValuePair<DTE2, int>(dte2, matching);
        }

        return (DTE2)maxMatch.Key;
    }

    /// <summary>
    /// Gets index of first non-equal char for two strings
    /// Not case sensitive.
    /// </summary>
    private static int GetMatchingCharsFromStart(string a, string b)
    {
        a = (a ?? string.Empty).ToLower();
        b = (b ?? string.Empty).ToLower();
        int matching = 0;
        for (int i = 0; i < Math.Min(a.Length, b.Length); i++)
        {
            if (!char.Equals(a[i], b[i]))
                break;

            matching++;
        }
        return matching;
    }
}

MessageFilter 类:

/// <summary>
/// Class containing the IOleMessageFilter
/// thread error-handling functions.
/// </summary>
public class MessageFilter : IOleMessageFilter
{
    // Start the filter.
    public static void Register()
    {
        IOleMessageFilter newFilter = new MessageFilter();
        IOleMessageFilter oldFilter = null;
        CoRegisterMessageFilter(newFilter, out oldFilter);
    }

    // Done with the filter, close it.
    public static void Revoke()
    {
        IOleMessageFilter oldFilter = null;
        CoRegisterMessageFilter(null, out oldFilter);
    }

    //
    // IOleMessageFilter functions.
    // Handle incoming thread requests.
    int IOleMessageFilter.HandleInComingCall(int dwCallType, System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo)
    {
        return 0; //Return the flag SERVERCALL_ISHANDLED.
    }

    // Thread call was rejected, so try again.
    int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
    {
        if (dwRejectType == 2)
        // flag = SERVERCALL_RETRYLATER.
        {
            return 99; // Retry the thread call immediately if return >=0 & <100.
        }
        return -1; // Too busy; cancel call.
    }

    int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
    {
        //Return the flag PENDINGMSG_WAITDEFPROCESS.
        return 2;
    }

    // Implement the IOleMessageFilter interface.
    [DllImport("Ole32.dll")]
    private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}

[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
    [PreserveSig]
    int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
    [PreserveSig]
    int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
    [PreserveSig]
    int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}

I felt the following points are unnerving, so I have addressed them and found a solution that works for me:

  • GetActiveObject("VisualStudio.DTE.10.0") only works for the first opened (I suppose) Visual Studio
  • The internal static DTE2 GetCurrent() method of Dennis' answer needs the Visual Studio process Id. That is fine if you run the code from add-ins (I think), but does not work e.g. in unit tests.
  • Problems in debug mode

I also started out with the GetCurrent method, taken from here. Problem was, I didn't know how to get to the ProcessId of the right VisualStudio process (usually multiple instances are running). So the approach I took was getting all VisualStudio ROT entries and their DTE2, then comparing DTE2.Solution.FullName to the executing assembly location (do you see a better choice?). While I readily admit this is not very exact science, it should work if you do not have rather special Output Path configurations.
Then I found that running my code in debug mode and accessing the DTE2 COM objects threw the following exception: System.Runtime.InteropServices.COMException: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED)). There is a remedy for that though, called MessageFilter. I have included the code at the bottom for completeness sake.

Test class containing a test method (usage example), the adjusted GetCurrent method and a helper method for string comparison:

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EnvDTE80;
using EnvDTE;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

[TestClass]
public class ProjectSettingsTest
{
    /// <summary>
    /// Tests that the platform for Mixed Platforms and Any CPU configurations 
    /// is Any CPU for all projects of this solution
    /// </summary>
    [TestMethod]
    public void TestReleaseBuildIsAnyCPU()
    {
        MessageFilter.Register();

        DTE2 dte2 = GetCurrent();
        Assert.IsNotNull(dte2);

        foreach (SolutionConfiguration2 config in dte2.Solution.SolutionBuild.SolutionConfigurations)
        {
            if (config.PlatformName.Contains("Mixed Platforms") || config.PlatformName.Contains("Any CPU"))
            {
                foreach (SolutionContext context in config.SolutionContexts)
                    Assert.AreEqual("Any CPU", context.PlatformName, string.Format("{0} is configured {1} in {2}/{3}", context.ProjectName, context.PlatformName, config.PlatformName, config.Name));
            }
        }

        MessageFilter.Revoke();
    }


    [DllImport("ole32.dll")]
    private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
    [DllImport("ole32.dll")]
    private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

    /// <summary>
    /// Gets the current visual studio's solution DTE2
    /// </summary>
    public static DTE2 GetCurrent()
    {
        List<DTE2> dte2s = new List<DTE2>();

        IRunningObjectTable rot;
        GetRunningObjectTable(0, out rot);
        IEnumMoniker enumMoniker;
        rot.EnumRunning(out enumMoniker);
        enumMoniker.Reset();
        IntPtr fetched = IntPtr.Zero;
        IMoniker[] moniker = new IMoniker[1];
        while (enumMoniker.Next(1, moniker, fetched) == 0)
        {
            IBindCtx bindCtx;
            CreateBindCtx(0, out bindCtx);
            string displayName;
            moniker[0].GetDisplayName(bindCtx, null, out displayName);
            // add all VisualStudio ROT entries to list
            if (displayName.StartsWith("!VisualStudio"))
            {
                object comObject;
                rot.GetObject(moniker[0], out comObject);
                dte2s.Add((DTE2)comObject);
            }
        }

        // get path of the executing assembly (assembly that holds this code) - you may need to adapt that to your setup
        string thisPath = System.Reflection.Assembly.GetExecutingAssembly().Location;

        // compare dte solution paths to find best match
        KeyValuePair<DTE2, int> maxMatch = new KeyValuePair<DTE2, int>(null, 0);
        foreach (DTE2 dte2 in dte2s)
        {
            int matching = GetMatchingCharsFromStart(thisPath, dte2.Solution.FullName);
            if (matching > maxMatch.Value)
                maxMatch = new KeyValuePair<DTE2, int>(dte2, matching);
        }

        return (DTE2)maxMatch.Key;
    }

    /// <summary>
    /// Gets index of first non-equal char for two strings
    /// Not case sensitive.
    /// </summary>
    private static int GetMatchingCharsFromStart(string a, string b)
    {
        a = (a ?? string.Empty).ToLower();
        b = (b ?? string.Empty).ToLower();
        int matching = 0;
        for (int i = 0; i < Math.Min(a.Length, b.Length); i++)
        {
            if (!char.Equals(a[i], b[i]))
                break;

            matching++;
        }
        return matching;
    }
}

MessageFilter class:

/// <summary>
/// Class containing the IOleMessageFilter
/// thread error-handling functions.
/// </summary>
public class MessageFilter : IOleMessageFilter
{
    // Start the filter.
    public static void Register()
    {
        IOleMessageFilter newFilter = new MessageFilter();
        IOleMessageFilter oldFilter = null;
        CoRegisterMessageFilter(newFilter, out oldFilter);
    }

    // Done with the filter, close it.
    public static void Revoke()
    {
        IOleMessageFilter oldFilter = null;
        CoRegisterMessageFilter(null, out oldFilter);
    }

    //
    // IOleMessageFilter functions.
    // Handle incoming thread requests.
    int IOleMessageFilter.HandleInComingCall(int dwCallType, System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo)
    {
        return 0; //Return the flag SERVERCALL_ISHANDLED.
    }

    // Thread call was rejected, so try again.
    int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
    {
        if (dwRejectType == 2)
        // flag = SERVERCALL_RETRYLATER.
        {
            return 99; // Retry the thread call immediately if return >=0 & <100.
        }
        return -1; // Too busy; cancel call.
    }

    int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
    {
        //Return the flag PENDINGMSG_WAITDEFPROCESS.
        return 2;
    }

    // Implement the IOleMessageFilter interface.
    [DllImport("Ole32.dll")]
    private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}

[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
    [PreserveSig]
    int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
    [PreserveSig]
    int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
    [PreserveSig]
    int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
仙女山的月亮 2024-10-19 17:31:24

我知道这是一个旧线程,但我们需要将此代码与多个 Visual Studio 版本一起使用。我们将代码调整如下:

string processID = Process.GetCurrentProcess().Id.ToString();
if (displayName.StartsWith("!VisualStudio.DTE.", StringComparison.OrdinalIgnoreCase) &&
        displayName.EndsWith(processID))

I know this is an old thread, but we needed to use this code with multiple Visual Studio versions. We tweaked the code as below:

string processID = Process.GetCurrentProcess().Id.ToString();
if (displayName.StartsWith("!VisualStudio.DTE.", StringComparison.OrdinalIgnoreCase) &&
        displayName.EndsWith(processID))
情定在深秋 2024-10-19 17:31:24

对于任何有兴趣使用 F# 执行此操作的人,这里提供了大部分完整的转换(当前设置为在 linqpad 中运行):

open System;
open System.Runtime.InteropServices;
open System.Runtime.InteropServices.ComTypes;
open EnvDTE;
open System.Diagnostics;
//http://stackoverflow.com/questions/10864595/getting-the-current-envdte-or-iserviceprovider-when-not-coding-an-addin

//http://stackoverflow.com/questions/6558789/how-to-convert-out-ref-extern-parameters-to-f
//http://stackoverflow.com/questions/1689460/f-syntax-for-p-invoke-signature-using-marshalas

[<System.Runtime.InteropServices.DllImport("ole32.dll")>] 
extern int CreateBindCtx(System.IntPtr inRef, IBindCtx& outParentRef);
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern int GetRunningObjectTable(System.IntPtr inRef, IRunningObjectTable& outParentRef);
//let dte = System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.12.0") :?> EnvDTE80.DTE2
let comName="VisualStudio.DTE.12.0"
let rotEntry = "!"+comName
//let mutable rot:IRunningObjectTable =null

let rot=
    let mutable result:IRunningObjectTable = null
    GetRunningObjectTable(nativeint 0, &result) |> ignore
    result


let mutable enumMoniker:IEnumMoniker = null
rot.EnumRunning (&enumMoniker) 
enumMoniker.Reset() |> ignore
let mutable fetched = IntPtr.Zero
let mutable moniker:IMoniker[] = Array.zeroCreate 1 //http://msdn.microsoft.com/en-us/library/dd233214.aspx

let matches = seq {
    while enumMoniker.Next(1, moniker, fetched) = 0 do
        "looping" |> Dump
        let mutable bindCtx:IBindCtx = null
        CreateBindCtx(nativeint 0, &bindCtx) |> ignore
        let mutable displayName:string = null
        moniker.[0].GetDisplayName(bindCtx,null, &displayName)
        displayName |> Dump
        if displayName.StartsWith(rotEntry) then
            let mutable comObject = null
            rot.GetObject(moniker.[0], &comObject) |> ignore
            let dte =  comObject:?>EnvDTE80.DTE2
            yield displayName,bindCtx,comObject,dte.FullName, dte
}
matches |> Dump

for anyone interested in doing this with F# a mostly complete conversion is here ( currently set to run in linqpad):

open System;
open System.Runtime.InteropServices;
open System.Runtime.InteropServices.ComTypes;
open EnvDTE;
open System.Diagnostics;
//http://stackoverflow.com/questions/10864595/getting-the-current-envdte-or-iserviceprovider-when-not-coding-an-addin

//http://stackoverflow.com/questions/6558789/how-to-convert-out-ref-extern-parameters-to-f
//http://stackoverflow.com/questions/1689460/f-syntax-for-p-invoke-signature-using-marshalas

[<System.Runtime.InteropServices.DllImport("ole32.dll")>] 
extern int CreateBindCtx(System.IntPtr inRef, IBindCtx& outParentRef);
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern int GetRunningObjectTable(System.IntPtr inRef, IRunningObjectTable& outParentRef);
//let dte = System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.12.0") :?> EnvDTE80.DTE2
let comName="VisualStudio.DTE.12.0"
let rotEntry = "!"+comName
//let mutable rot:IRunningObjectTable =null

let rot=
    let mutable result:IRunningObjectTable = null
    GetRunningObjectTable(nativeint 0, &result) |> ignore
    result


let mutable enumMoniker:IEnumMoniker = null
rot.EnumRunning (&enumMoniker) 
enumMoniker.Reset() |> ignore
let mutable fetched = IntPtr.Zero
let mutable moniker:IMoniker[] = Array.zeroCreate 1 //http://msdn.microsoft.com/en-us/library/dd233214.aspx

let matches = seq {
    while enumMoniker.Next(1, moniker, fetched) = 0 do
        "looping" |> Dump
        let mutable bindCtx:IBindCtx = null
        CreateBindCtx(nativeint 0, &bindCtx) |> ignore
        let mutable displayName:string = null
        moniker.[0].GetDisplayName(bindCtx,null, &displayName)
        displayName |> Dump
        if displayName.StartsWith(rotEntry) then
            let mutable comObject = null
            rot.GetObject(moniker.[0], &comObject) |> ignore
            let dte =  comObject:?>EnvDTE80.DTE2
            yield displayName,bindCtx,comObject,dte.FullName, dte
}
matches |> Dump
惟欲睡 2024-10-19 17:31:24

我在下面提出了更舒适的完美解决方案(没有火箭科学)。这个方法的工作原理是从 Visual Studio 20 降到 10,以找到独立于 VS 版本的 DTE。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE80;

namespace Fortrus.Metadata
{
    /// <summary>
    /// This class takes care of fetching the correct DTE instance for the current process
    /// The current implementation works it way down from Visual Studio version 20 to 10 so
    /// it should be farely version independent
    /// </summary>
    public static class Processes
    {
        [DllImport("ole32.dll")]
        private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
        [DllImport("ole32.dll")]
        private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

        private const int m_MaxVersion = 20;
        private const int m_MinVersion = 10;

        internal static DTE2 GetDTE()
        {
            DTE2 dte = null;

            for (int version = m_MaxVersion; version >= m_MinVersion; version--)
            {
                string versionString = string.Format("VisualStudio.DTE.{0}.0", version);

                dte = Processes.GetCurrent(versionString);

                if (dte != null)
                {
                    return dte;
                }
            }

            throw new Exception(string.Format("Can not get DTE object tried versions {0} through {1}", m_MaxVersion, m_MinVersion));
        }

        /// <summary>
        /// When multiple instances of Visual Studio are running there also multiple DTE available
        /// The method below takes care of selecting the right DTE for the current process
        /// </summary>
        /// <remarks>
        /// Found this at: http://stackoverflow.com/questions/4724381/get-the-reference-of-the-dte2-object-in-visual-c-sharp-2010/27057854#27057854
        /// </remarks>
        private static DTE2 GetCurrent(string versionString)
        {
            //rot entry for visual studio running under current process.
            string rotEntry = String.Format("!{0}:{1}", versionString, Process.GetCurrentProcess().Id);

            IRunningObjectTable rot;
            GetRunningObjectTable(0, out rot);

            IEnumMoniker enumMoniker;
            rot.EnumRunning(out enumMoniker);
            enumMoniker.Reset();

            IntPtr fetched = IntPtr.Zero;
            IMoniker[] moniker = new IMoniker[1];

            while (enumMoniker.Next(1, moniker, fetched) == 0)
            {
                IBindCtx bindCtx;
                CreateBindCtx(0, out bindCtx);
                string displayName;
                moniker[0].GetDisplayName(bindCtx, null, out displayName);

                if (displayName == rotEntry)
                {
                    object comObject;

                    rot.GetObject(moniker[0], out comObject);

                    return (EnvDTE80.DTE2)comObject;
                }
            }

            return null;
        }
    }
}

I've made the perfect solution below a bit more comfortabel (no Rocket Science). This one works it's way down from Visual Studio 20 to 10 to find the DTE independent of VS versions.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE80;

namespace Fortrus.Metadata
{
    /// <summary>
    /// This class takes care of fetching the correct DTE instance for the current process
    /// The current implementation works it way down from Visual Studio version 20 to 10 so
    /// it should be farely version independent
    /// </summary>
    public static class Processes
    {
        [DllImport("ole32.dll")]
        private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
        [DllImport("ole32.dll")]
        private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

        private const int m_MaxVersion = 20;
        private const int m_MinVersion = 10;

        internal static DTE2 GetDTE()
        {
            DTE2 dte = null;

            for (int version = m_MaxVersion; version >= m_MinVersion; version--)
            {
                string versionString = string.Format("VisualStudio.DTE.{0}.0", version);

                dte = Processes.GetCurrent(versionString);

                if (dte != null)
                {
                    return dte;
                }
            }

            throw new Exception(string.Format("Can not get DTE object tried versions {0} through {1}", m_MaxVersion, m_MinVersion));
        }

        /// <summary>
        /// When multiple instances of Visual Studio are running there also multiple DTE available
        /// The method below takes care of selecting the right DTE for the current process
        /// </summary>
        /// <remarks>
        /// Found this at: http://stackoverflow.com/questions/4724381/get-the-reference-of-the-dte2-object-in-visual-c-sharp-2010/27057854#27057854
        /// </remarks>
        private static DTE2 GetCurrent(string versionString)
        {
            //rot entry for visual studio running under current process.
            string rotEntry = String.Format("!{0}:{1}", versionString, Process.GetCurrentProcess().Id);

            IRunningObjectTable rot;
            GetRunningObjectTable(0, out rot);

            IEnumMoniker enumMoniker;
            rot.EnumRunning(out enumMoniker);
            enumMoniker.Reset();

            IntPtr fetched = IntPtr.Zero;
            IMoniker[] moniker = new IMoniker[1];

            while (enumMoniker.Next(1, moniker, fetched) == 0)
            {
                IBindCtx bindCtx;
                CreateBindCtx(0, out bindCtx);
                string displayName;
                moniker[0].GetDisplayName(bindCtx, null, out displayName);

                if (displayName == rotEntry)
                {
                    object comObject;

                    rot.GetObject(moniker[0], out comObject);

                    return (EnvDTE80.DTE2)comObject;
                }
            }

            return null;
        }
    }
}
狼性发作 2024-10-19 17:31:23

经过一番广泛的搜索和尝试,我终于使用添加到 MSDN 页面的评论得到了答案:http: //msdn.microsoft.com/en-us/library/ms228755.aspx

我向我的 C# 项目添加了一个静态类:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE80;

[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

  internal static DTE2 GetCurrent()
  {

     //rot entry for visual studio running under current process.
     string rotEntry = String.Format("!VisualStudio.DTE.10.0:{0}", Process.GetCurrentProcess().Id);
     IRunningObjectTable rot;
     GetRunningObjectTable(0, out rot);
     IEnumMoniker enumMoniker;
     rot.EnumRunning(out enumMoniker);
     enumMoniker.Reset();
     IntPtr fetched = IntPtr.Zero;
     IMoniker[] moniker = new IMoniker[1];
     while (enumMoniker.Next(1, moniker, fetched) == 0)
     {
        IBindCtx bindCtx;
        CreateBindCtx(0, out bindCtx);
        string displayName;
        moniker[0].GetDisplayName(bindCtx, null, out displayName);
        if (displayName == rotEntry)
        {
           object comObject;
           rot.GetObject(moniker[0], out comObject);
           return (EnvDTE80.DTE2)comObject;
        }
     }
     return null;
  }

当我想要访问当前 IDE 时:

var dte = CurrentIde.GetCurrent();
var sol = dte.Solution;

但请记住......该代码在调试期间将不起作用!以字符串 rotEntry... 开头的代码行调用 Process.GetCurrentProcess 来获取当前进程的 ID。

在调试我的插件中的某些功能时(使用 MME http://mme.codeplex.com/),我调用了一个需要当前IDE。我使用调用 addin 方法的 ConsoleApp 对此进行测试。在获取当前 IDE 时,当前进程不是 IDE,而是 ConsoleApp.vshost.exe。所以我的代码在调试期间不起作用,但在构建插件并安装此插件后确实起作用。

After some extensive searching and trying i finally got the answer using the comment that was added to the MSDN page: http://msdn.microsoft.com/en-us/library/ms228755.aspx

I added a static class to my c# project:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE80;

[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

  internal static DTE2 GetCurrent()
  {

     //rot entry for visual studio running under current process.
     string rotEntry = String.Format("!VisualStudio.DTE.10.0:{0}", Process.GetCurrentProcess().Id);
     IRunningObjectTable rot;
     GetRunningObjectTable(0, out rot);
     IEnumMoniker enumMoniker;
     rot.EnumRunning(out enumMoniker);
     enumMoniker.Reset();
     IntPtr fetched = IntPtr.Zero;
     IMoniker[] moniker = new IMoniker[1];
     while (enumMoniker.Next(1, moniker, fetched) == 0)
     {
        IBindCtx bindCtx;
        CreateBindCtx(0, out bindCtx);
        string displayName;
        moniker[0].GetDisplayName(bindCtx, null, out displayName);
        if (displayName == rotEntry)
        {
           object comObject;
           rot.GetObject(moniker[0], out comObject);
           return (EnvDTE80.DTE2)comObject;
        }
     }
     return null;
  }

And at the point that I want to access the current IDE:

var dte = CurrentIde.GetCurrent();
var sol = dte.Solution;

But remember.... This code will NOT work during debugging!!! The line of code starting with string rotEntry... has a call to the Process.GetCurrentProcess to get the ID of the current process.

While debugging some functionality in my addin (using MME http://mme.codeplex.com/) I call a method that needs the current IDE. I test this with a ConsoleApp that calls the addin method. At the point of getting the current IDE, the currentprocess is NOT the IDE, but the ConsoleApp.vshost.exe. So my code did not work during debugging, but DID work after building the addin and installing this addin.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文