Поделиться через


Программирование Visual C++ ADO

Справочник по API ADO описывает функциональные возможности интерфейса программирования приложений ADO (API), используя синтаксис, аналогичный Microsoft Visual Basic. Хотя целевая аудитория — это все пользователи, программисты ADO используют различные языки, такие как Visual Basic, Visual C++ (с директивой #import ) и Visual J++ (с пакетом класса ADO/WFC).

Замечание

В 2004 году была прекращена поддержка Visual J++.

В соответствии с этим разнообразием индексы синтаксиса ADO для Visual C++ предоставляют синтаксис для языка Visual C++ со ссылками на общие описания функций, параметров, исключительных действий и т. д. в справочнике по API.

ADO реализуется с интерфейсами COM (компонентная объектная модель). Однако программистам проще работать с COM на определенных языках программирования, чем другие. Например, почти все детали использования COM обрабатываются автоматически и неявно для программистов Visual Basic, в то время как программисты Visual C++ должны самостоятельно разбираться с этими деталями.

В следующих разделах приведены сведения для программистов C и C++, использующих ADO и директиву #import. Он фокусируется на типах данных, относящихся к COM (Variant, BSTR и SafeArray), а также обработке ошибок (_com_error).

Использование директивы компилятора #import

Директива компилятора Visual C++ #import упрощает работу с методами и свойствами ADO. Директива принимает имя файла, содержащего библиотеку типов, например ADO .dll (Msado15.dll), и создает файлы заголовков, содержащие объявления typedef, смарт-указатели для интерфейсов и перечисляемые константы. Каждый интерфейс инкапсулируется или упаковывается в класс.

Для каждой операции в классе (то есть вызова метода или свойства) существует объявление для вызова операции напрямую (то есть необработанной формы операции), а также объявление для вызова необработанной операции и генерации COM-ошибки, если операция не была выполнена успешно. Если операция является свойством, обычно существует директива компилятора, которая создает альтернативный синтаксис для операции, похожий на синтаксис Visual Basic.

Операции, извлекающие значение свойства, имеют имена формы, GetProperty. Операции, которые задают значение свойства, имеют имена формы , PutProperty. Операции, которые устанавливают значение свойства с помощью указателя на объект ADO, имеют имена формы PutRefProperty.

Вы можете получить или установить свойство с помощью вызовов следующего вида:

variable = objectPtr->GetProperty(); // get property value   
objectPtr->PutProperty(value);       // set property value  
objectPtr->PutRefProperty(&value);   // set property with object pointer  

Использование директив свойств

Директива компилятора __declspec(property...) — это расширение языка C, которое объявляет функцию, используемую в качестве свойства, для использования альтернативного синтаксиса. В результате можно задать или получить значения свойства таким образом, как и в Visual Basic. Например, вы можете задать и получить свойство таким образом:

objectPtr->property = value;        // set property value  
variable = objectPtr->property;     // get property value  

Обратите внимание, что вам не нужно кодировать:

objectPtr->PutProperty(value);      // set property value  
variable = objectPtr->GetProperty;  // get property value  

Компилятор создаст соответствующий вызов Get-, Put или PutRefProperty в зависимости от того, какой альтернативный синтаксис указан и считывается или записывается ли свойство.

Директива компилятора __declspec(property...) может объявлять только get, put или get и put как альтернативный синтаксис для функции. Операции только для чтения имеют декларацию get; операции только для записи имеют декларацию put; операции, которые могут как читать, так и записывать, имеют как декларации get, так и put.

С этой директивой возможны только два объявления; однако каждое свойство может иметь три функции свойств: GetProperty, PutProperty и PutRefProperty. В этом случае только две формы свойства имеют альтернативный синтаксис.

Например, для объекта Command свойство ActiveConnection объявлено альтернативным синтаксисом для GetActiveConnection и PutRefActiveConnection. Синтаксис PutRef — это хороший вариант, так как на практике обычно требуется поместить открытый объект Connection (т. е. указатель объекта Connection) в это свойство. С другой стороны, объект Recordset имеет операции Get-, Put- и PutRefActiveConnection, но нет альтернативного синтаксиса.

Коллекции, метод GetItem и свойство item

ADO определяет несколько коллекций, включая поля, параметры, свойства и ошибки. В Visual C++метод GetItem(index) возвращает элемент коллекции. Индекс — это Variant, значение которого является числовым индексом элемента в коллекции или строкой, содержащей имя элемента.

Директива компилятора __declspec(property...) объявляет свойство Item в качестве альтернативного синтаксиса для основного метода GetItem() каждой коллекции. Альтернативный синтаксис использует квадратные скобки и выглядит примерно так же, как ссылка на массив. Как правило, две формы выглядят следующим образом:

  
      collectionPtr->GetItem(index);  
collectionPtr->Item[index];  

Например, назначьте значение полю объекта Recordset с именем RS, производным от таблицы авторов базы данных pubs . Используйте свойство Item() для доступа к третьему полю коллекции полей объектов Recordset (коллекции индексируются от нуля; предположим, что третье поле называется au_fname). Затем вызовите метод Value() объекта Field , чтобы назначить строковое значение.

Это можно выразить в Visual Basic следующими четырьмя способами (последние две формы уникальны для Visual Basic; другие языки не имеют эквивалентов):

rs.Fields.Item(2).Value = "value"  
rs.Fields.Item("au_fname").Value = "value"  
rs(2) = "value"  
rs!au_fname = "value"  

Эквивалент в Visual C++ для первых двух указанных выше форм:

rs->Fields->GetItem(long(2))->PutValue("value");   
rs->Fields->GetItem("au_fname")->PutValue("value");  

-или- (альтернативный синтаксис для свойства Value также показан)

rs->Fields->Item[long(2)]->Value = "value";  
rs->Fields->Item["au_fname"]->Value = "value";  

Примеры итерации по коллекции см. в разделе "Коллекции ADO" статьи "Справочник по ADO".

Типы данных COM-Specific

Как правило, любой тип данных Visual Basic, который вы найдете в справочнике по API ADO, имеет эквивалент Visual C++. К ним относятся стандартные типы данных, например unsigned char для байта Visual Basic, короткие для целочисленного числа и длинные для long. Просмотрите синтаксические индексы, чтобы точно увидеть, что требуется для операндов заданного метода или свойства.

Исключениями этого правила являются типы данных, относящиеся к COM: Variant, BSTR и SafeArray.

Вариант

Variant — это структурированный тип данных, содержащий элемент значения и член типа данных. Вариант может содержать широкий спектр других типов данных, включая другой вариант, BSTR, логический, IDispatch или указатель IUnknown, валюту, дату и т. д. COM также предоставляет методы, которые упрощают преобразование одного типа данных в другой.

Класс _variant_t инкапсулирует и управляет типом данных Variant .

Когда справочник по API ADO говорит, что метод или операнд свойств принимает значение, обычно это означает, что значение передается в _variant_t.

Это правило явно верно, если раздел "Параметры " в разделах справочника по API ADO говорит, что операнд является вариантом. Одно из исключений заключается в том, что в документации явно указано, что операнда принимает стандартный тип данных, например Long или Byte, или перечисление. Другим исключением является случай, когда операнд принимает строку.

BSTR

BSTR (Basic STRing) — это структурированный тип данных, содержащий символьную строку и длину строки. COM предоставляет методы для выделения, управления и освобождения BSTR.

Класс _bstr_t инкапсулирует и управляет типом данных BSTR .

Если справочник по API ADO говорит, что метод или свойство принимает строковое значение, это означает, что значение находится в виде _bstr_t.

Приведение классов _variant_t и _bstr_t

Часто не нужно явно кодировать _variant_t или _bstr_t в аргументе операции. Если в классе _variant_t или _bstr_t есть конструктор, соответствующий типу данных аргумента, компилятор создаст соответствующие _variant_t или _bstr_t.

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

Например, объявление для метода Recordset::Open :

    HRESULT Open (  
        const _variant_t & Source,  
        const _variant_t & ActiveConnection,  
        enum CursorTypeEnum CursorType,  
        enum LockTypeEnum LockType,  
        long Options );  

Аргумент ActiveConnection принимает ссылку на _variant_t, которую можно кодировать как строку подключения или указатель на открытый объект Connection .

Правильный _variant_t будет создан неявно, если вы передаете строку, например "DSN=pubs;uid=MyUserName;pwd=<password>;", или указатель, например "(IDispatch *) pConn".

Замечание

Если вы подключаетесь к поставщику источников данных, поддерживающему аутентификацию Windows, следует указать Trusted_Connection=да или Интегрированная безопасность = SSPI вместо идентификатора пользователя и пароля в строке подключения.

Или вы можете явным образом закодить _variant_t , содержащую указатель, например "_variant_t((IDispatch *) pConn, true)". Приведение (IDispatch *) разрешает неоднозначность, используя другой конструктор, который принимает указатель на интерфейс IUnknown.

Это крайне важно, хотя редко упоминалось о том, что ADO является интерфейсом IDispatch. Всякий раз, когда указатель на объект ADO должен передаваться как Variant, этот указатель должен быть приведен как указатель на интерфейс IDispatch.

Последний случай явно задает второй логический аргумент конструктора, используя его необязательное значение по умолчанию true. Этот аргумент вызывает конструктор Variant, который в свою очередь вызывает метод AddRef(), компенсируя автоматический вызов метода _variant_t::Release() при завершении вызова метода или свойства ADO.

SafeArray

SafeArray — это структурированный тип данных, содержащий массив других типов данных. SafeArray называется безопасным, так как содержит сведения о границах каждого измерения массива и ограничивает доступ к элементам массива в пределах этих границ.

Если в справочнике по API ADO указано, что метод или свойство принимает или возвращает массив, это означает, что метод или свойство принимает или возвращает SafeArray, а не родной массив C/C++.

Например, для второго параметра метода OpenSchema объекта Connection требуется массив значений Variant. Эти значения Variant должны быть переданы как элементы SafeArray, и SafeArray необходимо задать в качестве значения другого Variant. Это тот другой вариант, который передается в качестве второго аргумента OpenSchema.

В качестве дополнительных примеров первый аргумент метода Find является Variant , значение которого является одномерным SafeArray; каждый из необязательных первого и второго аргументов AddNew является одномерным safeArray; и возвращаемое значение метода GetRowsvariant , значение которого является двухмерным SafeArray.

Отсутствующие параметры и параметры по умолчанию

Visual Basic позволяет пропускать параметры в методах. Например, метод Recordset object Open имеет пять параметров, но вы можете пропустить промежуточные параметры и оставить конечные параметры. BSTR или Variant по умолчанию будет подставлен в зависимости от типа данных отсутствующего операнда.

В C/C++все операнды должны быть указаны. Если вы хотите указать отсутствующий параметр, тип данных которого является строкой, укажите _bstr_t , содержащую пустую строку. Если вы хотите указать отсутствующий параметр, тип данных которого является Variant, укажите _variant_t со значением DISP_E_PARAMNOTFOUND и типом VT_ERROR. Кроме того, укажите эквивалентную константу _variant_tvtMissing, которая предоставляется директивой #import .

Три метода являются исключениями для типичного использования vtMissing. Это методы Execute объектов Connection и Command , а также метод NextRecordset объекта Recordset . Ниже приведены их подписи:

_RecordsetPtr <A HREF="mdmthcnnexecute.htm">Execute</A>( _bstr_t CommandText, VARIANT * RecordsAffected,   
        long Options );  // Connection  
_RecordsetPtr <A HREF="mdmthcmdexecute.htm">Execute</A>( VARIANT * RecordsAffected, VARIANT * Parameters,   
        long Options );  // Command  
_RecordsetPtr <A HREF="mdmthnextrec.htm">NextRecordset</A>( VARIANT * RecordsAffected );  // Recordset  

Параметры RecordsAffected и Parameters являются указателями на Variant. Параметры — это входной параметр, указывающий адрес Variant , содержащий один параметр или массив параметров, который изменит выполняемую команду. RecordsAffected — это выходной параметр, указывающий адрес Variant, в котором возвращается количество строк, затронутых методом.

В методе Execute объекта Command укажите, что параметры не заданы, установив Parameters в значение &vtMissing (что рекомендуется) или в null-указатель (то есть NULL или ноль (0)). Если для параметра задано значение NULL, метод внутренне заменяет эквивалент vtMissing, а затем завершает операцию.

Во всех методах укажите, что количество затронутых записей не должно быть возвращено, установив для RecordsAffected нулевой указатель. В этом случае указатель null является не столько отсутствующим параметром, сколько указанием на то, что метод должен игнорировать количество затронутых записей.

Таким образом, для этих трех методов допустимо кодировать что-то вроде:

pConnection->Execute("commandText", NULL, adCmdText);   
pCommand->Execute(NULL, NULL, adCmdText);  
pRecordset->NextRecordset(NULL);  

Обработка ошибок

В COM большинство операций возвращают код возврата HRESULT, указывающий, успешно ли выполнена функция. Директива #import создает код оболочки вокруг каждого метода или свойства raw и проверяет возвращенный HRESULT. Если HRESULT указывает на сбой, код-оболочка вызывает ошибку COM путем вызова _com_issue_errorex() с кодом возврата HRESULT в качестве аргумента. Объекты ошибок COM можно поймать в блоке try-catch. Для повышения эффективности создайте ссылку на объект _com_error.

Помните, что это ошибки ADO: они приводят к сбою операции ADO. Ошибки, возвращаемые базовым поставщиком, отображаются как объекты Error в коллекции ошибок объекта Подключения.

Директива #import создает только подпрограммы обработки ошибок для методов и свойств, объявленных в .dllADO. Однако вы можете воспользоваться этим же механизмом обработки ошибок, написав собственный макрос или встроенную функцию проверки ошибок. См. раздел, расширения Visual C++ или код в следующих разделах, например.

Эквиваленты Visual C++ правил и соглашений Visual Basic

Ниже приведена сводка по нескольким соглашениям в документации по ADO, закодированных в Visual Basic, а также их эквивалентов в Visual C++.

Объявление объекта ADO

В Visual Basic переменная объекта ADO (в данном случае для объекта Recordset ) объявляется следующим образом:

Dim rst As ADODB.Recordset  

Условие "ADODB.Recordset" - это ProgID объекта Recordset, как определено в реестре. Новый экземпляр объекта Record объявлен следующим образом:

Dim rst As New ADODB.Recordset  

-или-

Dim rst As ADODB.Recordset  
Set rst = New ADODB.Recordset  

В Visual C++директива #import создает объявления интеллектуального типа указателя для всех объектов ADO. Например, переменная, указывающая на объект _Recordset , имеет тип _RecordsetPtr, и объявляется следующим образом:

_RecordsetPtr  rs;  

Переменная, указывающая на новый экземпляр объекта _Recordset , объявляется следующим образом:

_RecordsetPtr  rs("ADODB.Recordset");  

-или-

_RecordsetPtr  rs;  
rs.CreateInstance("ADODB.Recordset");  

-или-

_RecordsetPtr  rs;  
rs.CreateInstance(__uuidof(_Recordset));  

После вызова метода CreateInstance переменная может использоваться следующим образом:

rs->Open(...);  

Обратите внимание, что в одном случае оператор "." используется так, как если бы переменная была экземпляром класса (rs.CreateInstance), а в другом случае оператор "->" используется как если бы переменная была указателем на интерфейс (rs->Open).

Одна переменная может использоваться двумя способами, так как оператор "->" перегружен, чтобы позволить экземпляру класса вести себя как указатель на интерфейс. Член частного класса переменной экземпляра содержит указатель на интерфейс _Recordset; оператор "->" возвращает этот указатель, а возвращённый указатель используется для обращения к членам объекта _Recordset.

Кодирование отсутствующих параметров — строка

Если вам нужно пропустить отсутствующий операнд String в Visual Basic, вы просто пропускаете операнд. Необходимо указать операнду в Visual C++. Код _bstr_t с пустой строкой в качестве значения.

_bstr_t strMissing(L"");  

Кодирование отсутствующих параметров — Variant

Если вам нужно закодировать отсутствующий операнд Variant в Visual Basic, вы просто опустите операнду. Необходимо указать все операнды в Visual C++. Задайте отсутствующий параметр Variant с использованием _variant_t, заданным специальным значением DISP_E_PARAMNOTFOUND и типом VT_ERROR. Кроме того, укажите vtMissing, которая является эквивалентной предопределенной константой, предоставленной директивой #import .

_variant_t  vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);   

