Основные понятия модели данных отладчика C++

В этом разделе описываются основные понятия модели данных отладчика C++ .

Основные понятия в модели данных

Искусственные объекты в модели данных — это фактически две вещи:

  • Словарь кортежей ключей, значений и метаданных.
  • Набор концепций (интерфейсов), поддерживаемых моделью данных. Основные понятия — это интерфейсы, которые клиент (в отличие от модели данных) реализует для предоставления заданного набора семантического поведения. Здесь перечислены поддерживаемые в настоящее время наборы концепций.
Концептуальный интерфейс Описание
IDataModelConcept Концепция является родительской моделью. Если эта модель автоматически присоединяется к собственному типу с помощью сигнатуры зарегистрированного типа, метод InitializeObject будет автоматически вызываться при каждом создании нового объекта такого типа.
IStringDisplayableConcept Объект можно преобразовать в строку для отображения.
IIterableConcept Объект является контейнером, и его можно выполнить итерации.
IIndexableConcept Объект является контейнером и может быть проиндексирован (доступ через случайный доступ) в одном или нескольких измерениях.
IPreferredRuntimeTypeConcept Объект понимает больше о типах, производных от него, чем базовая система типов, и хочет обрабатывать собственные преобразования из статического типа в тип среды выполнения.
IDynamicKeyProviderConcept Объект является динамическим поставщиком ключей и хочет взять на себя все ключевые запросы из основной модели данных. Этот интерфейс обычно используется в качестве моста к динамическим языкам, таким как JavaScript.
IDynamicConceptProviderConcept Объект является динамическим поставщиком концепций и хочет взять на себя все концептуальные запросы из основной модели данных. Этот интерфейс обычно используется в качестве моста к динамическим языкам, таким как JavaScript.

Концепция модели данных: IDataModelConcept

Любой объект модели, присоединенный к другому объекту модели в качестве родительской модели, должен напрямую поддерживать концепцию модели данных. Концепция модели данных требует поддержки интерфейса IDataModelConcept, определенного следующим образом.

DECLARE_INTERFACE_(IDataModelConcept, IUnknown)
{
    STDMETHOD(InitializeObject)(_In_ IModelObject* modelObject, _In_opt_ IDebugHostTypeSignature* matchingTypeSignature, _In_opt_ IDebugHostSymbolEnumerator* wildcardMatches) PURE;
    STDMETHOD(GetName)(_Out_ BSTR* modelName) PURE;
}

InitializeObject

Модель данных можно зарегистрировать как канонический визуализатор или как расширение для заданного машинного типа с помощью методов RegisterModelForTypeSignature или RegisterExtensionForTypeSignature диспетчера моделей данных. Когда модель регистрируется с помощью любого из этих методов, модель данных автоматически присоединяется как родительская модель к любому собственному объекту, тип которого соответствует сигнатуре, переданной при регистрации. В точке автоматического вложения вызывается метод InitializeObject в модели данных. Передается объект экземпляра, сигнатура типа, вызвавшего вложение, и перечислитель, который создает экземпляры типа (в линейном порядке), соответствующие подстановочным знакам в сигнатуре типа. Реализация модели данных может использовать этот вызов метода для инициализации всех необходимых кэшей.

GetName

Если данная модель данных зарегистрирована под именем по умолчанию с помощью метода RegisterNamedModel, интерфейс IDataModelConcept зарегистрированной модели данных должен возвращать это имя из этого метода. Обратите внимание, что вполне допустимо регистрировать модель под несколькими именами (здесь должно быть возвращено значение по умолчанию или лучшее). Модель может быть полностью безымяна (если она не зарегистрирована под именем). В таких случаях метод GetName должен возвращать E_NOTIMPL.

Отображаемая концепция строки: IStringDisplayableConcept

Объект, который хочет обеспечить преобразование строк в целях отображения, может реализовать концепцию отображаемой строки с помощью реализации интерфейса IStringDisplayableConcept. Интерфейс определяется следующим образом:

DECLARE_INTERFACE_(IStringDisplayableConcept, IUnknown)
{
    STDMETHOD(ToDisplayString)(_In_ IModelObject* contextObject, _In_opt_ IKeyStore* metadata, _Out_ BSTR* displayString) PURE;
}

ToDisplayString

