Acceso a datos mediante ADO.NET (C++/CLI)

ADO.NET es la API de .NET Framework para el acceso a datos y proporciona energía y facilidad de uso inigualables por las anteriores soluciones de acceso a los datos. En esta sección se describen algunos de los problemas relacionados con ADO.NET que son únicos para los usuarios de Visual C++, como serializar tipos nativos.

ADO.NET se ejecuta en Common Language Runtime (CLR). Por lo tanto, cualquier aplicación que interactúe con ADO.NET también debe tener como destino CLR. Sin embargo, esto no significa que las aplicaciones nativas no puedan usar ADO.NET. En estos ejemplos se muestra cómo interactuar con una base de datos de ADO.NET desde código nativo.

Serialización de cadenas ANSI para ADO.NET

Muestra cómo agregar una cadena nativa (char *) a una base de datos y cómo serializar System.String desde una base de datos a una cadena nativa.

Ejemplo

En este ejemplo, se crea la clase DatabaseClass para interactuar con un objeto DataTable de ADO.NET. Tenga en cuenta que esta clase es una class de C++ nativa (en comparación con ref class o value class). Esto es necesario porque queremos usar esta clase desde código nativo y no se pueden usar tipos administrados en código nativo. Esta clase se compilará para dirigirse a CLR, tal como se indica en la directiva #pragma managed anterior a la declaración de clase. Para más información sobre esta directiva, consulte managed, unmanaged.

Anote el miembro privado de la clase DatabaseClass: gcroot<DataTable ^> table. Dado que los tipos nativos no pueden contener tipos administrados, la palabra clave gcroot es necesaria. Para obtener más información sobre gcroot, consulte Declaración de manipuladores en tipos nativos.

El resto del código de este ejemplo es código de C++ nativo, como indica la directiva #pragma unmanaged que precede a main. En este ejemplo, vamos a crear una nueva instancia de DatabaseClass y llamar a sus métodos para crear una tabla y rellenar algunas filas de la tabla. Tenga en cuenta que las cadenas nativas de C++ se pasan como valores para la columna de base de datos StringCol. Dentro de DatabaseClass, estas cadenas se serializarán en cadenas administradas mediante la funcionalidad de serialización que se encuentra en el espacio de nombres System.Runtime.InteropServices. En concreto, el método PtrToStringAnsi se usa para serializar de char * a String, y el método StringToHGlobalAnsi se usa para serializar un objeto String a char *.

Nota:

La memoria asignada por StringToHGlobalAnsi se debe desasignar llamando a FreeHGlobal o GlobalFree.

// adonet_marshal_string_native.cpp
// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll
#include <comdef.h>
#include <gcroot.h>
#include <iostream>
using namespace std;

#using <System.Data.dll>
using namespace System;
using namespace System::Data;
using namespace System::Runtime::InteropServices;

#define MAXCOLS 100

#pragma managed
class DatabaseClass
{
public:
    DatabaseClass() : table(nullptr) { }

    void AddRow(char *stringColValue)
    {
        // Add a row to the table.
        DataRow ^row = table->NewRow();
        row["StringCol"] = Marshal::PtrToStringAnsi(
            (IntPtr)stringColValue);
        table->Rows->Add(row);
    }

    void CreateAndPopulateTable()
    {
        // Create a simple DataTable.
        table = gcnew DataTable("SampleTable");

        // Add a column of type String to the table.
        DataColumn ^column1 = gcnew DataColumn("StringCol",
            Type::GetType("System.String"));
        table->Columns->Add(column1);
    }

    int GetValuesForColumn(char *dataColumn, char **values,
        int valuesLength)
    {
        // Marshal the name of the column to a managed
        // String.
        String ^columnStr = Marshal::PtrToStringAnsi(
                (IntPtr)dataColumn);

        // Get all rows in the table.
        array<DataRow ^> ^rows = table->Select();
        int len = rows->Length;
        len = (len > valuesLength) ? valuesLength : len;
        for (int i = 0; i < len; i++)
        {
            // Marshal each column value from a managed string
            // to a char *.
            values[i] = (char *)Marshal::StringToHGlobalAnsi(
                (String ^)rows[i][columnStr]).ToPointer();
        }

        return len;
    }

private:
    // Using gcroot, you can use a managed type in
    // a native class.
    gcroot<DataTable ^> table;
};