-или использовать -

...vtMissing...;  

Объявление варианта

В Visual Basic Variant объявляется с помощью инструкции Dim следующим образом:

Dim VariableName As Variant  

В Visual C++объявите переменную как тип _variant_t. Ниже показаны несколько схемных объявлений _variant_t .

Замечание

Эти объявления просто дают грубое представление о том, что вы будете кодировать в собственной программе. Дополнительные сведения см. в приведенных ниже примерах и документации по Visual C++.

_variant_t  VariableName(value);  
_variant_t  VariableName((data type cast) value);  
_variant_t  VariableName(value, VT_DATATYPE);  
_variant_t  VariableName(interface * value, bool fAddRef = true);  

Использование массивов вариантов

В Visual Basic массивы с типом данных Variant можно объявить с помощью инструкции Dim или использовать функцию Array, как показано в следующем примере кода.

Public Sub ArrayOfVariants  
Dim cn As ADODB.Connection  
Dim rs As ADODB.Recordset  
Dim fld As ADODB.Field  
  
    cn.Open "DSN=pubs"  
    rs = cn.OpenSchema(adSchemaColumns, _  
        Array(Empty, Empty, "authors", Empty))  
    For Each fld in rs.Fields  
        Debug.Print "Name = "; fld.Name  
    Next fld  
    rs.Close  
    cn.Close  
