调试器数据模型 C++ 脚本

本主题介绍如何使用调试器数据模型 C++ 调试器数据模型 C++ 脚本来支持使用脚本通过调试器引擎实现自动化。

调试器数据模型中的脚本管理

除了数据模型管理器作为对象创建和扩展性的中心机构的角色外,它还负责管理脚本的抽象概念。 从数据模型管理器的脚本管理器部分的角度来看,脚本可由提供程序动态加载、卸载和调试,以便向数据模型扩展或提供新功能。

脚本提供程序是将语言 ((例如 NatVis、JavaScript 等)桥接到数据模型的组件,) 。 它注册一个或多个文件扩展名 (例如:“。NatVis“、”.js“) 由提供程序处理,允许调试器客户端或用户界面通过委托给提供程序加载具有该特定扩展名的脚本文件。

核心脚本管理器:IDataModelScriptManager

核心脚本管理器接口定义如下。

DECLARE_INTERFACE_(IDataModelScriptManager, IUnknown)
{
    STDMETHOD(GetDefaultNameBinder)(_COM_Outptr_ IDataModelNameBinder **ppNameBinder) PURE;
    STDMETHOD(RegisterScriptProvider)(_In_ IDataModelScriptProvider *provider) PURE;
    STDMETHOD(UnregisterScriptProvider)(_In_ IDataModelScriptProvider *provider) PURE;
    STDMETHOD(FindProviderForScriptType)(_In_ PCWSTR scriptType, _COM_Outptr_ IDataModelScriptProvider **provider) PURE;
    STDMETHOD(FindProviderForScriptExtension)(_In_ PCWSTR scriptExtension, _COM_Outptr_ IDataModelScriptProvider **provider) PURE;
    STDMETHOD(EnumerateScriptProviders)(_COM_Outptr_ IDataModelScriptProviderEnumerator **enumerator) PURE;
}

GetDefaultNameBinder

GetDefaultNameBinder 方法返回数据模型的默认脚本名称绑定器。 名称绑定器是解析对象上下文中名称的组件。 例如,给定表达式“foo.bar”,将调用名称联编程序来解析对象 foo 上下文中的名称栏。 此处返回的绑定器遵循数据模型的一组默认规则。 脚本提供程序可以使用此联编程序跨提供程序提供名称解析的一致性。

RegisterScriptProvider

RegisterScriptProvider 方法通知数据模型存在一个新的脚本提供程序,该提供程序能够将新语言桥接到数据模型。 调用此方法时,脚本管理器将立即回调给定的脚本提供程序,并查询它所管理的脚本的属性。 如果已在给定脚本提供程序指示的名称或文件扩展名下注册了提供程序,则此方法将失败。 只能将单个脚本提供程序注册为特定名称或文件扩展名的处理程序。

UnregisterScriptProvider

UnregisterScriptProvider 方法撤消对 RegisterScriptProvider 方法的调用。 传入的脚本提供程序给定的名称和文件扩展名将不再与之关联。 请务必注意,即使取消注册,也可能有大量对脚本提供程序的未完成 COM 引用。 此方法仅阻止加载/创建给定脚本提供程序所管理的类型的脚本。 如果该提供程序加载的脚本仍在加载或已操作调试器的对象模型 (或数据模型) ,则这些操作可能仍有引用返回到脚本中。 可能存在直接引用脚本中的构造的数据模型、方法或对象。 脚本提供程序必须准备好处理该操作。

FindProviderForScriptType

FindProviderForScriptType 方法在脚本管理器中搜索具有脚本类型字符串的提供程序,如此方法所示。 如果找不到,此方法将失败;否则,此类脚本提供程序将返回到调用方。

EnumerateScriptProviders

EnumerateScriptProviders 方法将返回一个枚举器,该枚举器将枚举通过之前调用 RegisterScriptProvider 方法注册到脚本管理器的每个脚本提供程序。

脚本提供程序枚举:IDataModelScriptProviderEnumerator

EnumerateScriptProviders 方法将返回以下形式的枚举器:

DECLARE_INTERFACE_(IDataModelScriptProviderEnumerator, IUnknown)
{
    STDMETHOD(Reset)() PURE;
    STDMETHOD(GetNext)(_COM_Outptr_ IDataModelScriptProvider **provider) PURE;
}

重置

Reset 方法会将枚举器移到返回第一个元素之前所在的位置。

GetNext

GetNext 方法将枚举器向前移动一个元素,并返回位于该元素的脚本提供程序。 当枚举器到达枚举末尾时,将返回E_BOUNDS。 收到此错误后调用 GetNext 方法将继续无限期返回E_BOUNDS。

用于脚本的调试器数据模型 C++ 主机接口

主机在脚本中的角色

调试主机公开了一系列非常低级别的接口,用于了解其调试目标的类型系统的性质 () 、用其调试目标的语言计算表达式 () 等...通常,它不涉及更高级别的构造,如脚本编写。 这会留给整个调试器应用程序或提供这些功能的扩展。 但是,这有一个例外。 任何想要参与数据模型提供的整体脚本编写体验的调试主机都需要实现几个简单的接口,以便为脚本提供上下文。 实际上,调试主机控制它希望脚本环境在数据模型的命名空间中放置函数和其他脚本提供的功能的位置。 参与此过程允许主机 (或不) 在其表达式计算器中使用此类函数。 从主机的角度来看,涉及的接口如下:

接口 说明
IDebugHostScriptHost 接口,指示调试主机参与脚本环境的能力。 此接口允许创建上下文,告知脚本引擎放置对象的位置。
IDataModelScriptHostContext 由脚本提供程序用作脚本内容的容器的主机接口。 脚本的内容如何呈现,而不是对调试器应用程序的对象模型执行的操作,由特定的调试主机决定。 此接口允许脚本提供程序获取有关放置其内容的位置的信息。 有关详细信息,请参阅本主题后面的 数据模型 C++ 脚本接口

