Programación ADO en Visual C++
La referencia de API de ADO describe la funcionalidad de la interfaz de programación de aplicaciones (API) de ADO mediante una sintaxis similar a Microsoft Visual Basic. Aunque el público previsto es todos los usuarios, los programadores de ADO emplean diversos lenguajes como Visual Basic, Visual C++ (con y sin la directiva #import ) y Visual J++ (con el paquete de clases ADO/WFC).
Nota
Microsoft finalizó la compatibilidad con Visual J++ en 2004.
Para tener en cuenta esta diversidad, los índices de la sintaxis de ADO para Visual C++ proporcionan sintaxis específica del lenguaje Visual C++ con vínculos a descripciones comunes de la funcionalidad, los parámetros, los comportamientos excepcionales, etc., en la referencia de API.
ADO se implementa con interfaces COM (modelo de objetos componentes). Sin embargo, es más fácil que los programadores trabajen con COM en determinados lenguajes de programación que otros. Por ejemplo, casi todos los detalles del uso de COM se controlan implícitamente para los programadores de Visual Basic, mientras que los programadores de Visual C++ deben tener en cuenta esos detalles.
En las secciones siguientes se resumen los detalles de los programadores de C y C++ que usan ADO y la directiva #import. Se centra en los tipos de datos específicos de COM (Variant, BSTR y SafeArray) y el control de errores (_com_error).
Uso de la directiva del compilador de #import
La directiva del compilador de Visual C++ de #import simplifica el trabajo con los métodos y las propiedades de ADO. La directiva adopta el nombre de un archivo que contiene una biblioteca de tipos, como el .dll de ADO (Msado15.dll), y genera archivos de encabezado que contienen declaraciones typedef, punteros inteligentes para interfaces y constantes enumeradas. Cada interfaz está encapsulada o ajustada en una clase.
Para cada operación dentro de una clase (es decir, una llamada a un método o una propiedad), hay una declaración para llamar a la operación directamente (es decir, el formato "sin procesar" de la operación) y una declaración para llamar a la operación sin procesar y generar un error COM si la operación no se ejecuta correctamente. Si la operación es una propiedad, normalmente hay una directiva del compilador que crea una sintaxis alternativa para la operación que tiene sintaxis como Visual Basic.
Las operaciones que recuperan el valor de una propiedad tienen nombres del formulario, GetProperty. Las operaciones que establecen el valor de una propiedad tienen nombres del formulario, PutProperty. Las operaciones que establecen el valor de una propiedad con un puntero a un objeto ADO tienen nombres del formulario, PutRefProperty.
Puede obtener o establecer una propiedad con llamadas de estos formularios:
variable = objectPtr->GetProperty(); // get property value
objectPtr->PutProperty(value); // set property value
objectPtr->PutRefProperty(&value); // set property with object pointer
Uso de directivas de propiedad
La directiva del compilador __declspec(property...) es una extensión del lenguaje C específica de Microsoft que declara una función que se usa como una propiedad para tener una sintaxis alternativa. Como resultado, puede establecer u obtener valores de una propiedad de forma similar a Visual Basic. Por ejemplo, puede establecer y obtener una propiedad de esta manera:
objectPtr->property = value; // set property value
variable = objectPtr->property; // get property value
Tenga en cuenta que no tiene que codificar:
objectPtr->PutProperty(value); // set property value
variable = objectPtr->GetProperty; // get property value
El compilador generará la llamada a Get-, Put- o PutRefProperty adecuada en función de qué sintaxis alternativa se declara y si la propiedad se está leyendo o escribiendo.
La directiva del compilador __declspec(property...) solo puede declarar la sintaxis alternativa get, put o get y put para una función. Las operaciones de solo lectura solo tienen una declaración get; las operaciones de solo escritura solo tienen una declaración put; las operaciones que son de lectura y escritura tienen declaraciones get y put.
Solo dos declaraciones son posibles con esta directiva; sin embargo, cada propiedad puede tener tres funciones de propiedad: GetProperty, PutProperty y PutRefProperty. En ese caso, solo dos formas de la propiedad tienen la sintaxis alternativa.
Por ejemplo, la propiedad ActiveConnection del objeto Command se declara con una sintaxis alternativa para GetActiveConnection y PutRefActiveConnection. La sintaxis PutRef- es una buena opción porque, en la práctica, normalmente querrá colocar un objeto Connection abierto (es decir, un puntero de objeto Connection) en esta propiedad. Por otro lado, el objeto Recordset tiene operaciones Get-, Put- y PutRefActiveConnection, pero ninguna sintaxis alternativa.
Colecciones, el método GetItem y la propiedad Item
ADO define varias colecciones, como Fields, Parameters, Properties y Errors. En Visual C++, el método GetItem(index) devuelve un miembro de la colección. Index es una propiedad Variant, cuyo valor es un índice numérico del miembro de la colección o una cadena que contiene el nombre del miembro.
La directiva del compilador __declspec(property...) declara la propiedad Item como una sintaxis alternativa al método GetItem() fundamental de cada colección. La sintaxis alternativa usa corchetes y tiene un aspecto similar a una referencia de matriz. En general, los dos formularios tienen un aspecto similar al siguiente:
collectionPtr->GetItem(index);
collectionPtr->Item[index];
Por ejemplo, asigne un valor a un campo de un objeto Recordset, denominado rs, derivado de la tabla authors de la base de datos pubs. Utilice la propiedad Item() para tener acceso al tercer elemento Field de la colección Fields del objeto Recordset (las colecciones se indexan desde cero; supongamos que el tercer campo se denomina au_fname). A continuación, llame al método Value() en el objeto Field para asignar un valor de cadena.
Esto se puede expresar en Visual Basic de las cuatro maneras siguientes (los dos últimos formularios son únicos para Visual Basic; otros lenguajes no tienen equivalentes):
rs.Fields.Item(2).Value = "value"
rs.Fields.Item("au_fname").Value = "value"
rs(2) = "value"
rs!au_fname = "value"
El equivalente de Visual C++ a los dos primeros formularios anteriores es el siguiente:
rs->Fields->GetItem(long(2))->PutValue("value");
rs->Fields->GetItem("au_fname")->PutValue("value");
-or- (también se muestra la sintaxis alternativa para la propiedad Value)
rs->Fields->Item[long(2)]->Value = "value";
rs->Fields->Item["au_fname"]->Value = "value";
Para obtener ejemplos de iteración a través de una colección, vea la sección "Colecciones de ADO" de la "Referencia de ADO".
Tipos de datos específicos de COM
En general, cualquier tipo de datos de Visual Basic que encuentre en la Referencia de la API de ADO tiene un equivalente de Visual C++. Estos incluyen tipos de datos estándar, como unsigned char para un objeto Byte de Visual Basic, short para Integer y long para Long. Busque en los índices de sintaxis para ver exactamente lo que es necesario para los operandos de un método o una propiedad determinados.
Las excepciones a esta regla son los tipos de datos específicos de COM: Variant, BSTR y SafeArray.
Variant
Un valor Variant es un tipo de datos estructurados que contiene un miembro de valor y un miembro de tipo de datos. Un valor Variant puede contener una amplia variedad de otros tipos de datos, incluidos otros valores Variant, BSTR, Boolean, IDispatch o puntero IUnknown, moneda, fecha, etc. COM también proporciona métodos que facilitan la conversión de un tipo de datos a otro.
La clase _variant_t encapsula y administra el tipo de datos Variant.
Cuando la referencia de la API de ADO indica que un método u operando de propiedad toma un valor, normalmente significa que el valor se pasa en una clase _variant_t.
Esta regla es explícitamente true cuando la sección Parámetros de los temas de la referencia de la API de ADO indica que un operando es un valor variant. Una excepción es cuando la documentación indica explícitamente que el operando toma un tipo de datos estándar, como Long o Byte, o una enumeración. Otra excepción es cuando el operando toma un valor String.
BSTR
Un tipo de datos BSTR (Basic STRing) es un tipo de datos estructurados que contiene una cadena de caracteres y la longitud de la cadena. COM proporciona métodos para asignar, manipular y liberar un tipo de datos BSTR.
La clase _bstr_t encapsula y administra el tipo de datos BSTR.
Cuando la referencia de la API de ADO indica que un método o una propiedad toman un valor String, significa que el valor presenta la forma de un valor _bstr_t.
Conversión de clases _variant_t y _bstr_t
A menudo no es necesario codificar explícitamente una clase _variant_t o _bstr_t en un argumento para una operación. Si la clase _variant_t o _bstr_t tiene un constructor que coincide con el tipo de datos del argumento, el compilador generará la clase _variant_t o _bstr_t apropiada.
Sin embargo, si el argumento es ambiguo, es decir, el tipo de datos del argumento coincide con más de un constructor, debe convertir el argumento con el tipo de datos adecuado para invocar el constructor correcto.
Por ejemplo, la declaración del método Recordset::Open es:
HRESULT Open (
const _variant_t & Source,
const _variant_t & ActiveConnection,
enum CursorTypeEnum CursorType,
enum LockTypeEnum LockType,
long Options );
El argumento ActiveConnection
toma una referencia a una clase _variant_t, que puede codificar como una cadena de conexión o un puntero a un objeto Connection abierto.
La clase _variant_t correcta se construirá implícitamente si pasa una cadena como "DSN=pubs;uid=MyUserName;pwd=MyPassword;
" o un puntero como "(IDispatch *) pConn
".
Nota
Si se conecta a un proveedor de orígenes de datos que admite autenticación de Windows, debe especificar Trusted_Connection=sí o Seguridad integrada = SSPI en lugar de la información de identificador de usuario y contraseña en la cadena de conexión.
O bien, puede codificar explícitamente una clase _variant_t que contenga un puntero como "_variant_t((IDispatch *) pConn, true)
". La conversión, (IDispatch *)
, resuelve la ambigüedad con otro constructor que toma un puntero a una interfaz IUnknown.
Es un aspecto importante, aunque rara vez se menciona, que ADO es una interfaz IDispatch. Cada vez que se debe pasar un puntero a un objeto ADO como Variant, ese puntero debe convertirse como puntero a una interfaz IDispatch.
El último caso codifica explícitamente el segundo argumento booleano del constructor con su valor predeterminado opcional de true
. Este argumento hace que el constructor Variant llame a su método AddRef(), que compensa a ADO llamando automáticamente al método _variant_t::Release() cuando se completa la llamada al método o a la propiedad de ADO.
SafeArray
SafeArray es un tipo de datos estructurados que contiene una matriz de otros tipos de datos. Un elemento SafeArray se denomina safe porque contiene información sobre los límites de cada dimensión de matriz y limita el acceso a los elementos de matriz dentro de esos límites.
Cuando la referencia de API de ADO indica que un método o una propiedad toman o devuelven una matriz, significa que el método o la propiedad toman o devuelven un valor SafeArray, no una matriz nativa de C/C++.
Por ejemplo, el segundo parámetro del método OpenSchema del objeto Connection requiere una matriz de valores Variant. Esos valores Variant se deben pasar como elementos de un método SafeArray, y dicho método SafeArray debe establecerse como el valor de otro valor Variant. Se trata de otro valor Variant que se pasa como el segundo argumento de OpenSchema.
Como ejemplos adicionales, el primer argumento del método Find es un objeto Variant cuyo valor es un método SafeArray unidimensional; cada uno de los argumentos opcionales first y second de AddNew es un método SafeArray unidimensional; y el valor devuelto del método GetRows es una propiedad Variant cuyo valor es un método SafeArray bidimensional.
Parámetros predeterminados y ausentes
Visual Basic permite que falten parámetros en los métodos. Por ejemplo, el método Open del objeto Recordset tiene cinco parámetros, pero puede omitir los parámetros intermedios y dejar los parámetros finales. Un valor predeterminado BSTR o Variant se sustituirá en función del tipo de datos del operando que falta.
En C/C++, se deben especificar todos los operandos. Si desea especificar un parámetro que falta cuyo tipo de datos es una cadena, especifique una clase _bstr_t que contenga una cadena null. Si desea especificar un parámetro que falta cuyo tipo de datos es Variant, especifique una clase _variant_t con un valor de DISP_E_PARAMNOTFOUND y un tipo de VT_ERROR. Como alternativa, especifique la constante _variant_t equivalente, vtMissing, que proporciona la directiva #import.
Existen tres métodos excepcionales al uso de vtMissing. Se trata de los métodos Execute de los objetos Connection y Command, y el método NextRecordset del objeto Recordset. A continuación se muestran sus firmas:
_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
Los parámetros, RecordsAffected y Parameters, son punteros a un objeto Variant. Parameters es un parámetro de entrada que especifica la dirección de un objeto Variant que contiene un único parámetro, o matriz de parámetros, que modificará el comando que se está ejecutando. RecordsAffected es un parámetro de salida que especifica la dirección de un objeto Variant, donde se devuelve el número de filas afectadas por el método.
En el método Execute del objeto Command, indique que no se especifica ningún parámetro estableciendo Parameters en &vtMissing
(opción recomendada) o en el puntero nulo (es decir, NULL o cero (0)). Si Parameters se establece en el puntero nulo, el método sustituye internamente el equivalente de vtMissing y, a continuación, completa la operación.
En todos los métodos, indique que el número de registros afectados no debe devolverse al establecer RecordsAffected en el puntero nulo. En este caso, el puntero nulo no es tanto un parámetro que falta sino una indicación de que el método debe descartar el número de registros afectados.
Por lo tanto, para estos tres métodos, es válido codificar algo como:
pConnection->Execute("commandText", NULL, adCmdText);
pCommand->Execute(NULL, NULL, adCmdText);
pRecordset->NextRecordset(NULL);
Tratamiento de errores
En COM, la mayoría de las operaciones devuelven un código de retorno HRESULT que indica si una función se completó correctamente. La directiva #import genera código contenedor alrededor de cada propiedad o método "sin procesar" y comprueba el valor HRESULT devuelto. Si HRESULT indica un error, el código contenedor produce un error COM llamando a _com_issue_errorex() con el código de retorno HRESULT como argumento. Los objetos de error COM se pueden detectar en un bloque try-catch. (Por motivos de eficiencia, capture una referencia a un objeto _com_error).
Recuerde que se trata de errores de ADO: se producen errores en la operación de ADO. Los errores devueltos por el proveedor subyacente aparecen como objetos Error en la colección Errors del objeto Connection.
La directiva #import solo crea rutinas de control de errores para métodos y propiedades declarados en el .dll de ADO. Sin embargo, puede aprovechar este mismo mecanismo de control de errores escribiendo su propia macro de comprobación de errores o función insertada. Consulte el tema Extensiones de Visual C++ o el código de las secciones siguientes para obtener ejemplos.
Equivalentes de Visual C++ de convenciones de Visual Basic
A continuación se muestra un resumen de varias convenciones de la documentación de ADO, codificadas en Visual Basic, así como sus equivalentes en Visual C++.
Declaración de un objeto ADO
En Visual Basic, una variable de objeto ADO (en este caso, para un objeto Recordset) se declara como sigue:
Dim rst As ADODB.Recordset
La cláusula "ADODB.Recordset
" es el valor ProgID del objeto Recordset tal y como se define en el registro. Una nueva instancia de un objeto Record se declara de la siguiente manera:
Dim rst As New ADODB.Recordset
O bien
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset
En Visual C++, la directiva #import genera declaraciones de tipo de puntero inteligente para todos los objetos ADO. Por ejemplo, una variable que apunta a un objeto _Recordset es de tipo _RecordsetPtr y se declara de la siguiente manera:
_RecordsetPtr rs;
Una variable que apunta a una nueva instancia de un objeto _Recordset se declara de la siguiente manera:
_RecordsetPtr rs("ADODB.Recordset");
o bien
_RecordsetPtr rs;
rs.CreateInstance("ADODB.Recordset");
o bien
_RecordsetPtr rs;
rs.CreateInstance(__uuidof(_Recordset));
Después de llamar al método CreateInstance, la variable se puede usar de la siguiente manera:
rs->Open(...);
Observe que, en un caso, el operador ".
" se usa como si la variable fuera una instancia de una clase (rs.CreateInstance
) y, en otro caso, el operador "->
" se usa como si la variable fuera un puntero a una interfaz (rs->Open
).
Una variable se puede usar de dos maneras porque el operador "->
" está sobrecargado, para permitir que una instancia de una clase se comporte como un puntero a una interfaz. Un miembro de clase privada de la variable de instancia contiene un puntero a la interfaz _Recordset; el operador "->
" devuelve ese puntero, y el puntero devuelto accede a los miembros del objeto _Recordset.
Codificación de un parámetro que falta: cadena
Cuando necesite codificar un operando String que falta en Visual Basic, solo tiene que omitir el operando. Debe especificar el operando en Visual C++. Codifique una clase _bstr_t que tiene una cadena vacía como valor.
_bstr_t strMissing(L"");
Codificación de un parámetro que falta: variante
Cuando necesite codificar un operando Variant que falta en Visual Basic, solo tiene que omitir el operando. Debe especificar todos los operandos en Visual C++. Codifique un parámetro Variant que falta con una clase _variant_t establecida en el valor especial DISP_E_PARAMNOTFOUND y el tipo VT_ERROR. Como alternativa, especifique vtMissing, que es una constante predefinida equivalente proporcionada por la directiva #import.
_variant_t vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);
O bien, use:
...vtMissing...;
Declaración de una variante
En Visual Basic, un objeto Variant se declara con la instrucción Dim de la siguiente manera:
Dim VariableName As Variant
En Visual C++, declare una variable como tipo _variant_t. A continuación se muestran algunas declaraciones _variant_t esquemáticas.
Nota
Estas declaraciones simplemente dan una idea aproximada de lo que codificaría en su propio programa. Para obtener más información, vea los ejemplos siguientes y la documentación de 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);
Uso de matrices de objetos Variant
En Visual Basic, las matrices de objetos Variant se pueden codificar con la instrucción Dim, o bien puede usar la función Array, como se muestra en el código de ejemplo siguiente:
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
En el siguiente ejemplo de Visual C++, se muestra el uso de un elemento SafeArray utilizado con una clase _variant_t.
Notas
Las notas siguientes corresponden a secciones comentadas en el ejemplo de código.
Una vez más, la función insertada TESTHR() se define para aprovechar el mecanismo de control de errores existente.
Solo necesita una matriz unidimensional, por lo que puede usar SafeArrayCreateVector, en lugar de la declaración SAFEARRAYBOUND de uso general y la función SafeArrayCreate. A continuación se muestra el aspecto que tendría ese código con SafeArrayCreate:
SAFEARRAYBOUND sabound[1]; sabound[0].lLbound = 0; sabound[0].cElements = 4; pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);
El esquema identificado por la constante enumerada, adSchemaColumns, está asociado a cuatro columnas de restricción: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME y COLUMN_NAME. Por lo tanto, se crea una matriz de valores Variant con cuatro elementos. A continuación, se especifica un valor de restricción que corresponde a la tercera columna, TABLE_NAME.
El objeto Recordset que se devuelve consta de varias columnas, un subconjunto de las cuales son las columnas de restricción. Los valores de las columnas de restricción para cada fila devuelta deben ser los mismos que los valores de restricción correspondientes.
Quienes estén familiarizados con SafeArrays pueden sorprenderse de que no se llame a SafeArrayDestroy() antes de salir. De hecho, llamar a SafeArrayDestroy() en este caso provocará una excepción en tiempo de ejecución. El motivo es que el destructor de
vtCriteria
llamará a VariantClear() cuando la clase _variant_t salga del ámbito, de tal forma que SafeArray quedará libre. Llamar a SafeArrayDestroy, sin borrar manualmente la clase _variant_t, provocaría que el destructor intente borrar un puntero SafeArray no válido.Si se llamara a SafeArrayDestroy, el código tendría el siguiente aspecto:
TESTHR(SafeArrayDestroy(pSa)); vtCriteria.vt = VT_EMPTY; vtCriteria.parray = NULL;
Sin embargo, es mucho más sencillo dejar que _variant_t administre 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();
}
Uso de la propiedad Get/Put/PutRef
En Visual Basic, el nombre de una propiedad no se califica en función de si se recupera, se asigna o se le asigna una referencia.
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
En este ejemplo de Visual C++ se muestra la propiedad Get/Put/PutRef.
Notas
Las notas siguientes corresponden a secciones comentadas en el ejemplo de código.
En este ejemplo se usan dos formas de un argumento de cadena que falta: una constante explícita, strMissing, y una cadena que el compilador usará para crear una clase _bstr_t temporal que existirá para el ámbito del método Open.
No es necesario convertir el operando de
rs->PutRefActiveConnection(cn)
en(IDispatch *)
porque el tipo del operando ya es(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();
}
Uso de GetItem(x) e Item[x]
En este ejemplo de Visual Basic se muestra la sintaxis estándar y alternativa de 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
En este ejemplo de Visual C++ se muestra Item.
Nota
La nota siguiente corresponde a las secciones comentadas del ejemplo de código: cuando se accede a la colección con Item, el índice, 2, debe convertirse a long para que se invoque un constructor adecuado.
// 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();
}
Conversión de punteros de objeto ADO con (IDispatch *)
En el siguiente ejemplo de Visual C++ se muestra el uso de (IDispatch *) para convertir punteros de objeto ADO.
Notas
Las notas siguientes corresponden a secciones comentadas en el ejemplo de código.
Especifique un objeto Connection abierto en una propiedad Variant codificada explícitamente. Realice la conversión con (IDispatch *), para que se invoque el constructor correcto. Además, establezca explícitamente el segundo parámetro _variant_t en el valor predeterminado de true, de tal forma que el recuento de referencias de objetos será correcto cuando finalice la operación Recordset::Open.
La expresión,
(_bstr_t)
, no es una conversión, sino un operador _variant_t que extrae una cadena _bstr_t de Variant devuelta por Value.
La expresión, (char*)
, no es una conversión, sino un operador _bstr_t que extrae un puntero a la cadena encapsulada en un objeto _bstr_t.
En esta sección de código se muestran algunos de los comportamientos útiles de los operadores _variant_t y _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();
}