Метод ToDisplayString вызывается всякий раз, когда клиент хочет преобразовать объект в строку для отображения (в консоль, в пользовательском интерфейсе и т. д.). Такое преобразование строк не должно использоваться для дополнительных программных манипуляций. Само преобразование строк может быть сильно зависеть от метаданных, передаваемых в вызов. При преобразовании строк следует выполнять каждую попытку учитывать ключи PreferredRadix и PreferredFormat.

Концепция Iterable: IIterableConcept и IModelIterator

Объект, который является контейнером других объектов и хочет выразить возможность перебора этих содержащихся объектов, может поддерживать итерируемую концепцию с помощью реализации интерфейсов IIterableConcept и IModelIterator. Существует очень важная связь между поддержкой итерируемой концепции и поддержкой индексируемой концепции. Объект, поддерживающий случайный доступ к автономным объектам, может поддерживать индексируемую концепцию в дополнение к итерируемой концепции. В этом случае итерированные элементы также должны создавать индекс по умолчанию, который при передаче в индексируемую концепцию ссылается на тот же объект. Неспособность удовлетворить этот инвариант приведет к неопределенному поведению в узле отладки.

IIterableConcept определяется следующим образом:

DECLARE_INTERFACE_(IIterableConcept, IUnknown)
{
    STDMETHOD(GetDefaultIndexDimensionality)(_In_ IModelObject* contextObject, _Out_ ULONG64* dimensionality) PURE;
    STDMETHOD(GetIterator)(_In_ IModelObject* contextObject, _Out_ IModelIterator** iterator) PURE;
}

Концепция IModelIterator определяется следующим образом:

DECLARE_INTERFACE_(IModelIterator, IUnknown)
{
   STDMETHOD(Reset)() PURE;
   STDMETHOD(GetNext)(_COM_Errorptr_ IModelObject** object, _In_ ULONG64 dimensions, _Out_writes_opt_(dimensions) IModelObject** indexers, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
}

GetDefaultIndexDimensionality IIterableConcept

Метод GetDefaultIndexDimensionality возвращает число измерений для индекса по умолчанию. Если объект не индексируется, этот метод должен возвращать значение 0 и успешно (S_OK). Любой объект, возвращающий ненулевое значение из этого метода, объявляет поддержку контракта протокола, в котором указано следующее:

  • Объект поддерживает индексируемую концепцию благодаря поддержке IIndexableConcept.
  • Метод GetNext IModelIterator, возвращенный методом GetIterator концепции итерации, вернет уникальный индекс по умолчанию для каждого создаваемого элемента. Такой индекс будет иметь количество измерений, как указано здесь.
  • Передача параметров, возвращаемых из метода GetNext IModelIterator, методу GetAt в индексируемой концепции (IIndexableConcept), будет ссылаться на тот же объект, что и GetNext. Возвращается то же значение.

GetIterator IIterableConcept

Метод GetIterator в концепции итерации возвращает интерфейс итератора, который можно использовать для итерации объекта. Возвращаемый итератор должен помнить объект контекста, переданный методу GetIterator. Он не будет передан в методы самого итератора.

Сброс IModelIterator

Метод Reset для итератора, возвращенного из итерируемой концепции, восстановит положение итератора до того места, где он был при создании итератора (до первого элемента). Хотя настоятельно рекомендуется, чтобы итератор поддерживал метод Reset, он не является обязательным. Итератор может быть эквивалентом входного итератора C++ и разрешать только один проход прямого итерации. В этом случае метод Reset может завершиться сбоем с E_NOTIMPL.

GetNext IModelIterator

Метод GetNext перемещает итератор вперед и извлекает следующий итерированный элемент. Если объект является индексируемым, а не является итерируемым и на это указывает аргумент GetDefaultIndexDimensionality, возвращающий ненулевое значение, этот метод может при необходимости возвращать значения по умолчанию, чтобы вернуться к полученному значению из индексатора. Обратите внимание, что вызывающий объект может передать значение 0/nullptr и не извлекать какие-либо типы. Запрашивать частичные типы (например, меньше, чем число, созданное getDefaultIndexDimensionality), считается недопустимым.

Если итератор успешно переместился вперед, но произошла ошибка при чтении значения итерированного элемента, метод может вернуть ошибку И заполнить "объект" объектом ошибки. В конце итерации содержащихся элементов итератор вернет E_BOUNDS из метода GetNext. Любой последующий вызов (если не был промежуточный вызов Reset) также будет возвращать E_BOUNDS.

Концепция индексируемых данных: IIndexableConcept

Объект, который хочет предоставить случайный доступ к набору содержимого, может поддерживать индексируемую концепцию через поддержку интерфейса IIndexableConcept. Большинство индексируемых объектов также будут доступны для итерации благодаря поддержке концепции итерации. Однако это не обязательно. Если это поддерживается, между итератором и индексатором существует важная связь. Итератор должен поддерживать GetDefaultIndexDimensionality, возвращать ненулевое значение из этого метода и поддерживать задокументированные там контракты. Интерфейс концепции индексатора определяется следующим образом:

DECLARE_INTERFACE_(IIndexableConcept, IUnknown)
{
    STDMETHOD(GetDimensionality)(_In_ IModelObject* contextObject, _Out_ ULONG64* dimensionality) PURE;
    STDMETHOD(GetAt)(_In_ IModelObject* contextObject, _In_ ULONG64 indexerCount, _In_reads_(indexerCount) IModelObject** indexers, _COM_Errorptr_ IModelObject** object, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
    STDMETHOD(SetAt)(_In_ IModelObject* contextObject, _In_ ULONG64 indexerCount, _In_reads_(indexerCount) IModelObject** indexers, _In_ IModelObject *value) PURE;
}

Ниже приведен пример использования индексатора (и его взаимодействия с итератором). В этом примере выполняется итерация содержимого индексируемого контейнера и используется индексатор для возврата к только что возвращенным значению. Хотя эта операция функционально бесполезна, как написано, она демонстрирует, как взаимодействуют эти интерфейсы. Обратите внимание, что приведенный ниже пример не относится к сбою выделения памяти. Предполагается, что создается новое (что может быть плохим предположением в зависимости от среды, в которой существует код. COM-методы модели данных не могут иметь исключение C++):

ComPtr<IModelObject> spObject;

//
// Assume we have gotten some object in spObject that is iterable (e.g.: an object which represents a std::vector<SOMESTRUCT>)
//
ComPtr<IIterableConcept> spIterable;
ComPtr<IIndexableConcept> spIndexer;
if (SUCCEEDED(spObject->GetConcept(__uuidof(IIterableConcept), &spIterable, nullptr)) &&
    SUCCEEDED(spObject->GetConcept(__uuidof(IIndexableConcept), &spIndexable, nullptr)))
{
    ComPtr<IModelIterator> spIterator;

    //
    // Determine how many dimensions the default indexer is and allocate the requisite buffer.
    //
    ULONG64 dimensions;
    if (SUCCEEDED(spIterable->GetDefaultIndexDimensionality(spObject.Get(), &dimensions)) && dimensions > 0 &&
        SUCCEEDED(spIterable->GetIterator(spObject.Get(), &spIterator)))
    {
        std::unique_ptr<ComPtr<IModelObject>[]> spIndexers(new ComPtr<IModelObject>[dimensions]);

        //
        // We have an iterator.  Error codes have semantic meaning here.  E_BOUNDS indicates the end of iteration.  E_ABORT indicates that
        // the debugger host or application is trying to abort whatever operation is occurring.  Anything else indicates
        // some other error (e.g.: memory read failure) where the iterator MIGHT still produce values.
        //
        for(;;)
        {
            ComPtr<IModelObject> spContainedStruct;
            ComPtr<IKeyStore> spContainedMetadata;

            //
            // When we fetch the value from the iterator, it will pass back the default indicies.
            //
            HRESULT hr = spIterable->GetNext(&spContainedStruct, dimensions, reinterpret_cast<IModelObject **>(spIndexers.get()), &spContainedMetadata);
            if (hr == E_BOUNDS || hr == E_ABORT)
            {
                break;
            }

            if (FAILED(hr))
            {
                //
                // Decide how to deal with failure to fetch an element.  Note that spContainedStruct *MAY* contain an error object
                // which has detailed information about why the failure occurred (e.g.: failure to read memory at address X).
                //
            }

            //
            // Use the indexer to get back to the same value.  We already have them, so there isn't much functional point to this.  It simply
            // highlights the interplay between iterator and indexer.
            //
            ComPtr<IModelObject> spIndexedStruct;
            ComPtr<IKeyStore> spIndexedMetadata;

            if (SUCCEEDED(spIndexer->GetAt(spObject.Get(), dimensions, reinterpret_cast<IModelObject **>(spIndexers.get()), &spIndexedStruct, &spIndexedMetadata)))
            {
                //
                // spContainedStruct and spIndexedStruct refer to the same object.  They may not have interface equality.
                // spContainedMetadata and spIndexedMetadata refer to the same metadata store with the same contents.  They may not have interface equality.
                //
            }
        }
    }
}

GetDimensionality

Метод GetDimensionality возвращает количество измерений, в которых индексируется объект. Обратите внимание, что если объект является одновременно итерируемым и индексируемым, реализация GetDefaultIndexDimensionality должна соответствовать реализации GetDimensionality относительно количества измерений, имеющихся в индексаторе.

Getat

Метод GetAt извлекает значение по определенному N-мерному индексу из индексированного объекта. Должен поддерживаться индексатор N-измерений, где N — это значение, возвращаемое из GetDimensionality. Обратите внимание, что объект может индексироваться в разных доменах по разным типам (например, индексироваться как по порядковым номерам, так и по строкам). Если индекс выходит за пределы диапазона (или не удается получить доступ), метод вернет ошибку; Однако в таких случаях выходной объект по-прежнему может быть задан как объект error.

Setat

Метод SetAt пытается задать значение по определенному N-мерному индексу из индексированного объекта. Должен поддерживаться индексатор N-измерений, где N — это значение, возвращаемое из GetDimensionality. Обратите внимание, что объект может индексироваться в разных доменах по разным типам (например, индексироваться как по порядковым номерам, так и по строкам). Некоторые индексаторы доступны только для чтения. В таких случаях E_NOTIMPL будут возвращены из любого вызова метода SetAt.

Концепция предпочтительного типа среды выполнения: IPreferredRuntimeTypeConcept

Узел отладки можно запросить, чтобы попытаться определить реальный тип среды выполнения объекта из статического типа, найденного в символьной информации. Это преобразование может быть основано на полностью точных сведениях (например, C++ RTTI) или на строгой эвристики, такой как форма любых виртуальных таблиц функций в объекте. Однако некоторые объекты не могут быть преобразованы из статического типа в тип среды выполнения, так как они не соответствуют эвристикам узла отладки (например, они не имеют RTTI или виртуальных таблиц функций). В таких случаях модель данных для объекта может переопределить поведение по умолчанию и объявить, что ей известно больше о "типе среды выполнения" объекта, чем способен понимать узел отладки. Для этого используется предпочтительная концепция типа среды выполнения и поддержка интерфейса IPreferredRuntimeTypeConcept.

Интерфейс IPreferredRuntimeTypeConcept объявляется следующим образом:

DECLARE_INTERFACE_(IPreferredRuntimeTypeConcept, IUnknown)
{
    STDMETHOD(CastToPreferredRuntimeType)(_In_ IModelObject* contextObject, _COM_Errorptr_ IModelObject** object) PURE;
}

CastToPreferredRuntimeType

Метод CastToPreferredRuntimeType вызывается всякий раз, когда клиент хочет выполнить преобразование из экземпляра статического типа в тип среды выполнения этого экземпляра. Если рассматриваемый объект поддерживает (через одну из присоединенных родительских моделей) концепцию предпочтительного типа среды выполнения, этот метод будет вызываться для выполнения преобразования. Этот метод может либо возвращать исходный объект (преобразование отсутствует или его не удалось проанализировать), либо возвращать новый экземпляр типа среды выполнения, сбой из-за неемантических причин (например, нехватки памяти) или возвращать E_NOT_SET. Код ошибки E_NOT_SET — это специальный код ошибки, который указывает модели данных, что реализация не хочет переопределять поведение по умолчанию и что модель данных должна вернуться к анализу, выполняемому узлом отладки (например, анализ RTTI, анализ формы виртуальных таблиц функций, и т. д.)

Основные понятия динамического поставщика: IDynamicKeyProviderConcept и IDynamicConceptProviderConcept

Хотя сама модель данных обычно обрабатывает управление ключами и понятиями для объектов, бывают случаи, когда это понятие является менее идеальным. В частности, если клиент хочет создать мост между моделью данных и чем-то другим, который является действительно динамическим (например, JavaScript), может быть полезно взять на себя управление ключами и концепциями из реализации в модели данных. Так как базовая модель данных является единственной реализацией IModelObject, это делается с помощью сочетания двух концепций: динамического поставщика ключей и концепции поставщика динамических концепций. Хотя было бы типично реализовать оба или ни то, ни другое, нет никаких требований к такому.

Если реализованы оба поставщика, необходимо добавить концепцию поставщика динамических ключей до концепции поставщика динамических концепций. Обе эти концепции являются особенными. Они эффективно переворачивают переключатель на объекте, изменяя его с "статически управляемым" на "динамически управляемый". Эти понятия можно задать только в том случае, если в объекте нет ключей и концепций, управляемых моделью данных. После добавления этих понятий к объекту действие выполнения этого действия становится необратимым.

Существует дополнительное семантическое различие в расширяемости между IModelObject, который является поставщиком динамических концепций, и тем, который не является. Эти концепции позволяют клиентам создавать мосты между моделью данных и динамическими языковыми системами, такими как JavaScript. Модель данных имеет концепцию расширяемости, которая несколько отличается от таких систем, как JavaScript, тем, что существует дерево родительских моделей, а не линейная цепочка, как цепочка прототипов JavaScript. Чтобы обеспечить более эффективную связь с такими системами, IModelObject, который является поставщиком динамических концепций, имеет один родительский объект модели данных. Один родительский объект модели данных является обычным объектом IModelObject, который может иметь произвольное количество родительских моделей, как это характерно для модели данных. Все запросы к поставщику динамических концепций для добавления или удаления родительских элементов автоматически перенаправляются в один родительский объект. С точки зрения аутсайдера поставщик динамических концепций имеет обычную цепочку стилей дерева родительских моделей. Реализацией концепции поставщика динамических концепций является единственный объект (за пределами основной модели данных), который знает о промежуточном единственном родительском объекте. Этот родительский элемент можно связать с динамической языковой системой, чтобы обеспечить мост (например, поместить в цепочку прототипов JavaScript).

Концепция поставщика динамических ключей определяется следующим образом:

DECLARE_INTERFACE_(IDynamicKeyProviderConcept, IUnknown)
{
    STDMETHOD(GetKey)(_In_ IModelObject *contextObject, _In_ PCWSTR key, _COM_Outptr_opt_result_maybenull_ IModelObject** keyValue, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata, _Out_opt_ bool *hasKey) PURE;
    STDMETHOD(SetKey)(_In_ IModelObject *contextObject, _In_ PCWSTR key, _In_ IModelObject *keyValue, _In_ IKeyStore *metadata) PURE;
    STDMETHOD(EnumerateKeys)(_In_ IModelObject *contextObject, _COM_Outptr_ IKeyEnumerator **ppEnumerator) PURE;
}

Концепция поставщика динамических концепций определяется следующим образом:

DECLARE_INTERFACE_(IDynamicConceptProviderConcept, IUnknown)
{
    STDMETHOD(GetConcept)(_In_ IModelObject *contextObject, _In_ REFIID conceptId, _COM_Outptr_result_maybenull_ IUnknown **conceptInterface, _COM_Outptr_opt_result_maybenull_ IKeyStore **conceptMetadata, _Out_ bool *hasConcept) PURE;
    STDMETHOD(SetConcept)(_In_ IModelObject *contextObject, _In_ REFIID conceptId, _In_ IUnknown *conceptInterface, _In_opt_ IKeyStore *conceptMetadata) PURE;
    STDMETHOD(NotifyParent)(_In_ IModelObject *parentModel) PURE;
    STDMETHOD(NotifyParentChange)(_In_ IModelObject *parentModel) PURE;
    STDMETHOD(NotifyDestruct)() PURE;
}

GetKey объекта IDynamicKeyProviderConcept

Метод GetKey в динамическом поставщике ключей в основном является переопределением метода GetKey в IModelObject. Поставщик динамических ключей должен возвращать значение ключа и все метаданные, связанные с этим ключом. Если ключ отсутствует (но не возникает других ошибок), поставщик должен вернуть значение false в параметре hasKey и успешно выполнить S_OK. Сбой этого вызова считается ошибкой при получении ключа и явно останавливает поиск ключа через цепочку родительской модели. При возврате false в hasKey и успешном выполнении будет продолжен поиск ключа. Обратите внимание, что для GetKey совершенно законно возвращать в качестве ключа коробный метод доступа свойства. Это будет семантически идентично методу GetKey в IModelObject, возвращающим метод доступа свойства.

Ключ SetKey объекта IDynamicKeyProviderConcept

Метод SetKey в динамическом поставщике ключей фактически является переопределением метода SetKey в IModelObject. При этом задается ключ в динамическом поставщике. Фактически это создание нового свойства в поставщике. Обратите внимание, что поставщик, который не поддерживает какие-либо понятия, например создание свойств expando, должен возвращать E_NOTIMPL здесь.

EnumerateKeys IDynamicKeyProviderConcept

Метод EnumerateKeys в динамическом поставщике ключей фактически является переопределением метода EnumerateKeys в IModelObject. При этом перечисляются все ключи в динамическом поставщике. Возвращаемый перечислитель имеет несколько ограничений, которые должны соблюдаться реализацией:

  • Он должен вести себя как вызов EnumerateKeys, а не EnumerateKeyValues или EnumerateKeyReferences. Он должен возвращать значения ключей, не разрешающие какие-либо базовые методы доступа к свойствам (если такая концепция существует в поставщике).
  • С точки зрения одного поставщика динамических ключей перечисление нескольких ключей с одним именем, которые являются физически разными ключами, является недопустимым. Это может произойти у разных поставщиков, подключенных через цепочку родительской модели, но это не может произойти с точки зрения одного поставщика.

GetConcept в IDynamicConceptProviderConcept

Метод GetConcept в поставщике динамических концепций фактически является переопределением метода GetConcept в IModelObject. Поставщик динамических концепций должен возвращать интерфейс для запрашиваемой концепции, если он существует, а также любые метаданные, связанные с этой концепцией. Если концепция не существует в поставщике, это должно быть указано с помощью ложного значения, возвращаемого в аргументе hasConcept, и успешного возврата. Сбой этого метода является неспособностью получить концепцию и явно остановит поиск концепции. Возвращая значение false для hasConcept, и успешный код продолжит поиск концепции через родительское дерево модели.

SetConcept в IDynamicConceptProviderConcept

Метод SetConcept в поставщике динамических концепций фактически является переопределением метода SetConcept в IModelObject. Динамический поставщик назначит эту концепцию. Это может сделать объект итерируемым, индексируемым, преобразуемым строк и т. д. Обратите внимание, что поставщик, который не позволяет создавать концепции на нем, должен возвращать E_NOPTIMPL здесь.

NotifyParent iDynamicConceptProviderConcept

Вызов NotifyParent в поставщике динамических концепций используется основной моделью данных для информирования динамического поставщика об одно родительской модели, созданной для обеспечения связи парадигмы "нескольких родительских моделей" модели данных с более динамическими языками. При любых манипуляциях с одной родительской моделью динамический поставщик будет отправлять дополнительные уведомления. Обратите внимание, что этот обратный вызов выполняется сразу после назначения концепции поставщика динамических концепций.

NotifyParentChange в IDynamicConceptProviderConviderConcept

Метод NotifyParent для поставщика динамических концепций — это обратный вызов, выполняемый основной моделью данных при выполнении статической манипуляции с одно родительской моделью объекта. Для любой добавленной родительской модели этот метод будет вызываться в первый раз при добавлении указанной родительской модели и во второй раз в случае удаления указанной родительской модели.

NotifyDestruct в IDynamicConceptProviderConviderConcept

Метод NotifyDestruct для поставщика динамических концепций — это обратный вызов, выполняемый основной моделью данных в начале уничтожения объекта, который является поставщиком динамических концепций. Он предоставляет дополнительные возможности очистки для клиентов, которым это требуется.

--

См. также

Этот раздел является частью серии, в которой описываются интерфейсы, доступные из C++, способы их использования для создания расширения отладчика на основе C++, а также способы использования других конструкций модели данных (например, JavaScript или NatVis) из расширения модели данных C++.

Общие сведения о модели данных отладчика C++

Интерфейсы C++ модели данных отладчика

Объекты C++ модели данных отладчика

Дополнительные интерфейсы C++ модели данных отладчика

Основные понятия модели данных отладчика C++

Скрипты C++ модели данных отладчика