Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este tópico descreve como usar objetos C++ do modelo de dados do depurador e como eles podem estender os recursos do depurador.
o modelo de objeto do depurador principal
Uma das coisas mais básicas, mas poderosas, sobre o modelo de dados é que ele padroniza a definição do que é um objeto e como se interage com um objeto. A interface IModelObject encapsula a noção de um objeto -- se esse objeto é um inteiro, um valor de ponto flutuante, uma cadeia de caracteres, algum tipo complexo no espaço de endereço de destino do depurador ou algum conceito de depurador, como a noção de um processo ou um módulo.
Há várias coisas diferentes que podem ser mantidas em (ou encaixotadas) em um IModelObject:
Valores Intrínsecos - Um IModelObject pode ser um contêiner para vários tipos básicos: inteiros assinados ou não assinados de 8, 16, 32 ou 64 bits, booleanos, strings, erros ou a noção de vazio.
Native Objects - Um IModelObject pode representar um tipo complexo (conforme definido pelo sistema de tipos do depurador) dentro do espaço de endereço de qualquer destino que o depurador esteja direcionando
Synthetic Objects - Um IModelObject pode ser um objeto dinâmico -- um dicionário, se quiser: uma coleção de tuplas de chave/valor/metadados e um conjunto de conceitos que definem comportamentos que não são simplesmente representados por pares chave/valor.
Properties - Um IModelObject pode representar uma propriedade: algo cujo valor pode ser recuperado ou alterado com uma chamada de método. Uma propriedade dentro de um IModelObject é efetivamente um IModelPropertyAccessor interface encaixotada em um IModelObject
Methods - Um IModelObject pode representar um método: algo que você pode chamar com um conjunto de argumentos e obter um valor de retorno. Um método dentro de um IModelObject é efetivamente um IModelMethod interface encaixotada em um IModelObject
Extensibilidade dentro do modelo de objeto
Um IModelObject não é um objeto isolado. Além de representar um dos tipos de objetos mostrados acima, cada objeto tem a noção de uma cadeia de modelos de dados pai. Esta cadeia comporta-se de forma muito semelhante a uma cadeia de protótipos JavaScript . Em vez de uma cadeia linear de protótipos como o JavaScript tem, cada objeto de modelo de dados define uma cadeia linear de modelos pai. Cada um desses modelos parentais, por sua vez, tem outra cadeia linear de seu próprio conjunto de pais. Em essência, cada objeto é uma agregação das capacidades (propriedades, etc...) de si mesmo e de cada objeto nesta árvore. Quando uma propriedade específica é consultada, se o objeto no qual ela é consultada não oferece suporte a essa propriedade, a consulta é passada em ordem linear para cada pai por sua vez. Isso cria um comportamento em que a pesquisa por uma propriedade é resolvida por uma pesquisa profunda da árvore agregada.
A extensibilidade dentro deste modelo de objeto é muito simples, dada esta noção de que cada objeto é um agregado de si mesmo e a árvore de modelos pai. Uma extensão pode entrar e adicionar-se à lista de modelos pai para outro objeto. Fazer isso se estende o objeto. Desta forma, é possível adicionar recursos a qualquer coisa: uma instância particular de um objeto ou valor, um tipo nativo, o conceito do depurador do que é um processo ou thread, ou mesmo a noção de "todos os objetos iteráveis".
Contexto, contexto e contexto: a este Pointer, o espaço de endereçamento e os dados privados de implementação
Existem três noções de contexto que são necessárias para entender dentro do contexto do modelo de objeto.
Contexto: A este Pointer
Como uma determinada propriedade ou método pode ser implementado em qualquer nível da árvore do modelo de dados, é necessário que a implementação do método ou propriedade possa acessar o objeto original (o que você pode chamar de este ponteiro em C++ ou o este objeto em JavaScript. Esse objeto de instância é passado para uma variedade de métodos como o primeiro argumento chamado contexto nos métodos descritos.
Contexto: O espaço de endereçamento
É importante observar que, ao contrário dos modelos de extensão anteriores, onde de contexto (o destino, o processo, o thread que você está olhando) é um conceito de interface do usuário com todas as APIs relativas ao estado atual da interface do usuário, as interfaces do modelo de dados normalmente usam esse contexto explícita ou implicitamente como uma interface IDebugHostContext. Cada IModelObject dentro do modelo de dados carrega esse tipo de informação de contexto junto com ele e pode propagar esse contexto para objetos que ele retorna. Isso significa que, quando você lê um valor nativo ou um valor de chave de um IModelObject, ele será lido fora do destino e do processo de onde o objeto foi originalmente adquirido.
Há um valor constante explícito, USE_CURRENT_HOST_CONTEXT, que pode ser passado para métodos que usam um IDebugHostContext argumento. Esse valor indica que o contexto deve realmente ser o estado atual da interface do usuário do depurador. Esta noção tem, no entanto, de ser explícita.
Contexto: Implementação de Dados Privados
Lembre-se de que cada objeto no modelo de dados é, na verdade, uma agregação da instância do objeto e da árvore de modelos pai anexados. Cada um desses modelos pai (que podem ser vinculados nas cadeias de muitos objetos diferentes) pode associar dados de implementação privados a qualquer objeto de instância. Cada IModelObject que é criado conceitualmente tem uma tabela de hash que mapeia de um modelo pai específico para dados de instância privada definidos por uma interface IUnknown. Isso permite que um modelo pai armazene em cache informações em cada instância ou tenha dados arbitrários associados.
Esse tipo de contexto é acessado por meio do GetContextForDataModel e métodos de SetContextForDataModel em IModelObject.
A interface de objeto do depurador principal: IModelObject
A interface IModelObject é definida da seguinte forma:
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;
}
Métodos Básicos
A seguir estão os métodos gerais aplicáveis a qualquer tipo de objeto representado por um 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;
O GetKind método retorna que tipo de objeto está encaixotado dentro do IModelObject.
O GetContext método retorna o contexto de host que está associado ao objeto.
O GetIntrinsicValue método retorna a coisa que está encaixotada dentro de um IModelObject. Este método só pode ser legalmente chamado em interfaces IModelObject que representam uma caixa intrínseca ou uma interface particular que é encaixotada. Ele não pode ser chamado em objetos nativos, sem objetos de valor, objetos sintéticos e objetos de referência. O método GetIntrinsicValueAs se comporta tanto quanto o método GetIntrinsicValue, exceto que ele converte o valor para o tipo de variante especificado. Se a conversão não puder ser executada, o método retornará um erro.
O método IsEqualTo compara dois objetos de modelo e retorna se eles são iguais em valor. Para objetos que têm uma ordenação, esse método que retorna true é equivalente ao método Compare que retorna 0. Para objetos que não têm ordenação, mas são equacionáveis, o método Compare falhará, mas isso não acontecerá. O significado de uma comparação baseada em valores é definido pelo tipo de objeto. Atualmente, isso só é definido para tipos intrínsecos e objetos de erro. Não existe um conceito atual de modelo de dados para equabilidade.
O método Dereference desreferencia um objeto. Esse método pode ser usado para cancelar a referência de uma referência baseada em modelo de dados (ObjectTargetObjectReference, ObjectKeyReference) ou uma referência de idioma nativo (um ponteiro ou uma referência de idioma). É importante notar que esse método remove um único nível de semântica de referência no objeto. É perfeitamente possível, por exemplo, ter uma referência de modelo de dados para uma referência de linguagem. Nesse caso, chamar o método Dereference pela primeira vez removeria a referência do modelo de dados e deixaria a referência de idioma. Chamar Dereference nesse objeto resultante removeria subsequentemente a referência de idioma e retornaria o valor nativo sob essa referência.
Métodos de manipulação de chaves
Qualquer objeto sintético que seja um dicionário de tuplas de chave, valor e metadados tem uma série de métodos para manipular essas chaves, valores e os metadados associados a eles.
As formas baseadas em valor das APIs são:
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;
As formas baseadas em referência das APIs são:
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;
O método GetKeyValue é o primeiro método ao qual um cliente recorrerá para obter o valor de (e os metadados associados a) uma determinada chave pelo nome. Se a chave for um acessador de propriedade -- ou seja, seu valor como um IModelObject que é um IModelPropertyAccessor in a box, o método GetKeyValue chamará automaticamente o método GetValue do acessador de propriedade para recuperar o valor real.
O método SetKeyValue é o primeiro método ao qual um cliente recorrerá para definir o valor de uma chave. Esse método não pode ser usado para criar uma nova chave em um objeto. Ele só definirá o valor de uma chave existente. Observe que muitas chaves são somente leitura (por exemplo: elas são implementadas por um acessador de propriedade que retorna E_NOT_IMPL de seu método SetValue). Este método falhará quando chamado em uma chave somente leitura.
O método EnumerateKeyValues é o primeiro método ao qual um cliente recorrerá para enumerar todas as chaves em um objeto (isso inclui todas as chaves implementadas em qualquer lugar na árvore de modelos pai). É importante observar que EnumerateKeyValues enumerará quaisquer chaves definidas por nomes duplicados na árvore de objetos; no entanto -- métodos como GetKeyValue e SetKeyValue manipularão apenas a primeira instância de uma chave com o nome próprio, conforme descoberto pela profundidade-primeira-travessia.
O método GetKey obterá o valor de (e os metadados associados a) uma determinada chave pelo nome. A maioria dos clientes deve utilizar o método GetKeyValue em vez disso. Se a chave for um acessador de propriedade, chamar esse método retornará o acessador de propriedade (uma interface IModelPropertyAccessor) encaixotado em um IModelObject. Ao contrário de GetKeyValue, esse método não resolverá automaticamente o valor subjacente da chave chamando o método GetValue. Essa responsabilidade é do interlocutor.
O método SetKey é o método ao qual um cliente recorrerá para criar uma chave em um objeto (e potencialmente associar metadados à chave criada). Se um determinado objeto já tiver uma chave com o nome próprio, ocorrerá um dos dois comportamentos. Se a chave estiver na instância dada por isso, o valor dessa chave será substituído como se a chave original não existisse. Se, por outro lado, a chave estiver na cadeia de modelos de dados pai da instância dada por isso, uma nova chave com o nome próprio será criada na instância dada por esta. Isso faria, com efeito, com que o objeto tivesse duas chaves do mesmo nome (semelhante a uma classe derivada sombreando um membro do mesmo nome que uma classe base).
O método EnumerateKeys se comporta de forma semelhante ao método EnumerateKeyValues, exceto que ele não resolve automaticamente os acessadores de propriedade no objeto. Isso significa que, se o valor de uma chave for um acessador de propriedade, o método EnumerateKeys retornará o acessador de propriedade (um IModelPropertyAccessorInterface) encaixotado em um IModelObject em vez de chamar automaticamente o método GetValue. Isso é semelhante à diferença entre GetKey e GetKeyValue.
O método ClearKeys remove todas as chaves e seus valores e metadados associados da instância do objeto especificado por isso. Esse método não tem efeito nos modelos pai anexados à instância de objeto específica.
O GetKeyReference método procurará uma chave do nome dado no objeto (ou sua cadeia de modelo pai) e retornará uma referência a essa chave dada por uma interface IModelKeyReference encaixotada em um IModelObject. Essa referência pode ser usada posteriormente para obter ou definir o valor da chave.
O método EnumerateKeyReferences se comporta de forma semelhante ao método EnumerateKeyValues, exceto que ele retorna referências às chaves que enumera (fornecidas por uma interface IModelKeyReference encaixotada em um IModelObject) em vez do valor da chave. Tais referências podem ser usadas para obter ou definir o valor subjacente das chaves.
Métodos de Manipulação de Conceitos
Além de um objeto modelo ser um dicionário de tuplas de chave/valor/metadados, ele também é um contêiner de conceitos. Um conceito é algo abstrato que pode ser realizado sobre ou por um objeto. Os conceitos são, em essência, um armazenamento dinâmico de interfaces que um objeto suporta. Vários conceitos são definidos pelo modelo de dados atualmente:
| Interface de conceito | Descrição |
|---|---|
| IDataModelConcept | O conceito é um modelo pai. Se esse modelo for anexado automaticamente a um tipo nativo por meio de uma assinatura de tipo registrada, o método InitializeObject será chamado automaticamente sempre que um novo objeto desse tipo for instanciado. |
| IStringDisplayableConcept | O objeto pode ser convertido em uma cadeia de caracteres para fins de exibição. |
| IIterableConcept | O objeto é um contêiner e pode ser iterado. |
| IIndexableConcept | O objeto é um contêiner e pode ser indexado (acessado via acesso aleatório) em uma ou mais dimensões. |
| IPreferredRuntimeTypeConcept | O objeto entende mais sobre os tipos derivados dele do que o sistema de tipo subjacente é capaz de fornecer e gostaria de lidar com suas próprias conversões do tipo estático para o tipo de tempo de execução. |
| IDynamicKeyProviderConcept | O objeto é um provedor dinâmico de chaves e deseja assumir todas as consultas de chave do modelo de dados principal. Essa interface é normalmente usada como uma ponte para linguagens dinâmicas, como JavaScript. |
| IDynamicConceptProviderConcept | O objeto é um provedor dinâmico de conceitos e deseja assumir todas as consultas de conceito do modelo de dados principal. Essa interface é normalmente usada como uma ponte para linguagens dinâmicas, como JavaScript. |
Os seguintes métodos em IModelObject são utilizados para manipular os conceitos que um objeto suporta.
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;
O método GetConcept procurará um conceito no objeto (ou sua cadeia de modelo pai) e retornará um ponteiro de interface para a interface de conceito. O comportamento e os métodos em uma interface de conceito são específicos para cada conceito. É importante notar, no entanto, que muitas das interfaces de conceito exigem que o chamador passe explicitamente o objeto de contexto (ou o que tradicionalmente se pode chamar de este ponteiro). É importante garantir que o objeto de contexto correto seja passado para cada interface de conceito.
O método SetConcept colocará um conceito especificado na instância do objeto especificada pelo ponteiro this. Se um modelo pai anexado à instância de objeto especificada por isso também oferecer suporte ao conceito, a implementação na instância substituirá essa no modelo pai.
O método ClearConcepts removerá todos os conceitos da instância do objeto especificado por isso.
métodos de objeto nativo
Enquanto muitos objetos de modelo se referem a construções intrínsecas (por exemplo: inteiros, strings) ou sintéticas (um dicionário de tuplas e conceitos de chave/valor/metadados), um objeto de modelo também pode se referir a uma construção nativa (por exemplo: um tipo definido pelo usuário no espaço de endereço do destino de depuração). A interface IModelObject tem uma série de métodos que acessam informações sobre esses objetos nativos. Esses métodos são:
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;
O GetRawValue método encontra uma construção nativa dentro do objeto dado. Tal construção pode ser um campo, uma classe base, um campo em uma classe base, uma função membro, etc...
O método EnumerateRawValues enumera todos os filhos nativos (por exemplo: campos, classes base, etc...) do objeto fornecido.
O método TryCastToRuntimeType solicitará ao host de depuração para executar uma análise e determinar o tipo de tempo de execução real (por exemplo: a classe mais derivada) do objeto fornecido. A análise exata utilizada é específica para o host de depuração e pode incluir RTTI (informações de tipo de tempo de execução C++), exame da estrutura V-Table (tabela de função virtual) do objeto ou qualquer outro meio que o host possa usar para determinar de forma confiável o tipo dinâmico/de tempo de execução a partir do tipo estático. A falha na conversão para um tipo de tempo de execução não significa que essa chamada de método falhará. Nesses casos, o método retornará o objeto dado (o ponteiro this) no argumento output.
O método GetLocation retornará o local do objeto nativo. Embora esse local seja normalmente um endereço virtual dentro do espaço de endereço do destino de depuração, não é necessariamente assim. O local retornado por esse método é um local abstrato que pode ser um endereço virtual, pode indicar posicionamento dentro de um registro ou sub-registro, ou pode indicar algum outro espaço de endereço arbitrário, conforme definido pelo host de depuração. Se o campo HostDefined do objeto Location resultante for 0, isso indica que o local é realmente um endereço virtual. Esse endereço virtual pode ser recuperado examinando o campo Offset do local resultante. Qualquer valor diferente de zero do campo HostDefined indica um espaço de endereçamento alternativo onde o campo Deslocamento é o deslocamento dentro desse espaço de endereço. O significado exato de valores HostDefined diferentes de zero aqui são privados para o host de depuração.
O método GetTypeInfo retornará o tipo nativo do objeto fornecido. Se o objeto não tiver informações de tipo nativas associadas a ele (por exemplo: é um intrínseco, etc...), a chamada ainda terá êxito, mas retornará null.
O método GetTargetInfo é efetivamente uma combinação dos métodos GetLocation e GetTypeInfo, retornando tanto o local abstrato quanto o tipo nativo do objeto fornecido.
O GetRawReference método encontra uma construção nativa dentro do objeto dado e retorna uma referência a ele. Tal construção pode ser um campo, uma classe base, um campo em uma classe base, uma função membro, etc... É importante distinguir a referência retornada aqui (um objeto do tipo ObjectTargetObjectReference) de uma referência de linguagem (por exemplo: uma referência de estilo & C++ ou &&).
O método EnumerateRawReferences enumera referências a todos os filhos nativos (por exemplo: campos, classes base, etc...) do objeto dado.
Métodos de extensibilidade
Conforme descrito anteriormente, um objeto modelo se comporta de forma muito semelhante a um objeto JavaScript e sua cadeia de protótipos. Além da instância representada por uma determinada interface IModelObject, pode haver um número arbitrário de modelos pai anexados ao objeto (cada um dos quais pode, por sua vez, ter um número arbitrário de modelos pai anexados a eles). Este é o principal meio de extensibilidade dentro do modelo de dados. Se uma determinada propriedade ou conceito não puder ser localizado dentro de uma determinada instância, uma pesquisa profunda da árvore de objetos (definida por modelos pai) enraizada na instância será executada.
Os métodos a seguir manipulam a cadeia de modelos pai associados a uma determinada instância 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;
O GetNumberOfParentModels método retorna o número de modelos pai que estão anexados à instância de objeto dada. Os modelos pai são pesquisados por propriedades em primeiro lugar na ordenação linear da cadeia de modelos pai.
O GetParentModel método retorna o i-ésimo modelo pai na cadeia de modelo pai do objeto dado. Os modelos pai são pesquisados por uma propriedade ou conceito na ordem linear em que são adicionados ou enumerados. O modelo pai com índice i de zero é pesquisado (hierarquicamente) antes do modelo pai com índice i + 1.
O AddParentModel método adiciona um novo modelo pai para o objeto fornecido. Esse modelo pode ser adicionado no final da cadeia de pesquisa (o argumento de substituição é especificado como falso) ou na frente da cadeia de pesquisa (o argumento de substituição é especificado como verdadeiro). Além disso, cada modelo pai pode, opcionalmente, ajustar o contexto (a semântica deste ponteiro) para qualquer propriedade ou conceito no pai dado (ou qualquer pessoa em sua hierarquia pai). O ajuste de contexto raramente é usado, mas permite alguns conceitos poderosos, como incorporação de objetos, construção de namespaces, etc...
O RemoveParentModel removerá um modelo pai especificado da cadeia de pesquisa pai do objeto fornecido.
O método SetContextForDataModel é usado pela implementação de um modelo de dados para colocar dados de implementação em objetos de instância. Conceitualmente, cada IModelObject (chame isso de instância para simplificar) contém uma tabela de estado de hash. A tabela de hash é indexada por outro IModelObject (chame isso de modelo de dados para simplificar) que está na hierarquia de modelo pai da instância. O valor contido neste hash é um conjunto de informações de estado contadas de referência representadas por uma instância IUnknown. Uma vez que o modelo de dados define esse estado na instância, ele pode armazenar dados de implementação arbitrários que podem ser recuperados durante coisas como getters de propriedade.
O GetContextForDataModel método é usado para recuperar informações de contexto que foi configurado com uma chamada anterior para SetContextForDataModel. Isso recupera informações de estado que foram definidas em um objeto de instância por um modelo de dados mais acima na hierarquia de modelo pai do objeto de instância. Para obter mais detalhes sobre esse contexto/estado e seu significado, consulte a documentação de SetContextForDataModel.
Tipos de objeto principal do modelo de dados do Depurador
Um objeto no modelo de dados é semelhante à noção de objeto no .NET. É o contêiner genérico no qual a construção que o modelo de dados entende que pode ser encaixotada. Além de objetos nativos e objetos sintéticos (dinâmicos), há uma série de tipos de objetos principais que podem ser colocados (ou encaixotados) no contêiner de um IModelObject. O recipiente no qual a maioria desses valores são colocados é uma variante COM/OLE padrão com uma série de restrições adicionais colocadas sobre o que essa VARIANTE pode conter. Os tipos mais básicos são:
- Valores não assinados e assinados de 8 bits (VT_UI1, VT_I1)
- Valores não assinados e assinados de 16 bits (VT_UI2, VT_UI2)
- Valores não assinados e assinados de 32 bits (VT_UI4, VT_I4)
- Valores não assinados e assinados de 64 bits (VT_UI8, VT_I8)
- Valores de ponto flutuante de precisão simples e dupla (VT_R4, VT_R8)
- Cordas (VT_BSTR)
- Booleanos (VT_BOOL)
Além desses tipos básicos, vários objetos de modelo de dados principais são colocados em IModelObject definido por VT_UNKNOWN onde o IUnknown armazenado tem a garantia de implementar uma interface específica. Estes tipos são:
- Acessadores de propriedade (IModelPropertyAccessor)
- Objetos de método (IModelMethod)
- Principais objetos de referência (IModelKeyReference ou IModelKeyReference2)
- Objetos de contexto (IDebugModelHostContext)
Acessadores de propriedade: IModelPropertyAccessor
Um acessador de propriedade no modelo de dados é uma implementação da interface IModelPropertyAccessor que é encaixotada em um IModelObject. O objeto de modelo retornará um tipo de ObjectPropertyAccessor quando consultado e o valor intrínseco é um VT_UNKNOWN que é garantido ser consultável para IModelPropertyAccessor. No processo, é garantido para ser estaticamente castable para IModelPropertyAccessor.
Um acessador de propriedade é uma maneira indireta de obter uma chamada de método para obter e definir um valor de chave no modelo de dados. Se o valor de uma determinada chave for um acessador de propriedade, os métodos GetKeyValue e SetKeyValue notarão isso automaticamente e chamarão os métodos GetValue ou SetValue subjacentes do acessador de propriedade, conforme apropriado.
A interface IModelPropertyAccessor é definida da seguinte forma:
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;
}
O GetValue método é o getter para o acessador de propriedade. É chamado sempre que um cliente deseja buscar o valor subjacente do imóvel. Observe que qualquer chamador que obtém diretamente um acessador de propriedade é responsável por passar o nome da chave e o objeto de instância preciso (esse ponteiro) para o método GetValue do acessador de propriedade.
O método SetValue é o setter para o acessador de propriedade. É chamado sempre que um cliente deseja atribuir um valor à propriedade subjacente. Muitas propriedades são somente leitura. Nesses casos, chamar o método SetValue retornará E_NOTIMPL. Observe que qualquer chamador que obtém diretamente um acessador de propriedade é responsável por passar o nome da chave e o objeto de instância preciso (esse ponteiro) para o método SetValue do acessador de propriedade.
Métodos: IModelMethod
Um método no modelo de dados é uma implementação da interface IModelMethod que é encaixotada em um IModelObject. O objeto de modelo retornará um tipo de ObjectMethod quando consultado e o valor intrínseco é um VT_UNKNOWN que é garantido ser consultável para IModelMethod. No processo, é garantido que ele seja estaticamente moldável para IModelMethod. Todos os métodos no modelo de dados são dinâmicos por natureza. Eles tomam como entrada um conjunto de 0 ou mais argumentos e retornam um único valor de saída. Não há resolução de sobrecarga nem metadados sobre nomes, tipos ou expectativas de parâmetros.
A interface IModelMethod é definida da seguinte forma:
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;
}
O método Call é a maneira pela qual qualquer método definido no modelo de dados é invocado. O chamador é responsável por passar um objeto de instância preciso (este ponteiro) e um conjunto arbitrário de argumentos. O resultado do método e quaisquer metadados opcionais associados a esse resultado são retornados. Os métodos que não retornam logicamente um valor ainda devem retornar um IModelObject válido. Nesse caso, o IModelObject é um valor sem caixa. No caso de um método falhar, ele pode retornar informações de erro estendidas opcionais no argumento de entrada (mesmo se o HRESULT retornado for uma falha). É imperativo que os interlocutores verifiquem esta situação.
Principais referências: IModelKeyReference ou IModelKeyReference2
Uma referência de chave é, em essência, um identificador para uma chave em um objeto específico. Um cliente pode recuperar esse identificador por meio de métodos como GetKeyReference e usar o identificador mais tarde para obter ou definir o valor da chave sem necessariamente manter o objeto original. Esse tipo de objeto é uma implementação da interface IModelKeyReference ou IModelKeyReference2 que é encaixotada em um IModelObject. O objeto de modelo retornará um tipo de ObjectKeyReference quando consultado e, em seguida, o valor intrínseco é um VT_UNKNOWN que é garantido ser consultável para IModelKeyReference. No processo, é garantido que ele seja estaticamente moldável para IModelKeyReference.
A interface de referência principal é definida da seguinte forma:
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;
}
O GetKeyName método retorna o nome da chave para a qual essa referência de chave é um identificador. A string retornada é um BSTR padrão e deve ser liberada por meio de uma chamada para SysFreeString.
O GetOriginalObject método retorna o objeto de instância a partir do qual a referência de chave foi criada. Observe que a chave pode estar em um modelo pai do objeto de instância.
O método GetContextObject retorna o contexto (esse ponteiro) que será passado para o método GetValue ou SetValue de um acessador de propriedade se a chave em questão se referir a um acessador de propriedade. O objeto de contexto retornado aqui pode ou não ser o mesmo que o objeto original buscado de GetOriginalObject. Se uma chave estiver em um modelo pai e houver um ajustador de contexto associado a esse modelo pai, o objeto original será o objeto de instância no qual GetKeyReference ou EnumerateKeyReferences foi chamado. O objeto de contexto seria qualquer coisa que saísse do ajustador de contexto final entre o objeto original e o modelo pai contendo a chave para a qual essa referência de chave é um identificador. Se não houver ajustadores de contexto, o objeto original e o objeto de contexto serão idênticos.
O método GetKey em uma referência de chave se comporta como o método GetKey em IModelObject faria. Ele retorna o valor da chave subjacente e quaisquer metadados associados à chave. Se o valor da chave for um acessador de propriedade, isso retornará o acessador de propriedade (IModelPropertyAccessor) encaixotado em um IModelObject. Esse método não chamará os métodos subjacentes GetValue ou SetValue no acessador de propriedade.
O GetKeyValue método em uma referência de chave se comporta como o GetKeyValue método em IModelObject faria. Ele retorna o valor da chave subjacente e quaisquer metadados associados à chave. Se o valor da chave for um acessador de propriedade, isso chamará o método GetValue subjacente no acessador de propriedade automaticamente.
O método SetKey em uma referência de chave se comporta como o método SetKey em IModelObject faria. Ele atribuirá o valor da chave. Se a chave original era um acessador de propriedade, isso substituirá o acessador de propriedade. Ele não chamará o método SetValue no acessador da propriedade.
O método SetKeyValue em uma referência de chave se comporta como o método SetKeyValue em IModelObject faria. Ele atribuirá o valor da chave. Se a chave original era um acessador de propriedade, isso chamará o método SetValue subjacente no acessador de propriedade em vez de substituir o próprio acessador de propriedade.
O método OverrideContextObject (presente apenas em IModelKeyReference2) é um método avançado que é usado para alterar permanentemente o objeto de contexto que essa referência de chave passará para os métodos GetValue ou SetValue de qualquer acessador de propriedade subjacente. O objeto passado para esse método também será retornado de uma chamada para GetContextObject. Esse método pode ser usado por provedores de script para replicar determinados comportamentos de linguagem dinâmica. A maioria dos clientes não deve chamar esse método.
objetos de contexto: IDebugHostContext
Os objetos de contexto são blobs opacos de informações que o host de depuração (em cooperação com o modelo de dados) associa a cada objeto. Pode incluir coisas como o contexto do processo ou o espaço de endereçamento de onde a informação vem, etc... Um objeto de contexto é uma implementação de IDebugHostContext encaixotado dentro de um IModelObject. Observe que IDebugHostContext é uma interface definida pelo host. Um cliente nunca implementará essa interface.
Para obter mais informações sobre objetos de contexto, consulte Debugger Data Model C++ Host Interfaces em Debugger Data Model C++ Interfaces.
O Gerenciador de Modelo de Dados
A interface principal para o gerenciador de modelo de dados, IDataModelManager2 (ou o IDataModelManager anterior) é definida da seguinte maneira:
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;
}
Métodos de Gestão
O seguinte conjunto de métodos é utilizado pelo aplicativo (por exemplo: depurador) que hospeda o modelo de dados.
STDMETHOD(Close)() PURE;
O método Close é chamado no gerenciador de modelo de dados por um aplicativo (por exemplo: depurador) que hospeda o modelo de dados para iniciar o processo de desligamento do gerenciador de modelo de dados. Um host do modelo de dados que não usa o método Close antes de liberar sua referência final no gerenciador do modelo de dados pode causar um comportamento indefinido, incluindo, mas não limitado a, vazamentos significativos da infraestrutura de gerenciamento para o modelo de dados.
Criação de Objetos / Métodos de Boxe
O conjunto de métodos a seguir é usado para criar novos objetos ou para encaixotar valores em um IModelObject -- a interface principal do modelo de dados.
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;
O método CreateNoValue cria um objeto "no value", encaixota-o em um IModelObject e o retorna. O objeto de modelo retornado tem um tipo de ObjectNoValue.
Um objeto "sem valor" tem vários significados semânticos:
- (Dependendo da linguagem), pode ser considerado o equivalente semântico de nulo, nulo ou indefinido
- O método GetValue de qualquer acessador de propriedade que retorna sucesso e um objeto "sem valor" resultante está indicando que a propriedade específica não tem valor para a instância específica e deve ser tratada como se a propriedade não existisse para essa instância específica.
- Os métodos de modelo de dados que não têm semanticamente um valor de retorno usam isso como um sentinela para indicar tal (como um método deve retornar um IModelObject válido).
O método CreateErrorObject cria um "objeto de erro". O modelo de dados não tem a noção de exceções e fluxo de exceções. A falha resulta de uma propriedade/método de duas maneiras:
- Um único HRESULT com falha sem informações de erro estendidas. Ou não há mais informações que podem ser dadas para o erro ou o erro em si é autoexplicativo do HRESULT retornado.
- Um único HRESULT com falha juntamente com informações de erro estendidas. As informações de erro estendidas são um objeto de erro retornado no argumento output da propriedade/método.
O método CreateTypedObject é o método que permite que um cliente crie uma representação de um objeto nativo/language no espaço de endereço de um destino de depuração. Se o tipo do objeto recém-criado (conforme indicado pelo argumento objectType) corresponder a uma ou mais assinaturas de tipo registradas no gerenciador de modelo de dados como visualizadores canônicos ou extensões, esses modelos de dados correspondentes serão automaticamente anexados ao objeto de instância criado antes que ele seja retornado ao chamador.
O método CreateTypedObjectReference é semanticamente semelhante ao método CreateTypedObject, exceto que ele cria uma referência à construção nativa/language subjacente. A referência criada é um objeto que tem um tipo de ObjectTargetObjectReference. Não é uma referência nativa, pois a linguagem subjacente pode suportar (por exemplo: um & C++ ou &&). É totalmente possível ter um ObjectTargetObjectReference para uma referência C++. Um objeto do tipo ObjectTargetObjectReference pode ser convertido para o valor subjacente através do uso do método Dereference em IModelObject. A referência também pode ser passada para o avaliador de expressão do anfitrião subjacente, a fim de atribuir de volta ao valor de uma forma apropriada à linguagem.
O método CreateSyntheticObject cria um objeto de modelo de dados vazio -- um dicionário de tuplas e conceitos de chave/valor/metadados. No momento da criação, não há chaves nem conceitos sobre o objeto. É uma tábua rasa para o chamador utilizar.
O método CreateDataModelObject é um wrapper auxiliar simples para criar objetos que são modelos de dados -- ou seja, objetos que serão anexados como modelos pai a outros objetos. Todos esses objetos devem suportar o conceito de modelo de dados via IDataModelConcept. Esse método cria um novo objeto sintético em branco sem contexto explícito e adiciona o IDataModelConcept inpassado como a implementação do conceito de modelo de dados do objeto recém-criado. Isso também pode ser feito com chamadas para CreateSyntheticObject e SetConcept.
O método CreateIntrinsicObject é o método que encaixota valores intrínsecos em IModelObject. O chamador coloca o valor em uma COM VARIANT e chama esse método. O gerenciador de modelo de dados retorna um IModelObject que representa o objeto. Note que este método também é usado para encaixotar tipos baseados em IUnknown fundamentais: acessadores de propriedade, métodos, contextos, etc... Nesses casos, o método objectKind indica que tipo de construção baseada em IUnknown o objeto representa e o campo punkVal da variante passada é o tipo derivado IUnknown. O tipo deve ser estaticamente moldável para a interface de modelo apropriada (por exemplo: IModelPropertyAccessor, IModelMethod, IDebugHostContext, etc...) no processo. Os tipos VARIANT suportados por esse método são VT_UI1, VT_I1, VT_UI2, VT_I2, VT_UI4, VT_I4, VT_UI8, VT_I8, VT_R4, VT_R8, VT_BOOL, VT_BSTR e VT_UNKNOWN (para um conjunto especializado de tipos derivados IUnknown conforme indicado pela enumeração ModelObjectKind.
O método CreateTypedintrinsicObject é semelhante ao método CreateIntrinsicObject, exceto que ele permite que um tipo nativo/idioma seja associado aos dados e transportado junto com o valor in a box. Isso permite que o modelo de dados represente construções como tipos de enumeração nativos (que são simplesmente valores VT_UI* ou VT_I*). Os tipos de ponteiro também são criados com esse método. Um ponteiro nativo no modelo de dados é uma quantidade zero estendida de 64 bits que representa um deslocamento no espaço de endereço virtual do destino de depuração. Ele é encaixotado dentro de um VT_UI8 e é criado com este método e um tipo que indica um ponteiro nativo/idioma.
O método CreateMetadataStore cria um armazenamento de chaves -- um contêiner simplificado de tuplas de chave/valor/metadados -- que é usado para armazenar metadados que podem ser associados a propriedades e uma variedade de outros valores. Um repositório de metadados pode ter um pai único (que, por sua vez, pode ter um pai único). Se uma determinada chave de metadados não estiver localizada em um determinado armazenamento, seus pais serão verificados. A maioria dos repositórios de metadados não tem pais. No entanto, proporciona uma forma de partilhar facilmente metadados comuns.
O método CreateTypedIntrinsicObjectEx é semanticamente semelhante ao método CreateTypedIntrinsicObject. A única diferença entre os dois é que esse método permite que o chamador especifique o contexto no qual os dados intrínsecos são válidos. Se nenhum contexto for passado, os dados serão considerados válidos em qualquer contexto herdado do argumento type (como CreateTypedIntrinsicObject se comporta). Isso permite a criação de valores de ponteiro digitados no destino de depuração que exigem um contexto mais específico do que pode ser herdado do tipo.
Métodos de Extensibilidade / Registo O conjunto de métodos a seguir gerencia o mecanismo de extensibilidade do modelo de dados, permitindo que um cliente estenda ou registre modelos existentes ou peça ao modelo de dados para anexar automaticamente um determinado modelo pai em tipos nativos que correspondam a um determinado critério.
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;
O método GetModelForTypeSignature retorna o modelo de dados que foi registrado em relação a uma assinatura de tipo específico por meio de uma chamada anterior para o método RegisterModelForTypeSignature. O modelo de dados retornado desse método é considerado o visualizador canônico para qualquer tipo que corresponda à assinatura de tipo passado. Como um visualizador canônico, esse modelo de dados assume a exibição do tipo. Os mecanismos de exibição ocultarão, por padrão, construções nativas/linguísticas do objeto em favor da exibição do objeto apresentado pelo modelo de dados.
O GetModelForType método retorna o modelo de dados que é o visualizador canônico para uma determinada instância de tipo. Na verdade, esse método encontra a melhor assinatura de tipo correspondente que foi registrada com uma chamada anterior para o método RegisterModelForTypeSignature e retorna o modelo de dados associado.
O método RegisterModelForTypeSignature é o método primário que um chamador utiliza para registrar um visualizador canônico para um determinado tipo (ou conjunto de tipos). Um visualizador canônico é um modelo de dados que, na verdade, assume a exibição de um determinado tipo (ou conjunto de tipos). Em vez da visualização nativa/linguística do tipo ser exibida em qualquer interface de usuário do depurador, a exibição do tipo conforme apresentada pelo modelo de dados registrado é exibida (juntamente com um meio de voltar à visualização nativa/idioma para um usuário que a deseje).
UnregisterModelForTypeSignature
O método UnregisterModelForTypeSignature desfaz uma chamada anterior para o método RegisterModelForTypeSignature. Esse método pode remover um determinado modelo de dados como o visualizador canônico para tipos que correspondem a uma assinatura de tipo específica ou pode remover um determinado modelo de dados como o visualizador canônico para cada assinatura de tipo sob a qual esse modelo de dados está registrado.
RegisterExtensionForTypeSignature
O método RegisterExtensionForTypeSignature é semelhante ao método RegisterModelForTypeSignature com uma diferença de chave. O modelo de dados que é passado para este método não é o visualizador canônico para qualquer tipo e não assumirá a exibição da visualização nativa/de idioma desse tipo. O modelo de dados que é passado para este método será automaticamente adicionado como um pai a qualquer tipo concreto que corresponda à assinatura de tipo fornecida. Ao contrário do método RegisterModelForTypeSignature, não há limite para assinaturas de tipo idênticas ou ambíguas serem registradas como extensões para um determinado tipo (ou conjunto de tipos). Cada extensão cuja assinatura de tipo corresponde a uma determinada instância de tipo concreto fará com que o modelo de dados registrado por meio desse método seja automaticamente anexado a objetos recém-criados como modelos pai. Isso, na verdade, permite que um número arbitrário de clientes estenda um tipo (ou conjunto de tipos) com novos campos ou funcionalidades.
UnregisterExtensionForTypeSignature
O método UnregisterExtensionForTypeSignature desfaz uma chamada anterior para RegisterExtensionForTypeSignature. Ele cancela o registro de um modelo de dados específico como uma extensão para uma assinatura de tipo específico ou como uma extensão para todas as assinaturas de tipo nas quais o modelo de dados foi registrado.
O método GetRootNamespace retorna o namespace raiz do modelo de dados. Este é um objeto que o modelo de dados gerencia e no qual o host de depuração coloca determinados objetos.
O método RegisterNamedModel registra um determinado modelo de dados sob um nome bem conhecido para que ele possa ser encontrado por clientes que desejam estendê-lo. Este é o objetivo principal da API -- publicar um modelo de dados como algo que pode ser estendido recuperando o modelo registrado sob esse nome bem conhecido e adicionando um modelo pai a ele.
O método UnregisterNamedModel desfaz uma chamada anterior para RegisterNamedModel. Ele remove a associação entre um modelo de dados e um nome sob o qual ele pode ser pesquisado.
Um chamador que deseja estender um modelo de dados registrado sob um determinado nome chama o método AcquireNamedModel para recuperar o objeto para o modelo de dados que deseja estender. Esse método retornará qualquer modelo de dados registrado por meio de uma chamada anterior para o método RegisterNamedModel. Como o objetivo principal do método AcquireNamedModel é estender o modelo, esse método tem um comportamento especial se nenhum modelo tiver sido registrado com o nome próprio ainda. Se nenhum modelo tiver sido registrado com o nome próprio ainda, um objeto stub será criado, temporariamente registrado sob o nome próprio e retornado ao chamador. Quando o modelo de dados real é registrado por meio de uma chamada para o método RegisterNamedModel, todas as alterações que foram feitas no objeto stub são, na verdade, feitas no modelo real. Isso elimina muitos problemas de dependência da ordem de carga dos componentes que se estendem uns aos outros.
Métodos Auxiliares
Os métodos a seguir são métodos auxiliares gerais que ajudam na execução de operações complexas em objetos no modelo de dados. Embora seja possível executar essas ações por meio de outros métodos no modelo de dados ou em seus objetos, esses métodos de conveniência facilitam significativamente:
STDMETHOD(AcquireSubNamespace)(_In_ PCWSTR modelName, _In_ PCWSTR subNamespaceModelName, _In_ PCWSTR accessName, _In_opt_ IKeyStore *metadata, _COM_Outptr_ IModelObject **namespaceModelObject) PURE;
O método AcquireSubNamespace ajuda na construção de algo que pode se parecer mais tradicionalmente com um namespace de linguagem do que com um novo objeto em uma linguagem dinâmica. Se, por exemplo, um chamador deseja categorizar propriedades em um objeto de processo para tornar o objeto de processo mais organizado e as propriedades mais fáceis de descobrir, um método de fazer isso seria criar um subobjeto para cada categoria no objeto de processo e colocar essas propriedades dentro desse objeto.
Ver também
Este tópico faz parte de uma série que descreve as interfaces acessíveis a partir de C++, como usá-las para criar uma extensão de depurador baseada em C++ e como fazer uso de outras construções de modelo de dados (por exemplo: JavaScript ou NatVis) a partir de uma extensão de modelo de dados C++.
Visão geral do Depurador Data Model C++
Depurador Data Model C++ Interfaces
Depurador Data Model C++ Interfaces adicionais