#pragma unmanaged
int main()
{
    // Create a table and add a few rows to it.
    DatabaseClass *db = new DatabaseClass();
    db->CreateAndPopulateTable();
    db->AddRow("This is string 1.");
    db->AddRow("This is string 2.");

    // Now retrieve the rows and display their contents.
    char *values[MAXCOLS];
    int len = db->GetValuesForColumn(
        "StringCol", values, MAXCOLS);
    for (int i = 0; i < len; i++)
    {
        cout << "StringCol: " << values[i] << endl;

        // Deallocate the memory allocated using
        // Marshal::StringToHGlobalAnsi.
        GlobalFree(values[i]);
    }

    delete db;

    return 0;
}
StringCol: This is string 1.
StringCol: This is string 2.

Compilar el código

  • Para compilar el código desde la línea de comandos, guarde el ejemplo de código en un archivo denominado adonet_marshal_string_native.cpp y escriba la siguiente instrucción:

    cl /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll adonet_marshal_string_native.cpp
    

Serialización de cadenas BSTR para ADO.NET

Muestra cómo agregar una cadena COM (BSTR) a una base de datos y cómo serializar System.String de una base de datos a BSTR.

Ejemplo

En este ejemplo, se crea la clase DatabaseClass para interactuar con un objeto DataTable de ADO.NET. Tenga en cuenta que esta clase es una class de C++ nativa (en comparación con ref class o value class). Esto es necesario porque queremos usar esta clase desde código nativo y no se pueden usar tipos administrados en código nativo. Esta clase se compilará para dirigirse a CLR, tal como se indica en la directiva #pragma managed anterior a la declaración de clase. Para más información sobre esta directiva, consulte managed, unmanaged.

Anote el miembro privado de la clase DatabaseClass: gcroot<DataTable ^> table. Dado que los tipos nativos no pueden contener tipos administrados, la palabra clave gcroot es necesaria. Para obtener más información sobre gcroot, consulte Declaración de manipuladores en tipos nativos.

El resto del código de este ejemplo es código de C++ nativo, como indica la directiva #pragma unmanaged que precede a main. En este ejemplo, vamos a crear una nueva instancia de DatabaseClass y llamar a sus métodos para crear una tabla y rellenar algunas filas de la tabla. Tenga en cuenta que las cadenas COM se pasan como valores para la columna de base de datos StringCol. Dentro de DatabaseClass, estas cadenas se serializarán en cadenas administradas mediante la funcionalidad de serialización que se encuentra en el espacio de nombres System.Runtime.InteropServices. En concreto, el método PtrToStringBSTR se usa para serializar de BSTR a String, y el método StringToBSTR se usa para serializar un objeto String a BSTR.

Nota:

La memoria asignada por StringToBSTR se debe desasignar llamando a FreeBSTR o SysFreeString.

// adonet_marshal_string_bstr.cpp
// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll
#include <comdef.h>
#include <gcroot.h>
#include <iostream>
using namespace std;

#using <System.Data.dll>
using namespace System;
using namespace System::Data;
using namespace System::Runtime::InteropServices;

#define MAXCOLS 100

#pragma managed
class DatabaseClass
{
public:
    DatabaseClass() : table(nullptr) { }

    void AddRow(BSTR stringColValue)
    {
        // Add a row to the table.
        DataRow ^row = table->NewRow();
        row["StringCol"] = Marshal::PtrToStringBSTR(
            (IntPtr)stringColValue);
        table->Rows->Add(row);
    }

    void CreateAndPopulateTable()
    {
        // Create a simple DataTable.
        table = gcnew DataTable("SampleTable");

        // Add a column of type String to the table.
        DataColumn ^column1 = gcnew DataColumn("StringCol",
            Type::GetType("System.String"));
        table->Columns->Add(column1);
    }

    int GetValuesForColumn(BSTR dataColumn, BSTR *values,
        int valuesLength)
    {
        // Marshal the name of the column to a managed
        // String.
        String ^columnStr = Marshal::PtrToStringBSTR(
                (IntPtr)dataColumn);

        // Get all rows in the table.
        array<DataRow ^> ^rows = table->Select();
        int len = rows->Length;
        len = (len > valuesLength) ? valuesLength : len;
        for (int i = 0; i < len; i++)
        {
            // Marshal each column value from a managed string
            // to a BSTR.
            values[i] = (BSTR)Marshal::StringToBSTR(
                (String ^)rows[i][columnStr]).ToPointer();
        }

        return len;
    }

private:
    // Using gcroot, you can use a managed type in
    // a native class.
    gcroot<DataTable ^> table;
};

