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

本主题介绍如何使用调试器数据模型 C++ 对象,以及这些对象如何扩展调试器的功能。

核心调试器对象模型

数据模型最基本但最强大的功能之一是,它规范了什么是对象以及如何与对象交互的定义。 IModelObject 接口封装了对象的概念 - 无论该对象是整数、浮点数值、字符串、调试器目标地址空间中的某种复杂类型,还是某种调试器概念(如进程或模块的概念)。

IModelObject 中可以容纳(或装入)几种不同的东西:

  • 固有值 - IModelObject 可以是许多基本类型的容器:8、16、32 或 64 位有符号或无符号整数、布尔值、字符串、错误或空概念。

  • 本地对象 - IModelObject 可以表示调试器目标地址空间中的复杂类型(由调试器的类型系统定义)。

  • 合成对象 - IModelObject 可以是一个动态对象 - 可以说是一个字典:键/值/元数据元组的集合和一组概念,这些概念定义的行为并非简单地用键/值对表示。

  • 属性 - IModelObject 可以表示一个属性:其值可以通过方法调用来检索或更改。 IModelObject 中的属性实际上是一个 IModelPropertyAccessor 接口,被装箱到了一个 IModelObject

  • 方法 - 一个 IModelObject 可以代表一种方法:可以用一组参数来调用它,并得到一个返回值。 IModelObject 中的方法实际上是一个 IModelMethod 接口,它被装箱到了一个 IModelObject

对象模型中的可扩展性

IModelObject 并不是一个孤立的对象。 除了代表上述对象类型之一外,每个对象还具有父数据模型链的概念。 此链的行为非常类似于 JavaScript 原型链。 与 JavaScript 的线性原型链不同,每个数据模型对象都定义了父模型的线性链。 这些父模型中的每一个又有自己的一组父模型的线性链。 就本质而言,每个对象都是自身和树中每个对象的能力(属性等)的聚合。 在查询特定属性时,如果被查询对象不支持该属性,则查询结果将以线性顺序依次传递给每个父对象。 这样就会产生一种行为,即通过对聚合树进行深度优先搜索来解决属性搜索问题。

鉴于每个对象都是自身和父模型树的聚合,这种对象模型的可扩展性非常简单。 扩展可以传入并将自己添加到另一个对象的父模型列表中。 执行此操作会扩展对象。 通过这种方式,可以在任何东西上添加功能:对象或值的特定实例、本地类型、调试器对进程或线程的概念,甚至是“所有可迭代对象”的概念。

上下文、上下文和上下文:指针、地址空间和实现专用数据

上下文有三个概念,有必要在对象模型的上下文中加以理解。

上下文:指针

由于给定的属性或方法可以在数据模型树的任何层级实现,因此方法或属性的实现必须能够访问原始对象(在 C++ 中称为指针,在 JavaScript 中称为对象)。 在所述方法中,该实例对象将作为称为上下文的第一个参数传递给各种方法。

上下文:地址空间

需要注意的是,与之前的扩展模型不同,上下文(正在查看的目标、进程、线程)是一个用户界面概念,所有 API 都与当前的用户界面状态相关,而数据模型接口通常会显式或隐式地将此上下文作为 IDebugHostContext 接口。 数据模型中的每个 IModelObject 都会随附这类上下文信息,并可将该上下文传播给其返回的对象。 这意味着,在从 IModelObject 中读出一个本地值或键值时,它将从目标和进程中读出该对象最初的获取位置。

有一个显式常量值 USE_CURRENT_HOST_CONTEXT 可以传递给包含 IDebugHostContext 参数的方法。 该值表示上下文应该是调试器当前的 UI 状态。 但是,这一概念必须加以明确。

上下文:实现专用数据

请记住,数据模型中的每个对象实际上都是对象实例和所附父模型树的聚合。 每个父模型(可以在许多不同对象的链中链接)都可以将专用实现数据与任何实例对象相关联。 概念上创建的每个 IModelObject 都有一个哈希表,该表会将特定父模型映射到 IUnknown 接口定义的专用实例数据。 这样,父模型就可以缓存每个实例的信息,或者具有其他任意数据关联。

此类上下文可通过 IModelObject 上的 GetContextForDataModelSetContextForDataModel 方法来访问。

核心调试器对象接口:IModelObject


IModelObject 接口定义如下:

DECLARE_INTERFACE_(IModelObject, IUnknown)
{
    STDMETHOD(QueryInterface)(_In_ REFIID iid, _COM_Outptr_ PVOID* iface);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)() PURE;
    STDMETHOD(GetContext)(_COM_Outptr_result_maybenull_ IDebugHostContext** context) PURE;
    STDMETHOD(GetKind)(_Out_ ModelObjectKind *kind) PURE;
    STDMETHOD(GetIntrinsicValue)(_Out_ VARIANT* intrinsicData);
    STDMETHOD(GetIntrinsicValueAs)(_In_ VARTYPE vt, _Out_ VARIANT* intrinsicData) PURE;
    STDMETHOD(GetKeyValue)(_In_ PCWSTR key, _COM_Errorptr_opt_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(SetKeyValue)(_In_ PCWSTR key, _In_opt_ IModelObject* object) PURE;
    STDMETHOD(EnumerateKeyValues)(_COM_Outptr_ IKeyEnumerator** enumerator) PURE;
    STDMETHOD(GetRawValue)(_In_ SymbolKind kind, _In_ PCWSTR name, _In_ ULONG searchFlags, _COM_Errorptr_ IModelObject** object) PURE;
    STDMETHOD(EnumerateRawValues)(_In_ SymbolKind kind, _In_ ULONG searchFlags, _COM_Outptr_ IRawEnumerator** enumerator) PURE;
    STDMETHOD(Dereference)(_COM_Errorptr_ IModelObject** object) PURE;
    STDMETHOD(TryCastToRuntimeType)(_COM_Errorptr_ IModelObject** runtimeTypedObject) PURE;
    STDMETHOD(GetConcept)(_In_ REFIID conceptId, _COM_Outptr_ IUnknown** conceptInterface, _COM_Outptr_opt_result_maybenull_ IKeyStore** conceptMetadata) PURE;
    STDMETHOD(GetLocation)(_Out_ Location* location) PURE;
    STDMETHOD(GetTypeInfo)(_Out_ IDebugHostType** type) PURE;
    STDMETHOD(GetTargetInfo)(_Out_ Location* location, _Out_ IDebugHostType** type) PURE;
    STDMETHOD(GetNumberOfParentModels)(_Out_ ULONG64* numModels) PURE;
    STDMETHOD(GetParentModel)(_In_ ULONG64 i, _COM_Outptr_ IModelObject **model, _COM_Outptr_result_maybenull_ IModelObject **contextObject) PURE;
    STDMETHOD(AddParentModel)(_In_ IModelObject* model, _In_opt_ IModelObject* contextObject, _In_ bool override) PURE;
    STDMETHOD(RemoveParentModel)(_In_ IModelObject* model) PURE;
    STDMETHOD(GetKey)(_In_ PCWSTR key, _COM_Errorptr_opt_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(GetKeyReference)(_In_ PCWSTR key, _COM_Errorptr_opt_ IModelObject** objectReference, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(SetKey)(_In_ PCWSTR key, _In_opt_ IModelObject* object, _In_opt_ IKeyStore* metadata) PURE;
    STDMETHOD(ClearKeys)() PURE;
    STDMETHOD(EnumerateKeys)(_COM_Outptr_ IKeyEnumerator** enumerator) PURE;
    STDMETHOD(EnumerateKeyReferences)(_COM_Outptr_ IKeyEnumerator** enumerator) PURE;
    STDMETHOD(SetConcept)(_In_ REFIID conceptId, _In_ IUnknown* conceptInterface, _In_opt_ IKeyStore* conceptMetadata) PURE;
    STDMETHOD(ClearConcepts)() PURE;
    STDMETHOD(GetRawReference)(_In_ SymbolKind kind, _In_ PCWSTR name, _In_ ULONG searchFlags, _COM_Errorptr_ IModelObject** object) PURE;
    STDMETHOD(EnumerateRawReferences)(_In_ SymbolKind kind, _In_ ULONG searchFlags, _COM_Outptr_ IRawEnumerator** enumerator) PURE;
    STDMETHOD(SetContextForDataModel)(_In_ IModelObject* dataModelObject, _In_ IUnknown* context) PURE;
    STDMETHOD(GetContextForDataModel)(_In_ IModelObject* dataModelObject, _Out_ IUnknown** context) PURE;
    STDMETHOD(Compare)(_In_ IModelObject* other, _COM_Outptr_opt_result_maybenull_ IModelObject **ppResult) PURE;
    STDMETHOD(IsEqualTo)(_In_ IModelObject* other, _Out_ bool* equal) PURE;
}

基本方法

以下是适用于 IModelObject 所表示的任何类型对象的一般方法。

STDMETHOD(GetKind)(_Out_ ModelObjectKind *kind) PURE;
STDMETHOD(GetContext)(_COM_Outptr_result_maybenull_ IDebugHostContext** context) PURE;
STDMETHOD(GetIntrinsicValue)(_Out_ VARIANT* intrinsicData);
STDMETHOD(GetIntrinsicValueAs)(_In_ VARTYPE vt, _Out_ VARIANT* intrinsicData) PURE;
STDMETHOD(Compare)(_In_ IModelObject* other, _COM_Outptr_opt_result_maybenull_ IModelObject **ppResult) PURE;
STDMETHOD(IsEqualTo)(_In_ IModelObject* other, _Out_ bool* equal) PURE;
STDMETHOD(Dereference)(_COM_Errorptr_ IModelObject** object) PURE;

GetKind

GetKind 方法会返回 IModelObject 内部装箱的对象类型。

GetContext

GetContext 方法会返回与对象相关联的主机上下文。

GetIntrinsicValue

GetIntrinsicValue 方法会返回 IModelObject 内装箱的东西。 此方法只能在 IModelObject 接口上合法调用,而这些接口表示装箱的固有接口或特定的装箱接口。 它不能在本地对象、无值对象、合成对象和引用对象上调用。 GetIntrinsicValueAs 方法的行为与 GetIntrinsicValue 方法相似,只是它会将值转换为指定的变体类型。 如果无法进行转换,此方法将返回错误。

IsEqualTo

IsEqualTo 方法会比较两个模型对象,并返回它们的值是否相等的信息。 对于排序的对象,此方法返回 true 相当于 Compare 方法返回 0。 对于没有排序但相等的对象,Compare 方法会失败,但此方法不会失败。 基于值的比较的含义由对象类型定义。 目前,这只针对固有类型和错误对象。 目前还没有关于均等性的数据模型概念。

Dereference

Dereference 方法会取消引用一个对象。 此方法可用于取消引用基于数据模型的引用(ObjectTargetObjectReference、ObjectKeyReference)或本地语言引用(指针或语言引用)。 需要注意的是,此方法只会删除对象的单层引用语义。 例如,完全可以将数据模型引用到语言引用中。 在这种情况下,首次调用 Dereference 方法将删除数据模型引用而留下语言引用。 随后,在生成的对象上调用 Dereference 会移除语言引用,同时返回该引用下的本地值。

键操作方法

任何合成对象(键、值和元数据元组的字典)都有一系列方法来操作这些键、值以及与之关联的元数据。

基于值的 API 形式包括:

STDMETHOD(GetKeyValue)(_In_ PCWSTR key, _COM_Errorptr_opt_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
STDMETHOD(SetKeyValue)(_In_ PCWSTR key, _In_opt_ IModelObject* object) PURE;
STDMETHOD(EnumerateKeyValues)(_COM_Outptr_ IKeyEnumerator** enumerator) PURE;
The key based forms of the APIs (including those used for key creation) are: 
STDMETHOD(GetKey)(_In_ PCWSTR key, _COM_Errorptr_opt_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
STDMETHOD(SetKey)(_In_ PCWSTR key, _In_opt_ IModelObject* object, _In_opt_ IKeyStore* metadata) PURE;
STDMETHOD(EnumerateKeys)(_COM_Outptr_ IKeyEnumerator** enumerator) PURE;
STDMETHOD(ClearKeys)() PURE;

基于引用的 API 形式包括:

STDMETHOD(GetKeyReference)(_In_ PCWSTR key, _COM_Errorptr_opt_ IModelObject** objectReference, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
STDMETHOD(EnumerateKeyReferences)(_COM_Outptr_ IKeyEnumerator** enumerator) PURE;

GetKeyValue

GetKeyValue 方法是客户端通过名称获取给定键值(以及与之关联的元数据)的第一种方法。 如果键是一个属性访问器 - 即它的值是一个 IModelObject(它是一个装箱的 IModelPropertyAccessor),则 GetKeyValue 方法将自动调用属性访问器的 GetValue 方法以检索实际值。

SetKeyValue

SetKeyValue 方法是客户端用于设置键值的第一种方法。 此方法不能用于在对象上创建新键。 它只会设置现有键的值。 请注意,很多键均为只读(例如:它们由属性访问器实现,该访问器的 SetValue 方法会返回 E_NOT_IMPL)。 在只读键上调用此方法时会失败。

EnumerateKeyValues

EnumerateKeyValues 方法是客户端要枚举对象上所有键值(包括父模型树中任何地方实现的所有键)时使用的第一种方法。 值得注意的是,EnumerateKeyValues 将枚举对象树中由重复名称定义的任何键;但是,GetKeyValue 和 SetKeyValue 等方法只能操作深度优先遍历发现的具有给定名称的键的第一个实例。

GetKey

GetKey 方法将按名称获取给定键的值(以及与之关联的元数据)。 大多数客户端都应改为使用 GetKeyValue 方法。 如果键是一个属性访问器,则调用此方法将返回装箱到 IModelObject 中的属性访问器(IModelPropertyAccessor 接口)。 与 GetKeyValue 不同,此方法不会通过调用 GetValue 方法来自动解析键的基础值。 这一责任由调用方承担。

SetKey

SetKey 方法是客户端在对象上创建键(并可能将元数据与创建的键相关联)时使用的方法。 如果给定对象已经有了一个名称相同的键,则会出现两种行为之一。 如果键位于 this 给出的实例中,则该键的值将被替换,就像原来的键不存在一样。 另一方面,如果该键在 this 给出的实例的父数据模型链中,则会在 this 给出的实例上创建一个带有给定名称的新键。 实际上,这将导致对象有两个同名键(类似于派生类对基类同名成员的阴影)。

EnumerateKeys

EnumerateKeys 方法的行为与 EnumerateKeyValues 方法类似,只是它不会自动解析对象上的属性访问器。 这意味着,如果键的值是一个属性访问器,那么 EnumerateKeys 方法将返回装箱到 IModelObject 中的属性访问器(一个 IModelPropertyAccessorInterface),而不是自动调用 GetValue 方法。 这类似于 GetKey 和 GetKeyValue 之间的区别。

ClearKeys

ClearKeys 方法会从 this 指定的对象实例中删除所有键及其关联的值和元数据。 此方法对附加到特定对象实例的父模型没有影响。

GetKeyReference

GetKeyReference 方法将在对象(或其父模型链)上搜索给定名称的键,并返回通过装箱到 IModelObject 中的 IModelKeyReference 接口给出的该键的引用。 该引用随后可用于获取或设置键值。

EnumerateKeyReferences

EnumerateKeyReferences 方法的行为类似于 EnumerateKeyValues 方法,不同之处在于它返回的是对所枚举的键(通过装箱到 IModelObject 中的 IModelKeyReference 接口给出)的引用,而不是键值。 此类引用可用于获取或设置键的基础值。

概念操作方法

模型对象不仅是键/值/元数据元组的字典,也是概念的容器。 概念是一种抽象的东西,可以在物体上或通过物体来实现。 从本质上讲,概念是对象支持的接口的动态存储。 如今,数据模型定义了许多概念:

概念接口 说明
IDataModelConcept 概念是一个父模型。 如果此模型通过注册的类型签名自动附加到本地类型,那么每次实例化该类型的新对象时,InitializeObject 方法都会被自动调用。
IStringDisplayableConcept 对象可转换为字符串以供显示。
IIterableConcept 对象是一个容器并可以迭代。
IIndexableConcept 对象是一个容器,可以在一个或多个维度上进行索引(通过随机存取进行访问)。
IPreferredRuntimeTypeConcept 对象对派生类型的理解比基础类型系统所能提供的要多,因此希望自己处理从静态类型到运行时类型的转换。
IDynamicKeyProviderConcept 该对象是键的动态提供程序,希望接管核心数据模型的所有键查询。 此接口通常用作连接 JavaScript 等动态语言的桥梁。
IDynamicConceptProviderConcept 该对象是概念的动态提供程序,希望接管核心数据模型的所有概念查询。 此接口通常用作连接 JavaScript 等动态语言的桥梁。

IModelObject 上的以下方法可用于操作对象所支持的概念。

STDMETHOD(GetConcept)(_In_ REFIID conceptId, _COM_Outptr_ IUnknown** conceptInterface, _COM_Outptr_opt_result_maybenull_ IKeyStore** conceptMetadata) PURE;
STDMETHOD(SetConcept)(_In_ REFIID conceptId, _In_ IUnknown* conceptInterface, _In_opt_ IKeyStore* conceptMetadata) PURE;
STDMETHOD(ClearConcepts)() PURE;

GetConcept

GetConcept 方法将在对象(或其父模型链)上搜索概念,并返回指向概念接口的接口指针。 概念接口上的行为和方法特定于每个概念。 但需要注意的是,许多概念接口都要求调用方显式传递上下文对象(或传统意义上的 this 指针)。 确保向每个概念接口传递正确的上下文对象非常重要。

SetConcept

SetConcept 方法将在 this 指针指定的对象实例上放置一个指定的概念。 如果此参数指定的对象实例的父模型也支持此概念,则实例中的实现将覆盖父模型中的实现。

ClearConcepts

ClearConcepts 方法将从 this 指定的对象实例中删除所有概念。

本地对象方法

虽然许多模型对象引用的固有构造(如整数、字符串)或合成构造(键/值/元数据元组和概念的字典),但模型对象也可能引用本地构造(如调试目标地址空间中的用户定义类型)。 IModelObject 接口上有一系列方法可以访问此类本地对象的信息。 这些方法包括:

STDMETHOD(GetRawValue)(_In_ SymbolKind kind, _In_ PCWSTR name, _In_ ULONG searchFlags, _COM_Errorptr_ IModelObject** object) PURE;
STDMETHOD(EnumerateRawValues)(_In_ SymbolKind kind, _In_ ULONG searchFlags, _COM_Outptr_ IRawEnumerator** enumerator) PURE;
STDMETHOD(TryCastToRuntimeType)(_COM_Errorptr_ IModelObject** runtimeTypedObject) PURE;
STDMETHOD(GetLocation)(_Out_ Location* location) PURE;
STDMETHOD(GetTypeInfo)(_Out_ IDebugHostType** type) PURE;
STDMETHOD(GetTargetInfo)(_Out_ Location* location, _Out_ IDebugHostType** type) PURE;
STDMETHOD(GetRawReference)(_In_ SymbolKind kind, _In_ PCWSTR name, _In_ ULONG searchFlags, _COM_Errorptr_ IModelObject** object) PURE;
STDMETHOD(EnumerateRawReferences)(_In_ SymbolKind kind, _In_ ULONG searchFlags, _COM_Outptr_ IRawEnumerator** enumerator) PURE;

GetRawValue

GetRawValue 方法会在给定对象中查找本地构造。 此类构造可以是一个字段、一个基类、基类中的一个字段、一个成员函数等...

EnumerateRawValues

EnumerateRawValues 方法枚举给定对象的所有本地子类(例如:字段、基类等...)。

TryCastToRuntimeType

TryCastToRuntimeType 方法将要求调试主机执行分析,并确定给定对象的实际运行时类型(例如:大多数派生类)。 具体的分析方法取决于调试主机,可能包括 RTTI(C++ 运行时类型信息)、检查对象的 V-Table(虚拟函数表)结构,或主机可以用来从静态类型可靠地确定动态/运行时类型的任何其他方法。 无法转换为运行时类型并不意味着此方法调用将失败。 在这种情况下,该方法将在输出参数中返回给定对象(this 指针)。

GetLocation

GetLocation 方法将返回本地对象的位置。 虽然此类位置通常是调试目标地址空间内的虚拟地址,但也不一定如此。 此方法返回的位置是一个抽象位置,可能是虚拟地址,可能表示寄存器或子寄存器内的位置,也可能表示调试主机定义的其他任意地址空间。 如果生成的 Location 对象的 HostDefined 字段为 0,则表明该位置实际上是一个虚拟地址。 可以通过检查结果位置的 Offset 字段来检索此类虚拟地址。 HostDefined 字段的任何非零值都表示一个备用地址空间,其中 Offset 字段是该地址空间内的偏移量。 这里的 HostDefined 非零值的确切含义是调试主机的专用值。

GetTypeInfo

GetTypeInfo 方法将返回给定对象的本地类型。 如果对象没有与之关联的本地类型信息(例如:它是一个固有类型等),调用仍将成功,但将返回空值。

GetTargetInfo

GetTargetInfo 方法实际上是 GetLocation 和 GetTypeInfo 方法的组合,同时返回给定对象的抽象位置和本地类型。

GetRawReference

GetRawReference 方法在给定对象中查找本地构造,并返回该本地构造的引用。 此类构造可以是一个字段、一个基类、基类中的一个字段、一个成员函数等。重要的是要将此处返回的引用(ObjectTargetObjectReference 类型的对象)与语言引用(例如:C++ & 或 && 风格的引用)区分开来。

EnumerateRawReferences

EnumerateRawReferences 方法枚举给定对象的所有本地子类(例如:字段、基类等...)的引用。

可扩展性方法

如前所述,模型对象的行为与 JavaScript 对象及其原型链非常相似。 除了某个 IModelObject 接口所代表的实例外,该对象还可能有任意数量的父模型(每个父模型又可能有任意数量的父模型)。 这是数据模型内可扩展性的主要手段。 如果给定的属性或概念无法在给定的实例中找到,就会对以实例为根的对象树(由父模型定义)进行深度优先搜索。

以下方法可操作与给定 IModelObject 实例相关的父模型链:

STDMETHOD(GetNumberOfParentModels)(_Out_ ULONG64* numModels) PURE;
STDMETHOD(GetParentModel)(_In_ ULONG64 i, _COM_Outptr_ IModelObject **model, _COM_Outptr_result_maybenull_ IModelObject **contextObject) PURE;
STDMETHOD(AddParentModel)(_In_ IModelObject* model, _In_opt_ IModelObject* contextObject, _In_ bool override) PURE;
STDMETHOD(RemoveParentModel)(_In_ IModelObject* model) PURE;
STDMETHOD(SetContextForDataModel)(_In_ IModelObject* dataModelObject, _In_ IUnknown* context) PURE;
STDMETHOD(GetContextForDataModel)(_In_ IModelObject* dataModelObject, _Out_ IUnknown** context) PURE;

GetNumberOfParentModels

GetNumberOfParentModels 方法返回附加到给定对象实例的父模型的数量。 父模型按父模型链的线性排序深度优先搜索属性。

GetParentModel

GetParentModel 方法返回给定对象父模型链中的第 i 个父模型。 父模型按添加或枚举的线性顺序来搜索属性或概念。 在索引为 i + 1 的父模型之前,先搜索索引为 0 的父模型(分层)。

AddParentModel

AddParentModel 方法可为给定对象添加一个新的父模型。 此类模型可以添加到搜索链的末尾(覆盖参数指定为 false)或搜索链的前端(覆盖参数指定为 true)。 此外,每个父模型可以选择性地调整给定父模型(或父模型层次结构中的任何模型)上任何属性或概念的上下文(this 指针的语义)。 上下文调整很少使用,但它允许使用一些强大的概念,如对象嵌入、构建命名空间等...

RemoveParentModel

RemoveParentModel 将从给定对象的父搜索链中删除指定的父模型。

SetContextForDataModel

数据模型的实现使用 SetContextForDataModel 方法在实例对象上放置实现数据。 就概念而言,每个 IModelObject(为简单起见,称其为实例)都包含一个状态哈希表。 哈希表由实例的父模型层次结构中的另一个 IModelObject(为简单起见,称其为数据模型)索引。 该哈希中包含的值是由 IUnknown 实例表示的一组引用计数状态信息。 一旦数据模型在实例上设置了这种状态,它就可以存储任意的执行数据,而这些数据可以在属性获取器等程序中进行检索。

GetContextForDataModel

GetContextForDataModel 方法用于检索之前调用 SetContextForDataModel 时设置的上下文信息。 这将检索由实例对象父模型层次结构中更高一级的数据模型在实例对象上设置的状态信息。 有关此上下文/状态及其含义的详细信息,请参阅 SetContextForDataModel 的文档。

调试器数据模型核心对象类型

数据模型中的对象类似于 .NET 中的 Object 概念。 它是一个通用容器,数据模型所能理解的构造都可以装入其中。 除了本地对象和合成(动态)对象外,还有一系列核心对象类型可以放入(或装箱)到 IModelObject 的容器中。 放置这些值的容器是一个标准的 COM/OLE 变量,并对该变量所能包含的内容做了一些额外的限制。 其中最基本的类型包括:

  • 8 位无符号值和有符号值 (VT_UI1, VT_I1)
  • 16 位无符号值和有符号值 (VT_UI2, VT_UI2)
  • 32 位无符号值和有符号值 (VT_UI4, VT_I4)
  • 64 位无符号值和有符号值 (VT_UI8, VT_I8)
  • 单精度和双精度浮点值 (VT_R4, VT_R8)
  • 字符串 (VT_BSTR)
  • 布尔值 (VT_BOOL)

除了这些基本类型外,一些核心数据模型对象被放入由 VT_UNKNOWN 定义的 IModelObject 中,其中存储的 IUnknown 可以保证实现特定的接口。 这些类型包括:

  • 属性访问器 (IModelPropertyAccessor)
  • 方法对象 (IModelMethod)
  • 键引用对象(IModelKeyReference 或 IModelKeyReference2)
  • 上下文对象 (IDebugModelHostContext)

属性访问器:IModelPropertyAccessor

数据模型中的属性访问器是 IModelPropertyAccessor 接口的实现,它会被装箱到 IModelObject 中。 在查询时,模型对象将返回一种 ObjectPropertyAccessor,其固有值是保证 IModelPropertyAccessor 可以查询的 VT_UNKNOWN。 在此过程中,它可以保证静态转换为 IModelPropertyAccessor。

属性访问器是一种间接的方法,用于获取和设置数据模型中的键值。 如果给定键的值是一个属性访问器,GetKeyValue 和 SetKeyValue 方法会自动注意到这一点,并根据情况调用属性访问器的基础 GetValue 或 SetValue 方法。

IModelPropertyAccessor 接口定义如下:

DECLARE_INTERFACE_(IModelPropertyAccessor, IUnknown)
{
    STDMETHOD(GetValue)(_In_ PCWSTR key, _In_opt_ IModelObject* contextObject, _COM_Outptr_ IModelObject** value) PURE;
    STDMETHOD(SetValue)(_In_ PCWSTR key, _In_opt_ IModelObject* contextObject, _In_ IModelObject* value) PURE; 
}

GetValue

GetValue 方法是属性访问器的 getter。 每当客户端希望获取属性的基础值时,就会调用它。 请注意,任何直接获取属性访问器的调用方都有责任将键名和准确的实例对象(this 指针)传递给属性访问器的 GetValue 方法。

SetValue

SetValue 方法是属性访问器的 setter。 每当客户端希望为基础属性赋值时,就会调用它。 很多属性都是只读的。 在这种情况下,调用 SetValue 方法将返回 E_NOTIMPL。 请注意,任何直接获取属性访问器的调用方都有责任将键名和准确的实例对象(this 指针)传递给属性访问器的 SetValue 方法。

方法:IModelMethod

数据模型中的方法是 IModelMethod 接口的实现,它会被装箱到 IModelObject 中。 在查询时,模型对象将返回一种 ObjectMethod,其固有值是保证 IModelMethod 可以查询的 VT_UNKNOWN。 在此过程中,它可以保证静态转换为 IModelMethod。 数据模型中的所有方法本质上都是动态的。 它们会将一组 0 个或多个参数作为输入,并返回单个输出值。 没有重载解析,也没有关于参数名称、类型或期望值的元数据。

IModelMethod 接口定义如下:

DECLARE_INTERFACE_(IModelMethod, IUnknown)
{
    STDMETHOD(Call)(_In_opt_ IModelObject *pContextObject, _In_ ULONG64 argCount, _In_reads_(argCount) IModelObject **ppArguments, _COM_Errorptr_ IModelObject **ppResult, _COM_Outptr_opt_result_maybenull_ IKeyStore **ppMetadata) PURE;
}

调用

Call 方法是调用数据模型中定义的任何方法的方式。 调用方负责传递一个准确的实例对象(this 指针)和一组任意参数。 方法的结果以及与该结果相关的任何可选元数据都会返回。 逻辑上不返回值的方法仍必须返回一个有效的 IModelObject。 在这种情况下,IModelObject 是一个装箱的“no value”。 如果方法失败,它可以在输入参数中返回可选的扩展错误信息(即使返回的 HRESULT 失败)。 调用方必须检查这一点。

键引用:IModelKeyReference 或 IModelKeyReference2

键引用实质上是特定对象上一个键的句柄。 客户端可以通过 GetKeyReference 等方法来获取此类句柄,并在以后使用该句柄来获取或设置键值,而不必保留原始对象。 这种类型的对象是 IModelKeyReference 或 IModelKeyReference2 接口的实现,它被装箱到了 IModelObject 中。 在查询时,模型对象将返回一种 ObjectKeyReference,其固有值是保证 IModelKeyReference 可以查询的 VT_UNKNOWN。 在此过程中,它可以保证静态转换为 IModelKeyReference。

键引用接口定义如下:

DECLARE_INTERFACE_(IModelKeyReference2, IModelKeyReference)
{
    STDMETHOD(GetKeyName)(_Out_ BSTR* keyName) PURE;
    STDMETHOD(GetOriginalObject)(_COM_Outptr_ IModelObject** originalObject) PURE;
    STDMETHOD(GetContextObject)(_COM_Outptr_ IModelObject** containingObject) PURE;
    STDMETHOD(GetKey)(_COM_Errorptr_opt_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(GetKeyValue)(_COM_Errorptr_opt_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(SetKey)(_In_opt_ IModelObject* object, _In_opt_ IKeyStore* metadata) PURE;
    STDMETHOD(SetKeyValue)(_In_ IModelObject* object) PURE;
    STDMETHOD(OverrideContextObject)(_In_ IModelObject* newContextObject) PURE;
}

GetKeyName

GetKeyName 方法会返回键引用作为句柄的键的名称。 返回的字符串是标准 BSTR,必须通过调用 SysFreeString 释放。

GetOriginalObject

GetOriginalObject 方法返回创建键引用的实例对象。 请注意,该键本身可能位于实例对象的父模型上。

GetContextObject

GetContextObject 方法返回上下文(this 指针),如果相关键值指向一个属性访问器,该上下文将被传递给属性访问器的 GetValue 或 SetValue 方法。 此处返回的上下文对象可能与从 GetOriginalObject 获取的原始对象相同,也可能不相同。 如果键位于父模型上,并且有一个上下文调整器与该父模型相关联,那么原始对象就是调用 GetKeyReference 或 EnumerateKeyReferences 的实例对象。 上下文对象将是原始对象和父模型之间的最终上下文调整器所产生的任何对象,父模型包含键,而对该键的引用是句柄。 如果没有上下文调整器,则原始对象和上下文对象完全相同。

GetKey

键引用上的 GetKey 方法与 IModelObject 上的 GetKey 方法行为相同。 它返回基础键的值以及与该键关联的任何元数据。 如果键值恰好是一个属性访问器,则将返回装箱到 IModelObject 中的属性访问器 (IModelPropertyAccessor)。 此方法不会调用属性访问器上的基础 GetValue 或 SetValue 方法。

GetKeyValue

键引用上的 GetKeyValue 方法与 IModelObject 上的 GetKeyValue 方法行为相同。 它返回基础键的值以及与该键关联的任何元数据。 如果键值恰好是一个属性访问器,则会自动调用属性访问器的基础 GetValue 方法。

SetKey

键引用上的 SetKey 方法与 IModelObject 上的 SetKey 方法行为相同。 它将分配键值。 如果原始键是一个属性访问器,则会取代属性访问器。 它不会调用属性访问器上的 SetValue 方法。

SetKeyValue

键引用上的 SetKeyValue 方法与 IModelObject 上的 SetKeyValue 方法行为相同。 它将分配键值。 如果原始键是一个属性访问器,则会调用属性访问器的基础 SetValue 方法,而不是替换属性访问器本身。

OverrideContextObject

OverrideContextObject 方法(仅存在于 IModelKeyReference2 中)是一种高级方法,用于永久更改上下文对象,而此键引用将把该上下文对象传递给任何基础属性访问器的 GetValue 或 SetValue 方法。 调用 GetContextObject 时也会返回传递给此方法的对象。 脚本提供程序可使用此方法来复制某些动态语言行为。 大多数客户端不应调用此方法。

上下文对象:IDebugHostContext

上下文对象是调试主机(与数据模型合作)与每个对象关联的不透明信息 blob。 它可能包括进程上下文或信息来自的地址空间等。上下文对象是 IDebugHostContext 在 IModelObject 中的实现。 请注意,IDebugHostContext 是主机定义的接口。 客户端永远不会实现这个接口。

有关上下文对象的详细信息,请参阅“调试器数据模型 C++ 接口”中的调试器数据模型 C++ 主机接口

数据模型管理器

数据模型管理器 IDataModelManager2(或之前的 IDataModelManager)的核心接口定义如下:

DECLARE_INTERFACE_(IDataModelManager2, IDataModelManager)
{
    //
    // IDataModelManager:
    //
    STDMETHOD(Close)() PURE;
    STDMETHOD(CreateNoValue)(_Out_ IModelObject** object) PURE;
    STDMETHOD(CreateErrorObject)(_In_ HRESULT hrError, _In_opt_ PCWSTR pwszMessage, _COM_Outptr_ IModelObject** object) PURE;
    STDMETHOD(CreateTypedObject)(_In_opt_ IDebugHostContext* context, _In_ Location objectLocation, _In_ IDebugHostType* objectType, _COM_Errorptr_ IModelObject** object) PURE;
    STDMETHOD(CreateTypedObjectReference)(_In_opt_ IDebugHostContext* context, _In_ Location objectLocation, _In_ IDebugHostType* objectType, _COM_Errorptr_ IModelObject** object) PURE;
    STDMETHOD(CreateSyntheticObject)(_In_opt_ IDebugHostContext* context, _COM_Outptr_ IModelObject** object) PURE;
    STDMETHOD(CreateDataModelObject)(_In_ IDataModelConcept* dataModel, _COM_Outptr_ IModelObject** object) PURE;
    STDMETHOD(CreateIntrinsicObject)(_In_ ModelObjectKind objectKind, _In_ VARIANT* intrinsicData, _COM_Outptr_ IModelObject** object) PURE;
    STDMETHOD(CreateTypedIntrinsicObject)(_In_ VARIANT* intrinsicData, _In_ IDebugHostType* type, _COM_Outptr_ IModelObject** object) PURE;
    STDMETHOD(GetModelForTypeSignature)(_In_ IDebugHostTypeSignature* typeSignature, _Out_ IModelObject** dataModel) PURE;
    STDMETHOD(GetModelForType)(_In_ IDebugHostType* type, _COM_Outptr_ IModelObject** dataModel, _COM_Outptr_opt_ IDebugHostTypeSignature** typeSignature, _COM_Outptr_opt_ IDebugHostSymbolEnumerator** wildcardMatches) PURE;
    STDMETHOD(RegisterModelForTypeSignature)(_In_ IDebugHostTypeSignature* typeSignature, _In_ IModelObject* dataModel) PURE;
    STDMETHOD(UnregisterModelForTypeSignature)(_In_ IModelObject* dataModel, _In_opt_ IDebugHostTypeSignature* typeSignature) PURE;
    STDMETHOD(RegisterExtensionForTypeSignature)(_In_ IDebugHostTypeSignature* typeSignature, _In_ IModelObject* dataModel) PURE;
    STDMETHOD(UnregisterExtensionForTypeSignature)(_In_ IModelObject* dataModel, _In_opt_ IDebugHostTypeSignature* typeSignature) PURE;
    STDMETHOD(CreateMetadataStore)(_In_opt_ IKeyStore* parentStore, _COM_Outptr_ IKeyStore** metadataStore) PURE;
    STDMETHOD(GetRootNamespace)(_COM_Outptr_ IModelObject** rootNamespace) PURE;
    STDMETHOD(RegisterNamedModel)(_In_ PCWSTR modelName, _In_ IModelObject *modeObject) PURE;
    STDMETHOD(UnregisterNamedModel)(_In_ PCWSTR modelName) PURE;
    STDMETHOD(AcquireNamedModel)(_In_ PCWSTR modelName, _COM_Outptr_ IModelObject **modelObject) PURE;
    //
    // IDataModelManager2:
    //
    STDMETHOD(AcquireSubNamespace)(_In_ PCWSTR modelName, _In_ PCWSTR subNamespaceModelName, _In_ PCWSTR accessName, _In_opt_ IKeyStore *metadata, _COM_Outptr_ IModelObject **namespaceModelObject) PURE;
    STDMETHOD(CreateTypedIntrinsicObjectEx)(_In_opt_ IDebugHostContext* context, _In_ VARIANT* intrinsicData, _In_ IDebugHostType* type, _COM_Outptr_ IModelObject** object) PURE;
}

管理方法

托管数据模型的应用程序(如调试器)使用以下方法集。

STDMETHOD(Close)() PURE;

Close

托管数据模型的应用程序(如调试器)会在数据模型管理器上调用 Close 方法,以便启动数据模型管理器的关闭进程。 如果数据模型主机在数据模型管理器上释放其最终引用之前没有采用 Close 方法,则可能导致未定义的行为,包括但不限于数据模型管理基础架构的重大泄漏。

对象创建/装箱方法

以下一组方法用于创建新对象或将值装箱到 IModelObject 中 - 数据模型的核心接口。

STDMETHOD(CreateNoValue)(_Out_ IModelObject** object) PURE;
STDMETHOD(CreateErrorObject)(_In_ HRESULT hrError, _In_opt_ PCWSTR pwszMessage, _COM_Outptr_ IModelObject** object) PURE;
STDMETHOD(CreateTypedObject)(_In_opt_ IDebugHostContext* context, _In_ Location objectLocation, _In_ IDebugHostType* objectType, _COM_Errorptr_ IModelObject** object) PURE;
STDMETHOD(CreateTypedObjectReference)(_In_opt_ IDebugHostContext* context, _In_ Location objectLocation, _In_ IDebugHostType* objectType, _COM_Errorptr_ IModelObject** object) PURE;
STDMETHOD(CreateSyntheticObject)(_In_opt_ IDebugHostContext* context, _COM_Outptr_ IModelObject** object) PURE;
STDMETHOD(CreateDataModelObject)(_In_ IDataModelConcept* dataModel, _COM_Outptr_ IModelObject** object) PURE;
STDMETHOD(CreateIntrinsicObject)(_In_ ModelObjectKind objectKind, _In_ VARIANT* intrinsicData, _COM_Outptr_ IModelObject** object) PURE;
STDMETHOD(CreateTypedIntrinsicObject)(_In_ VARIANT* intrinsicData, _In_ IDebugHostType* type, _COM_Outptr_ IModelObject** object) PURE;
STDMETHOD(CreateMetadataStore)(_In_opt_ IKeyStore* parentStore, _COM_Outptr_ IKeyStore** metadataStore) PURE;
STDMETHOD(CreateTypedIntrinsicObjectEx)(_In_opt_ IDebugHostContext* context, _In_ VARIANT* intrinsicData, _In_ IDebugHostType* type, _COM_Outptr_ IModelObject** object) PURE;

CreateNoValue

CreateNoValue 方法会创建一个“no value”对象,将其装箱到一个 IModelObject 中并将其返回。 返回的模型对象具有一种 ObjectNoValue。

“no value”对象有多种语义:

  • (视语言而定)它在语义上相当于 void、null 或 undefined
  • 任何属性访问器的 GetValue 方法如果返回成功,并产生一个“no value”对象,则表明该特定属性对给定实例没有值,应将其视为该特定实例不存在该属性。
  • 从语义上讲没有返回值的数据模型方法使用此值作为 sentinel 来表示(因为方法必须返回一个有效的 IModelObject)。

CreateErrorObject

CreateErrorObject 方法会创建一个“error object”。 数据模型没有异常和异常流的概念。 属性/方法的失败有两种方式:

  • 单个失败的 HRESULT,无扩展的错误信息。 要么是无法提供更多的错误信息,要么是错误本身在返回的 HRESULT 中已不言自明。
  • 单个失败的 HRESULT,有扩展的错误信息。 扩展错误信息是属性/方法输出参数中返回的错误对象。

CreateTypedObject

CreateTypedObject 方法允许客户端在调试目标的地址空间中创建一个本地/语言对象的表示。 如果新创建对象的类型(如 objectType 参数所示)恰好与数据模型管理器注册为规范可视化工具或扩展的一个或多个类型签名相匹配,那么在将实例对象返回给调用方之前,这些匹配的数据模型将自动附加到创建的实例对象上。

CreateTypedObjectReference

CreateTypedObjectReference 方法在语义上与 CreateTypedObject 方法相似,只是它创建的是对基本本地/语言构造的引用。 创建的引用是一个具有 ObjectTargetObjectReference 类型的对象。 它不是基本语言可能支持的本地引用(例如:C++ & 或 &&)。 完全可以将 ObjectTargetObjectReference 作为 C++ 引用。 通过使用 IModelObject 上的 Dereference 方法,可以将 ObjectTargetObjectReference 类型的对象转换为基础值。 该引用还可以传递给基础主机的表达式计算器,以便以适合语言的方式重新分配给值。

CreateSyntheticObject

CreateSyntheticObject 方法会创建一个空数据模型对象 - 一个包含键/值/元数据元组和概念的字典。 在创建时,对象上既没有键也没有概念。 这是调用方可以利用的一个干净盖板。

CreateDataModelObject

CreateDataModelObject 方法是一个简单的帮助程序包装器,用于创建作为数据模型的对象,即作为父模型附加到其他对象的对象。 所有此类对象都必须通过 IDataModelConcept 支持数据模型概念。 此方法会创建一个没有显式上下文的新空白合成对象,并添加传入的 IDataModelConcept 作为新创建对象的数据模型概念实现。 这同样可以通过调用 CreateSyntheticObject 和 SetConcept 来实现。

CreateIntrinsicObject

CreateIntrinsicObject 方法是将固有值装箱到 IModelObject 中的方法。 调用方将值放入 COM VARIANT,然后调用此方法。 数据模型管理器会返回一个表示该对象的 IModelObject。 请注意,该方法也用于装箱基于 IUnknown 的基本类型:属性访问器、方法、上下文等。在这种情况下,objectKind 方法表示对象代表哪种基于 IUnknown 的构造,而传递变量的 punkVal 字段就是 IUnknown 派生类型。 在过程中,该类型必须可静态地投向相应的模型接口(例如:IModelPropertyAccessor、IModelMethod、IDebugHostContext 等...)。 此方法支持的 VARIANT 类型包括 VT_UI1、VT_I1、VT_UI2、VT_I2、VT_UI4、VT_I4、VT_UI8、VT_I8、VT_R4、VT_R8、VT_BOOL、VT_BSTR 和 VT_UNKNOWN(用于由枚举 ModelObjectKind 表示的一组专门的 IUnknown 派生类型)。

CreateTypedIntrinsicObject

CreateTypedintrinsicObject 方法与 CreateIntrinsicObject 方法类似,但它允许将本地/语言类型与数据关联起来,并随装箱值一起携带。 这样,数据模型就可以表示诸如本地枚举类型(即简单的 VT_UI* 或 VT_I* 值)之类的构造。 指针类型也可以用这种方法来创建。 数据模型中的本地指针是一个零扩展的 64 位数,表示调试目标虚拟地址空间的偏移量。 它被装箱在 VT_UI8 内,使用此方法和表示本地/语言指针的类型创建。

CreateMetadataStore

CreateMetadataStore 方法会创建一个键存储 - 一个键/值/元数据元组的简化容器,用于保存与属性和其他各种值相关联的元数据。 元数据存储区可能只有一个父级(而父级也可能只有一个父级)。 如果给定的元数据键未在给定的存储空间中,则会检查其父级。 大多数元数据存储都没有父级。 但是,它确实提供了一种轻松共享通用元数据的方法。

CreateTypedIntrinsicObjectEx

CreateTypedIntrinsicObjectEx 方法在语义上类似于 CreateTypedIntrinsicObject 方法。 两者唯一的区别是,此方法允许调用方指定固有数据有效的上下文。 如果没有传递上下文,则数据将在类型参数继承的任何上下文中被视为有效(CreateTypedIntrinsicObject 的行为方式)。 这样就可以在调试目标中创建类型化指针值,而这些指针值所需的特定上下文比从类型中继承的更多。

可扩展性/注册方法以下方法集管理数据模型的可扩展性机制,允许客户端扩展或注册现有模型,或要求数据模型自动将给定的父模型附加到符合给定标准的本地类型上。

    STDMETHOD(GetModelForTypeSignature)(_In_ IDebugHostTypeSignature* typeSignature, _Out_ IModelObject** dataModel) PURE;
    STDMETHOD(GetModelForType)(_In_ IDebugHostType* type, _COM_Outptr_ IModelObject** dataModel, _COM_Outptr_opt_ IDebugHostTypeSignature** typeSignature, _COM_Outptr_opt_ IDebugHostSymbolEnumerator** wildcardMatches) PURE;
    STDMETHOD(RegisterModelForTypeSignature)(_In_ IDebugHostTypeSignature* typeSignature, _In_ IModelObject* dataModel) PURE;
    STDMETHOD(UnregisterModelForTypeSignature)(_In_ IModelObject* dataModel, _In_opt_ IDebugHostTypeSignature* typeSignature) PURE;
    STDMETHOD(RegisterExtensionForTypeSignature)(_In_ IDebugHostTypeSignature* typeSignature, _In_ IModelObject* dataModel) PURE;
    STDMETHOD(UnregisterExtensionForTypeSignature)(_In_ IModelObject* dataModel, _In_opt_ IDebugHostTypeSignature* typeSignature) PURE;
    STDMETHOD(GetRootNamespace)(_COM_Outptr_ IModelObject** rootNamespace) PURE;
    STDMETHOD(RegisterNamedModel)(_In_ PCWSTR modelName, _In_ IModelObject *modeObject) PURE;
    STDMETHOD(UnregisterNamedModel)(_In_ PCWSTR modelName) PURE;
    STDMETHOD(AcquireNamedModel)(_In_ PCWSTR modelName, _COM_Outptr_ IModelObject **modelObject) PURE;

GetModelForTypeSignature

GetModelForTypeSignature 方法会返回通过事先调用 RegisterModelForTypeSignature 方法针对特定类型签名注册的数据模型。 此方法返回的数据模型被认为是与传递的类型签名相匹配的任何类型的规范可视化工具。 作为一个规范可视化工具,该数据模型会接管该类型的显示。 默认情况下,显示引擎会隐藏对象的本地/语言结构,而采用数据模型所呈现的对象视图。

GetModelForType

GetModelForType 方法返回的数据模型是给定类型实例的规范可视化工具。 实际上,此方法会找到事先调用 RegisterModelForTypeSignature 方法注册的最佳匹配类型签名,并返回相关的数据模型。

RegisterModelForTypeSignature

RegisterModelForTypeSignature 方法是调用方用来为给定类型(或类型集)注册规范可视化工具的主要方法。 规范可视化工具是一种数据模型,它实际上接管了给定类型(或类型集)的显示。 在任何调试器用户界面中都不会显示类型的本地/语言视图,而是显示注册数据模型所呈现的类型视图(同时还为需要的用户提供了返回本地/语言视图的方法)。

UnregisterModelForTypeSignature

UnregisterModelForTypeSignature 方法会撤销之前对 RegisterModelForTypeSignature 方法的调用。 此方法可以删除作为与特定类型签名匹配的类型的规范可视化工具的给定数据模型,也可以删除为该数据模型所注册的每个类型签名的规范可视化工具的给定数据模型。

RegisterExtensionForTypeSignature

RegisterExtensionForTypeSignature 方法与 RegisterModelForTypeSignature 方法相似,但有一个主要区别。 传递给此方法的数据模型不是任何类型的典型可视化工具,也不会取代该类型的本地/语言视图的显示。 传递给此方法的数据模型将自动被添加为任何符合所提供类型签名的具体类型的父类型。 与 RegisterModelForTypeSignature 方法不同的是,注册相同或模糊类型签名作为给定类型(或类型集)的扩展没有限制。 类型签名与给定具体类型实例相匹配的每个扩展都会导致通过此方法注册的数据模型作为父模型自动附加到新创建的对象上。 实际上,这样将允许任意数量的客户端使用新字段或功能来扩展一个类型(或一组类型)。

UnregisterExtensionForTypeSignature

UnregisterExtensionForTypeSignature 方法会撤销之前对 RegisterExtensionForTypeSignature 的调用。 它可以将特定数据模型取消注册为特定类型签名的扩展,或取消注册为该数据模型所对应的所有类型签名的扩展。

GetRootNamespace

GetRootNamespace 方法会返回数据模型的根命名空间。 这是数据模型管理的对象,调试主机会将某些对象放入其中。

RegisterNamedModel

RegisterNamedModel 方法将给定的数据模型注册为一个众所周知的名称,以便希望扩展该模型的客户端可以找到它。 这就是 API 的主要用途 - 作为可以通过检索以这个众所周知的名称注册的模型并向其添加父模型来扩展的数据模型进行发布。

UnregisterNamedModel

UnregisterNamedModel 方法会撤销之前对 RegisterNamedModel 的调用。 它消除了数据模型与名称之间的关联,而数据模型可以在名称下查找。

AcquireNamedModel

如果调用方希望扩展以给定名称注册的数据模型,则需要调用 AcquireNamedModel 方法,以获取其希望扩展的数据模型的对象。 此方法将返回通过之前调用 RegisterNamedModel 方法注册的任何数据模型。 由于 AcquireNamedModel 方法的主要目的是扩展模型,如果给定名称下尚未注册任何模型,则此方法会产生特殊行为。 如果给定名称下尚未注册任何模型,则会创建一个存根对象,并临时注册到给定名称下,然后返回给调用方。 当通过调用 RegisterNamedModel 方法注册真实数据模型时,对存根对象所做的任何更改实际上都是对真实模型所做的更改。 这样将消除组件之间相互扩展时的许多负载顺序依赖问题。

帮助器方法

以下方法是一般帮助程序方法,可帮助对数据模型中的对象执行复杂操作。 虽然可以通过数据模型或其对象上的其他方法来执行这些操作,但这些方便的方法大大简化了操作:

STDMETHOD(AcquireSubNamespace)(_In_ PCWSTR modelName, _In_ PCWSTR subNamespaceModelName, _In_ PCWSTR accessName, _In_opt_ IKeyStore *metadata, _COM_Outptr_ IModelObject **namespaceModelObject) PURE;

AcquireSubNamespace

AcquireSubNamespace 方法有助于构建一个更像语言命名空间的东西,而不是动态语言中的一个新对象。 例如,如果调用方希望对进程对象上的属性进行分类,以便让流程对象更有条理,属性更容易被发现,那么一种方法就是为进程对象上的每个类别创建一个子对象,并将这些属性放在该对象中。

另请参阅

本专题是系列专题的一部分,主要介绍 C++ 可访问的接口、如何使用这些接口构建基于 C++ 的调试器扩展,以及如何通过 C++ 数据模型扩展使用其他数据模型构造(如 JavaScript 或 NatVis)。

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

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

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

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

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