
Visual Studio 2012的一项创新是伴随着一个名为Concord的新的自定义调试器的出现。 其组件系统允许VSIX插件自定义调试器的行为,并编写新的,上下文相关的工具,这些工具可以根据需要使用调试器。 其API提供了许多QOL功能,例如托管/非托管代码之间的封送处理,与远程/本地调试过程的无缝集成等。 实际上,几乎可以在IDE中完成的所有操作都可以使用Concord API以编程方式完成! 随时更改特定变量的值,根据要求调用函数(或专门使程序跳过对它们的调用!),插件可以按PDB(!),分步跳过甚至代码修改进行搜索! 打开猫,您将学到自行车制造领域的这些鲜为人知的创新。
您可能应该从头开始。 调试器通过从VSIX插件清单引用的
vsdconfig文件中读取信息来检测组件。 反之,
vsdconfig指示哪些接口由插件组件实现,以及如何找到这些组件(链接到.dll文件,指示类,或者,如果是本机实现,则指示CLSID。我将在C#中给出示例)。 而且,指示每个组件的唯一标识符(GUID)及其“级别”。 级别决定了将按什么顺序处理插件,以及在哪种过程的上下文中确定此实现将被加载到IDE进程或已调试应用程序的进程中。 这是由于以下事实:某些功能只能在IDE的上下文中起作用,反之亦然-仅在调试过程的上下文中才可以起作用。 一些API函数到处都是相同的方式。 而且,许多组件都有自己的布局规则,因为它们可能取决于位于其固定级别的现有调试器元素。 为避免发生事故,我建议在单独的沙箱中使用RTFM(https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.visualstudio.debugger.componentinterfaces?view=visualstudiosdk-2017)和独立实验,这并不可惜删除是否发生了什么(再次如此细微地联系在一起-在某些情况下,甚至还不清楚为什么不加载程序集或类型,因为我仍然找不到日志中出现的位置,可以稳定地指出问题。错误,例如,参考无法加载到目标进程的依赖项,它可能会出现在输出st中 二,还是不行。要akuratno,采取频繁的提交,
而不要坐在车轮后面醉)。级别列表如下(我将提供英文文本,以使读者在执行RFTM行为时不会发生事件):
IDE组件级别(值> 100,000):
目标流程的组件级别(值<99.999):
接下来,依次介绍创建项目的过程。 如果没有任何重要的细微差别,我可以仅描述或略过此过程,但现实情况完全不同-我们将需要许多库依赖项,以及用于创建配置文件的工具,由于某种原因,该文件未随Visual Studio一起发布,但可以使用只能用nuget。 实际上,现在我们需要继续讲究本质。 创建和设置项目的过程的结构如下:
- 打开Visual Studio。 就我而言,2017年社区版
- VSIX项目( Visual C#->扩展性选项卡,或通过搜索)。 我们称之为“ HelloVSIX”
- 在解决方案中添加一个新的类库项目,并将其命名为“ DebuggeePlugin”
- 我们将引用放在项目“ HelloVSIX”中的项目“ DebuggeePlugin”上
- 我们在DebuggeePlugin项目中将对程序集“ Microsoft.VisualStudio.Debugger.Engine”的引用
- 将nuget包Microsoft.VSSDK.Debugger.VSDConfigTool的引用添加到项目“ DebuggeePlugin”。 这是我们用于生成VSD配置的工具。
现在,我们准备让我们的插件做一些有用的事情。 让我们做最简单的事情-当目标进程遇到入口点时,它显示一个MessageBox并显示“ Hello VSIX”。 为此,我们需要创建一个实现
IDkmEntryPointNotification接口的类,并填写几个配置文件。 添加一个名为DkmEntryPointNotificationService的新公共类,并继承
IDkmEntryPointNotification接口,并保留默认实现:
using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DebuggeePlugin { class DkmEntryPointNotificationService : IDkmEntryPointNotification { public void OnEntryPoint(DkmProcess process, DkmThread thread, DkmEventDescriptor eventDescriptor) { throw new NotImplementedException(); } } }
将文件“ DkmEntryPointNotificationService.vsdconfigxml”添加到“ DebuggeePlugin”项目。 对于应该通过Microsoft.VisualStudio.Debugger.ComponentInterfaces命名空间接口的实现接收通知的每个声明的类,您都应该具有这样的文件。 顺便说一句,可以在一类中一次实现多个这样的接口。 现在,我们需要更改“ .vsdconfigxml”文件的生成操作。 为此,您必须手动编辑项目文件(我很认真)。 我们卸载DebuggeePlugin项目,并使用Studio编辑器将其打开。 我们需要找到以下XLM标签:
<None Include="DkmEntryPointNotificationService.vsdconfigxml" />
并将此标记移动到您自己的ItemGroup,将类型从“无”更改为VsdConfigXmlFiles:
<ItemGroup> <VsdConfigXmlFiles Include="DkmEntryPointNotificationService.vsdconfigxml" /> </ItemGroup>
您可以保存并重新加载项目。
现在,转到配置。 要做的第一件事:如果将vsdconfig.xsd文件添加到DebuggeePlugin项目,则应将其删除。 我们现在将替换它,因为它更容易处理原始文本。 打开DkmEntryPointNotificationService.vsdconfigxml并将文本替换为以下内容:
<?xml version="1.0" encoding="utf-8"?> <Configuration xmlns="http://schemas.microsoft.com/vstudio/vsdconfig/2008"> <ManagedComponent ComponentId="422413E1-450E-40A6-AE24-7E80A81CC668" ComponentLevel=^_^quot𘙮quot^_^ AssemblyName="DebuggeePlugin"> <Class Name="DebuggeePlugin.DkmEntryPointNotificationService"> <Implements> <InterfaceGroup> <NoFilter/> <Interface Name="IDkmEntryPointNotification"/> </InterfaceGroup> </Implements> </Class> </ManagedComponent> </Configuration>
在任何此类文件中,我们都需要指出以下内容:
- ComponentId-可以使用GUID生成工具(工具-> CreateGUID)生成此值
- ComponentLevel是组件在层次结构中的级别。 请参阅上表和MSDN上的帮助信息以选择所需的值范围。
- Assemblyname是我们的程序集的名称(不是解决方案!)。 在这种情况下,将有一个DebuggeePlugin
- 类名-应该指出,包括类所在的名称空间。 在这种情况下,DebuggeePlugin.DkmEntryPointNotificationService
- InterfaceGroup数组-其中的每个条目指示此组件实现的接口。 在每个InterfaceGroup节点内,应该有一个子节点,该子节点指示该组中每个人(过滤器)的通用接口,但后面有关过滤器的信息。 现在,我们只有一个Interface节点,并带有IdkmEntryPointNotification接口的名称。 如果我们有多个接口,那么将有多个Interface节点。
让我提醒您,对于每个应该从调试器接收通知的类,必须有一个这样的文件。 但是乐趣并没有就此结束。 随后,每个这样的文件都收集在项目输出目录中的.vsdconfig文件中。 并且应该在插件清单中引用它们。 这样做如下:
- 生成“ .vsdconfigxml”文件后,我们必须...收集一次解决方案,否则项目的输出目录中将没有任何.vsdconfig文件)
- 之后,打开source.extension.vsixmanifest文件的文本编辑器,并在关闭PackageManifest标记之前添加以下代码:
<Assets> <Asset Type="DebuggerEngineExtension" d:Source="File" Path="DebuggeePlugin.vsdconfig" /> </Assets>
如果在完成操作后,“ DebuggeePlugin.vsdconfig”文件出现在HelloVSIX项目中,则应
从项目中将其
删除 ,并应再次收集解决方案,否则将不会更新。
准备工作结束了! 您可以开始调试我们的插件。 这是通过启动VisualStudio的实验实例来完成的(对于VSIX项目,这是默认的调试目标,因此不需要其他步骤)。 实际上,我们单击Debug-> StartDebugging,然后看到VisualStudio的实验实例。 在其中,默认情况下,我们的插件应已安装。 您可以通过“工具”->“扩展和更新”菜单进行验证。
由于我们实现了IDkmEntryPointNotification接口,因此我们将不得不在VisualStudio的实验实例中创建一个测试项目。 实际上,我们创建了一个新项目,选择
C ++->控制台应用程序 (选择C ++,因为以下示例将包含C ++特定内容),将其
称为VSIXTestApp ,无需进行任何更改
即可运行,收集并看到我们的实验实例停止在DebuggeePlugin方法内引发异常。 DkmEntryPointNotificationService.OnEntryPoint。 太好了! 现在,您需要显示MessageBox。 为此,必须将以下引用添加到DebuggeePlugin项目:
- Microsoft.VisualStudio.Shell.15.0
- Microsoft.VisualStudio.Shell.Interop
- Microsoft.VisualStudio.Shell.Interop.8.0
- Microsoft.VisualStudio.OLE.Interop
在DkmEntryPointNotificationService.cs文件的开头添加两个用法:
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop;
并在
DkmEntryPointNotificationService.OnEntryPoint方法中添加对
VsShellUtilities.ShowMessageBox方法的调用:
using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DebuggeePlugin { class DkmEntryPointNotificationService : IDkmEntryPointNotification { public void OnEntryPoint(DkmProcess process, DkmThread thread, DkmEventDescriptor eventDescriptor) { VsShellUtilities.ShowMessageBox(Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider, "Hello VSIX", "Hello VSIX", OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); } } }
我们重建,启动工作室的实验实例,启动测试项目,瞧!
我们看到工作室的测试实例创建了MessageBox!

而实际上有什么好处呢?
在这里,我们学习了如何设置VSIX项目,该项目包含用于Visual Studio调试器的插件,同时考虑了妨碍结果的大多数细微差别。 这是更详细工作的起点。 在下一篇文章中,我将向您展示另一个重要点:如何执行IDE与Debug目标组件之间的通信。
有关使用Concord API的进一步帮助,您不仅可以引用MSDN,还可以引用github上的以下Microsoft存储库:
github.com/microsoft/PTVSgithub.com/Microsoft/ConcordExtensibilitySamples