End Sub  

В следующем примере Visual C++ показано использование SafeArray, используемого с _variant_t.

Примечания.

Следующие заметки соответствуют закомментированных разделам в примере кода.

  1. Еще раз встроенная функция TESTHR() определяется для использования существующего механизма обработки ошибок.

  2. Вам нужен только одномерный массив, поэтому вы можете использовать SafeArrayCreateVector вместо объявления SAFEARRAYBOUND и функции SafeArrayCreate . Ниже показано, как выглядит этот код с помощью SafeArrayCreate:

       SAFEARRAYBOUND   sabound[1];  
       sabound[0].lLbound = 0;  
       sabound[0].cElements = 4;  
       pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);  
    
  3. Схема, определяемая перечисленной константой AdSchemaColumns, связана с четырьмя столбцами ограничений: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME и COLUMN_NAME. Поэтому создается массив значений Variant с четырьмя элементами. Затем указывается значение ограничения, соответствующее третьему столбцу, TABLE_NAME.

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

  4. Знающие SafeArrays могут быть удивлены тем, что SafeArrayDestroy() не вызывается перед выходом. На самом деле вызов SafeArrayDestroy() в этом случае приведет к исключению во время выполнения. Причина заключается в том, что деструктор для vtCriteria вызовет VariantClear(), когда _variant_t выходит из области видимости, что освободит SafeArray. Вызов SafeArrayDestroy, не очищая _variant_t вручную, может привести к тому, что деструктор попытается очистить недействительный указатель SafeArray.

    Если был бы вызван SafeArrayDestroy, код выглядел бы следующим образом:

          TESTHR(SafeArrayDestroy(pSa));  
       vtCriteria.vt = VT_EMPTY;  
          vtCriteria.parray = NULL;  
    

    Тем не менее, гораздо проще позволить _variant_t управлять SafeArray.