#pragma unmanaged
int main()
{
    // Create a table and add a few rows to it.
    DatabaseClass *db = new DatabaseClass();
    db->CreateAndPopulateTable();

    BSTR str1 = SysAllocString(L"This is string 1.");
    db->AddRow(str1);

    BSTR str2 = SysAllocString(L"This is string 2.");
    db->AddRow(str2);

    // Now retrieve the rows and display their contents.
    BSTR values[MAXCOLS];
    BSTR str3 = SysAllocString(L"StringCol");
    int len = db->GetValuesForColumn(
        str3, values, MAXCOLS);
    for (int i = 0; i < len; i++)
    {
        wcout << "StringCol: " << values[i] << endl;

        // Deallocate the memory allocated using
        // Marshal::StringToBSTR.
        SysFreeString(values[i]);
    }

    SysFreeString(str1);
    SysFreeString(str2);
    SysFreeString(str3);
    delete db;

    return 0;
}
StringCol: This is string 1.
StringCol: This is string 2.

Compilar el código

  • Para compilar el código desde la línea de comandos, guarde el ejemplo de código en un archivo denominado adonet_marshal_string_native.cpp y escriba la siguiente instrucción:

    cl /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll adonet_marshal_string_native.cpp
    

Serialización de cadenas Unicode para ADO.NET

Muestra cómo agregar una cadena nativa de Unicode (wchar_t *) a una base de datos y cómo serializar System.String desde una base de datos a una cadena nativa de Unicode.

Ejemplo

En este ejemplo, se crea la clase DatabaseClass para interactuar con un objeto DataTable de ADO.NET. Tenga en cuenta que esta clase es una class de C++ nativa (en comparación con ref class o value class). Esto es necesario porque queremos usar esta clase desde código nativo y no se pueden usar tipos administrados en código nativo. Esta clase se compilará para dirigirse a CLR, tal como se indica en la directiva #pragma managed anterior a la declaración de clase. Para más información sobre esta directiva, consulte managed, unmanaged.

Anote el miembro privado de la clase DatabaseClass: gcroot<DataTable ^> table. Dado que los tipos nativos no pueden contener tipos administrados, la palabra clave gcroot es necesaria. Para obtener más información sobre gcroot, consulte Declaración de manipuladores en tipos nativos.

El resto del código de este ejemplo es código de C++ nativo, como indica la directiva #pragma unmanaged que precede a main. En este ejemplo, vamos a crear una nueva instancia de DatabaseClass y llamar a sus métodos para crear una tabla y rellenar algunas filas de la tabla. Tenga en cuenta que las cadenas de C++ de Unicode se pasan como valores para la columna de base de datos StringCol. Dentro de DatabaseClass, estas cadenas se serializarán en cadenas administradas mediante la funcionalidad de serialización que se encuentra en el espacio de nombres System.Runtime.InteropServices. En concreto, el método PtrToStringUni se usa para serializar de wchar_t * a String, y el método StringToHGlobalUni se usa para serializar un objeto String a wchar_t *.

Nota:

La memoria asignada por StringToHGlobalUni se debe desasignar llamando a FreeHGlobal o GlobalFree.

// adonet_marshal_string_wide.cpp
// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll
#include <comdef.h>
#include <gcroot.h>
#include <iostream>
using namespace std;

#using <System.Data.dll>
using namespace System;
using namespace System::Data;
using namespace System::Runtime::InteropServices;

#define MAXCOLS 100

#pragma managed
class DatabaseClass
{
public:
    DatabaseClass() : table(nullptr) { }

    void AddRow(wchar_t *stringColValue)
    {
        // Add a row to the table.
        DataRow ^row = table->NewRow();
        row["StringCol"] = Marshal::PtrToStringUni(
            (IntPtr)stringColValue);
        table->Rows->Add(row);
    }

    void CreateAndPopulateTable()
    {
        // Create a simple DataTable.
        table = gcnew DataTable("SampleTable");

        // Add a column of type String to the table.
        DataColumn ^column1 = gcnew DataColumn("StringCol",
            Type::GetType("System.String"));
        table->Columns->Add(column1);
    }

