Программирование для ADO на Visual C++
Область применения: Access 2013, Office 2013
Справочник по API ADO описывает функциональные возможности интерфейса программирования приложений (API) ADO с использованием синтаксиса, аналогичного Microsoft Visual Basic. Хотя целевая аудитория — это все пользователи, программисты ADO используют различные языки, такие как Visual Basic, Visual C++ (с директивой #import и без нее) и Visual J++ (с пакетом классов ADO/WFC).
Чтобы удовлетворить это разнообразие, синтаксические индексы 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. Директива принимает имя файла, содержащего библиотеку типов, например .dll ADO (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-илиPutRef в зависимости от того, какой альтернативный синтаксис объявлен, а также от того, считывается ли или записывается свойство.
Директива компилятора __declspec(property...) может объявлять только альтернативный синтаксис get, put или get and put для функции. Операции только для чтения имеют только объявление get ; Операции, доступные только для записи, имеют только объявление put ; Операции чтения и записи имеют объявления get и put .
С помощью этой директивы возможны только два объявления; Однако каждое свойство может иметь три функции свойств: GetProperty, PutProperty и PutRefProperty. В этом случае только две формы свойства имеют альтернативный синтаксис.
Например, свойство ActiveConnection объекта Command объявляется с альтернативным синтаксисом для GetActiveConnection и PutRefActiveConnection. Синтаксис PutRef- является хорошим выбором, так как на практике обычно требуется поместить открытый объект Connection (т. е. указатель объекта Connection ) в это свойство. С другой стороны, объект Recordset имеет операции Get-, Put-иPutRefActiveConnection , но нет альтернативного синтаксиса.
Коллекции, метод GetItem и свойство Item
ADO определяет несколько коллекций, включая Поля, Параметры, Свойства и Ошибки. В Visual C++ метод GetItem(index) возвращает член коллекции. Index — это variant, значение которого является числовым индексом элемента в коллекции или строкой, содержащей имя элемента.
Директива компилятора __declspec(property...) объявляет свойство Item в качестве альтернативного синтаксиса для основного метода GetItem() каждой коллекции. Альтернативный синтаксис использует квадратные скобки и выглядит как ссылка на массив. Как правило, две формы выглядят следующим образом:
collectionPtr->GetItem(index);
collectionPtr->Item[index];
Например, присвойте значение полю объекта Recordset с именем rs, производным от таблицы авторов базы данных pubs . Используйте свойство Item() для доступа к третьему полю коллекции Fields объекта 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";
Типы данных, относящиеся к COM
Как правило, любой тип данных Visual Basic, который можно найти в справочнике по API ADO, имеет эквивалент Visual C++. К ним относятся стандартные типы данных, такие как unsigned char для байта Visual Basic, сокращение от integer и long для Long. Просмотрите синтаксические индексы, чтобы увидеть, что именно требуется для операндов заданного метода или свойства.
Исключениями из этого правила являются типы данных, относящиеся к COM: Variant, BSTR и SafeArray.
Variant
Variant — это структурированный тип данных, содержащий элемент значения и элемент типа данных. Тип Variant может содержать широкий спектр других типов данных, включая другой тип данных Variant, BSTR, логическое значение, указатель IDispatch или IUnknown, валюту, дату и т. д. COM также предоставляет методы, упрощающие преобразование одного типа данных в другой.
Класс _variant_t инкапсулирует тип данных Variant и управляет им.
Если в справочнике по API ADO указано, что операнд метода или свойства принимает значение, это обычно означает, что значение передается в _variant_t.
Это правило явно верно, если в разделе Параметры раздела Справочника по API ADO указано, что операнд является Variant. Одним из исключений является то, что в документации явно указано, что операнд принимает стандартный тип данных, например Long или Byte, или перечисление. Еще одним исключением является то, что операнд принимает значение String.
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=sa; pwd=;", или указатель, например "(IDispatch *) pConn".
Кроме того, можно явно запрограммировку _variant_t , содержащую указатель, например "_variant_t((IDispatch *) pConn, true)". Приведение (IDispatch *) разрешает неоднозначность с помощью другого конструктора, который принимает указатель на интерфейс IUnknown.
Это важный, хотя редко упоминаемый факт, что ADO является интерфейсом IDispatch. Всякий раз, когда указатель на объект ADO должен передаваться как Variant, этот указатель должен быть приведен в качестве указателя на интерфейс IDispatch.
Последний случай явно кодирует второй логический аргумент конструктора с его необязательным значением по умолчанию true. Этот аргумент заставляет конструктор Variant вызывать свой метод AddRef(), который компенсирует автоматический вызов метода ADO _variant_t::Release() после завершения вызова метода ADO или свойства.
Safearray
SafeArray — это структурированный тип данных, содержащий массив других типов данных. SafeArray называется безопасным, так как он содержит сведения о границах каждого измерения массива и ограничивает доступ к элементам массива в пределах этих границ.
Если в справочнике по API ADO указано, что метод или свойство принимает или возвращает массив, это означает, что метод или свойство принимает или возвращает SafeArray, а не собственный массив C/C++.
Например, для второго параметра метода OpenSchema объекта Connection требуется массив значений Variant. Эти значения Variant должны быть переданы как элементы SafeArray, а значение SafeArray должно быть задано как значение другого Variant. Именно другой вариант Variant передается в качестве второго аргумента OpenSchema.
В качестве других примеров первым аргументом метода Find является Variant , значение которого является одномерным SafeArray; каждый из необязательных первого и второго аргументов AddNew является одномерным SafeArray; и возвращаемое значение метода GetRows — это Variant , значение которого является двумерным SafeArray.
Отсутствуют параметры и параметры по умолчанию
Visual Basic позволяет отсутствовать параметры в методах. Например, метод Open объекта Recordset имеет пять параметров, но можно пропустить промежуточные параметры и оставить конечные параметры. BSTR или Variant по умолчанию будет заменен в зависимости от типа данных отсутствующих операнда.
В C/C++ должны быть указаны все операнды. Если вы хотите указать отсутствующий параметр, тип данных которого является строкой, укажите _bstr_t , содержащий строку NULL. Если вы хотите указать отсутствующий параметр, тип данных которого является Variant, укажите _variant_t со значением DISP_E_PARAMNOTFOUND и типом VT_ERROR. Кроме того, можно указать эквивалентную константу _variant_tvtMissing, которая предоставляется директивой #import .
Три метода являются исключениями из типичного использования vtMissing. Это методы Execute объектов Connection и Command , а также метод NextRecordset объекта Recordset . Ниже приведены их сигнатуры.
_RecordsetPtr Invalid DDUE based on source, error:link not allowed in code, link filename:mdmthcnnexecute_HV10294345.xml( _bstr_t CommandText, VARIANT * RecordsAffected,
long Options ); // Connection
_RecordsetPtr Invalid DDUE based on source, error:link not allowed in code, link filename:mdmthcmdexecute_HV10294344.xml( VARIANT * RecordsAffected, VARIANT * Parameters,
long Options ); // Command
_RecordsetPtr Invalid DDUE based on source, error:link not allowed in code, link filename:mdmthnextrec_HV10294541.xml( VARIANT * RecordsAffected ); // Recordset
Параметры RecordsAffected и Parameters являются указателями на Variant. Параметры — это входной параметр, указывающий адрес объекта Variant , содержащего один параметр или массив параметров, который изменяет выполняемую команду. RecordsAffected — это выходной параметр, указывающий адрес Variant, где возвращается количество строк, затронутых методом .
В методе Execute объекта Command укажите, что параметры не задаются, задав параметру Parameters значение &vtMissing (рекомендуется) или пустой указатель (то есть NULL или ноль (0)). Если параметру Parameters задан пустой указатель, метод внутренне заменяет эквивалент vtMissing, а затем завершает операцию.
Во всех методах укажите, что количество затронутых записей не должно возвращаться, задав recordsAffected пустой указатель. В этом случае пустой указатель является не столько отсутствующим параметром, сколько указанием на то, что метод должен отменить количество затронутых записей.
Таким образом, для этих трех методов допустимо кодировать такие коды, как:
pConnection->Execute("commandText", NULL, adCmdText);
pCommand->Execute(NULL, NULL, adCmdText);
pRecordset->NextRecordset(NULL);
Обработка ошибок
В COM большинство операций возвращают код возврата HRESULT, указывающий, успешно ли выполнена функция. Директива #import создает код-оболочку для каждого "необработанного" метода или свойства и проверяет возвращенный HRESULT. Если HRESULT указывает на сбой, код оболочки выдает com-ошибку, вызывая _com_issue_errorex() с кодом возврата HRESULT в качестве аргумента. Объекты com-ошибок можно поймать в блоке try-catch . (Для повышения эффективности перехватите ссылку на объект _com_error .)
Помните, что это ошибки ADO: они возникают в результате сбоя операции ADO. Ошибки, возвращаемые базовым поставщиком, отображаются как объекты Error в коллекции "Ошибки объекта подключения".
Директива #import создает только подпрограммы обработки ошибок для методов и свойств, объявленных в .dll ADO. Однако вы можете воспользоваться этим же механизмом обработки ошибок, написав собственный макрос или встроенную функцию проверки ошибок. Примеры см. в разделе Расширения Visual C++ или коде в следующих разделах.
Эквиваленты соглашений Visual Basic в Visual C++
Ниже приведена сводка по нескольким соглашениям в документации по 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
Если необходимо закодировать отсутствующий операнд 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 массивы Variants можно закодировать с помощью инструкции Dim или использовать функцию Array , как показано в следующем примере кода:
Public Sub ArrayOfVariants
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim fld As ADODB.Field
cn.Open "DSN=pubs", "sa", ""
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.
Примечание.
Приведенные ниже примечания соответствуют закомментированных разделам в примере кода.
Опять же, встроенная функция TESTHR() определена, чтобы воспользоваться преимуществами существующего механизма обработки ошибок.
Вам нужен только одномерный массив, поэтому вместо объявления SAFEARRAYBOUND и функции SafeArrayCreateVector можно использовать вместо объявления SAFEARRAYBOUND общего назначения и функции SafeArrayCreate. Ниже показано, как будет выглядеть этот код с помощью SafeArrayCreate:
SAFEARRAYBOUND sabound[1]; sabound[0].lLbound = 0; sabound[0].cElements = 4; pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);
Схема, определяемая перечисленной константой adSchemaColumns, связана с четырьмя столбцами ограничений: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME и COLUMN_NAME. Таким образом, создается массив значений Variant с четырьмя элементами. Затем указывается значение ограничения, соответствующее третьему столбцу, TABLE_NAME. Возвращаемый набор записей состоит из нескольких столбцов, подмножеством которых являются столбцы ограничения. Значения столбцов ограничения для каждой возвращаемой строки должны совпадать с значениями соответствующих ограничений.
Те, кто знаком с SafeArrays , могут быть удивлены тем, что SafeArrayDeкиострой() не вызывается до выхода. На самом деле вызов SafeArrayDe пример() в этом случае приведет к исключению во время выполнения. Причина заключается в том, что деструктор для vtCriteria вызывает VariantClear(), когда _variant_t выходит из область, что освобождает SafeArray. Вызов SafeArrayDestruct, без очистки _variant_t вручную, приведет к тому, что деструктор попытается очистить недопустимый указатель SafeArray . Если бы был вызван safeArrayDestroy , код будет выглядеть следующим образом:
TESTHR(SafeArrayDestroy(pSa)); vtCriteria.vt = VT_EMPTY; vtCriteria.parray = NULL;
Однако гораздо проще позволить _variant_t управлять SafeArray.
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile")
#include <stdio.h>
// Note 1
inline void TESTHR( HRESULT _hr )
{ if FAILED(_hr) _com_issue_error(_hr); }
void main(void)
{
CoInitialize(NULL);
try
{
_RecordsetPtr pRs("ADODB.Recordset");
_ConnectionPtr pCn("ADODB.Connection");
_variant_t vtTableName("authors"),
vtCriteria;
long ix[1];
SAFEARRAY *pSa = NULL;
pCn->Open("DSN=pubs;User ID=MyUserId;pwd=MyPassword;Provider=MSDASQL;", "", "",
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/PutRef/.
Примечание.
Приведенные ниже примечания соответствуют закомментированных разделам в примере кода.
В этом примере используются две формы отсутствующего строкового аргумента: явная константа, strMissing и строка, которую компилятор будет использовать для создания временного _bstr_t, который будет существовать для область метода Open.
Приведение операнда rs-PutRefActiveConnection>(cn) к (IDispatch *) необязательно, так как тип операнда уже имеет тип (IDispatch *).
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") #include <stdio.h> void main(void) { CoInitialize(NULL); try { _ConnectionPtr cn("ADODB.Connection"); _RecordsetPtr rs("ADODB.Recordset"); _bstr_t strMissing(L""); long oldPgSz = 0, newPgSz = 5; // Note 1 cn->Open("Provider=sqloledb;Data Source=MyServer;" "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 , чтобы вызвать соответствующий конструктор.
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") #include <stdio.h> void main(void) { CoInitialize(NULL); try { _RecordsetPtr rs("ADODB.Recordset"); _variant_t vtFirstName; rs->Open("authors", "Provider=sqloledb;Data Source=MyServer;" "Initial Catalog=pubs;Integrated Security=SSPI;", 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.
Примечание.
Приведенные ниже примечания соответствуют закомментированных разделам в примере кода.
Укажите открытый объект Connection в явно закодированном варианте Variant. Приведите его с помощью (IDispatch *), чтобы вызвать правильный конструктор. Кроме того, явно задайте для второго параметра _variant_t значение по умолчанию true, чтобы количество ссылок на объекты было правильным при завершении операции Recordset::Open .
Выражение (_bstr_t) не является приведением, а оператором _variant_t , который извлекает строку _bstr_t из типа Variant , возвращаемого значением. Выражение (char*) не является приведением, а оператором _bstr_t , который извлекает указатель на инкапсулированную строку в объекте _bstr_t . В этом разделе кода показано несколько полезных действий операторов _variant_t и _bstr_t .
#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") #include <stdio.h> void main(void) { CoInitialize(NULL); try { _ConnectionPtr pConn("ADODB.Connection"); _RecordsetPtr pRst("ADODB.Recordset"); pConn->Open("Provider=sqloledb;Data Source=MyServer;" "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(); }