Objetos C++ del modelo de datos del depurador
En este tema se describe cómo usar objetos C++ del modelo de datos del depurador y cómo pueden ampliar las funcionalidades del depurador.
Modelo de objetos del depurador principal
Uno de los aspectos más básicos y eficaces sobre el modelo de datos es que normaliza la definición de lo que es un objeto y cómo se interactúa con él. La interfaz IModelObject encapsula la noción de un objeto, ya sea que ese objeto sea un entero, un valor de punto flotante, una cadena, algún tipo complejo en el espacio de direcciones de destino del depurador o algún concepto del depurador, como la noción de un proceso o un módulo.
Hay varias elementos diferentes que se pueden mantener (o incluir) en un objeto IModelObject:
Valores intrínsecos: un IModelObject puede ser un contenedor para varios tipos básicos: enteros con signo o sin signo de 8, 16, 32 o 64 bits, booleanos, cadenas, errores o la noción de vacío.
Objetos nativos: un IModelObject puede representar un tipo complejo (tal como lo define el sistema del tipo de depurador) dentro del espacio de direcciones de cualquier elemento que el depurador tenga como destino.
Objetos sintéticos: un objeto IModelObject puede ser un objeto dinámico, un diccionario si se quiere: una colección de tuplas de clave/valor/metadatos y un conjunto de conceptos que definen comportamientos que no se representan simplemente mediante pares de clave/valor.
Propiedades: un IModelObject puede representar una propiedad: algo cuyo valor se puede recuperar o modificar con una llamada de método. Una propiedad dentro de un IModelObject es efectivamente una interfaz IModelPropertyAccessor incluida en un IModelObject
Métodos: un IModelObject puede representar un método: algo a lo que se puede llamar con un conjunto de argumentos y obtener un valor devuelto. Un método dentro de un IModelObject es efectivamente una interfaz IModelMethod incluida en un IModelObject
Extensibilidad dentro del modelo de objetos
Un IModelObject no es un objeto de forma aislada. Además de representar uno de los tipos de objetos mostrados anteriormente, cada objeto tiene la noción de una cadena de modelos de datos primarios. Esta cadena se comporta de forma muy similar a una cadena de prototipos de JavaScript. En lugar de una cadena lineal de prototipos como JavaScript, cada objeto de modelo de datos define una cadena lineal de modelos primarios. Cada uno de esos modelos primarios a su vez tiene otra cadena lineal de su propio conjunto de elementos primarios. En esencia, cada objeto es una agregación de las funcionalidades (propiedades, etc.) de sí mismo y de cada objeto de este árbol. Cuando se consulta una propiedad específica, si el objeto en el que se consulta no admite esa propiedad, la consulta se pasa linealmente a cada elemento primario a su vez. Esto crea un comportamiento en el que la búsqueda de una propiedad se resuelve mediante una búsqueda en primer lugar de profundidad del árbol agregado.
La extensibilidad dentro de este modelo de objetos es muy sencilla, dado que cada objeto es un agregado de sí mismo y del árbol de modelos primarios. Una extensión puede entrar y agregarse a la lista de modelos primarios para otro objeto. Al hacerlo, se extiende el objeto. De esta manera, es posible agregar funcionalidades a cualquier cosa: una instancia determinada de un objeto o valor, un tipo nativo, el concepto del depurador de lo que es un proceso o subproceso, o incluso la noción de "todos los objetos iterables".
Contexto, contexto y contexto: el puntero this, el espacio de direcciones y los datos privados de implementación
Hay tres nociones de contexto que son necesarias para comprender dentro del contexto del modelo de objetos.
Contexto: el puntero this
Dado que una propiedad o un método determinado se pueden implementar en cualquier nivel del árbol del modelo de datos, es necesario que la implementación del método o la propiedad puedan tener acceso al objeto original (que puede llamar puntero this en C++ o el objeto this en JavaScript. Ese objeto de instancia se pasa a una variedad de métodos como primer argumento, denominado contexto en los métodos descritos.
Contexto: el espacio de direcciones
Es importante tener en cuenta que, a diferencia de los modelos de extensión anteriores en los que el contexto (el destino , el proceso, el subproceso que está viendo) es un concepto de interfaz de usuario con todas las API relativas al estado actual de la interfaz de usuario, las interfaces del modelo de datos suelen tomar este contexto explícita o implícitamente como una interfaz IDebugHostContext. Cada IModelObject dentro del modelo de datos incluye este tipo de información de contexto junto con él y puede propagar ese contexto a los objetos que devuelve. Esto significa que, al leer un valor nativo o un valor de clave fuera de un IModelObject, se leerá fuera del destino y el proceso desde el que el objeto se adquirió originalmente.
Hay un valor constante explícito, USE_CURRENT_HOST_CONTEXT, que se puede pasar a métodos que toman un argumento IDebugHostContext. Este valor indica que el contexto debe ser realmente el estado actual de la interfaz de usuario del depurador. Sin embargo, esta noción tiene que ser explícita.
Contexto: datos privados de implementación
Recuerde que cada objeto del modelo de datos es realmente un agregado de la instancia de objeto y el árbol de modelos primarios que están asociados. Cada uno de esos modelos primarios (que se pueden vincular en las cadenas de muchos objetos diferentes) puede asociar datos privados de implementación con cualquier objeto de instancia. Cada IModelObject que se crea conceptualmente tiene una tabla hash que se asigna desde un modelo primario determinado a los datos de instancia privada definidos por una interfaz IUnknown. Esto permite a un modelo primario almacenar en caché la información de cada instancia o tener datos arbitrarios asociados.
Se accede a este tipo de contexto a través de los métodos GetContextForDataModel y SetContextForDataModel en IModelObject.
La interfaz de objeto de depurador principal: IModelObject
La interfaz IModelObject se define de la manera siguiente:
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 continuación se muestran métodos generales aplicables a cualquier tipo de objeto representado por un 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;
El método GetKind devuelve qué tipo de objeto está incluido dentro del IModelObject.
El método GetContext devuelve el contexto de host asociado al objeto.
El método GetIntrinsicValue devuelve lo que está incluido dentro de un IModelObject. Este método solo se puede llamar legalmente en interfaces IModelObject que representan una interfaz intrínseca incluida o una interfaz determinada que está incluida. No se puede llamar en objetos nativos, objetos no de valor, objetos sintéticos ni objetos de referencia. El método GetIntrinsicValueAs se comporta de forma muy similar al método GetIntrinsicValue, con la salvedad de que convierte el valor al tipo de variante especificado. Si no se puede realizar la conversión, el método devuelve un error.
El método IsEqualTo compara dos objetos del modelo y devuelve si tienen el mismo valor. Para los objetos que tienen un orden, este método que devuelve true es equivalente al método Compare que devuelve 0. Para los objetos que no tienen orden pero son equiparables, el método Compare fallará, pero este no. El significado de una comparación basada en valores se define mediante el tipo de objeto. En la actualidad, esto solo se define para tipos intrínsecos y objetos de error. No existe un concepto actual de modelo de datos para la equidad.
El método Dereference desreferencia un objeto. Este método se puede usar para desreferenciar una referencia basada en un modelo de datos (ObjectTargetObjectReference, ObjectKeyReference) o una referencia de lenguaje nativo (un puntero o una referencia de lenguaje). Es importante tener en cuenta que este método quita un único nivel de semántica de referencia en el objeto. Por ejemplo, es posible tener una referencia de modelo de datos a una referencia de lenguaje. En tal caso, llamar al método Dereference la primera vez quitaría la referencia del modelo de datos y dejaría la referencia del lenguaje. Al llamar a Dereference en ese objeto resultante, se quitaría posteriormente la referencia de lenguaje y se devolvería el valor nativo en esa referencia.
Métodos de manipulación de claves
Cualquier objeto sintético que sea un diccionario de tuplas de clave, valor y metadatos tiene una serie de métodos para manipular esas claves, valores y metadatos asociados.
Las formas basadas en valores de las API son:
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;
Las formas basadas en referencias de las API son:
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;
El método GetKeyValue es el primer método al que un cliente recurrirá para obtener el valor de una clave determinada por nombre (y los metadatos asociados). Si la clave es un descriptor de acceso de propiedad, es decir, es un valor como IModelObject, que es un IModelPropertyAccessor incluido, el método GetKeyValue llamará automáticamente al método GetValue del descriptor de acceso de propiedad para recuperar el valor real.
El método SetKeyValue es el primer método al que un cliente recurrirá para establecer el valor de una clave. Este método no se puede usar para crear una nueva clave en un objeto. Solo establecerá el valor de una clave existente. Tenga en cuenta que muchas claves son de solo lectura (por ejemplo, se implementan mediante un descriptor de acceso de propiedad que devuelve E_NOT_IMPL del método SetValue). Este método producirá un error cuando se llame en una clave de solo lectura.
El método EnumerateKeyValues es el primer método al que un cliente recurrirá para enumerar todas las claves de un objeto (esto incluye todas las claves implementadas en cualquier lugar del árbol de modelos primarios). Es importante tener en cuenta que EnumerateKeyValues enumerará las claves definidas por nombres duplicados en el árbol de objetos; sin embargo, los métodos como GetKeyValue y SetKeyValue solo manipularán la primera instancia de una clave con el nombre especificado, tal y como detecta el recorrido de profundidad.
El método GetKey obtendrá el valor de una clave determinada por nombre (y los metadatos asociados). La mayoría de los clientes deben usar el método GetKeyValue en su lugar. Si la clave es un descriptor de acceso de propiedad, al llamar a este método se devolverá el descriptor de acceso de propiedad (una interfaz IModelPropertyAccessor) incluida en un IModelObject. A diferencia de GetKeyValue, este método no resolverá automáticamente el valor subyacente de la clave llamando al método GetValue. Es responsabilidad del llamador.
El método SetKey es el método al que un cliente recurrirá para crear una clave en un objeto (y posiblemente asociar metadatos a la clave creada). Si un objeto determinado ya tiene una clave con el nombre especificado, se producirá uno de los dos comportamientos. Si la clave está en la instancia que indica, el valor de esa clave se reemplazará como si la clave original no existiera. Si, por otro lado, la clave está en la cadena de modelos de datos primarios de la instancia indicada, se creará una nueva clave con el nombre especificado en la instancia indicada. Esto provocaría, en efecto, que el objeto tuviera dos claves del mismo nombre (similar a una clase derivada que sombrea un miembro del mismo nombre que una clase base).
El método EnumerateKeys se comporta de forma similar al método EnumerateKeyValues, con la salvedad de que no resuelve automáticamente los descriptores de acceso de propiedad en el objeto. Esto significa que si el valor de una clave es un descriptor de acceso de propiedad, el método EnumerateKeys devolverá el descriptor de acceso de propiedad (un IModelPropertyAccessorInterface) incluido en un IModelObject en lugar de llamar automáticamente al método GetValue. Esto es similar a la diferencia entre GetKey y GetKeyValue.
El método ClearKeys quita todas las claves y sus valores y metadatos asociados de la instancia del objeto especificado por este. Este método no tiene ningún efecto en los modelos primarios adjuntos a la instancia de objeto determinada.
El método GetKeyReference buscará una clave del nombre especificado en el objeto (o su cadena de modelos primaria) y devolverá una referencia a esa clave indicada mediante una interfaz IModelKeyReference incluida en un IModelObject. Esa referencia se puede usar posteriormente para obtener o establecer el valor de la clave.
El método EnumerateKeyReferences se comporta de forma similar al método EnumerateKeyValues, con la salvedad de que devuelve referencias a las claves que enumera (indicadas por una interfaz IModelKeyReference incluida en un IModelObject) en lugar del valor de la clave. Estas referencias se pueden usar para obtener o establecer el valor subyacente de las claves.
Métodos de manipulación de conceptos
Además de que un objeto de modelo es un diccionario de tuplas de clave/valor/metadatos, también es un contenedor de conceptos. Un concepto es algo abstracto que se puede realizar en un objeto o mediante este. Los conceptos son, en esencia, un almacén dinámico de interfaces que admite un objeto. En la actualidad, el modelo de datos define varios conceptos:
Interfaz de concepto | Descripción |
---|---|
IDataModelConcept | El concepto es un modelo primario. Si este modelo se adjunta automáticamente a un tipo nativo a través de una firma de tipo registrado, se llamará automáticamente al método InitializeObject cada vez que se cree una instancia de un nuevo objeto de este tipo. |
IStringDisplayableConcept | El objeto se puede convertir en una cadena con fines de visualización. |
IIterableConcept | El objeto es un contenedor y se puede iterar. |
IIndexableConcept | El objeto es un contenedor y se puede indexar (al que se accede a través del acceso aleatorio) en una o varias dimensiones. |
IPreferredRuntimeTypeConcept | El objeto entiende más sobre los tipos derivados de él de lo que el sistema de tipos subyacente es capaz de proporcionar y desea controlar sus propias conversiones de tipo estático a tipo en tiempo de ejecución. |
IDynamicKeyProviderConcept | El objeto es un proveedor dinámico de claves y desea asumir todas las consultas de clave del modelo de datos principal. Esta interfaz se usa normalmente como puente a lenguajes dinámicos como JavaScript. |
IDynamicConceptProviderConcept | El objeto es un proveedor dinámico de conceptos y desea asumir todas las consultas de concepto del modelo de datos principal. Esta interfaz se usa normalmente como puente a lenguajes dinámicos como JavaScript. |
Los métodos siguientes en IModelObject se usan para manipular los conceptos que admite un objeto.
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;
El método GetConcept buscará un concepto en el objeto (o su cadena de modelos primario) y devolverá un puntero de interfaz a la interfaz de concepto. El comportamiento y los métodos de una interfaz de concepto son específicos de cada concepto. Sin embargo, es importante tener en cuenta que muchas de las interfaces de concepto requieren que el llamador pase explícitamente el objeto de contexto (o lo que se podría llamar tradicionalmente al puntero "this"). Es importante asegurarse de que el objeto de contexto correcto se pasa a cada interfaz de concepto.
El método SetConcept colocará un concepto especificado en la instancia de objeto especificada por el puntero "this". Si un modelo primario adjunto a la instancia de objeto especificada por esto también admite el concepto, la implementación de la instancia invalidará la del modelo primario.
El método ClearConcepts quitará todos los conceptos de la instancia del objeto especificado por este.
Métodos de objeto nativos
Aunque muchos objetos de modelo hacen referencia a elementos intrínsecos (por ejemplo: enteros, cadenas) o construcciones sintéticas (un diccionario de tuplas de clave/valor/metadatos y conceptos), un objeto de modelo también puede hacer referencia a una construcción nativa (por ejemplo: un tipo definido por el usuario en el espacio de direcciones del destino de depuración). La interfaz IModelObject tiene una serie de métodos que acceden a información sobre estos objetos nativos. Estos métodos son:
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;
El método GetRawValue busca una construcción nativa dentro del objeto especificado. Esta construcción puede ser un campo, una clase base, un campo en una clase base, una función miembro, etc.
El método EnumerateRawValues enumera todos los elementos secundarios nativos (por ejemplo: campos, clases base, etc.) del objeto especificado.
El método TryCastToRuntimeType pedirá al host de depuración que realice un análisis y determine el tipo en tiempo de ejecución real (por ejemplo: la mayoría de las clases derivadas) del objeto especificado. El análisis exacto utilizado es específico del host de depuración y puede incluir RTTI (información de tipo de tiempo de ejecución de C++), examen de la estructura V-Table (tabla de funciones virtuales) del objeto, o cualquier otro medio que el host pueda usar para determinar de forma confiable el tipo dinámico o en tiempo de ejecución desde el tipo estático. Si no se realiza la conversión a un tipo en tiempo de ejecución, no significa que se producirá un error en esta llamada al método. En tales casos, el método devolverá el objeto especificado (el puntero "this") en el argumento de salida.
El método GetLocation devolverá la ubicación del objeto nativo. Aunque esta ubicación suele ser una dirección virtual dentro del espacio de direcciones del destino de depuración, no tiene que ser necesariamente así. La ubicación devuelta por este método es una ubicación abstracta que puede ser una dirección virtual, puede indicar la ubicación dentro de un registro o un subregistro, o puede indicar algún otro espacio de direcciones arbitrario según lo definido por el host de depuración. Si el campo HostDefined del objeto Location resultante es 0, indica que la ubicación es realmente una dirección virtual. Dicha dirección virtual se puede recuperar examinando el campo Offset de la ubicación resultante. Cualquier valor distinto de cero del campo HostDefined indica un espacio de direcciones alternativo donde el campo Offset es el desplazamiento dentro de ese espacio de direcciones. El significado exacto de los valores HostDefined distintos de cero es privado para el host de depuración.
El método GetTypeInfo devolverá el tipo nativo del objeto especificado. Si el objeto no tiene información de tipo nativo asociada (por ejemplo, es intrínseco, etc.), la llamada seguirá siendo correcta, pero devolverá null.
El método GetTargetInfo es una combinación eficaz de los métodos GetLocation y GetTypeInfo que devuelven tanto la ubicación abstracta como el tipo nativo del objeto especificado.
El método GetRawReference busca una construcción nativa dentro del objeto especificado y devuelve una referencia a él. Esta construcción puede ser un campo, una clase base, un campo en una clase base, una función miembro, etc. Es importante distinguir la referencia devuelta (un objeto del tipo ObjectTargetObjectReference) de una referencia de lenguaje (por ejemplo, una referencia de C++ & o de estilo &&).
El método EnumerateRawReferences enumera las referencias a todos los elementos secundarios nativos (por ejemplo: campos, clases base, etc.) del objeto especificado.
Métodos de extensibilidad
Como se ha descrito anteriormente, un objeto de modelo se comporta de forma muy similar a un objeto de JavaScript y su cadena de prototipos. Además de la instancia representada por una interfaz IModelObject determinada, puede haber un número arbitrario de modelos primarios adjuntos al objeto (cada uno de los cuales puede, a su vez, tener un número arbitrario de modelos primarios adjuntos). Este es el medio principal para la extensibilidad dentro del modelo de datos. Si una propiedad o concepto determinado no se puede ubicar dentro de una instancia determinada, se realiza una búsqueda en profundidad del árbol de objetos (definido por los modelos primarios) con raíz en la instancia.
Los métodos siguientes manipulan la cadena de modelos primarios asociados a una instancia IModelObject determinada:
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;
El método GetNumberOfParentModels devuelve el número de modelos primarios adjuntados a la instancia de objeto especificada. Las propiedades de los modelos primarios se buscan primero en profundidad siguiendo el orden lineal de la cadena de modelos primarios.
El método GetParentModel devuelve el modelo primario i-th en la cadena del modelo primario del objeto especificado. Las propiedades o conceptos de los modelos primarios se buscan siguiendo el orden lineal en el que se agregaron o enumeraron. Se busca en el modelo primario con el índice i de cero (jerárquicamente) antes de hacerlo en el modelo primario con el índice i + 1.
El método AddParentModel agrega un nuevo modelo primario al objeto especificado. Este modelo se puede agregar al final de la cadena de búsqueda (el argumento override se especifica como false) o en la parte frontal de la cadena de búsqueda (el argumento override se especifica como true). Además, cada modelo primario puede ajustar opcionalmente el contexto (la semántica del puntero "this") para cualquier propiedad o concepto en el elemento primario indicado (o cualquiera de su jerarquía primaria). El ajuste de contexto rara vez se usa, pero permite algunos conceptos eficaces como la inserción de objetos, la construcción de espacios de nombres, etc...
RemoveParentModel quitará un modelo primario especificado de la cadena de búsqueda primaria del objeto especificado.
La implementación de un modelo de datos usa el método SetContextForDataModel para colocar datos de implementación en objetos de instancia. Conceptualmente, cada IModelObject (lo llamamos instancia para simplificar) contiene una tabla hash de estado. La tabla hash se indexa mediante otro IModelObject (lo llamamos el modelo de datos para simplificar) que se encuentra en la jerarquía del modelo primario de la instancia. El valor contenido en este hash es un conjunto de información de estado con recuento de referencia representada por una instancia IUnknown. Una vez que el modelo de datos establece este estado en la instancia, puede almacenar datos arbitrarios de implementación que se pueden recuperar durante cosas como captadores de propiedades.
El método GetContextForDataModel se usa para recuperar información de contexto que se configuró con una llamada anterior a SetContextForDataModel. Recupera la información de estado establecida en un objeto instancia por un modelo de datos situado más arriba en la jerarquía del modelo primario del objeto de la instancia. Para obtener más información sobre este contexto/estado y su significado, consulte la documentación de SetContextForDataModel.
Tipos de objetos principales del modelo de datos del depurador
Un objeto del modelo de datos es similar a la noción de Object en .NET. Es el contenedor genérico en el que se puede colocar el modelo de datos. Además de los objetos nativos y los objetos sintéticos (dinámicos), hay una serie de tipos de objetos principales que se pueden colocar (o incluir) en el contenedor de un IModelObject. El contenedor en el que se colocan la mayoría de estos valores es una COM/OLE VARIANT estándar con una serie de restricciones adicionales que se aplican a lo que puede contener VARIANT. Los tipos más básicos son:
- Valores sin signo y con signo de 8 bits (VT_UI1, VT_I1)
- Valores sin signo y con signo de 16 bits (VT_UI2, VT_UI2)
- Valores sin signo y con signo de 32 bits (VT_UI4, VT_I4)
- Valores sin signo y con signo de 64 bits (VT_UI8, VT_I8)
- Valores de punto flotante de precisión simple y doble (VT_R4, VT_R8)
- Cadenas (VT_BSTR)
- Booleanos (VT_BOOL)
Además de estos tipos básicos, se colocan varios objetos de modelo de datos principales en IModelObject definidos por VT_UNKNOWN donde se garantiza que el IUnknown almacenado implemente una interfaz específica. Estos tipos son:
- Descriptores de acceso de propiedad (IModelPropertyAccessor)
- Objetos de método (IModelMethod)
- Objetos de referencia de clave (IModelKeyReference o IModelKeyReference2)
- Objetos de contexto (IDebugModelHostContext)
Descriptores de acceso de propiedad: IModelPropertyAccessor
Un descriptor de acceso de propiedad en el modelo de datos es una implementación de la interfaz IModelPropertyAccessor que se incluye en un IModelObject. El objeto de modelo devolverá un tipo de ObjectPropertyAccessor cuando se consulte y el valor intrínseco es un VT_UNKNOWN que se garantiza que se pueda consultar para IModelPropertyAccessor. En proceso, se garantiza que se pueda convertir estáticamente a IModelPropertyAccessor.
Un descriptor de acceso de propiedad es una manera indirecta de obtener una llamada al método para obtener y establecer un valor de clave en el modelo de datos. Si el valor de una clave determinada es un descriptor de acceso de propiedad, los métodos GetKeyValue y SetKeyValue se darán cuenta automáticamente y llamarán a los métodos GetValue o SetValue subyacentes del descriptor de acceso de la propiedad según corresponda.
La interfaz IModelPropertyAccessor se define de la manera siguiente:
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;
}
El método GetValue es el captador del descriptor de acceso de propiedad. Se llama cada vez que un cliente desea capturar el valor subyacente de la propiedad. Tenga en cuenta que cualquier llamador que obtiene directamente un descriptor de acceso de propiedad es responsable de pasar el nombre de clave y el objeto de instancia preciso (puntero "this") al método GetValue del descriptor de acceso de propiedad.
El método SetValue es el establecedor del descriptor de acceso de propiedad. Se llama cada vez que un cliente desea asignar un valor a la propiedad subyacente. Muchas propiedades son de solo lectura. En tales casos, la llamada al método SetValue devolverá E_NOTIMPL. Tenga en cuenta que cualquier llamador que obtiene directamente un descriptor de acceso de propiedad es responsable de pasar el nombre de clave y el objeto de instancia preciso (puntero "this") al método SetValue del descriptor de acceso de propiedad.
Métodos: IModelMethod
Un método en el modelo de datos es una implementación de la interfaz IModelMethod que se incluye en un IModelObject. El objeto de modelo devolverá un tipo de ObjectMethod cuando se consulte y el valor intrínseco es un VT_UNKNOWN que se garantiza que se pueda consultar para IModelMethod. En proceso, se garantiza que se pueda convertir estáticamente a IModelMethod. Todos los métodos del modelo de datos son dinámicos por naturaleza. Toman como entrada un conjunto de 0 o más argumentos y devuelven un único valor de salida. No hay ninguna resolución de sobrecarga y no hay metadatos sobre nombres de parámetros, tipos o expectativas.
La interfaz IModelMethod se define de la siguiente manera:
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;
}
El método Call es la forma en que se invoca cualquier método definido en el modelo de datos. El llamador es responsable de pasar un objeto de instancia preciso (puntero "this") y un conjunto arbitrario de argumentos. Se devuelve el resultado del método y cualquier metadato opcional asociado a ese resultado. Los métodos que no devuelven lógicamente un valor deben devolver un IModelObject válido. En tal caso, el IModelObject es un elemento sin valor incluido. En caso de que se produzca un error en un método, puede devolver información de error extendida opcional en el argumento de entrada (incluso si HRESULT devuelto es un error). Es imperativo que los llamadores lo comprueben.
Referencias de clave: IModelKeyReference o IModelKeyReference2
Una referencia de clave es, en esencia, un identificador de una clave en un objeto determinado. Un cliente puede recuperar este identificador a través de métodos como GetKeyReference y usar el identificador más adelante para obtener o establecer el valor de la clave sin necesidad de conservar el objeto original. Este tipo de objeto es una implementación de la interfaz IModelKeyReference o IModelKeyReference2 que se incluye en un IModelObject. El objeto de modelo devolverá un tipo de ObjectKeyReference cuando se consulte y el valor intrínseco es un VT_UNKNOWN que se garantiza que se pueda consultar para IModelKeyReference. En proceso, se garantiza que se pueda convertir estáticamente a IModelKeyReference.
La interfaz de referencia de clave se define de la manera siguiente:
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;
}
El método GetKeyName devuelve el nombre de la clave para la que esta referencia de clave es un identificador. La cadena devuelta es un BSTR estándar y debe liberarse a través de una llamada a SysFreeString.
El método GetOriginalObject devuelve el objeto de instancia desde el que se creó la referencia de clave. Tenga en cuenta que la clave puede estar en un modelo primario del objeto de instancia.
El método GetContextObject devuelve el contexto (puntero "this") que se pasará al método GetValue o SetValue de un descriptor de acceso de propiedad si la clave en cuestión hace referencia a un descriptor de acceso de propiedad. El objeto de contexto devuelto puede o no ser el mismo que el objeto original capturado de GetOriginalObject. Si una clave está en un modelo primario y hay un ajustador de contexto asociado a ese modelo primario, el objeto original es el objeto de instancia en el que se llamó a GetKeyReference o EnumerateKeyReferences. El objeto de contexto sería lo que salga del ajustador de contexto final entre el objeto original y el modelo primario que contiene la clave para la que esta referencia de clave es un identificador. Si no hay ningún ajustador de contexto, el objeto original y el objeto de contexto son idénticos.
El método GetKey de una referencia de clave se comporta como lo haría el método GetKey en IModelObject. Devuelve el valor de la clave subyacente y los metadatos asociados a la clave. Si el valor de la clave es un descriptor de acceso de propiedad, devolverá el descriptor de acceso de propiedad (IModelPropertyAccessor) incluido en un IModelObject. Este método no llamará a los métodos GetValue o SetValue subyacentes en el descriptor de acceso de la propiedad.
El método GetKeyValue en una referencia de clave se comporta como lo haría el método GetKeyValue en IModelObject. Devuelve el valor de la clave subyacente y los metadatos asociados a la clave. Si el valor de la clave es un descriptor de acceso de propiedad, se llamará automáticamente al método GetValue subyacente en el descriptor de acceso de propiedad.
El método SetKey de una referencia de clave se comporta como lo haría el método SetKey en IModelObject. Asignará el valor de la clave. Si la clave original era un descriptor de acceso de propiedad, reemplazará el descriptor de acceso de propiedad. No llamará al método SetValue en el descriptor de acceso de propiedad.
El método SetKeyValue en una referencia de clave se comporta como lo haría el método SetKeyValue en IModelObject. Asignará el valor de la clave. Si la clave original era un descriptor de acceso de propiedad, llamará al método SetValue subyacente en el descriptor de acceso de propiedad en lugar de reemplazar el propio descriptor de acceso de propiedad.
El método OverrideContextObject (solo presente en IModelKeyReference2) es un método avanzado que se usa para modificar permanentemente el objeto de contexto que esta referencia de clave pasará a los métodos GetValue o SetValue de cualquier descriptor de acceso de propiedad subyacente. El objeto pasado a este método también se devolverá desde una llamada a GetContextObject. Los proveedores de scripts pueden usar este método para replicar determinados comportamientos de lenguaje dinámico. La mayoría de los clientes no deben llamar a este método.
Objetos de contexto: IDebugHostContext
Los objetos de contexto son blobs opacos de información que el host de depuración (en cooperación con el modelo de datos) asocia a cada objeto. Puede incluir elementos como el contexto del proceso o el espacio de direcciones del que procede la información, etc. Un objeto de contexto es una implementación de IDebugHostContext incluido en un IModelObject. Tenga en cuenta que IDebugHostContext es una interfaz definida por host. Un cliente nunca implementará esta interfaz.
Para obtener más información sobre los objetos de contexto, consulte Interfaces de host de C++ del modelo de datos del depurador en Interfaces de C++ del modelo de datos del depurador.
El administrador del modelo de datos
La interfaz principal del administrador del modelos de datos, IDataModelManager2 (o el IDataModelManager anterior) se define de la siguiente manera:
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 administración
La aplicación utiliza el siguiente conjunto de métodos (por ejemplo: depurador) que hospeda el modelo de datos.
STDMETHOD(Close)() PURE;
Una aplicación llama al método Close en el administrador del modelos de datos (por ejemplo, depurador) que hospeda el modelo de datos para iniciar el proceso de apagado del administrador del modelos de datos. Un host del modelo de datos que no es el método Close antes de publicar su referencia final en el administrador del modelos de datos puede provocar un comportamiento no definido, incluidos, entre otros, pérdidas significativas de la infraestructura de administración para el modelo de datos.
Métodos de creación/inclusión de objetos
El siguiente conjunto de métodos se usa para crear nuevos objetos o para incluirlos en un IModelObject, la interfaz principal del modelo de datos.
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;
El método CreateNoValue crea un objeto "sin valor", lo incluye en un IModelObject y lo devuelve. El objeto de modelo devuelto tiene un tipo de ObjectNoValue.
Un objeto "sin valor" tiene varios significados semánticos:
- (Dependiendo del lenguaje), se puede considerar el equivalente semántico de void, null o undefined
- Cualquier método GetValue del descriptor de acceso de propiedad que devuelve un resultado correcto y un objeto "sin valor" resultante indica que la propiedad concreta no tiene ningún valor para la instancia determinada y se debe tratar como si la propiedad no existiera para esa instancia determinada.
- Los métodos del modelo de datos que no tienen semánticamente un valor devuelto usan este valor como centinela para indicarlo (ya que un método debe devolver un IModelObject válido).
El método CreateErrorObject crea un "objeto de error". El modelo de datos no tiene la noción de excepciones y flujo de excepciones. El error sale de una propiedad/método de dos maneras:
- Un único HRESULT con error único sin información de error extendida. O bien no hay más información que se pueda dar para el error o el propio error se explica por sí mismo a partir del HRESULT devuelto.
- Un único HRESULT con error único con información de error extendida. La información de error extendida es un objeto de error devuelto en el argumento de salida de la propiedad/método.
El método CreateTypedObject es el método que permite a un cliente crear una representación de un objeto nativo o de lenguaje en el espacio de direcciones de un destino de depuración. Si el tipo del objeto recién creado (como se indica en el argumento objectType) coincide con una o varias firmas de tipo registradas con el administrador del modelos de datos como visualizadores canónicos o extensiones, esos modelos de datos coincidentes se asociarán automáticamente al objeto de instancia creado antes de que se devuelva al llamador.
El método CreateTypedObjectReference es semánticamente similar al método CreateTypedObject, con la salvedad de que crea una referencia a la construcción de un objeto de lenguaje o nativo subyacente. La referencia creada es un objeto que tiene un tipo de ObjectTargetObjectReference. No es una referencia nativa como la que puede admitir el lenguaje subyacente (por ejemplo: C++ & o &&). Es posible tener una ObjectTargetObjectReference a una referencia de C++. Un objeto de tipo ObjectTargetObjectReference se puede convertir al valor subyacente mediante el uso del método Dereference en IModelObject. La referencia también se puede pasar al evaluador de expresiones del host subyacente para volver a asignar el valor de forma adecuada al lenguaje.
El método CreateSyntheticObject crea un objeto de modelo de datos vacío: un diccionario de tuplas de clave/valor/metadatos y conceptos. En el momento de la creación, no hay claves ni conceptos en el objeto. Es una careta limpia para que el llamador la utilice.
El método CreateDataModelObject es un contenedor auxiliar sencillo para crear objetos que son modelos de datos, es decir, que se van a asociar como modelos primarios a otros objetos. Todos estos objetos deben admitir el concepto del modelo de datos a través de IDataModelConcept. Este método crea un nuevo objeto sintético en blanco sin contexto explícito y agrega el IDataModelConcept transferido como la implementación del objeto recién creado del concepto del modelo de datos. Esto se puede realizar de forma similar con llamadas a CreateSyntheticObject y SetConcept.
El método CreateIntrinsicObject es el método que incluye los valores intrínsecos en IModelObject. El llamador coloca el valor en una COM VARIANT y llama a este método. El administrador del modelos de datos devuelve un IModelObject que representa el objeto. Tenga en cuenta que este método también se usa para incluir tipos básicos basados en IUnknown: descriptores de acceso de propiedad, métodos, contextos, etc. En tales casos, el método objectKind indica qué tipo de construcción basada en IUnknown representa el objeto y el campo punkVal de la variante pasada es el tipo derivado IUnknown. El tipo debe convertirse estáticamente en la interfaz de modelo adecuada (por ejemplo: IModelPropertyAccessor, IModelMethod, IDebugHostContext, etc.) en proceso. Los tipos VARIANT admitidos por este método son VT_UI1, VT_I1, VT_UI2, VT_I2, VT_UI4, VT_I4, VT_UI8, VT_I8, VT_R4, VT_R8, VT_BOOL, VT_BSTR y VT_UNKNOWN (para un conjunto especializado de tipos derivados de IUnknown como se indica en la enumeración ModelObjectKind.
El método CreateTypedintrinsicObject es similar al método CreateIntrinsicObject, con la salvedad de que permite asociar un tipo nativo o de lenguaje a los datos y llevarlo junto con el valor incluido. Esto permite que el modelo de datos represente construcciones como tipos de enumeración nativos (que simplemente son valores VT_UI* o VT_I*). Los tipos de puntero también se crean con este método. Un puntero nativo en el modelo de datos es una cantidad extendida de 64 bits cero que representa un desplazamiento en el espacio de direcciones virtuales del destino de depuración. Se incluye dentro de un VT_UI8 y se crea con este método y un tipo que indica un puntero nativo o de lenguaje.
El método CreateMetadataStore crea un almacén de claves (un contenedor simplificado de tuplas de clave/valor/metadatos), que se usa para contener metadatos que se pueden asociar a propiedades y una variedad de otros valores. Un almacén de metadatos puede tener un único elemento primario (que, a su vez, puede tener un único elemento primario). Si una clave de metadatos determinada no se encuentra en un almacén determinado, se comprueban sus elementos primarios. La mayoría de los almacenes de metadatos no tienen elementos primarios. Sin embargo, proporcionan una manera de compartir fácilmente metadatos comunes.
El método CreateTypedIntrinsicObjectEx es semánticamente similar al método CreateTypedIntrinsicObject. La única diferencia entre los dos es que este método permite al llamador especificar el contexto en el que los datos intrínsecos son válidos. Si no se pasa ningún contexto, los datos se consideran válidos en cualquier contexto que se herede del argumento type (cómo se comporta CreateTypedIntrinsicObject). Esto permite crear valores de puntero con tipo en el destino de depuración que requieren un contexto más específico del que se puede heredar del tipo.
Métodos de extensibilidad y registro El siguiente conjunto de métodos administra el mecanismo de extensibilidad del modelo de datos, lo que permite a un cliente ampliar o registrar modelos existentes o pedir al modelo de datos que asocie automáticamente un modelo primario determinado en tipos nativos que coincidan con un criterio determinado.
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;
El método GetModelForTypeSignature devuelve el modelo de datos registrado en una firma de tipo determinada a través de una llamada anterior al método RegisterModelForTypeSignature. El modelo de datos devuelto desde este método se considera el visualizador canónico para cualquier tipo que coincida con la firma de tipo pasada. Como visualizador canónico, ese modelo de datos toma el control de la visualización del tipo. Los motores de visualización ocultarán de forma predeterminada construcciones nativas o de lenguaje del objeto en favor de la vista del objeto presentado por el modelo de datos.
El método GetModelForType devuelve el modelo de datos que es el visualizador canónico de una instancia de tipo determinada. En efecto, este método busca la mejor firma de tipo coincidente que se registró con una llamada anterior al método RegisterModelForTypeSignature y devuelve el modelo de datos asociado.
El método RegisterModelForTypeSignature es el método principal que un llamador utiliza para registrar un visualizador canónico para un tipo determinado (o conjunto de tipos). Un visualizador canónico es un modelo de datos que, en efecto, toma el control de la visualización de un tipo determinado (o conjunto de tipos). En lugar de la vista nativa o de lenguaje del tipo que se muestra en cualquier interfaz de usuario del depurador, se muestra la vista del tipo tal y como la presenta el modelo de datos registrado (junto con un medio para volver a la vista nativa o de lenguaje para el usuario que lo desee).
UnregisterModelForTypeSignature
El método UnregisterModelForTypeSignature deshace una llamada anterior al método RegisterModelForTypeSignature. Este método puede quitar un modelo de datos determinado como visualizador canónico para tipos que coincidan con una firma de tipo determinada o puede quitar un modelo de datos determinado como visualizador canónico para cada firma de tipo en la que se registra ese modelo de datos.
RegisterExtensionForTypeSignature
El método RegisterExtensionForTypeSignature es similar al método RegisterModelForTypeSignature con una diferencia clave. El modelo de datos que se pasa a este método no es el visualizador canónico de ningún tipo y no se ocupará de mostrar la vista nativa o de lenguaje de ese tipo. El modelo de datos que se pasa a este método se agregará automáticamente como elemento primario a cualquier tipo concreto que coincida con la firma de tipo proporcionada. A diferencia del método RegisterModelForTypeSignature, no hay ningún límite en cuanto a las firmas de tipo idénticas o ambiguas que se registran como extensiones en un tipo determinado (o conjunto de tipos). Cada extensión cuya firma de tipo coincida con una instancia de tipo concreto determinada hará que el modelo de datos registrado a través de este método se asocie automáticamente a objetos recién creados como modelos primarios. Esto, en efecto, permite que un número arbitrario de clientes extienda un tipo (o conjunto de tipos) con nuevos campos o funcionalidades.
UnregisterExtensionForTypeSignature
El método UnregisterExtensionForTypeSignature deshace una llamada anterior a RegisterExtensionForTypeSignature. Anula el registro de un modelo de datos determinado como una extensión para una firma de tipo determinada o como una extensión para todas las firmas de tipo en las que se registró el modelo de datos.
El método GetRootNamespace devuelve el espacio de nombres raíz del modelo de datos. Se trata de un objeto que administra el modelo de datos y en el que el host de depuración coloca determinados objetos.
El método RegisterNamedModel registra un modelo de datos determinado bajo un nombre conocido para que los clientes que deseen ampliarlo puedan encontrarlo. Este es el propósito principal de la API: publicar un modelo de datos como algo que se puede ampliar recuperando el modelo registrado bajo este nombre conocido y agregando un modelo primario.
El método UnregisterNamedModel deshace una llamada anterior a RegisterNamedModel. Quita la asociación entre un modelo de datos y un nombre con el que se puede buscar.
Un llamador que desea extender un modelo de datos que está registrado con un nombre determinado llama al método AcquireNamedModel para recuperar el objeto del modelo de datos que desea extender. Este método devolverá el modelo de datos registrado a través de una llamada anterior al método RegisterNamedModel. Como objetivo principal del método AcquireNamedModel es ampliar el modelo, este método tiene un comportamiento especial si aún no se ha registrado ningún modelo con el nombre especificado. Si aún no se ha registrado ningún modelo con el nombre especificado, se crea un objeto de código auxiliar, se registra temporalmente con el nombre especificado y se devuelve al llamador. Cuando el modelo de datos real se registra a través de una llamada al método RegisterNamedModel, cualquier cambio que se haya realizado en el objeto objeto de código auxiliar se realiza, en efecto, en el modelo real. Esto quita muchos problemas de dependencia de orden de carga de los componentes que se extienden entre sí.
métodos del asistente
Los métodos siguientes son métodos auxiliares generales que ayudan a realizar operaciones complejas en objetos del modelo de datos. Aunque es posible realizar estas acciones a través de otros métodos en el modelo de datos o sus objetos, estos métodos de conveniencia lo facilitan considerablemente:
STDMETHOD(AcquireSubNamespace)(_In_ PCWSTR modelName, _In_ PCWSTR subNamespaceModelName, _In_ PCWSTR accessName, _In_opt_ IKeyStore *metadata, _COM_Outptr_ IModelObject **namespaceModelObject) PURE;
El método AcquireSubNamespace ayuda en la construcción de algo que podría parecerse más tradicionalmente a un espacio de nombres de lenguaje que a un nuevo objeto en un lenguaje dinámico. Si, por ejemplo, un llamador desea clasificar las propiedades de un objeto de proceso para que el objeto de proceso esté más organizado y las propiedades sean más fáciles de detectar, un método de hacerlo sería crear un subobjeto para cada categoría en el objeto de proceso y colocar esas propiedades dentro de ese objeto.
Vea también
Este tema forma parte de una serie que describe las interfaces accesibles desde C++, cómo usarlas para compilar una extensión de depurador basada en C++ y cómo usar otras construcciones de modelo de datos (por ejemplo: JavaScript o NatVis) desde una extensión de modelo de datos de C++.
Información general sobre el modelo de datos del depurador de C++
Interfaces del modelo de datos del depurador de C++
Interfaces adicionales del modelo de datos del depurador de C++