    int GetValuesForColumn(wchar_t *dataColumn, wchar_t **values,
        int valuesLength)
    {
        // Marshal the name of the column to a managed
        // String.
        String ^columnStr = Marshal::PtrToStringUni(
                (IntPtr)dataColumn);

        // Get all rows in the table.
        array<DataRow ^> ^rows = table->Select();
        int len = rows->Length;
        len = (len > valuesLength) ? valuesLength : len;
        for (int i = 0; i < len; i++)
        {
            // Marshal each column value from a managed string
            // to a wchar_t *.
            values[i] = (wchar_t *)Marshal::StringToHGlobalUni(
                (String ^)rows[i][columnStr]).ToPointer();
        }

        return len;
    }

private:
    // Using gcroot, you can use a managed type in
    // a native class.
    gcroot<DataTable ^> table;
};

#pragma unmanaged
int main()
{
    // Create a table and add a few rows to it.
    DatabaseClass *db = new DatabaseClass();
    db->CreateAndPopulateTable();
    db->AddRow(L"This is string 1.");
    db->AddRow(L"This is string 2.");

    // Now retrieve the rows and display their contents.
    wchar_t *values[MAXCOLS];
    int len = db->GetValuesForColumn(
        L"StringCol", values, MAXCOLS);
    for (int i = 0; i < len; i++)
    {
        wcout << "StringCol: " << values[i] << endl;

        // Deallocate the memory allocated using
        // Marshal::StringToHGlobalUni.
        GlobalFree(values[i]);
    }

    delete db;

    return 0;
}
StringCol: This is string 1.
StringCol: This is string 2.

Compilar el código

  • Para compilar el código desde la línea de comandos, guarde el ejemplo de código en un archivo denominado adonet_marshal_string_wide.cpp y escriba la siguiente instrucción:

    cl /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll adonet_marshal_string_wide.cpp
    

Serialización de un tipo de datos VARIANT para ADO.NET

Muestra cómo agregar un VARIANT nativo a una base de datos y cómo serializar System.Object desde una base de datos a un VARIANT nativo.

Ejemplo

En este ejemplo, se crea la clase DatabaseClass para interactuar con un objeto DataTable de ADO.NET. Tenga en cuenta que esta clase es una class de C++ nativa (en comparación con ref class o value class). Esto es necesario porque queremos usar esta clase desde código nativo y no se pueden usar tipos administrados en código nativo. Esta clase se compilará para dirigirse a CLR, tal como se indica en la directiva #pragma managed anterior a la declaración de clase. Para más información sobre esta directiva, consulte managed, unmanaged.

Anote el miembro privado de la clase DatabaseClass: gcroot<DataTable ^> table. Dado que los tipos nativos no pueden contener tipos administrados, la palabra clave gcroot es necesaria. Para obtener más información sobre gcroot, consulte Declaración de manipuladores en tipos nativos.

El resto del código de este ejemplo es código de C++ nativo, como indica la directiva #pragma unmanaged que precede a main. En este ejemplo, vamos a crear una nueva instancia de DatabaseClass y llamar a sus métodos para crear una tabla y rellenar algunas filas de la tabla. Tenga en cuenta que los tipos nativos VARIANT se pasan como valores para la columna de base de datos ObjectCol. Dentro de DatabaseClass, estos tipos VARIANT se serializarán en objetos administrados mediante la funcionalidad de serialización que se encuentra en el espacio de nombres System.Runtime.InteropServices. En concreto, el método GetObjectForNativeVariant se usa para serializar de VARIANT a Object, y el método GetNativeVariantForObject se usa para serializar un objeto Object a VARIANT.

// adonet_marshal_variant.cpp
// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll
#include <comdef.h>
#include <gcroot.h>
#include <iostream>
using namespace std;

#using <System.Data.dll>
using namespace System;
using namespace System::Data;
using namespace System::Runtime::InteropServices;

#define MAXCOLS 100

#pragma managed
class DatabaseClass
{
public:
    DatabaseClass() : table(nullptr) { }

    void AddRow(VARIANT *objectColValue)
    {
        // Add a row to the table.
        DataRow ^row = table->NewRow();
        row["ObjectCol"] = Marshal::GetObjectForNativeVariant(
            IntPtr(objectColValue));
        table->Rows->Add(row);
    }