// Visual_CPP_ADO_Prog_1.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
// Note 1  
inline void TESTHR( HRESULT _hr ) {   
   if FAILED(_hr)   
      _com_issue_error(_hr);   
}  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _RecordsetPtr pRs("ADODB.Recordset");  
      _ConnectionPtr pCn("ADODB.Connection");  
      _variant_t vtTableName("authors"), vtCriteria;  
      long ix[1];  
      SAFEARRAY *pSa = NULL;  
  
      pCn->Provider = "sqloledb";  
      pCn->Open("Data Source='(local)';Initial Catalog='pubs';Integrated Security=SSPI", "", "", adConnectUnspecified);  
      // Note 2, Note 3  
      pSa = SafeArrayCreateVector(VT_VARIANT, 1, 4);  
      if (!pSa)   
         _com_issue_error(E_OUTOFMEMORY);  
  
      // Specify TABLE_NAME in the third array element (index of 2).   
      ix[0] = 2;        
      TESTHR(SafeArrayPutElement(pSa, ix, &vtTableName));  
  
      // There is no Variant constructor for a SafeArray, so manually set the   
      // type (SafeArray of Variant) and value (pointer to a SafeArray).  
  
      vtCriteria.vt = VT_ARRAY | VT_VARIANT;  
      vtCriteria.parray = pSa;  
  
      pRs = pCn->OpenSchema(adSchemaColumns, vtCriteria, vtMissing);  
  
      long limit = pRs->GetFields()->Count;  
      for ( long x = 0 ; x < limit ; x++ )  
         printf( "%d: %s\n", x + 1, ((char*) pRs->GetFields()->Item[x]->Name) );  
      // Note 4  
      pRs->Close();  
      pCn->Close();  
   }  
   catch (_com_error &e) {  
      printf("Error:\n");  
      printf("Code = %08lx\n", e.Error());  
      printf("Code meaning = %s\n", (char*) e.ErrorMessage());  
      printf("Source = %s\n", (char*) e.Source());  
      printf("Description = %s\n", (char*) e.Description());  
   }  
   CoUninitialize();  
}  

