Oggetti C++ del modello di dati del debugger
In questo argomento viene descritto come usare oggetti C++ del modello di dati del debugger e come possono estendere le funzionalità del debugger.
Modello a oggetti del debugger di base
Uno degli elementi più semplici ma potenti del modello di dati è che standardizza la definizione di ciò che un oggetto è e come interagisce con un oggetto. L'interfaccia IModelObject incapsula la nozione di oggetto, indipendentemente dal fatto che l'oggetto sia un numero intero, un valore a virgola mobile, una stringa, un tipo complesso nello spazio degli indirizzi di destinazione del debugger o un concetto del debugger, ad esempio il concetto di un processo o di un modulo.
Esistono diversi elementi che possono essere mantenuti in (o boxed in) in un IModelObject:
Valori intrinseci: un oggetto IModelObject può essere un contenitore per diversi tipi di base: 8, 16, 32 o interi senza segno a 64 bit, valori booleani, stringhe, errori o la nozione di valori vuoti.
Oggetti nativi: un oggetto IModelObject può rappresentare un tipo complesso (come definito dal sistema di tipi del debugger) all'interno dello spazio degli indirizzi di qualsiasi destinazione del debugger
Oggetti sintetici - Un oggetto IModelObject può essere un oggetto dinamico- un dizionario se si vuole: una raccolta di tuple chiave/valore/metadati e un set di concetti che definiscono comportamenti che non sono semplicemente rappresentati da coppie chiave/valore.
Proprietà: un oggetto IModelObject può rappresentare una proprietà: un elemento il cui valore può essere recuperato o modificato con una chiamata al metodo. Una proprietà all'interno di un IModelObject è effettivamente un'interfaccia IModelPropertyAccessor boxed in un IModelObject
Metodi : un oggetto IModelObject può rappresentare un metodo: un oggetto che è possibile chiamare con un set di argomenti e ottenere un valore restituito. Un metodo all'interno di un IModelObject è effettivamente un'interfaccia IModelMethod boxed in un IModelObject
Estendibilità all'interno del modello a oggetti
Un IModelObject non è un oggetto in isolamento. Oltre a rappresentare uno dei tipi di oggetti illustrati in precedenza, ogni oggetto ha la nozione di una catena di modelli di dati padre. Questa catena si comporta in modo molto simile a una catena di prototipi JavaScript. Anziché una catena lineare di prototipi come JavaScript, ogni oggetto modello di dati definisce una catena lineare di modelli padre. Ognuno di questi modelli padre a sua volta ha un'altra catena lineare del proprio set di elementi padre. In sostanza, ogni oggetto è un'aggregazione delle funzionalità (proprietà e così via...) di se stesso e di ogni oggetto in questo albero. Quando viene eseguita una query su una proprietà specifica, se l'oggetto su cui viene eseguita una query non supporta tale proprietà, la query viene passata in modo lineare per ogni elemento padre a sua volta. In questo modo viene creato un comportamento in cui la ricerca di una proprietà viene risolta da una ricerca depth-first dell'albero di aggregazione.
L'estendibilità all'interno di questo modello a oggetti è molto semplice dato che ogni oggetto è un'aggregazione di se stessa e l'albero dei modelli padre. Un'estensione può entrare e aggiungersi all'elenco dei modelli padre per un altro oggetto. In questo modo l'oggetto viene esteso . In questo modo, è possibile aggiungere funzionalità a qualsiasi elemento: una particolare istanza di un oggetto o valore, un tipo nativo, il concetto del debugger di ciò che un processo o un thread è o anche la nozione di "tutti gli oggetti iterabili".
Contesto, contesto e contesto: puntatore , spazio indirizzi e dati privati di implementazione
Esistono tre nozioni di contesto che sono necessarie per comprendere all'interno del contesto del modello a oggetti.
Contesto: puntatore
Poiché una determinata proprietà o metodo può essere implementata a qualsiasi livello dell'albero del modello di dati, è necessario che l'implementazione del metodo o della proprietà sia in grado di accedere all'oggetto originale (ciò che è possibile chiamare il puntatore in C++ o l'oggetto in JavaScript). L'oggetto istanza viene passato a un'ampia gamma di metodi come primo argomento denominato contesto nei metodi descritti.
Contesto: spazio indirizzi
È importante notare che a differenza dei modelli di estensione precedenti in cui il contesto (destinazione, processo, thread che si sta esaminando) è un concetto di interfaccia utente con tutte le API relative allo stato corrente dell'interfaccia utente, le interfacce del modello di dati in genere accettano questo contesto in modo esplicito o implicito come interfaccia IDebugHostContext . Ogni oggetto IModelObject all'interno del modello di dati contiene questo tipo di informazioni di contesto insieme a esso e può propagare tale contesto agli oggetti restituiti. Ciò significa che quando si legge un valore nativo o un valore di chiave fuori da un oggetto IModelObject, leggerà la destinazione ed eseguirà il processo da cui l'oggetto è stato originariamente acquisito.
Esiste un valore costante esplicito, U edizione Standard_CURRENT_HOST_CONTEXT, che può essere passato ai metodi che accettano un argomento IDebugHostContext. Questo valore indica che il contesto deve effettivamente essere lo stato corrente dell'interfaccia utente del debugger. Questa nozione, tuttavia, deve essere esplicita.
Contesto: implementazione di dati privati
Tenere presente che ogni oggetto nel modello di dati è in realtà un'aggregazione dell'istanza dell'oggetto e l'albero dei modelli padre associati. Ognuno di questi modelli padre (che può essere collegato nelle catene di molti oggetti diversi) può associare dati di implementazione privata a qualsiasi oggetto istanza. Ogni IModelObject creato concettualmente ha una tabella hash che esegue il mapping da un modello padre specifico ai dati dell'istanza privata definiti da un'interfaccia IUnknown . Ciò consente a un modello padre di memorizzare nella cache le informazioni su ogni istanza o di avere dati altrimenti arbitrari associati.
Questo tipo di contesto è accessibile tramite i metodi GetContextForDataModel e SetContextForDataModel in IModelObject.
Interfaccia oggetto del debugger principale: IModelObject
L'interfaccia IModelObject è definita come segue:
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;
}
Metodi di base
Di seguito sono riportati i metodi generali applicabili a qualsiasi tipo di oggetto rappresentato da un oggetto 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;
Il metodo GetKind restituisce il tipo di oggetto sottoposto a boxing all'interno di IModelObject.
Il metodo GetContext restituisce il contesto host associato all'oggetto .
Il metodo GetIntrinsicValue restituisce l'elemento sottoposto a boxing all'interno di un oggetto IModelObject. Questo metodo può essere chiamato legalmente solo sulle interfacce IModelObject che rappresentano un intrinseco boxed o una particolare interfaccia che viene boxed. Non è possibile chiamare oggetti nativi, oggetti valore, oggetti sintetici e oggetti di riferimento. Il metodo GetIntrinsicValueAs si comporta in modo molto simile al metodo GetIntrinsicValue, ad eccezione del fatto che converte il valore nel tipo variant specificato. Se non è possibile eseguire la conversione, il metodo restituisce un errore.
Il metodo IsEqualTo confronta due oggetti modello e restituisce se sono uguali in valore. Per l'oggetto con un ordinamento, questo metodo che restituisce true equivale al metodo Compare che restituisce 0. Per gli oggetti che non dispongono di un ordinamento ma sono equivalenti, il metodo Compare avrà esito negativo, ma non lo sarà. Il significato di un confronto basato su valori è definito dal tipo di oggetto . Attualmente, questa opzione è definita solo per i tipi intrinseci e gli oggetti errore. Non esiste un concetto di modello di dati corrente per l'equabilità.
Il metodo Dereference dereferenzia un oggetto . Questo metodo può essere usato per dereferenziare un riferimento basato su un modello di dati (ObjectTargetObjectReference, ObjectKeyReference) o un riferimento al linguaggio nativo (un puntatore o un riferimento al linguaggio). È importante notare che questo metodo rimuove un singolo livello di semantica di riferimento nell'oggetto . È possibile, ad esempio, avere un riferimento al modello di dati a un riferimento al linguaggio. In questo caso, la chiamata al metodo Dereference la prima volta rimuove il riferimento al modello di dati e lascia il riferimento al linguaggio. La chiamata di Dereference su tale oggetto risultante rimuoverà successivamente il riferimento alla lingua e restituirà il valore nativo in tale riferimento.
Metodi di manipolazione delle chiavi
Qualsiasi oggetto sintetico che è un dizionario di tuple chiave, valore e metadati dispone di una serie di metodi per modificare tali chiavi, valori e i metadati associati.
Le forme basate sul valore delle API sono:
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;
Le forme basate sui riferimenti delle API sono:
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;
Il metodo GetKeyValue è il primo metodo a cui un client si rivolge per ottenere il valore di (e i metadati associati a) una determinata chiave in base al nome. Se la chiave è una funzione di accesso alla proprietà, ovvero come valore di IModelObject che è un IModelPropertyAccessor boxed, il metodo GetKeyValue chiamerà automaticamente il metodo GetValue della funzione di accesso alla proprietà per recuperare il valore effettivo.
Il metodo SetKeyValue è il primo metodo a cui un client si rivolge per impostare il valore di una chiave. Questo metodo non può essere utilizzato per creare una nuova chiave in un oggetto . Imposta solo il valore di una chiave esistente. Si noti che molte chiavi sono di sola lettura( ad esempio, sono implementate da una funzione di accesso della proprietà che restituisce E_NOT_IMPL dal metodo SetValue). Questo metodo avrà esito negativo quando viene chiamato su una chiave di sola lettura.
Il metodo EnumerateKeyValues è il primo metodo a cui un client si rivolge per enumerare tutte le chiavi in un oggetto (incluse tutte le chiavi implementate ovunque nell'albero dei modelli padre). È importante notare che EnumerateKeyValues enumererà tutte le chiavi definite dai nomi duplicati nell'albero degli oggetti; tuttavia, i metodi come GetKeyValue e SetKeyValue modificano solo la prima istanza di una chiave con il nome specificato come individuato dall'attraversamento depth-first-traversal.
Il metodo GetKey otterrà il valore di (e i metadati associati a) una determinata chiave in base al nome. La maggior parte dei client deve usare invece il metodo GetKeyValue. Se la chiave è una funzione di accesso alla proprietà, la chiamata a questo metodo restituirà la funzione di accesso alla proprietà (un'interfaccia IModelPropertyAccessor) boxed in un oggetto IModelObject. A differenza di GetKeyValue, questo metodo non risolverà automaticamente il valore sottostante della chiave chiamando il metodo GetValue. Questa responsabilità è quella del chiamante.
Il metodo SetKey è il metodo a cui un client si rivolge per creare una chiave su un oggetto (e potenzialmente associare metadati alla chiave creata). Se un determinato oggetto ha già una chiave con il nome specificato, si verificherà uno dei due comportamenti. Se la chiave si trova nell'istanza specificata da questo, il valore di tale chiave verrà sostituito come se la chiave originale non esistesse. Se, d'altra parte, la chiave si trova nella catena di modelli di dati padre dell'istanza specificata da questo, verrà creata una nuova chiave con il nome specificato nell'istanza specificata da questo. In questo modo, in effetti, l'oggetto avrà due chiavi con lo stesso nome (simile a una classe derivata che ombreggiatura un membro con lo stesso nome di una classe di base).
Il metodo EnumerateKeys si comporta in modo simile al metodo EnumerateKeyValues, ad eccezione del fatto che non risolve automaticamente le funzioni di accesso alle proprietà nell'oggetto . Ciò significa che se il valore di una chiave è una funzione di accesso alla proprietà, il metodo EnumerateKeys restituirà la funzione di accesso alla proprietà (un oggetto IModelPropertyAccessorInterface) in un oggetto IModelObject anziché chiamare automaticamente il metodo GetValue. Questo è simile alla differenza tra GetKey e GetKeyValue.
Il metodo ClearKeys rimuove tutte le chiavi e i relativi valori e metadati associati dall'istanza dell'oggetto specificato da questo oggetto. Questo metodo non ha alcun effetto sui modelli padre associati all'istanza di oggetto specifica.
Il metodo GetKeyReference cercherà una chiave del nome specificato nell'oggetto (o nella relativa catena di modelli padre) e restituirà un riferimento a tale chiave specificata da un'interfaccia IModelKeyReference boxed in un IModelObject. Tale riferimento può essere usato successivamente per ottenere o impostare il valore della chiave.
Il metodo EnumerateKeyReferences si comporta in modo simile al metodo EnumerateKeyValues, ad eccezione del fatto che restituisce riferimenti alle chiavi enumerate (date da un'interfaccia IModelKeyReference boxed in un oggetto IModelObject) anziché il valore della chiave. Tali riferimenti possono essere usati per ottenere o impostare il valore sottostante delle chiavi.
Metodi di manipolazione dei concetti
Oltre a un oggetto modello come dizionario di tuple chiave/valore/metadati, è anche un contenitore di concetti. Un concetto è un concetto astratto che può essere eseguito su o da un oggetto . I concetti sono, in sostanza, un archivio dinamico di interfacce supportate da un oggetto. Un certo numero di concetti è definito oggi dal modello di dati:
Interfaccia concept | Descrizione |
---|---|
IDataModelConcept | Il concetto è un modello padre. Se questo modello viene collegato automaticamente a un tipo nativo tramite una firma di tipo registrato, il metodo InitializeObject verrà chiamato automaticamente ogni volta che viene creata un'istanza di un nuovo oggetto di tale tipo. |
IStringDisplayableConcept | L'oggetto può essere convertito in una stringa a scopo di visualizzazione. |
IIterableConcept | L'oggetto è un contenitore e può essere iterato. |
IIndexableConcept | L'oggetto è un contenitore e può essere indicizzato (accessibile tramite accesso casuale) in una o più dimensioni. |
IPreferredRuntimeTypeConcept | L'oggetto comprende di più sui tipi derivati rispetto al sistema di tipi sottostante in grado di fornire e di gestire le proprie conversioni dal tipo statico al tipo di runtime. |
IDynamicKeyProviderConcept | L'oggetto è un provider dinamico di chiavi e vuole assumere tutte le query chiave dal modello di dati di base. Questa interfaccia viene in genere usata come bridge per linguaggi dinamici, ad esempio JavaScript. |
IDynamicConceptProviderConcept | L'oggetto è un provider dinamico di concetti e vuole assumere tutte le query di concetto dal modello di dati di base. Questa interfaccia viene in genere usata come bridge per linguaggi dinamici, ad esempio JavaScript. |
I metodi seguenti in IModelObject vengono utilizzati per modificare i concetti supportati da un oggetto.
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;
Il metodo GetConcept cercherà un concetto nell'oggetto (o nella relativa catena di modelli padre) e restituirà un puntatore di interfaccia all'interfaccia del concetto. Il comportamento e i metodi in un'interfaccia del concetto sono specifici di ogni concetto. È importante notare, tuttavia, che molte interfacce del concetto richiedono al chiamante di passare in modo esplicito l'oggetto contesto (o quello che si potrebbe chiamare tradizionalmente questo puntatore). È importante assicurarsi che l'oggetto contesto corretto venga passato a ogni interfaccia del concetto.
Il metodo SetConcept inserisce un concetto specificato nell'istanza dell'oggetto specificata dal puntatore. Se un modello padre associato all'istanza dell'oggetto specificato da supporta anche il concetto, l'implementazione nell'istanza eseguirà l'override di quella nel modello padre.
Il metodo ClearConcepts rimuoverà tutti i concetti dall'istanza dell'oggetto specificato da questo.
Metodi di oggetto nativo
Sebbene molti oggetti modello facciano riferimento a oggetti intrinseci (ad esempio numeri interi, stringhe) o costrutti sintetici (un dizionario di tuple chiave/valore/metadati e concetti), un oggetto modello può anche fare riferimento a un costrutto nativo (ad esempio, un tipo definito dall'utente nello spazio indirizzi della destinazione di debug). L'interfaccia IModelObject include una serie di metodi su di esso che accedono alle informazioni su tali oggetti nativi. Questi metodi sono:
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;
Il metodo GetRawValue trova un costrutto nativo all'interno dell'oggetto specificato. Un costrutto di questo tipo può essere un campo, una classe base, un campo in una classe base, una funzione membro e così via...
Il metodo EnumerateRawValues enumera tutti gli elementi figlio nativi ,ad esempio campi, classi di base e così via, dell'oggetto specificato.
Il metodo TryCastToRuntimeType chiederà all'host di debug di eseguire un'analisi e determinare il tipo di runtime effettivo (ad esempio, la classe più derivata) dell'oggetto specificato. L'analisi esatta utilizzata è specifica dell'host di debug e può includere informazioni sul tipo di runtime RTTI (C++), l'esame della struttura V-Table (tabella funzione virtuale) dell'oggetto o qualsiasi altro modo che l'host possa usare per determinare in modo affidabile il tipo di runtime/dinamico dal tipo statico. L'errore di conversione in un tipo di runtime non significa che la chiamata al metodo avrà esito negativo. In questi casi, il metodo restituirà l'oggetto specificato (questo puntatore) nell'argomento di output.
Il metodo GetLocation restituirà la posizione dell'oggetto nativo. Anche se tale posizione è in genere un indirizzo virtuale all'interno dello spazio indirizzi della destinazione di debug, non è necessariamente così. Il percorso restituito da questo metodo è una posizione astratta che può essere un indirizzo virtuale, può indicare la posizione all'interno di un registro o un registro secondario oppure può indicare un altro spazio indirizzi arbitrario definito dall'host di debug. Se il campo HostDefined dell'oggetto Location risultante è 0, indica che la posizione è effettivamente un indirizzo virtuale. Tale indirizzo virtuale può essere recuperato esaminando il campo Offset della posizione risultante. Qualsiasi valore diverso da zero del campo HostDefined indica uno spazio indirizzi alternativo in cui il campo Offset è l'offset all'interno di tale spazio indirizzi. Il significato esatto dei valori HostDefined diversi da zero qui è privato per l'host di debug.
Il metodo GetTypeInfo restituirà il tipo nativo dell'oggetto specificato. Se all'oggetto non sono associate informazioni sul tipo nativo, ad esempio un intrinseco e così via, la chiamata avrà comunque esito positivo ma restituirà Null.
Il metodo GetTargetInfo è in effetti una combinazione dei metodi GetLocation e GetTypeInfo che restituiscono sia la posizione astratta che il tipo nativo dell'oggetto specificato.
Il metodo GetRawReference trova un costrutto nativo all'interno dell'oggetto specificato e ne restituisce un riferimento. Un costrutto di questo tipo può essere un campo, una classe base, un campo in una classe base, una funzione membro e così via... È importante distinguere il riferimento restituito qui (un oggetto del tipo ObjectTargetObjectReference) da un riferimento al linguaggio (ad esempio, un riferimento a C++ & o && style).
Il metodo EnumerateRawReferences enumera i riferimenti a tutti gli elementi figlio nativi ,ad esempio campi, classi di base e così via, dell'oggetto specificato.
Metodi di estendibilità
Come descritto in precedenza, un oggetto modello si comporta in modo molto simile a un oggetto JavaScript e alla relativa catena di prototipi. Oltre all'istanza rappresentata da una determinata interfaccia IModelObject, è possibile che vi sia un numero arbitrario di modelli padre collegati all'oggetto (ognuno dei quali può, a sua volta, avere un numero arbitrario di modelli padre associati a tali modelli). Questo è il mezzo principale per l'estendibilità all'interno del modello di dati. Se una determinata proprietà o concetto non può trovarsi all'interno di una determinata istanza, viene eseguita una ricerca di profondità dell'albero degli oggetti (definito dai modelli padre) nell'istanza.
I metodi seguenti modificano la catena di modelli padre associati a una determinata istanza di 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;
Il metodo GetNumberOfParentModels restituisce il numero di modelli padre associati all'istanza dell'oggetto specificata. I modelli padre vengono ricercati per le proprietà depth-first nell'ordinamento lineare della catena di modelli padre.
Il metodo GetParentModel restituisce il modello padre i-th nella catena di modelli padre dell'oggetto specificato. I modelli padre vengono cercati una proprietà o un concetto nell'ordine lineare in cui vengono aggiunti o enumerati. Il modello padre con indice i di zero viene cercato (gerarchicamente) prima del modello padre con indice i + 1.
Il metodo AddParentModel aggiunge un nuovo modello padre all'oggetto specificato. Tale modello può essere aggiunto alla fine della catena di ricerca (l'argomento di override è specificato come false) o all'inizio della catena di ricerca (l'argomento di override viene specificato come true). Inoltre, ogni modello padre può facoltativamente modificare il contesto (il puntatore semantico) per qualsiasi proprietà o concetto nell'elemento padre specificato (o chiunque nella gerarchia padre). La regolazione del contesto viene usata raramente, ma consente alcuni concetti potenti come l'incorporamento di oggetti, la costruzione di spazi dei nomi e così via...
RemoveParentModel rimuoverà un modello padre specificato dalla catena di ricerca padre dell'oggetto specificato.
Il metodo SetContextForDataModel viene utilizzato dall'implementazione di un modello di dati per inserire i dati di implementazione negli oggetti dell'istanza. Concettualmente, ogni IModelObject (chiamare questa istanza per semplicità) contiene una tabella hash di stato. La tabella hash viene indicizzata da un altro IModelObject (chiamare questo modello di dati per semplicità) che si trova nella gerarchia del modello padre dell'istanza. Il valore contenuto in questo hash è un set di informazioni sullo stato con conteggio dei riferimenti rappresentate da un'istanza IUnknown. Dopo che il modello di dati imposta questo stato nell'istanza, può archiviare dati di implementazione arbitrari che possono essere recuperati durante operazioni come i getter delle proprietà.
Il metodo GetContextForDataModel viene utilizzato per recuperare le informazioni sul contesto configurate con una chiamata precedente a SetContextForDataModel. Recupera le informazioni sullo stato impostate su un oggetto istanza da un modello di dati più avanti nella gerarchia del modello padre dell'oggetto istanza. Per altri dettagli su questo contesto/stato e sul relativo significato, vedere la documentazione per SetContextForDataModel.
Tipi di oggetto core del modello di dati del debugger
Un oggetto nel modello di dati è simile al concetto di Oggetto in .NET. Si tratta del contenitore generico in cui costruire che il modello di dati riconosce può essere sottoposto a boxing. Oltre agli oggetti nativi e agli oggetti sintetici (dinamici), esistono una serie di tipi di oggetto di base che possono essere inseriti (o boxed) nel contenitore di un oggetto IModelObject. Il contenitore in cui vengono inseriti la maggior parte di questi valori è un valore COM/OLE VARIANT standard con una serie di restrizioni aggiuntive applicate a ciò che può contenere VARIANT. I tipi di base di questi sono i seguenti:
- Valori senza segno a 8 bit e con segno (VT_UI1, VT_I1)
- Valori senza segno a 16 bit (VT_UI2, VT_UI2)
- Valori senza segno a 32 bit (VT_UI4, VT_I4)
- Valori senza segno a 64 bit (VT_UI8, VT_I8)
- Valori a virgola mobile a precisione singola e doppia (VT_R4, VT_R8)
- Stringhe (VT_BSTR)
- Valori booleani (VT_BOOL)
Oltre a questi tipi di base, diversi oggetti modello di dati di base vengono inseriti in IModelObject definiti da VT_UNKNOWN in cui l'oggetto IUnknown archiviato è garantito di implementare un'interfaccia specifica. Questi tipi sono:
- Funzioni di accesso alle proprietà (IModelPropertyAccessor)
- Oggetti metodo (IModelMethod)
- Oggetti di riferimento chiave (IModelKeyReference o IModelKeyReference2)
- Oggetti Context (IDebugModelHostContext)
Funzioni di accesso alle proprietà: IModelPropertyAccessor
Una funzione di accesso alle proprietà nel modello di dati è un'implementazione dell'interfaccia IModelPropertyAccessor boxed in un IModelObject. L'oggetto modello restituirà un tipo di ObjectPropertyAccessor quando viene eseguita una query e il valore intrinseco è un VT_UNKNOWN che è garantito che possa essere sottoposto a query per IModelPropertyAccessor. In fase di elaborazione, è garantito che sia possibile eseguire il cast statico in IModelPropertyAccessor.
Una funzione di accesso alle proprietà è un modo indiretto per ottenere una chiamata al metodo per ottenere e impostare un valore di chiave nel modello di dati. Se il valore di una determinata chiave è una funzione di accesso alla proprietà, i metodi GetKeyValue e SetKeyValue noteranno automaticamente questo valore e chiameranno i metodi GetValue o SetValue sottostanti della funzione di accesso della proprietà in base alle esigenze.
L'interfaccia IModelPropertyAccessor è definita come segue:
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;
}
Il metodo GetValue è il getter per la funzione di accesso della proprietà. Viene chiamato ogni volta che un client desidera recuperare il valore sottostante della proprietà. Si noti che qualsiasi chiamante che ottiene direttamente una funzione di accesso alla proprietà è responsabile del passaggio del nome della chiave e dell'oggetto istanza accurato (questo puntatore) al metodo GetValue della funzione di accesso della proprietà.
Il metodo SetValue è il setter per la funzione di accesso alla proprietà. Viene chiamato ogni volta che un client desidera assegnare un valore alla proprietà sottostante. Molte proprietà sono di sola lettura. In questi casi, la chiamata al metodo SetValue restituirà E_NOTIMPL. Si noti che qualsiasi chiamante che ottiene direttamente una funzione di accesso alla proprietà è responsabile del passaggio del nome della chiave e dell'oggetto istanza accurato (questo puntatore) al metodo SetValue della funzione di accesso della proprietà.
Metodi: IModelMethod
Un metodo nel modello di dati è un'implementazione dell'interfaccia IModelMethod che viene sottoposta a boxing in un IModelObject. L'oggetto modello restituirà un tipo di ObjectMethod quando viene eseguita una query e il valore intrinseco è un VT_UNKNOWN che è garantito che possa essere sottoposto a query per IModelMethod. In fase di elaborazione, è garantito che sia possibile eseguire il cast statico in IModelMethod. Tutti i metodi nel modello di dati sono di natura dinamica. Accettano come input un set di 0 o più argomenti e restituiscono un singolo valore di output. Non esiste alcuna risoluzione dell'overload e nessun metadati relativi ai nomi, ai tipi o alle aspettative dei parametri.
L'interfaccia IModelMethod è definita nel modo seguente:
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;
}
Il metodo Call è il modo in cui viene richiamato qualsiasi metodo definito nel modello di dati. Il chiamante è responsabile del passaggio di un oggetto istanza accurato (questo puntatore) e di un set arbitrario di argomenti. Viene restituito il risultato del metodo e tutti i metadati facoltativi associati a tale risultato. I metodi che non restituiscono logicamente un valore devono comunque restituire un oggetto IModelObject valido. In questo caso, IModelObject è un valore boxed senza valore. Nel caso in cui un metodo non riesca, può restituire informazioni facoltative sull'errore esteso nell'argomento di input (anche se il valore HRESULT restituito è un errore). È fondamentale che i chiamanti verifichino questo problema.
Riferimenti chiave: IModelKeyReference o IModelKeyReference2
Un riferimento alla chiave è, in sostanza, un handle per una chiave su un oggetto specifico. Un client può recuperare tale handle tramite metodi come GetKeyReference e usare l'handle in un secondo momento per ottenere o impostare il valore della chiave senza necessariamente tenere premuto l'oggetto originale. Questo tipo di oggetto è un'implementazione dell'interfaccia IModelKeyReference o IModelKeyReference2 che viene sottoposto a boxing in un IModelObject. L'oggetto modello restituirà un tipo di ObjectKeyReference quando viene eseguita una query e quindi un valore intrinseco è un VT_UNKNOWN che è garantito che sia queryable per IModelKeyReference. In fase di elaborazione, è garantito che sia possibile eseguire il cast statico in IModelKeyReference.
L'interfaccia di riferimento chiave è definita come segue:
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;
}
Il metodo GetKeyName restituisce il nome della chiave a cui fa riferimento questa chiave è un handle. La stringa restituita è una stringa BSTR standard e deve essere liberata tramite una chiamata a SysFreeString.
Il metodo GetOriginalObject restituisce l'oggetto istanza da cui è stato creato il riferimento alla chiave. Si noti che la chiave stessa può trovarsi in un modello padre dell'oggetto istanza.
Il metodo GetContextObject restituisce il contesto (questo puntatore) che verrà passato al metodo GetValue o SetValue di una funzione di accesso a una proprietà se la chiave in questione fa riferimento a una funzione di accesso alla proprietà. L'oggetto di contesto restituito qui può essere o meno uguale all'oggetto originale recuperato da GetOriginalObject. Se una chiave si trova in un modello padre e a tale modello padre è associato un regolatore di contesto, l'oggetto originale è l'oggetto istanza in cui è stato chiamato GetKeyReference o EnumerateKeyReferences. L'oggetto contesto sarà qualsiasi elemento che esce dal regolatore di contesto finale tra l'oggetto originale e il modello padre contenente la chiave a cui questo riferimento chiave è un handle. Se non sono presenti regolatori di contesto, l'oggetto originale e l'oggetto contesto sono identici.
Il metodo GetKey in un riferimento di chiave si comporta come il metodo GetKey in IModelObject. Restituisce il valore della chiave sottostante ed eventuali metadati associati alla chiave. Se il valore della chiave è una funzione di accesso alla proprietà, verrà restituita la funzione di accesso alla proprietà (IModelPropertyAccessor) boxed in un oggetto IModelObject. Questo metodo non chiamerà i metodi GetValue o SetValue sottostanti nella funzione di accesso alla proprietà.
Il metodo GetKeyValue in un riferimento di chiave si comporta come il metodo GetKeyValue su IModelObject. Restituisce il valore della chiave sottostante ed eventuali metadati associati alla chiave. Se il valore della chiave è una funzione di accesso alla proprietà, verrà chiamato automaticamente il metodo GetValue sottostante nella funzione di accesso della proprietà.
Il metodo SetKey in un riferimento di chiave si comporta come il metodo SetKey su IModelObject. Assegnerà il valore della chiave. Se la chiave originale era una funzione di accesso alla proprietà, questa sostituirà la funzione di accesso alla proprietà. Non chiamerà il metodo SetValue nella funzione di accesso della proprietà.
Il metodo SetKeyValue su un riferimento chiave si comporta come il metodo SetKeyValue su IModelObject. Assegnerà il valore della chiave. Se la chiave originale è una funzione di accesso alla proprietà, verrà chiamato il metodo SetValue sottostante nella funzione di accesso della proprietà anziché sostituire la funzione di accesso alla proprietà stessa.
Il metodo OverrideContextObject (presente solo in IModelKeyReference2) è un metodo avanzato che viene usato per modificare in modo permanente l'oggetto contesto che questo riferimento di chiave passerà a qualsiasi funzione di accesso alla proprietà sottostante GetValue o SetValue. L'oggetto passato a questo metodo verrà restituito anche da una chiamata a GetContextObject. Questo metodo può essere usato dai provider di script per replicare determinati comportamenti del linguaggio dinamico. La maggior parte dei client non deve chiamare questo metodo.
Oggetti context: IDebugHostContext
Gli oggetti contesto sono BLOB opachi di informazioni associate all'host di debug (in collaborazione con il modello di dati). Può includere elementi come il contesto del processo o lo spazio indirizzi da cui provengono le informazioni e così via... Un oggetto contesto è un'implementazione di IDebugHostContext boxed all'interno di un IModelObject. Si noti che IDebugHostContext è un'interfaccia definita dall'host. Un client non implementerà mai questa interfaccia.
Per altre informazioni sugli oggetti contesto, vedere Debugger Data Model C++ Host Interfaces in Debugger Data Model C++ Interfaces .For more information about context objects, see Debugger Data Model C++ Host Interfaces in Debugger Data Model C++Interfaces.
Gestione modelli di dati
L'interfaccia principale per gestione modelli di dati, IDataModelManager2 (o i precedenti IDataModelManager) è definita come segue:
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;
}
Metodi di gestione
Il set di metodi seguente viene utilizzato dall'applicazione (ad esempio, debugger) che ospita il modello di dati.
STDMETHOD(Close)() PURE;
Il metodo Close viene chiamato sul gestore di modelli di dati da un'applicazione (ad esempio, debugger) che ospita il modello di dati per avviare il processo di arresto di Gestione modelli di dati. Un host del modello di dati che non esegue il metodo Close prima di rilasciare il riferimento finale in Gestione modelli di dati può causare comportamenti non definiti, tra cui perdite significative dell'infrastruttura di gestione per il modello di dati.
Metodi di creazione/conversione boxing di oggetti
Il set di metodi seguente viene usato per creare nuovi oggetti o per raggruppare i valori in un oggetto IModelObject, ovvero l'interfaccia principale del modello di dati.
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;
Il metodo CreateNoValue crea un oggetto "nessun valore", lo inserisce in un oggetto IModelObject e lo restituisce. L'oggetto modello restituito ha un tipo di ObjectNoValue.
Un oggetto "no value" ha diversi significati semantici:
- (A seconda del linguaggio), può essere considerato l'equivalente semantico di void, null o non definito
- Qualsiasi metodo GetValue della funzione di accesso alle proprietà che restituisce l'esito positivo e un oggetto "nessun valore" risultante indica che la proprietà specifica non ha alcun valore per l'istanza specificata e deve essere considerata come se la proprietà non esistesse per tale istanza specifica.
- I metodi del modello di dati che non dispongono semanticamente di un valore restituito usano questo valore come sentinel per indicare, ad esempio un metodo deve restituire un IModelObject valido.
Il metodo CreateErrorObject crea un "oggetto error". Il modello di dati non ha la nozione di eccezioni e flusso di eccezioni. L'errore esce da una proprietà o da un metodo in due modi:
- Un singolo HRESULT con errore non riuscito senza informazioni di errore estese. Non sono disponibili altre informazioni che possono essere fornite per l'errore o l'errore stesso è autoesplicativo dall'HRESULT restituito.
- Un singolo HRESULT con errore singolo abbinato a informazioni di errore estese. Le informazioni estese sull'errore sono un oggetto di errore restituito nell'argomento di output della proprietà/metodo.
Il metodo CreateTypedObject è il metodo che consente a un client di creare una rappresentazione di un oggetto nativo/linguistico nello spazio indirizzi di una destinazione di debug. Se il tipo dell'oggetto appena creato (come indicato dall'argomento objectType) corrisponde a una o più firme di tipo registrate con il gestore modelli di dati come visualizzatori canonici o estensioni, tali modelli di dati corrispondenti verranno automaticamente associati all'oggetto istanza creato prima che venga restituito al chiamante.
Il metodo CreateTypedObjectReference è semanticamente simile al metodo CreateTypedObject, ad eccezione del fatto che crea un riferimento al costrutto nativo/linguaggio sottostante. Il riferimento creato è un oggetto con un tipo objectTargetObjectReference. Non è un riferimento nativo perché il linguaggio sottostante potrebbe supportare (ad esempio, C++ & o &&). È completamente possibile avere un ObjectTargetObjectReference a un riferimento C++. Un oggetto di tipo ObjectTargetObjectReference può essere convertito nel valore sottostante tramite l'uso del metodo Dereference in IModelObject. Il riferimento può anche essere passato all'analizzatore di espressioni dell'host sottostante per assegnare nuovamente al valore in un linguaggio appropriato.
Il metodo CreateSyntheticObject crea un oggetto modello di dati vuoto, ovvero un dizionario di tuple chiave/valore/metadati e concetti. Al momento della creazione, non esistono chiavi né concetti sull'oggetto. Si tratta di uno slate pulito per il chiamante da utilizzare.
Il metodo CreateDataModelObject è un semplice wrapper helper per creare oggetti che sono modelli di dati, ovvero oggetti che verranno associati come modelli padre ad altri oggetti. Tutti questi oggetti devono supportare il concetto di modello di dati tramite IDataModelConcept. Questo metodo crea un nuovo oggetto sintetico vuoto senza contesto esplicito e aggiunge l'oggetto IDataModelConcept inpassato come implementazione dell'oggetto appena creato del concetto di modello di dati. Questa operazione può essere eseguita in modo analogo con le chiamate a CreateSyntheticObject e SetConcept.
Il metodo CreateIntrinsicObject è il metodo che riquadri i valori intrinseci in IModelObject. Il chiamante inserisce il valore in com VARIANT e chiama questo metodo. Gestione modelli di dati restituisce un oggetto IModelObject che rappresenta l'oggetto . Si noti che questo metodo viene usato anche per boxare tipi di base IUnknown fondamentali: funzioni di accesso alle proprietà, metodi, contesti e così via... In questi casi, il metodo objectKind indica il tipo di costrutto basato su IUnknown rappresentato dall'oggetto e il campo punkVal della variante passata è il tipo derivato IUnknown. Il tipo deve essere sottoposto a cast statico all'interfaccia del modello appropriata, ad esempio IModelPropertyAccessor, IModelMethod, IDebugHostContext e così via. I tipi VARIANT supportati da questo metodo sono 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 (per un set specializzato di tipi derivati IUnknown, come indicato dall'enumerazione ModelObjectKind.
Il metodo CreateTypedintrinsicObject è simile al metodo CreateIntrinsicObject, ad eccezione del fatto che consente l'associazione di un tipo nativo/lingua ai dati e viene eseguito insieme al valore boxed. In questo modo il modello di dati può rappresentare costrutti come i tipi di enumerazione nativa (che sono semplicemente VT_UI* o VT_I* valori). I tipi di puntatore vengono creati anche con questo metodo. Un puntatore nativo nel modello di dati è una quantità a 64 bit zero estesa che rappresenta un offset nello spazio indirizzi virtuale della destinazione di debug. Viene sottoposto a boxing all'interno di un VT_UI8 e viene creato con questo metodo e un tipo che indica un puntatore nativo/linguistico.
Il metodo CreateMetadataStore crea un archivio chiavi, un contenitore semplificato di tuple chiave/valore/metadati, che viene usato per contenere i metadati che possono essere associati alle proprietà e a un'ampia gamma di altri valori. Un archivio di metadati può avere un singolo elemento padre ,che a sua volta può avere un singolo elemento padre. Se una determinata chiave di metadati non si trova in un determinato archivio, i relativi elementi padre vengono controllati. La maggior parte degli archivi di metadati non ha elementi padre. Tuttavia, fornisce un modo per condividere facilmente i metadati comuni.
Il metodo CreateTypedIntrinsicObjectEx è semanticamente simile al metodo CreateTypedIntrinsicObject. L'unica differenza tra i due è che questo metodo consente al chiamante di specificare il contesto in cui i dati intrinseci sono validi. Se non viene passato alcun contesto, i dati vengono considerati validi in qualsiasi contesto ereditato dall'argomento di tipo (come si comporta CreateTypedIntrinsicObject). Ciò consente la creazione di valori di puntatore tipizzati nella destinazione di debug che richiedono un contesto più specifico di quanto possa essere ereditato dal tipo.
Estendibilità/Metodi di registrazione Il set di metodi seguente gestisce il meccanismo di estendibilità del modello di dati, consentendo a un client di estendere o registrare modelli esistenti o chiedere al modello di dati di associare automaticamente un determinato modello padre ai tipi nativi che corrispondono a un determinato criterio.
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;
Il metodo GetModelForTypeSignature restituisce il modello di dati registrato su una determinata firma di tipo tramite una chiamata precedente al metodo RegisterModelForTypeSignature. Il modello di dati restituito da questo metodo viene considerato il visualizzatore canonico per qualsiasi tipo che corrisponde alla firma del tipo passato. Come visualizzatore canonico, tale modello di dati assume la visualizzazione del tipo. Per impostazione predefinita, i motori di visualizzazione nasconderanno costrutti nativi/linguistici dell'oggetto a favore della visualizzazione dell'oggetto presentato dal modello di dati.
Il metodo GetModelForType restituisce il modello di dati che è il visualizzatore canonico per una determinata istanza del tipo. In effetti, questo metodo trova la firma del tipo corrispondente migliore registrata con una chiamata precedente al metodo RegisterModelForTypeSignature e restituisce il modello di dati associato.
Il metodo RegisterModelForTypeSignature è il metodo primario utilizzato da un chiamante per registrare un visualizzatore canonico per un determinato tipo (o set di tipi). Un visualizzatore canonico è un modello di dati che, in effetti, assume la visualizzazione di un determinato tipo (o set di tipi). Anziché la visualizzazione nativa/lingua del tipo visualizzato in qualsiasi interfaccia utente del debugger, viene visualizzata la visualizzazione del tipo presentato dal modello di dati registrato (insieme a un modo per tornare alla visualizzazione nativa/lingua per un utente che lo desidera).
UnregisterModelForTypeSignature
Il metodo UnregisterModelForTypeSignature annulla una chiamata precedente al metodo RegisterModelForTypeSignature. Questo metodo può rimuovere un determinato modello di dati come visualizzatore canonico per i tipi che corrispondono a una firma di tipo specifico oppure può rimuovere un determinato modello di dati come visualizzatore canonico per ogni firma di tipo in cui è registrato il modello di dati.
RegisterExtensionForTypeSignature
Il metodo RegisterExtensionForTypeSignature è simile al metodo RegisterModelForTypeSignature con una differenza chiave. Il modello di dati passato a questo metodo non è il visualizzatore canonico per alcun tipo e non assumerà la visualizzazione della visualizzazione nativa/lingua di tale tipo. Il modello di dati passato a questo metodo verrà aggiunto automaticamente come elemento padre a qualsiasi tipo concreto che corrisponde alla firma del tipo fornito. A differenza del metodo RegisterModelForTypeSignature, non esiste alcun limite per le firme di tipo identiche o ambigue registrate come estensioni a un determinato tipo (o set di tipi). Ogni estensione la cui firma di tipo corrisponde a una determinata istanza di tipo concreto causerà l'associazione automatica del modello di dati registrato tramite questo metodo agli oggetti appena creati come modelli padre. In questo modo, in effetti, consente a un numero arbitrario di client di estendere un tipo (o un set di tipi) con nuovi campi o funzionalità.
UnregisterExtensionForTypeSignature
Il metodo UnregisterExtensionForTypeSignature annulla una chiamata precedente a RegisterExtensionForTypeSignature. Annulla la registrazione di un particolare modello di dati come estensione per una firma di tipo particolare o come estensione per tutte le firme di tipo in cui è stato registrato il modello di dati.
Il metodo GetRootNamespace restituisce lo spazio dei nomi radice del modello di dati. Si tratta di un oggetto gestito dal modello di dati e in cui l'host di debug inserisce determinati oggetti.
Il metodo RegisterNamedModel registra un determinato modello di dati con un nome noto in modo che possa essere trovato dai client che desiderano estenderlo. Questo è lo scopo principale dell'API: pubblicare un modello di dati come qualcosa che può essere esteso recuperando il modello registrato con questo nome noto e aggiungendo un modello padre.
Annulla registrazioneNamedModel
Il metodo UnregisterNamedModel annulla una chiamata precedente a RegisterNamedModel. Rimuove l'associazione tra un modello di dati e un nome in base al quale può essere cercata.
Un chiamante che desidera estendere un modello di dati registrato con un determinato nome chiama il metodo AcquireNamedModel per recuperare l'oggetto per il modello di dati che desidera estendere. Questo metodo restituirà qualsiasi modello di dati registrato tramite una chiamata precedente al metodo RegisterNamedModel. Poiché lo scopo principale del metodo AcquireNamedModel è estendere il modello, questo metodo ha un comportamento speciale se non è ancora stato registrato alcun modello con il nome specificato. Se non è ancora stato registrato alcun modello con il nome specificato, viene creato un oggetto stub, registrato temporaneamente con il nome specificato e restituito al chiamante. Quando il modello di dati reale viene registrato tramite una chiamata al metodo RegisterNamedModel, tutte le modifiche apportate all'oggetto stub sono, in effetti, apportate al modello reale. In questo modo vengono rimossi molti problemi di dipendenza dell'ordine di carico dai componenti che si estendono l'uno dall'altro.
Metodi helper
I metodi seguenti sono metodi helper generali che consentono di eseguire operazioni complesse sugli oggetti nel modello di dati. Sebbene sia possibile eseguire queste azioni tramite altri metodi sul modello di dati o sui relativi oggetti, questi metodi pratici semplificano notevolmente:
STDMETHOD(AcquireSubNamespace)(_In_ PCWSTR modelName, _In_ PCWSTR subNamespaceModelName, _In_ PCWSTR accessName, _In_opt_ IKeyStore *metadata, _COM_Outptr_ IModelObject **namespaceModelObject) PURE;
Il metodo AcquireSubNamespace consente di costruire un elemento che potrebbe essere più tradizionalemente simile a uno spazio dei nomi del linguaggio rispetto a un nuovo oggetto in un linguaggio dinamico. Se, ad esempio, un chiamante desidera classificare le proprietà su un oggetto processo per rendere l'oggetto processo più organizzato e le proprietà più facili da individuare, un metodo di questa operazione consiste nel creare un sottooggetto per ogni categoria nell'oggetto processo e posizionare tali proprietà all'interno di tale oggetto.
Vedere anche
Questo argomento fa parte di una serie che descrive le interfacce accessibili da C++, come usarle per compilare un'estensione del debugger basata su C++ e come usare altri costrutti di modelli di dati (ad esempio JavaScript o NatVis) da un'estensione del modello di dati C++.
Panoramica del modello di dati del debugger C++
Interfacce C++ del modello di dati del debugger
Interfacce aggiuntive del modello di dati del debugger C++