    void CreateAndPopulateTable()
    {
        // Create a simple DataTable.
        table = gcnew DataTable("SampleTable");

        // Add a column of type String to the table.
        DataColumn ^column1 = gcnew DataColumn("ObjectCol",
            Type::GetType("System.Object"));
        table->Columns->Add(column1);
    }

    int GetValuesForColumn(wchar_t *dataColumn, VARIANT *values,
        int valuesLength)
    {
        // Marshal the name of the column to a managed
        // String.
        String ^columnStr = Marshal::PtrToStringUni(
                (IntPtr)dataColumn);

        // Get all rows in the table.
        array<DataRow ^> ^rows = table->Select();
        int len = rows->Length;
        len = (len > valuesLength) ? valuesLength : len;
        for (int i = 0; i < len; i++)
        {
            // Marshal each column value from a managed object
            // to a VARIANT.
            Marshal::GetNativeVariantForObject(
                rows[i][columnStr], IntPtr(&values[i]));
        }

        return len;
    }

private:
    // Using gcroot, you can use a managed type in
    // a native class.
    gcroot<DataTable ^> table;
};

#pragma unmanaged
int main()
{
    // Create a table and add a few rows to it.
    DatabaseClass *db = new DatabaseClass();
    db->CreateAndPopulateTable();

    BSTR bstr1 = SysAllocString(L"This is a BSTR in a VARIANT.");
    VARIANT v1;
    v1.vt = VT_BSTR;
    v1.bstrVal = bstr1;
    db->AddRow(&v1);

    int i = 42;
    VARIANT v2;
    v2.vt = VT_I4;
    v2.lVal = i;
    db->AddRow(&v2);

    // Now retrieve the rows and display their contents.
    VARIANT values[MAXCOLS];
    int len = db->GetValuesForColumn(
        L"ObjectCol", values, MAXCOLS);
    for (int i = 0; i < len; i++)
    {
        switch (values[i].vt)
        {
            case VT_BSTR:
                wcout << L"ObjectCol: " << values[i].bstrVal << endl;
                break;
            case VT_I4:
                cout << "ObjectCol: " << values[i].lVal << endl;
                break;
            default:
                break;
        }

    }

    SysFreeString(bstr1);
    delete db;

    return 0;
}
ObjectCol: This is a BSTR in a VARIANT.
ObjectCol: 42

Compilar el código

  • Para compilar el código desde la línea de comandos, guarde el ejemplo de código en un archivo denominado adonet_marshal_variant.cpp y escriba la siguiente instrucción:

    cl /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll adonet_marshal_variant.cpp
    

Serialización de una matriz SAFEARRAY para ADO.NET

Muestra cómo agregar un SAFEARRAY nativo a una base de datos y cómo serializar una matriz administrada desde una base de datos a un SAFEARRAY nativo.

Ejemplo

En este ejemplo, se crea la clase DatabaseClass para interactuar con un objeto DataTable de ADO.NET. Tenga en cuenta que esta clase es una class de C++ nativa (en comparación con ref class o value class). Esto es necesario porque queremos usar esta clase desde código nativo y no se pueden usar tipos administrados en código nativo. Esta clase se compilará para dirigirse a CLR, tal como se indica en la directiva #pragma managed anterior a la declaración de clase. Para más información sobre esta directiva, consulte managed, unmanaged.

Anote el miembro privado de la clase DatabaseClass: gcroot<DataTable ^> table. Dado que los tipos nativos no pueden contener tipos administrados, la palabra clave gcroot es necesaria. Para obtener más información sobre gcroot, consulte Declaración de manipuladores en tipos nativos.

El resto del código de este ejemplo es código de C++ nativo, como indica la directiva #pragma unmanaged que precede a main. En este ejemplo, vamos a crear una nueva instancia de DatabaseClass y llamar a sus métodos para crear una tabla y rellenar algunas filas de la tabla. Tenga en cuenta que los tipos nativos SAFEARRAY se pasan como valores para la columna de base de datos ArrayIntsCol. Dentro de DatabaseClass, estos tipos SAFEARRAY se serializarán en objetos administrados mediante la funcionalidad de serialización que se encuentra en el espacio de nombres System.Runtime.InteropServices. En concreto, el método Copy se usa para serializar un SAFEARRAY en una matriz administrada de enteros, y el método Copy se usa para serializar una matriz administrada de enteros en un SAFEARRAY.

// adonet_marshal_safearray.cpp
// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll
#include <comdef.h>
#include <gcroot.h>
#include <iostream>
using namespace std;