调试主机的脚本主机:IDebugHostScriptHost

IDebugHostScriptHost 接口是脚本提供程序用于从新创建的脚本的调试主机获取上下文的接口。 此上下文包括由调试主机 (提供的对象) ,其中脚本提供程序可以在数据模型和脚本环境之间放置任何桥。 例如,此类桥可能是调用脚本函数的数据模型方法。 这样做允许数据模型端的调用方利用 IModelMethod 接口上的 Call 方法调用脚本方法。

IDebugHostScriptHost 接口的定义如下。

DECLARE_INTERFACE_(IDebugHostScriptHost, IUnknown)
{
    STDMETHOD(CreateContext)(_In_ IDataModelScript* script, _COM_Outptr_ IDataModelScriptHostContext** scriptContext) PURE;
}

CreateContext

脚本提供程序调用 CreateContext 方法以创建一个新上下文,用于放置脚本的内容。 此类上下文由“数据模型 C++ 脚本接口”页上详细介绍的 IDataModelScriptHostContext 接口表示。

调试器数据模型 C++ 脚本接口

脚本和脚本接口

数据模型的整体体系结构允许第三方定义某种语言和数据模型的对象模型之间的桥梁。 通常,桥接的语言是一种脚本语言,因为数据模型的环境非常动态。 在语言和数据模型的对象模型之间定义和实现此桥的组件称为脚本提供程序。 初始化后,脚本提供程序会将自身注册到数据模型管理器的脚本管理器部分,管理扩展性的任何接口随后都允许编辑、加载、卸载和调试以脚本提供程序管理的语言编写的脚本。

请注意,适用于 Windows 的调试工具目前定义了两个脚本提供程序。

  • NatVis 提供程序。 此提供程序嵌入在 NatVis XML 和数据模型之间的DbgEng.dll和桥中,允许本机/语言数据类型的可视化。
  • JavaScript 提供程序。 此提供程序包含在旧版调试器扩展中:JsProvider.dll。 它在以 JavaScript 语言编写的脚本和数据模型之间架起桥梁,从而允许任意形式的调试器控件和扩展性。

可以编写新的提供程序,以桥接其他语言 (例如 Python 等...) 到数据模型。 此类当前封装在旧版调试器扩展中,以便进行加载。 脚本提供程序本身应最大程度地减少旧引擎接口的依赖项,并且应尽可能仅利用数据模型 API。 这样,提供程序可以更方便地移植到其他环境。

有两类与脚本提供程序相关的接口。 第一类接口用于对脚本提供程序及其所管理的脚本进行常规管理。 第二类接口用于支持脚本调试。 虽然对第一个集的支持是必需的,但对第二个集的支持是可选的,对于每个提供程序都可能没有意义。

常规管理接口包括:

接口 说明
IDataModelScriptProvider 脚本提供程序必须实现的核心接口。 这是向数据模型管理器的脚本管理器部分注册的接口,用于播发提供程序对特定类型脚本的支持并针对特定文件扩展名进行注册
IDataModelScript 由提供程序管理的特定脚本的抽象。 加载或编辑的每个脚本都有单独的 IDataModelScript 实例
IDataModelScriptClient 脚本提供程序用于将信息传达给用户界面的客户端接口。 脚本提供程序不实现此接口。 托管希望使用脚本提供程序的数据模型的应用程序会使用脚本提供程序。 脚本提供程序将调用脚本客户端的方法来报告状态、错误等...
IDataModelScriptHostContext 脚本提供程序用作脚本内容的容器的主机接口。 除了脚本对调试器应用程序的对象模型执行的操作之外,脚本的内容如何呈现,由特定的调试主机决定。 此接口允许脚本提供程序获取有关放置其内容的位置的信息。
IDataModelScriptTemplate 脚本提供程序可以提供一个或多个模板,用作用户创作脚本的起点。 提供内置编辑器的调试器应用程序可以使用提供程序通过此接口播发的模板内容预填充新脚本。
IDataModelScriptTemplateEnumerator 脚本提供程序实现的枚举器接口,用于播发它支持的所有各种模板。
IDataModelNameBinder 一个名称绑定器 - 一个对象,它可以将上下文中的名称与值相关联。 对于给定的表达式(如“foo.bar”),名称绑定器能够在对象“foo”的上下文中绑定名称“bar”,并生成值或对它的引用。 名称绑定器通常不由脚本提供程序实现;相反,可以从数据模型获取默认绑定程序,并由脚本提供程序使用

调试接口包括:

接口 说明
IDataModelScriptDebug 脚本提供程序为使脚本可调试而必须提供的核心接口。 如果脚本可调试,则 IDataModelScript 接口的实现类必须为 IDataModelScriptDebug 的 QueryInterface。
IDataModelScriptDebugClient 希望提供脚本调试功能的用户界面实现 IDataModelScriptDebugClient 接口。 脚本提供程序利用此接口来 (来回传递调试信息,例如发生的事件、断点等...)
IDataModelScriptDebugStack 脚本提供程序实现此接口,以向脚本调试器公开调用堆栈的概念。
IDataModelScriptDebugStackFrame 脚本提供程序实现此接口以公开调用堆栈中特定堆栈帧的概念。
IDataModelScriptDebugVariableSetEnumerator 脚本提供程序实现此接口以公开一组变量。 此集可以表示函数的参数集、局部变量集或特定范围内的变量集。 确切的含义取决于获取接口的方式。
IDataModelScriptDebugBreakpoint 脚本提供程序实现此接口以公开脚本中特定断点的概念和控制。
IDataModelScriptDebugBreakpointEnumerator 脚本提供程序实现此操作以枚举脚本中当前存在的所有断点 (无论是否) 。