Использование свойства Get/Put/PutRef

В Visual Basic имя свойства не уточняется, извлекается ли оно, присваивается или присваивается ссылка.

Public Sub GetPutPutRef  
Dim rs As New ADODB.Recordset  
Dim cn As New ADODB.Connection  
Dim sz as Integer  
cn.Open "Provider=sqloledb;Data Source=yourserver;" & _  
         "Initial Catalog=pubs;Integrated Security=SSPI;"  
rs.PageSize = 10  
sz = rs.PageSize  
rs.ActiveConnection = cn  
rs.Open "authors",,adOpenStatic  
' ...  
rs.Close  
cn.Close  
End Sub  

В этом примере Visual C++ демонстрируется Get/Put/PutRefProperty.

Примечания.

Следующие заметки соответствуют закомментированных разделам в примере кода.

  1. В этом примере используются две формы отсутствующего строкового аргумента: явная константа, strMissing и строка, используемая компилятором для создания временной _bstr_t , которая будет существовать для области метода Open .

  2. Не обязательно приводить операнд из rs->PutRefActiveConnection(cn) к (IDispatch *), потому что тип операнда уже (IDispatch *).

// Visual_CPP_ado_prog_2.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr cn("ADODB.Connection");  
      _RecordsetPtr rs("ADODB.Recordset");  
      _bstr_t strMissing(L"");  
      long oldPgSz = 0, newPgSz = 5;  
  
      // Note 1  
      cn->Provider = "sqloledb";  
      cn->Open("Data Source='(local)';Initial Catalog=pubs;Integrated Security=SSPI;", strMissing, "", adConnectUnspecified);  
  
      oldPgSz = rs->GetPageSize();  
      // -or-  
      // oldPgSz = rs->PageSize;  
  
      rs->PutPageSize(newPgSz);  
      // -or-  
      // rs->PageSize = newPgSz;  
  
      // Note 2  
      rs->PutRefActiveConnection( cn );  
      rs->Open("authors", vtMissing, adOpenStatic, adLockReadOnly, adCmdTable);  
      printf("Original pagesize = %d, new pagesize = %d\n", oldPgSz, rs->GetPageSize());  
      rs->Close();  
      cn->Close();  
  
   }  
   catch (_com_error &e) {  
      printf("Description = %s\n", (char*) e.Description());  
   }  
   ::CoUninitialize();  
}  

