Den*_*nis 10 c# add-in visual-studio-2010 envdte
我想在Visual Studio 2010中使用带有C#的DTE2对象来获得对当前解决方案的引用.
我首先尝试了以下代码:
var dte = Marshal.GetActiveObject("VisualStudio.DTE.10.0") as EnvDTE80.DTE2;
Run Code Online (Sandbox Code Playgroud)
但是当我打开2个解决方案,并且这个代码在第一个解决方案中时,我得不到对当前解决方案的引用,而是对我加载的最后一个解决方案的引用.我需要当前的解决方案......
在互联网上搜索,我发现以下解决方案如何从VSPackage获取当前的解决方案目录?:
// Get an instance of the currently running Visual Studio IDE
DTE dte = (DTE)GetService(typeof(DTE));
Run Code Online (Sandbox Code Playgroud)
但是当我使用它时,我的dte对象总是为NULL.
那么如何使用C#on .net framework 4.0获取VS2010中当前的解决方案对象?
Den*_*nis 12
经过一番广泛的搜索和尝试后,我终于使用添加到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;
}
Run Code Online (Sandbox Code Playgroud)
在我想要访问当前IDE的时候:
var dte = CurrentIde.GetCurrent();
var sol = dte.Solution;
Run Code Online (Sandbox Code Playgroud)
但请记住....这段代码在调试过程中不起作用!以string rotEntry ...开头的代码行调用Process.GetCurrentProcess来获取当前进程的ID.
在我的插件中调试一些功能时(使用MME http://mme.codeplex.com/),我调用了一个需要当前IDE的方法.我用调用addin方法的ConsoleApp测试它.在获取当前IDE时,当前进程不是IDE,而是ConsoleApp.vshost.exe.所以我的代码在调试期间不起作用,但DID在构建插件并安装此插件后工作.
我觉得以下几点令人不安,所以我已经解决了这些问题并找到了适合我的解决方案:
GetActiveObject("VisualStudio.DTE.10.0") 仅适用于第一次打开(我猜)Visual Studiointernal static DTE2 GetCurrent()丹尼斯的答案的方法需要在Visual Studio进程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.为了完整起见,我已将代码包含在底部.
包含测试方法(用法示例)的测试类,调整后的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;
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8833 次 |
| 最近记录: |