核心脚本提供程序:IDataModelScriptProvider

任何想要成为脚本提供程序的扩展都必须提供 IDataModelScriptProvider 接口的实现,并通过 RegisterScriptProvider 方法向数据模型管理器的脚本管理器部分注册此类扩展。 必须实现的此核心接口定义如下。

DECLARE_INTERFACE_(IDataModelScriptProvider, IUnknown)
{
    STDMETHOD(GetName)(_Out_ BSTR *name) PURE;
    STDMETHOD(GetExtension)(_Out_ BSTR *extension) PURE;
    STDMETHOD(CreateScript)(_COM_Outptr_ IDataModelScript **script) PURE;
    STDMETHOD(GetDefaultTemplateContent)(_COM_Outptr_ IDataModelScriptTemplate **templateContent) PURE;
    STDMETHOD(EnumerateTemplates)(_COM_Outptr_ IDataModelScriptTemplateEnumerator **enumerator) PURE;
}

GetName

GetName 方法返回 (类型或) 脚本语言的名称,提供程序将其作为通过 SysAllocString 方法分配的字符串进行管理。 调用方负责通过 SysFreeString 释放返回的字符串。 可能从此方法返回的字符串示例为“JavaScript”或“NatVis”。 返回的字符串可能会显示在托管数据模型的调试器应用程序的用户界面中。 (不区分大小写) ,任何两个脚本提供程序都不能返回同一名称。

GetExtension