Использование GetItem(x) и Item[x]

В этом примере Visual Basic показан стандартный и альтернативный синтаксис для Item().

Public Sub GetItemItem  
Dim rs As New ADODB.Recordset  
Dim name as String  
rs = rs.Open "authors", "DSN=pubs;", adOpenDynamic, _  
         adLockBatchOptimistic, adTable  
name = rs(0)  
' -or-  
name = rs.Fields.Item(0)  
rs(0) = "Test"  
rs.UpdateBatch  
' Restore name  
rs(0) = name  
rs.UpdateBatch  
rs.Close  
End Sub  

В этом примере Visual C++ демонстрируется элемент.

Замечание

Следующая заметка соответствует закомментированным разделам в примере кода: когда к коллекции обращаются с использованием Item, индекс 2 необходимо привести к типу long, чтобы вызвать соответствующий конструктор.

// Visual_CPP_ado_prog_3.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
void main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr cn("ADODB.Connection");  
      _RecordsetPtr rs("ADODB.Recordset");  
      _variant_t vtFirstName;  
  
      cn->Provider = "sqloledb";  
      cn->Open("Data Source='(local)';Initial Catalog=pubs;Integrated Security=SSPI;", "", "", adConnectUnspecified);  
  
      rs->PutRefActiveConnection( cn );  
      rs->Open("authors", vtMissing, adOpenStatic, adLockOptimistic, adCmdTable);  
      rs->MoveFirst();  
  
      // Note 1. Get a field.  
      vtFirstName = rs->Fields->GetItem((long)2)->GetValue();  
      // -or-  
      vtFirstName = rs->Fields->Item[(long)2]->Value;  
  
      printf( "First name = '%s'\n", (char*)( (_bstr_t)vtFirstName) );  
  
      rs->Fields->GetItem((long)2)->Value = L"TEST";  
      rs->Update(vtMissing, vtMissing);  
  
      // Restore name  
      rs->Fields->GetItem((long)2)->PutValue(vtFirstName);  
      // -or-  
      rs->Fields->GetItem((long)2)->Value = vtFirstName;  
      rs->Update(vtMissing, vtMissing);  
      rs->Close();  
   }  
   catch (_com_error &e) {  
      printf("Description = '%s'\n", (char*) e.Description());  
   }  
   ::CoUninitialize();  
}  