#using <System.Data.dll>
using namespace System;
using namespace System::Data;
using namespace System::Runtime::InteropServices;

#define MAXCOLS 100

#pragma managed
class DatabaseClass
{
public:
    DatabaseClass() : table(nullptr) { }

    void AddRow(SAFEARRAY *arrayIntsColValue)
    {
        // Add a row to the table.
        DataRow ^row = table->NewRow();
        int len = arrayIntsColValue->rgsabound[0].cElements;
        array<int> ^arr = gcnew array<int>(len);

        int *pData;
        SafeArrayAccessData(arrayIntsColValue, (void **)&pData);
        Marshal::Copy(IntPtr(pData), arr, 0, len);
        SafeArrayUnaccessData(arrayIntsColValue);

        row["ArrayIntsCol"] = arr;
        table->Rows->Add(row);
    }

    void CreateAndPopulateTable()
    {
        // Create a simple DataTable.
        table = gcnew DataTable("SampleTable");

        // Add a column of type String to the table.
        DataColumn ^column1 = gcnew DataColumn("ArrayIntsCol",
            Type::GetType("System.Int32[]"));
        table->Columns->Add(column1);
    }

    int GetValuesForColumn(wchar_t *dataColumn, SAFEARRAY **values,
        int valuesLength)
    {
        // Marshal the name of the column to a managed
        // String.
        String ^columnStr = Marshal::PtrToStringUni(
                (IntPtr)dataColumn);

        // Get all rows in the table.
        array<DataRow ^> ^rows = table->Select();
        int len = rows->Length;
        len = (len > valuesLength) ? valuesLength : len;
        for (int i = 0; i < len; i++)
        {
            // Marshal each column value from a managed array
            // of Int32s to a SAFEARRAY of type VT_I4.
            values[i] = SafeArrayCreateVector(VT_I4, 0, 10);
            int *pData;
            SafeArrayAccessData(values[i], (void **)&pData);
            Marshal::Copy((array<int> ^)rows[i][columnStr], 0,
                IntPtr(pData), 10);
            SafeArrayUnaccessData(values[i]);
        }

        return len;
    }

private:
    // Using gcroot, you can use a managed type in
    // a native class.
    gcroot<DataTable ^> table;
};

#pragma unmanaged
int main()
{
    // Create a table and add a few rows to it.
    DatabaseClass *db = new DatabaseClass();
    db->CreateAndPopulateTable();

    // Create a standard array.
    int originalArray[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    // Create a SAFEARRAY.
    SAFEARRAY *psa;
    psa = SafeArrayCreateVector(VT_I4, 0, 10);

    // Copy the data from the original array to the SAFEARRAY.
    int *pData;
    HRESULT hr = SafeArrayAccessData(psa, (void **)&pData);
    memcpy(pData, &originalArray, 40);
    SafeArrayUnaccessData(psa);
    db->AddRow(psa);

    // Now retrieve the rows and display their contents.
    SAFEARRAY *values[MAXCOLS];
    int len = db->GetValuesForColumn(
        L"ArrayIntsCol", values, MAXCOLS);
    for (int i = 0; i < len; i++)
    {
        int *pData;
        SafeArrayAccessData(values[i], (void **)&pData);
        for (int j = 0; j < 10; j++)
        {
            cout << pData[j] << " ";
        }
        cout << endl;
        SafeArrayUnaccessData(values[i]);

        // Deallocate the memory allocated using
        // SafeArrayCreateVector.
        SafeArrayDestroy(values[i]);
    }

    SafeArrayDestroy(psa);
    delete db;

    return 0;
}
0 1 2 3 4 5 6 7 8 9

Compilar el código

  • Para compilar el código desde la línea de comandos, guarde el ejemplo de código en un archivo denominado adonet_marshal_safearray.cpp y escriba la siguiente instrucción:

    cl /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll adonet_marshal_safearray.cpp
    

Seguridad de .NET Framework

Para obtener información sobre los problemas de seguridad relacionados con ADO.NET, consulte Protección de aplicaciones ADO.NET.

Sección Descripción
ADO.NET Proporciona información general sobre ADO.NET, un conjunto de clases que exponen servicios de acceso a datos para el programador de .NET.

Consulte también

Programación de .NET con C++/CLI (Visual C++)

Interoperabilidad nativa y de .NET

System.Runtime.InteropServices

Interoperabilidad