GetExtension 方法返回此提供程序管理的脚本的文件扩展名, (没有点) 作为通过 SysAllocString 方法分配的字符串。 托管具有脚本支持) 的数据模型 (的调试器应用程序会将使用此扩展名打开的脚本文件委托给脚本提供程序。 调用方负责通过 SysFreeString 释放返回的字符串。 可能从此方法返回的字符串示例为“js”或“NatVis”。

CreateScript

调用 CreateScript 方法以创建新脚本。 每当调用此方法时,脚本提供程序都必须返回由返回的 IDataModelScript 接口表示的新空脚本。 请注意,无论用户界面是创建新的空白脚本供用户编辑,还是调试器应用程序是否正在从磁盘加载脚本,都会调用此方法。 提供程序不参与文件 I/O。 它仅通过传递给 IDataModelScript 上方法的流来处理来自托管应用程序的请求。

GetDefaultTemplateContent

GetDefaultTemplateContent 方法返回提供程序的默认模板内容的接口。 这是脚本提供程序希望在编辑窗口中为新创建的脚本预先填充的内容。 如果脚本提供程序没有 (模板,或者没有指定为默认内容) 的模板内容,则脚本提供程序可能会从此方法返回E_NOTIMPL。

EnumerateTemplates

EnumerateTemplates 方法返回一个枚举器,该枚举器能够枚举脚本提供程序提供的各种模板。 模板内容是脚本提供程序希望在创建新脚本时“预填充”到编辑窗口中的内容。 如果支持多个不同的模板,则可以 (命名这些模板,例如:“命令性脚本”、“扩展脚本”) ,托管数据模型的调试器应用程序可以选择如何向用户呈现“模板”。

核心脚本接口:IDataModelScript

管理提供程序实现的单个脚本的 main 接口是 IDataModelScript 接口。 当客户端希望创建新的空白脚本并在 IDataModelScriptProvider 上调用 CreateScript 方法时,将返回实现此接口的组件。

提供程序创建的每个脚本都应位于一个独立的接收器中。 一个脚本不应影响另一个脚本,除非通过数据模型与外部对象进行显式交互。 例如,两个脚本都可以扩展某种类型或概念 (例如:调试器对进程) 的概念。 然后,任一脚本都可以通过外部进程对象访问彼此的字段。

接口定义如下。

DECLARE_INTERFACE_(IDataModelScript, IUnknown)
{
    STDMETHOD(GetName)(_Out_ BSTR *scriptName) PURE;
    STDMETHOD(Rename)(_In_ PCWSTR scriptName) PURE;
    STDMETHOD(Populate)(_In_ IStream *contentStream) PURE;
    STDMETHOD(Execute)(_In_ IDataModelScriptClient *client) PURE;
    STDMETHOD(Unlink)() PURE;
    STDMETHOD(IsInvocable)(_Out_ bool *isInvocable) PURE;
    STDMETHOD(InvokeMain)(_In_ IDataModelScriptClient *client) PURE; 
}

GetName

GetName 方法通过 SysAllocString 函数以分配的字符串的形式返回脚本的名称。 如果脚本尚没有名称,该方法应返回空 BSTR。 在这种情况下,它不应失败。 如果通过调用 Rename 方法显式重命名脚本,则 GetName 方法应返回新分配的名称。

重命名

Rename 方法为脚本分配新名称。 脚本实现负责保存此名称,并在调用 GetName 方法时返回它。 当用户界面选择将脚本另存为新名称时,通常会调用它。 请注意,重命名脚本可能会影响宿主应用程序选择投影脚本内容的位置。

填充

客户端调用 Populate 方法以更改或同步脚本的“内容”。 这是向脚本提供程序发出的脚本代码已更改的通知。 请务必注意,此方法不会导致脚本的执行或更改脚本操作的任何对象。 这只是向脚本提供程序发出通知,告知脚本的内容已更改,以便它可以同步自己的内部状态。

执行

Execute 方法执行由上次成功填充调用所指示的脚本内容,并根据该内容修改调试器的对象模型。 如果语言 (或脚本提供程序) 定义了一个“main函数”(作者希望在单击用户界面中虚构的“执行脚本”按钮时调用的函数),则在执行操作期间不会调用此类“main函数”。 可将 Execute 操作视为仅 (执行初始化和对象模型操作,例如执行根代码和设置扩展点) 。

取消链接

Unlink 方法撤消 Execute 操作。 在执行脚本期间建立的任何对象模型操作或扩展点都是撤消的。 执行取消链接操作后,可以通过调用 Execute 重新执行脚本,也可以释放该脚本。

IsInvocable

IsInvocable 方法返回脚本是否可调用 ,即是否具有由其语言或提供程序定义的“main函数”。 从概念上讲,如果用户界面中按下了虚构的“执行脚本”按钮,脚本作者需要调用这种“main函数”。

InvokeMain

如果脚本具有旨在从 UI 调用执行的“main函数”,则它通过 IsInvocable 方法的真实返回来指示此类函数。 然后,用户界面可以调用 InvokeMain 方法,以实际“调用”脚本。 请注意,这不同于 Execute ,后者运行所有根代码并将脚本桥接到基础主机的命名空间。

**脚本客户端:IDataModelScriptClient **

托管数据模型的应用程序想要管理脚本并具有用户界面 (围绕此概念的图形或控制台) 实现 IDataModelScriptClient 接口。 在执行或调用期间,此接口将传递给任何脚本提供程序或脚本,以便将错误和事件信息传递回用户界面。

IDataModelScriptClient 接口的定义如下。

DECLARE_INTERFACE_(IDataModelScriptClient, IUnknown)
{
   STDMETHOD(ReportError)(_In_ ErrorClass errClass, _In_ HRESULT hrFail, _In_opt_ PCWSTR message, _In_ ULONG line, _In_ ULONG position) PURE;
}

ReportError

如果在脚本执行或调用期间发生错误,脚本提供程序将调用 ReportError 方法以通知用户界面错误。

脚本的主机上下文:IDataModelScriptHostContext

调试主机对数据模型脚本内容的投影方式和位置有一定影响。 预期每个脚本都会要求主机提供一个上下文,以便在其中放置脚本 (桥,例如:可调用的函数对象等...) 。通过调用 IDebugHostScriptHost 上的 CreateContext 方法并获取 IDataModelScriptHostContext 检索此上下文。

IDataModelScriptHostContext 接口的定义如下。

DECLARE_INTERFACE_(IDataModelScriptHostContext, IUnknown)
{
   STDMETHOD(NotifyScriptChange)(_In_ IDataModelScript* script, _In_ ScriptChangeKind changeKind) PURE;
   STDMETHOD(GetNamespaceObject)(_COM_Outptr_ IModelObject** namespaceObject) PURE;
}

NotifyScriptChange

在通过对关联上下文上的 NotifyScriptChange 方法进行方法调用时,脚本提供程序要求在发生某些操作时通知调试主机。 此类操作定义为 ScriptChangeKind 枚举的成员

GetNamespaceObject

GetNamespaceObject 方法返回一个 对象,脚本提供程序可以在数据模型和脚本之间放置任何桥。 例如,脚本提供程序可能会将数据模型方法对象 (IModelMethod 接口放入 IModelObject) 其实现调用脚本中相应命名的函数。

新创建的脚本的模板:IDataModelScriptTemplate

想要为新脚本呈现预先填充的内容的脚本提供程序 (例如:帮助用户在调试器用户界面中编写脚本) 可以通过提供一个或多个脚本模板来执行此操作。 此类模板是实现 IDataModelScriptTemplate 接口的组件,并通过脚本提供程序上的 GetDefaultTemplate 方法或 EnumerateTemplates 方法返回。

IDataModelScriptTemplate 接口的定义如下。

DECLARE_INTERFACE_(IDataModelScriptTemplate, IUnknown)
{
   STDMETHOD(GetName)(_Out_ BSTR *templateName) PURE;
   STDMETHOD(GetDescription)(_Out_ BSTR *templateDescription) PURE;
   STDMETHOD(GetContent)(_COM_Outptr_ IStream **contentStream) PURE;
}

GetName

GetName 方法返回模板的名称。 如果模板没有名称,则此操作可能会失败并E_NOTIMPL。 如果存在,则单个默认模板 () 不需要具有名称。 所有其他模板都是。 这些名称可以在用户界面中显示为菜单的一部分,以选择要创建的模板。

GetDescription

GetDescription 方法返回模板的说明。 此类说明将在更具描述性的界面中呈现给用户,以帮助用户了解模板的设计目的。 如果模板没有说明,则可能会从此方法返回E_NOTIMPL。

GetContent

GetContent 方法返回模板的内容 (或代码) 。 如果用户选择从此模板创建新脚本,则会预先填充到编辑窗口中。 该模板负责创建 (并返回) 客户端可以拉取的内容的标准流。

提供程序的模板内容的枚举:IDataModelScriptTemplateEnumerator

脚本提供程序可以提供一个或多个模板,用于将内容预先填充到某些用户界面中新创建的脚本中。 如果提供了这些模板中的任何一个,则脚本提供程序必须对其实现枚举器,该枚举器在调用 EnumerateTemplates 方法时返回。

此类枚举器是 IDataModelScriptTemplateEnumerator 接口的实现,定义如下。

DECLARE_INTERFACE_(IDataModelScriptTemplateEnumerator, IUnknown)
{
   STDMETHOD(Reset)() PURE;
   STDMETHOD(GetNext)(_COM_Outptr_ IDataModelScriptTemplate **templateContent) PURE;
}

重置

Reset 方法将枚举器重置为首次创建枚举器时所处的位置,即生成第一个模板之前的位置。

GetNext

GetNext 方法将枚举器移动到下一个模板并返回它。 枚举结束时,枚举器返回E_BOUNDS。 命中E_BOUNDS标记后,枚举器将继续无限期地生成E_BOUNDS错误,直到发出 Reset 调用。

解析名称的含义:IDataModelNameBinder

数据模型为脚本提供程序提供了一种标准方法,用于确定给定上下文中给定名称的含义, (例如:确定条形图对于将跨各种脚本提供程序运行的 foo.bar) 的含义。 此机制称为名称绑定器,由 IDataModelNameBinder 接口表示。 此类绑定器封装一组规则,这些规则涉及名称如何解析以及如何处理在对象上多次定义名称的冲突解决。 这些规则的一部分包括诸如投影名称 (数据模型添加的名称) 如何根据本机名称解析 (正调试) 语言的类型系统中的本机名称。

为了在脚本提供程序之间提供一定程度的一致性,数据模型的脚本管理器提供了默认名称绑定器。 可以通过调用 IDataModelScriptManager 接口上的 GetDefaultNameBinder 方法获取此默认名称绑定器。 名称绑定器接口定义如下。

DECLARE_INTERFACE_(IDataModelNameBinder, IUnknown)
{
   STDMETHOD(BindValue)(_In_ IModelObject* contextObject, _In_ PCWSTR name, _COM_Errorptr_ IModelObject** value, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
   STDMETHOD(BindReference)(_In_ IModelObject* contextObject, _In_ PCWSTR name, _COM_Errorptr_ IModelObject** reference, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
   STDMETHOD(EnumerateValues)(_In_ IModelObject* contextObject, _COM_Outptr_ IKeyEnumerator** enumerator) PURE;
   STDMETHOD(EnumerateReferences)(_In_ IModelObject* contextObject, _COM_Outptr_ IKeyEnumerator** enumerator) PURE;
}

BindValue

BindValue 方法根据一组绑定规则对给定对象执行等效的 contextObject.name。 此绑定的结果为 值。 作为一个值,基础脚本提供程序不能使用 值来执行赋回名称。

BindReference

BindReference 方法类似于 BindValue,因为它还根据一组绑定规则对给定对象执行等效的 contextObject.name。 但是,此方法的绑定结果是引用而不是值。 作为引用,脚本提供程序可以利用引用来执行赋回名称。

EnumerateValues

EnumerateValues 方法枚举一组名称和值,这些名称和值将根据 BindValue 方法的规则绑定到对象。 与 IModelObject 上的 EnumerateKeys、EnumerateValues 和类似方法不同,后者可能返回多个具有相同值的名称, (基类、父模型等) ,此枚举器将仅返回将与 BindValue 和 BindReference 绑定的特定名称集。 名称永远不会重复。 请注意,与调用 IModelObject 方法相比,通过名称绑定器枚举对象的成本要高得多。

EnumerateReferences

EnumerateReferences 方法枚举名称和对它们的引用集,这些名称和引用将根据 BindReference 方法的规则绑定到对象。 与 IModelObject 上的 EnumerateKeys、EnumerateValues 和类似方法不同,后者可能返回多个具有相同值的名称, (基类、父模型等) ,此枚举器将仅返回将与 BindValue 和 BindReference 绑定的特定名称集。 名称永远不会重复。 请注意,与调用 IModelObject 方法相比,通过名称绑定器枚举对象的成本要高得多。

调试器数据模型 C++ 脚本调试接口

数据模型中脚本提供程序的基础结构还提供了有关调试脚本的概念。 任何希望向调试主机和托管数据模型的调试器应用程序公开调试功能的脚本都可以通过让可调试脚本实现 IDataModelScriptDebug 接口以及 IDataModelScript 接口来实现此目的。 脚本上存在此接口向基础结构表明该接口可调试。

虽然 IDataModelScriptDebug 接口是访问脚本提供程序的调试功能的起点,但它是由一组提供整体调试功能的接口联接起来的。

调试接口包括:

接口 说明
IDataModelScriptDebug 脚本提供程序为使脚本可调试而必须提供的核心接口。 如果脚本可调试,则 IDataModelScript 接口的实现类必须为 IDataModelScriptDebug 的 QueryInterface。
IDataModelScriptDebugClient 希望提供脚本调试功能的用户界面实现 IDataModelScriptDebugClient 接口。 脚本提供程序利用此接口 (来回传递调试信息,例如发生的事件、断点等...)
IDataModelScriptDebugStack 脚本提供程序实现此接口以向脚本调试器公开调用堆栈的概念。
IDataModelScriptDebugStackFrame 脚本提供程序实现此接口以公开调用堆栈中特定堆栈帧的概念。
IDataModelScriptDebugVariableSetEnumerator 脚本提供程序实现此接口以公开一组变量。 此集可以表示函数的参数集、局部变量集或特定范围内的变量集。 确切的含义取决于获取接口的方式。
IDataModelScriptDebugBreakpoint 脚本提供程序实现此接口以公开脚本中特定断点的概念和控制。
IDataModelScriptDebugBreakpointEnumerator 脚本提供程序实现此操作以枚举脚本中当前存在的所有断点 (无论是否) 。

常规管理接口包括:

接口 说明
IDataModelScriptProvider 脚本提供程序必须实现的核心接口。 这是向数据模型管理器的脚本管理器部分注册的接口,以便播发提供程序对特定类型脚本的支持并针对特定文件扩展名注册
IDataModelScript 由提供程序管理的特定脚本的抽象。 加载或正在编辑的每个脚本都有单独的 IDataModelScript 实例
IDataModelScriptClient 脚本提供程序用于将信息传达给用户界面的客户端接口。 脚本提供程序不实现此接口。 托管希望使用脚本提供程序的数据模型的应用程序会使用脚本提供程序。 脚本提供程序将调用脚本客户端的方法来报告状态、错误等...
IDataModelScriptHostContext 脚本提供程序用作脚本内容的容器的主机接口。 除了脚本对调试器应用程序的对象模型执行的操作之外,脚本的内容如何呈现,由特定的调试主机决定。 此接口允许脚本提供程序获取有关放置其内容的位置的信息。
IDataModelScriptTemplate 脚本提供程序可以提供一个或多个模板,用作用户创作脚本的起点。 提供内置编辑器的调试器应用程序可以使用提供程序通过此接口播发的模板内容预填充新脚本。
IDataModelScriptTemplateEnumerator 脚本提供程序实现的枚举器接口,用于播发它支持的所有各种模板。
IDataModelNameBinder 一个名称绑定器 - 一个对象,它可以将上下文中的名称与值相关联。 对于给定的表达式(如“foo.bar”),名称绑定器能够在对象“foo”的上下文中绑定名称“bar”,并生成值或对它的引用。 名称绑定器通常不由脚本提供程序实现;相反,可以从数据模型获取默认绑定程序,并由脚本提供程序使用。

使脚本可调试:IDataModelScriptDebug

任何可调试的脚本都通过实现 IDataModelScript 的同一组件上存在 IDataModelScriptDebug 接口来指示此功能。 调试主机或托管数据模型的调试程序应用程序对此接口的查询指示存在调试功能。

IDataModelScriptDebug 接口定义如下。

DECLARE_INTERFACE_(IDataModelScriptDebug, IUnknown)
{
   STDMETHOD_(ScriptDebugState, GetDebugState)() PURE;
   STDMETHOD(GetCurrentPosition)(_Out_ ScriptDebugPosition *currentPosition, _Out_opt_ ScriptDebugPosition *positionSpanEnd, _Out_opt_ BSTR *lineText) PURE;
   STDMETHOD(GetStack)(_COM_Outptr_ IDataModelScriptDebugStack **stack) PURE;
   STDMETHOD(SetBreakpoint)(_In_ ULONG linePosition, _In_ ULONG columnPosition, _COM_Outptr_ IDataModelScriptDebugBreakpoint **breakpoint) PURE;
   STDMETHOD(FindBreakpointById)(_In_ ULONG64 breakpointId, _COM_Outptr_ IDataModelScriptDebugBreakpoint **breakpoint) PURE;
   STDMETHOD(EnumerateBreakpoints)(_COM_Outptr_ IDataModelScriptDebugBreakpointEnumerator **breakpointEnum) PURE;
   STDMETHOD(GetEventFilter)(_In_ ScriptDebugEventFilter eventFilter, _Out_ bool *isBreakEnabled) PURE;
   STDMETHOD(SetEventFilter)(_In_ ScriptDebugEventFilter eventFilter, _In_ bool isBreakEnabled) PURE;
   STDMETHOD(StartDebugging)(_In_ IDataModelScriptDebugClient *debugClient) PURE;
   STDMETHOD(StopDebugging)(_In_ IDataModelScriptDebugClient *debugClient) PURE;
}

GetDebugState

GetDebugState 方法 (返回脚本的当前状态,例如:它是否) 执行。 状态由 ScriptDebugState 枚举中的值定义。

GetCurrentPosition

GetCurrentPosition 的 方法返回脚本中的当前位置。 仅当脚本被分解到调试器中时,才能调用 GetScriptState 将返回 ScriptDebugBreak。 对此方法的任何其他调用都无效,并且将失败。

GetStack

GetStack 方法在中断位置获取当前调用堆栈。 仅当脚本被分解为调试器时,才能调用此方法。

SetBreakpoint

SetBreakpoint 方法在脚本中设置断点。 请注意,实现可以自由调整传入的行和列位置,以前进到适当的代码位置。 通过返回的 IDataModelScriptDebugBreakpoint 接口上的方法调用,可以检索放置断点的实际行号和列号。

FindBreakpointById

通过 SetBreakpoint 方法在脚本中创建的每个断点都分配有一个唯一标识符, (实现) 的 64 位无符号整数。 FindBreakpointById 方法用于从给定标识符获取到断点的接口。

EnumerateBreakpoints

EnumerateBreakpoints 方法返回一个枚举器,该枚举器能够枚举特定脚本中设置的每个断点。

GetEventFilter

GetEventFilter 方法返回是否为特定事件启用“中断事件”。 可能导致“事件中断”的事件由 ScriptDebugEventFilter 枚举的成员描述。

SetEventFilter

SetEventFilter 方法更改由 ScriptDebugEventFilter 枚举成员定义的特定事件的“中断事件”行为。 可以在 GetEventFilter 方法的文档中找到 (可用事件的完整列表,以及此枚举) 的说明。

StartDebugging

StartDebugging 方法“打开”特定脚本的调试器。 启动调试的行为不会主动导致任何执行中断或单步执行。 它只是使脚本可调试,并为客户端提供一组接口,以便与调试接口通信。

StopDebugging

StopDebugging 方法由要停止调试的客户端调用。 在 StartDebugging 成功执行后,可以随时调用此方法, (例如:在中断期间,脚本正在执行,等等...) 。调用会立即停止所有调试活动,并在调用 StartDebugging 之前将状态重置回 。

调试接口:IDataModelScriptDebugClient

希望围绕脚本调试提供接口的调试主机或调试器应用程序必须通过脚本调试接口上的 StartDebugging 方法为脚本调试器提供 IDataModelScriptDebugClient 接口的实现。

IDataModelScriptDebugClient 是传递调试事件的通信通道,控制从脚本执行引擎传递到调试器接口。 它的定义如下。

DECLARE_INTERFACE_(IDataModelScriptDebugClient, IUnknown)
{
   STDMETHOD(NotifyDebugEvent)(_In_ ScriptDebugEventInformation *pEventInfo, _In_ IDataModelScript *pScript, _In_opt_ IModelObject *pEventDataObject, _Inout_ ScriptExecutionKind *resumeEventKind) PURE;
}

NotifyDebugEvent

每当发生任何中断脚本调试器的事件时,调试代码本身会通过 NotifyDebugEvent 方法调用接口。 此方法是同步方法。 在接口从 事件返回之前,脚本不会继续执行。 脚本调试器的定义很简单:绝对没有需要处理的嵌套事件。 调试事件由称为 ScriptDebugEventInformation 的变体记录定义。 事件信息中的哪些字段有效主要由 DebugEvent 成员定义。 它定义由 ScriptDebugEvent 枚举成员描述的事件类型。

调用堆栈:IDataModelScriptDebugStack

当发生中断脚本调试器的事件时,调试接口需要检索中断位置的调用堆栈。 这是通过 GetStack 方法完成的。 此类堆栈通过 IDataModelScriptDebugStack 表示,如下所述定义。

请注意,整个堆栈可能跨多个脚本和/或多个脚本提供程序。 从对特定脚本调试接口上的 GetStack 方法的单个调用返回的调用堆栈应仅返回该脚本边界内的调用堆栈段。 如果同一提供程序的两个脚本交互,则脚本调试引擎完全有可能检索跨多个脚本上下文的调用堆栈。 GetStack 方法不应返回另一个脚本中的堆栈部分。 相反,如果可以检测到这种情况,则作为脚本边界帧的堆栈帧应通过该堆栈帧上的 IsTransitionPoint 和 GetTransition 方法的实现将自己标记为过渡帧。 调试器接口预期会将现有多个堆栈段的整体堆栈拼凑在一起。

必须以这种方式实现转换,否则调试接口可能会将有关局部变量、参数、断点和其他特定于脚本的构造的查询定向到错误的脚本上下文! 这将导致调试器接口中出现未定义的行为。

DECLARE_INTERFACE_(IDataModelScriptDebugStack, IUnknown)
{
   STDMETHOD_(ULONG64, GetFrameCount)() PURE;
   STDMETHOD(GetStackFrame)(_In_ ULONG64 frameNumber, _COM_Outptr_ IDataModelScriptDebugStackFrame **stackFrame) PURE;
}

GetFrameCount

GetFrameCount 方法返回调用堆栈的此段中的堆栈帧数。 如果提供程序可以检测不同脚本上下文中或不同提供程序中的帧,则它应通过在此堆栈段的入口帧上实现 IsTransitionPoint 和 GetTransition 方法向调用方表明这一点。

GetStackFrame

GetStackFrame 从堆栈段获取特定的堆栈帧。 调用堆栈具有从零开始的索引系统:发生中断事件的当前堆栈帧为帧 0。 当前方法的调用方为第 1 帧 (,以此类推) 。

检查中断时的状态:IDataModelScriptDebugStackFrame

在分解为脚本调试器时,可以通过调用 IDataModelScriptDebugStack 接口上的 GetStackFrame 方法来检索调用堆栈的特定帧,该方法表示发生中断的堆栈段。 返回以表示此帧的 IDataModelScriptDebugStackFrame 接口定义如下。

DECLARE_INTERFACE_(IDataModelScriptDebugStackFrame, IUnknown)
{
   STDMETHOD(GetName)(_Out_ BSTR *name) PURE;
   STDMETHOD(GetPosition)(_Out_ ScriptDebugPosition *position, _Out_opt_ ScriptDebugPosition *positionSpanEnd, _Out_opt_ BSTR *lineText) PURE;
   STDMETHOD(IsTransitionPoint)(_Out_ bool *isTransitionPoint) PURE;
   STDMETHOD(GetTransition)(_COM_Outptr_ IDataModelScript **transitionScript, _Out_ bool *isTransitionContiguous) PURE;
   STDMETHOD(Evaluate)(_In_ PCWSTR pwszExpression, _COM_Outptr_ IModelObject **ppResult) PURE;
   STDMETHOD(EnumerateLocals)(_COM_Outptr_ IDataModelScriptDebugVariableSetEnumerator **variablesEnum) PURE;
   STDMETHOD(EnumerateArguments)(_COM_Outptr_ IDataModelScriptDebugVariableSetEnumerator **variablesEnum) PURE;
}

GetName

GetName 方法返回显示名称 (例如:此帧的函数名称) 。 此类名称将显示在调试器界面中向用户显示的堆栈回溯中。

GetPosition

GetPosition 方法返回堆栈帧表示的脚本中的位置。 仅当脚本位于包含此帧的堆栈表示的中断内时,才能调用此方法。 始终返回此帧中的线条和列位置。 如果调试器能够返回脚本中“执行位置”的跨度,则可以在 positionSpanEnd 参数中返回结束位置。 如果调试器无法执行此操作,则范围末尾的行和列值 (如果请求) 应设置为零。

IsTransitionPoint

IDataModelScriptDebugStack 接口表示调用堆栈的一段 ,该部分包含在一个脚本的上下文中。 如果调试器能够检测从一个脚本到另一个脚本 (或一个脚本提供程序到另一个) 的转换,则它可以通过实现 IsTransitionPoint 方法并根据需要返回 true 或 false 来指示这一点。 进入应用段的脚本的调用堆栈帧应被视为转换点。 所有其他帧都不是。

GetTransition

如果给定的堆栈帧是由 IsTransition 方法确定的过渡点 (请参阅文档了解) 转换点的定义,则 GetTransition 方法将返回有关转换的信息。 具体而言,此方法返回上一个脚本,即调用包含此 IDataModelScriptDebugStackFrame 的堆栈段所表示的脚本的脚本。

评估

Evaluate 方法在调用此方法的 IDataModelScriptDebugStackFrame 接口所表示的堆栈帧的上下文中计算脚本提供程序) (语言的表达式。 表达式计算的结果必须作为 IModelObject 封送出脚本提供程序。 当调试器处于中断状态时,必须能够获取生成的 IModelObject 上的属性和其他构造。

EnumerateLocals

EnumerateLocals 方法返回一 (个由 IDataModelScriptDebugVariableSetEnumerator 接口) 表示的变量集,该变量集由 IDataModelScriptDebugVariableSetEnumerator 接口表示,这些变量在调用此方法的 IDataModelDebugStackFrame 接口所代表的堆栈帧的上下文中。

EnumerateArguments

EnumerateArguments 方法返回一个变量集 (,该变量集由 IDataModelScriptDebugVariableSetEnumerator 接口表示,) 为调用此方法的 IDataModelScriptDebugStackFrame 接口所表示的堆栈帧中调用的函数的所有函数参数。

查看变量:IDataModelScriptDebugVariableSetEnumerator

要调试的脚本中的一组变量 (特定范围内的变量、函数的局部变量、函数的参数等...) 是否由通过 IDataModelScriptDebugVariableSetEnumerator 接口定义的变量集表示:

DECLARE_INTERFACE_(IDataModelScriptDebugVariableSetEnumerator, IUnknown)
{
    STDMETHOD(Reset)() PURE;
    STDMETHOD(GetNext)(_Out_ BSTR *variableName, _COM_Outptr_opt_ IModelObject **variableValue, _COM_Outptr_opt_result_maybenull_ IKeyStore **variableMetadata) PURE;
}

重置

Reset 方法将枚举器的位置重置为创建后紧接的位置,即在集合的第一个元素之前。

GetNext

GetNext 方法将枚举器移动到集中的下一个变量,并返回变量的名称、值以及与它关联的任何元数据。 如果枚举器到达集的末尾,则返回错误E_BOUNDS。 从 GetNext 方法返回E_BOUNDS标记后,在再次调用时,它将继续生成E_BOUNDS,除非进行了干预重置调用。

断点:IDataModelScriptDebugBreakpoint

脚本断点是通过给定脚本的调试接口上的 SetBreakpoint 方法设置的。 此类断点由唯一 ID 和 IDataModelScriptDebugBreakpoint 接口的实现表示,定义如下。

DECLARE_INTERFACE_(IDataModelScriptDebugBreakpoint, IUnknown)
{
    STDMETHOD_(ULONG64, GetId)() PURE;
    STDMETHOD_(bool, IsEnabled)() PURE;
    STDMETHOD_(void, Enable)() PURE;
    STDMETHOD_(void, Disable)() PURE;
    STDMETHOD_(void, Remove)() PURE;
    STDMETHOD(GetPosition)(_Out_ ScriptDebugPosition *position, _Out_opt_ ScriptDebugPosition *positionSpanEnd, _Out_opt_ BSTR *lineText) PURE;
}

GetId

GetId 方法返回由脚本提供程序的调试引擎分配给断点的唯一标识符。 此标识符在包含脚本的上下文中必须是唯一的。 断点标识符对提供程序可能是唯一的;但是,这不是必需的。

IsEnabled

IsEnabled 方法返回是否启用断点。 禁用的断点仍然存在,并且仍在脚本的断点列表中,它只是暂时“关闭”。 应在已启用状态下创建所有断点。

启用

Enable 方法启用断点。 如果断点已禁用,则调用此方法后“命中断点”将导致中断调试器。

Disable

Disable 方法禁用断点。 在此调用之后,调用此方法后“命中断点”不会进入调试器。 断点虽然仍然存在,但被视为“已关闭”。

删除

Remove 方法从其包含列表中删除断点。 此方法返回后,断点在语义上不再存在。 表示断点的 IDataModelScriptDebugBreakpoint 接口在调用后被视为孤立。 在此调用之后,除了释放它之外,其他任何 (法律) 都做不了的事。

GetPosition

GetPosition 方法返回脚本中断点的位置。 脚本调试器必须在源代码中返回断点所在的行和列。 如果它能够这样做,它还可以通过填写由 positionSpanEnd 参数定义的结束位置来返回由断点表示的源范围。 如果调试器无法生成此范围,并且调用方请求它,则范围结束位置的“行”和“列”字段应填充为零,指示无法提供值。

断点枚举:IDataModelScriptDebugBreakpointEnumerator

如果脚本提供程序支持调试,则它还必须跟踪与每个脚本关联的所有断点,并且能够将这些断点枚举到调试接口。 断点的枚举器是通过给定脚本的调试接口上的 EnumerateBreakpoints 方法获取的,定义如下。

DECLARE_INTERFACE_(IDataModelScriptDebugBreakpointEnumerator, IUnknown)
{
   STDMETHOD(Reset)() PURE;
   STDMETHOD(GetNext)(_COM_Outptr_ IDataModelScriptDebugBreakpoint **breakpoint) PURE;
}

重置

Reset 方法将枚举器的位置重置为创建枚举器之后的位置,即第一个枚举断点之前的位置。

GetNext

GetNext 方法将枚举器向前移动到要枚举的下一个断点,并返回该断点的 IDataModelScriptDebugBreakpoint 接口。 如果枚举器已到达枚举的末尾,则返回E_BOUNDS。 生成E_BOUNDS错误后,对 GetNext 方法的后续调用将继续生成E_BOUNDS,除非已对 Reset 方法进行了干预调用。


另请参阅

本主题是一系列文章的一部分,该系列介绍了可从 C++ 访问的接口、如何使用接口生成基于 C++ 的调试器扩展,以及如何利用其他数据模型构造 (例如:JavaScript 或 NatVis) 从 C++ 数据模型扩展。

调试器数据模型 C++ 概述

调试器数据模型 C++ 接口

调试器数据模型 C++ 对象

调试器数据模型 C++ 的其他接口

调试器数据模型 C++ 的概念