Приведение указателей объектов ADO с помощью (IDispatch *)

В следующем примере Visual C++ показано использование (IDispatch *) для приведения указателей объектов ADO.

Примечания.

Следующие заметки соответствуют закомментированных разделам в примере кода.

  1. Укажите открытый объект Connection в явно закодированном варианте. Приведение к нему с помощью (IDispatch *) для вызова правильного конструктора. Кроме того, явным образом задайте второй параметр _variant_t значение true по умолчанию, поэтому число ссылок на объект будет исправлено при завершении операции Recordset::Open .

  2. Выражение (_bstr_t) не является приведением, а оператором _variant_t, который извлекает строку _bstr_t из Variant, возвращаемого Value.

Выражение (char*) не является приведением, а представляет собой оператор _bstr_t, который извлекает указатель на инкапсулированную строку в объекте _bstr_t.

В этом разделе кода демонстрируется некоторые полезные действия операторов _variant_t и _bstr_t .

// Visual_CPP_ado_prog_4.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr pConn("ADODB.Connection");  
      _RecordsetPtr pRst("ADODB.Recordset");  
  
      pConn->Provider = "sqloledb";  
      pConn->Open("Data Source='(local)';Initial Catalog='pubs';Integrated Security=SSPI", "", "", adConnectUnspecified);  
  
      // Note 1.  
      pRst->Open("authors", _variant_t((IDispatch *) pConn, true), adOpenStatic, adLockReadOnly, adCmdTable);  
      pRst->MoveLast();  
  
      // Note 2.  
      printf("Last name is '%s %s'\n",   
         (char*) ((_bstr_t) pRst->GetFields()->GetItem("au_fname")->GetValue()),  
         (char*) ((_bstr_t) pRst->Fields->Item["au_lname"]->Value));  
  
      pRst->Close();  
      pConn->Close();  
   }  
   catch (_com_error &e) {  
      printf("Description = '%s'\n", (char*) e.Description());  
   }     
   ::CoUninitialize();  
}