11

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で現在のソリューションオブジェクトにアクセスするにはどうすればよいですか?

4

5 に答える 5

14

いくつかの広範な検索と試行の後、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... で始まるコード行には、現在のプロセスの ID を取得するための Process.GetCurrentProcess への呼び出しがあります。

アドイン (MME http://mme.codeplex.com/を使用) でいくつかの機能をデバッグしているときに、現在の IDE を必要とするメソッドを呼び出します。アドイン メソッドを呼び出す ConsoleApp でこれをテストします。現在の IDE を取得する時点で、現在のプロセスは IDE ではなく、ConsoleApp.vshost.exe です。したがって、私のコードはデバッグ中に機能しませんでしたが、アドインをビルドしてこのアドインをインストールした後、DID は機能しました。

于 2011-01-18T13:59:39.427 に答える
9

私は次の点が不安であると感じたので、私はそれらに対処し、私のために働く解決策を見つけました:

  • GetActiveObject("VisualStudio.DTE.10.0")最初に開いた(おそらく)VisualStudioでのみ機能します
  • デニスの答えのinternal static DTE2 GetCurrent()方法には、VisualStudioプロセスIDが必要です。アドインからコードを実行する場合は問題ありませんが(私は思う)、単体テストなどでは機能しません。
  • デバッグモードでの問題

また、ここから取得したGetCurrentメソッドから始めました。問題は、適切なVisualStudioプロセスのProcessIdに到達する方法がわからなかったことです(通常、複数のインスタンスが実行されています)。したがって、私が採用したアプローチは、すべてのVisualStudio ROTエントリとそのDTE2を取得し、DTE2.Solution.FullNameを実行中のアセンブリの場所と比較することでした(より良い選択がありますか?)。これはあまり正確な科学ではないことは容易に認めますが、特別な出力パス構成がない場合は機能するはずです。次に、コードをデバッグモードで実行し、DTE2 COMオブジェクトにアクセスすると、次の例外がスローされることがわかりましたSystem.Runtime.InteropServices.COMException: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))ただし、 MessageFilterと呼ばれるそのための救済策があります。完全を期すために、下部にコードを含めました。

テストメソッド(使用例)、adjustedGetCurrentメソッド、および文字列比較用のヘルパーメソッドを含むテストクラス:

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);
}
于 2012-10-23T16:12:13.397 に答える
1

これが古いスレッドであることは承知していますが、このコードを複数の Visual Studio バージョンで使用する必要がありました。以下のようにコードを微調整しました。

string processID = Process.GetCurrentProcess().Id.ToString();
if (displayName.StartsWith("!VisualStudio.DTE.", StringComparison.OrdinalIgnoreCase) &&
        displayName.EndsWith(processID))
于 2013-05-16T13:36:32.167 に答える
1

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
于 2014-05-02T21:02:12.913 に答える