C# 使用EnvDTE自动化visualstudio
我正在使用以下代码成功实例化/自动化Visual Studio:C# 使用EnvDTE自动化visualstudio,c#,visual-studio,automation,C#,Visual Studio,Automation,我正在使用以下代码成功实例化/自动化Visual Studio: System.Type t = System.Type.GetTypeFromProgID("VisualStudio.DTE.9.0"); object obj = Activator.CreateInstance(t, true); dte = (DTE)obj; Solution sln = dte.Solution; sln.Open(SolutionFile); System.Threading.Thread.Sleep
System.Type t = System.Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
object obj = Activator.CreateInstance(t, true);
dte = (DTE)obj;
Solution sln = dte.Solution;
sln.Open(SolutionFile);
System.Threading.Thread.Sleep(1000);
//Do stuff with the solution
注意线程。Sleep(1000)
调用?如果我没有包括这些,代码会在实例准备就绪之前尝试对其进行bug,我会得到一个异常:
the message filter indicated that the application is busy.
有没有办法轮询此对象是否准备就绪,而不是只等待n秒?作为此问题的解决方案,您可以注册到一个事件,该事件在解决方案加载完成时发出通知 这是一个示例类,可用于侦听解决方案加载时的事件:
public class SolutionEventsListener : IVsSolutionEvents, IDisposable
{
private IVsSolution solution;
private uint solutionEventsCookie;
public event Action AfterSolutionLoaded;
public event Action BeforeSolutionClosed;
public SolutionEventsListener(IServiceProvider serviceProvider)
{
InitNullEvents();
solution = serviceProvider.GetService(typeof (SVsSolution)) as IVsSolution;
if (solution != null)
{
solution.AdviseSolutionEvents(this, out solutionEventsCookie);
}
}
private void InitNullEvents()
{
AfterSolutionLoaded += () => { };
BeforeSolutionClosed += () => { };
}
#region IVsSolutionEvents Members
int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
{
AfterSolutionLoaded();
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)
{
BeforeSolutionClosed();
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel)
{
return VSConstants.S_OK;
}
int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)
{
return VSConstants.S_OK;
}
#endregion
#region IDisposable Members
public void Dispose()
{
if (solution != null && solutionEventsCookie != 0)
{
GC.SuppressFinalize(this);
solution.UnadviseSolutionEvents(solutionEventsCookie);
AfterSolutionLoaded = null;
BeforeSolutionClosed = null;
solutionEventsCookie = 0;
solution = null;
}
}
#endregion
}
用法示例:
DTE2 applicationObject = dte;
var serviceProvider = new ServiceProvider(applicationObject as IServiceProvider);
solutionEventsListener = new SolutionEventsListener(serviceProvider);
solutionEventsListener.AfterSolutionLoaded += () => /* logic here */ ;
我在IVSSolutionEvents技术方面运气不太好(尽管我没有像上面那样尝试代码)。相反,我创建了一个小函数来帮助我重试调用。我知道这并不特别漂亮,但它很容易调用,而且很有效 下面是我对另一个类似问题的回答的链接:
(在调用其他EnvDTE函数以及打开和关闭解决方案时,它也有助于解决“服务器忙”错误。)虽然这里的解决方案很有创意,但它们要么不能完全解决问题,要么使用起来很麻烦。你应该这样做 为方便起见,将代码复制到此处(将
VisualStudio.DTE.10.0
替换为您想要打开的VS的任何版本),只需注意使用STAThread
属性修饰Main
方法,没有它消息过滤将无法工作,并且在原始MSDN解决方案中会跳过它
using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using EnvDTE80;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleApplication2
{
class Program
{
[STAThread]
static void Main(string[] args)
{
EnvDTE80.DTE2 dte;
object obj = null;
System.Type t = null;
// Get the ProgID for DTE 8.0.
t = System.Type.GetTypeFromProgID("VisualStudio.DTE.10.0",
true);
// Create a new instance of the IDE.
obj = System.Activator.CreateInstance(t, true);
// Cast the instance to DTE2 and assign to variable dte.
dte = (EnvDTE80.DTE2)obj;
// Register the IOleMessageFilter to handle any threading
// errors.
MessageFilter.Register();
// Display the Visual Studio IDE.
dte.MainWindow.Activate();
// =====================================
// ==Insert your automation code here.==
// =====================================
// For example, get a reference to the solution2 object
// and do what you like with it.
Solution2 soln = (Solution2)dte.Solution;
System.Windows.Forms.MessageBox.Show
("Solution count: " + soln.Count);
// =====================================
// All done, so shut down the IDE...
dte.Quit();
// and turn off the IOleMessageFilter.
MessageFilter.Revoke();
}
}
public class MessageFilter : IOleMessageFilter
{
//
// Class containing the IOleMessageFilter
// thread error-handling functions.
// Start the filter.
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
int hr = CoRegisterMessageFilter(newFilter, out oldFilter);
if (hr != 0)
Marshal.ThrowExceptionForHR(hr);
}
// 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 the flag SERVERCALL_ISHANDLED.
return 0;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(System.IntPtr
hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
// Retry the thread call immediately if return >=0 &
// <100.
return 99;
}
// Too busy; cancel call.
return -1;
}
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);
}
}
使用系统;
使用System.Collections.Generic;
使用系统文本;
使用EnvDTE;
使用EnvDTE80;
使用System.Runtime.InteropServices;
使用System.Windows.Forms;
命名空间控制台应用程序2
{
班级计划
{
[状态线程]
静态void Main(字符串[]参数)
{
EnvDTE80.DTE2-dte;
objectobj=null;
系统类型t=null;
//获取DTE 8.0的ProgID。
t=System.Type.GetTypeFromProgID(“VisualStudio.DTE.10.0”,
正确的);
//创建IDE的新实例。
obj=System.Activator.CreateInstance(t,true);
//将实例强制转换为DTE2并分配给变量dte。
dte=(EnvDTE80.DTE2)obj;
//注册IOleMessageFilter以处理任何线程
//错误。
MessageFilter.Register();
//显示VisualStudioIDE。
dte.MainWindow.Activate();
// =====================================
//==在此处插入您的自动化代码==
// =====================================
//例如,获取对solution2对象的引用
//你喜欢怎么做就怎么做。
Solution2 soln=(Solution2)dte.Solution;
System.Windows.Forms.MessageBox.Show
(“溶液计数:”+soln.count);
// =====================================
//全部完成,所以关闭IDE。。。
dte.Quit();
//并关闭IOleMessageFilter。
MessageFilter.Revoke();
}
}
公共类MessageFilter:IOleMessageFilter
{
//
//包含IOleMessageFilter的类
//线程错误处理函数。
//启动过滤器。
公共静态无效寄存器()
{
IOleMessageFilter newFilter=新消息过滤器();
IOleMessageFilter-oldFilter=null;
int hr=CoRegisterMessageFilter(新过滤器、旧过滤器);
如果(hr!=0)
元帅。通过hr(hr)的例外情况;
}
//完成过滤器,关闭它。
公共静态void Revoke()
{
IOleMessageFilter-oldFilter=null;
CoRegisterMessageFilter(null,out oldFilter);
}
//
//IOleMessageFilter函数。
//处理传入的线程请求。
int IOleMessageFilter.HandleInComingCall(int dwCallType,
System.IntPtr hTaskCaller,int dwTickCount,System.IntPtr
LPEINFO)
{
//返回标志SERVERCALL\u ISHANDLED。
返回0;
}
//线程调用被拒绝,请重试。
int IOleMessageFilter.RetryRejectedCall(System.IntPtr
hTaskCallee、int-dwTickCount、int-dwreturnType)
{
if(dwRejectType==2)
//flag=SERVERCALL\u RETRYLATER。
{
//如果返回>=0&
//这是一个很好的答案,效果很好。顺便说一句,我发现了一个同样有效的作弊方法-在使用Solution.Open打开解决方案后检查Solution.IsOpen属性。这显然是一个阻塞调用,它会强制等待devenv.exe的实例准备就绪。@Dave Swersky,我喜欢这个解决方案。IsOpen,它节省了大量代码:)这也帮助了我在VS11中解决了问题!很好的解决方案,这应该是答案。应该这样做。其他人没有一个对我来说是可靠的。只是一个提示,请注意[StatThread]在顶部,如果没有它,MessageFilter将不会注册,因为MTA不支持它。谢谢!中的重试解决方案或多或少也会这样做,但使用起来要麻烦得多。这使用com中内置的消息筛选器系统来重试调用。我使用了类似的编码UI,我不会等待页面加载,just尝试执行操作直到它工作。但是正如您所说的,长期使用它不那么透明和令人不快。特别是那些COM异常不仅可能在第一次调用中发生,而且以后也可能发生。