Partilhar via


Acesso a dados usando ADO.NET (C++/CLI)

ADO.NET é a API .NET Framework para acesso a dados e fornece energia e facilidade de uso incompatíveis com soluções de acesso a dados anteriores. Esta seção descreve alguns dos problemas que envolvem o ADO.NET exclusivos para usuários do Visual C++, como marshaling de tipos nativos.

ADO.NET é executado no CLR (Common Language Runtime). Portanto, qualquer aplicativo que interaja com ADO.NET também deve ter como destino o CLR. No entanto, isso não significa que os aplicativos nativos não possam usar ADO.NET. Esses exemplos demonstrarão como interagir com um banco de dados ADO.NET do código nativo.

Realizar marshaling de cadeias de caracteres ANSI para ADO.NET

Demonstra como adicionar uma cadeia de caracteres nativa (char *) a um banco de dados e como realizar marshaling de um System.String de um banco de dados para uma cadeia de caracteres nativa.

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto DataTable ADO.NET. Observe que essa classe é uma class C++ nativa (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe do código nativo e você não pode usar tipos gerenciados no código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela diretiva #pragma managed, anterior à declaração de classe. Para obter mais informações sobre essa diretiva, consulte gerenciado, não gerenciado.

Observe o membro privado da classe DatabaseClass: gcroot<DataTable ^> table. Como os tipos nativos não podem conter tipos gerenciados, a palavra-chave gcroot é necessária. Para obter mais informações sobre gcroot, consulte Como declarar identificadores em tipos nativos.

O restante do código neste exemplo é o código C++ nativo, conforme indicado pela diretiva #pragma unmanaged anterior a main. Neste exemplo, criamos uma nova instância de DatabaseClass e chamando seus métodos para criar uma tabela e preencher algumas linhas na tabela. Observe que cadeias de caracteres C++ nativas são passadas como valores para a coluna de banco de dados StringCol. Dentro do DatabaseClass, essas cadeias de caracteres são marshaled para cadeias de caracteres gerenciadas usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método PtrToStringAnsi é usado para realizar marshaling de um char * em um String e o método StringToHGlobalAnsi é usado para realizar marshaling de um String em um char *.

Observação

A memória alocada por StringToHGlobalAnsi deve ser desalocada chamando FreeHGlobal ou 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.

Compilando o código

  • Para compilar o código da linha de comando, salve o exemplo de código em um arquivo chamado adonet_marshal_string_native.cpp e insira a seguinte instrução:

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

Realizar marshaling de cadeias de caracteres BSTR para ADO.NET

Demonstra como adicionar uma cadeia de caracteres COM (BSTR) a um banco de dados e como realizar marshaling de um System.String de um banco de dados para um BSTR.

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto DataTable ADO.NET. Observe que essa classe é uma class C++ nativa (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe do código nativo e você não pode usar tipos gerenciados no código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela diretiva #pragma managed, anterior à declaração de classe. Para obter mais informações sobre essa diretiva, consulte gerenciado, não gerenciado.

Observe o membro privado da classe DatabaseClass: gcroot<DataTable ^> table. Como os tipos nativos não podem conter tipos gerenciados, a palavra-chave gcroot é necessária. Para obter mais informações sobre gcroot, consulte Como declarar identificadores em tipos nativos.

O restante do código neste exemplo é o código C++ nativo, conforme indicado pela diretiva #pragma unmanaged anterior a main. Neste exemplo, criamos uma nova instância de DatabaseClass e chamando seus métodos para criar uma tabela e preencher algumas linhas na tabela. Observe que as cadeias de caracteres COM são passadas como valores para a coluna de banco de dados StringCol. Dentro do DatabaseClass, essas cadeias de caracteres são marshaled para cadeias de caracteres gerenciadas usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método PtrToStringBSTR é usado para realizar marshaling de um BSTR em um String e o método StringToBSTR é usado para realizar marshaling de um String em um BSTR.

Observação

A memória alocada por StringToBSTR deve ser desalocada chamando FreeBSTR ou 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.

Compilando o código

  • Para compilar o código da linha de comando, salve o exemplo de código em um arquivo chamado adonet_marshal_string_native.cpp e insira a seguinte instrução:

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

Realizar marshaling de cadeias de caracteres Unicode para ADO.NET

Demonstra como adicionar uma cadeia de caracteres Unicode nativa (wchar_t *) a um banco de dados e como fazer marshal de um System.String banco de dados para uma cadeia de caracteres Unicode nativa.

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto DataTable ADO.NET. Observe que essa classe é uma class C++ nativa (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe do código nativo e você não pode usar tipos gerenciados no código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela diretiva #pragma managed, anterior à declaração de classe. Para obter mais informações sobre essa diretiva, consulte gerenciado, não gerenciado.

Observe o membro privado da classe DatabaseClass: gcroot<DataTable ^> table. Como os tipos nativos não podem conter tipos gerenciados, a palavra-chave gcroot é necessária. Para obter mais informações sobre gcroot, consulte Como declarar identificadores em tipos nativos.

O restante do código neste exemplo é o código C++ nativo, conforme indicado pela diretiva #pragma unmanaged anterior a main. Neste exemplo, criamos uma nova instância de DatabaseClass e chamando seus métodos para criar uma tabela e preencher algumas linhas na tabela. Observe que cadeias de caracteres C++ Unicode estão sendo passadas como valores para a coluna de banco de dados StringCol. Dentro do DatabaseClass, essas cadeias de caracteres são marshaled para cadeias de caracteres gerenciadas usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método PtrToStringUni é usado para realizar marshaling de um wchar_t * em um String e o método StringToHGlobalUni é usado para realizar marshaling de um String em um wchar_t *.

Observação

A memória alocada por StringToHGlobalUni deve ser desalocada chamando FreeHGlobal ou 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.

Compilando o código

  • Para compilar o código da linha de comando, salve o exemplo de código em um arquivo chamado adonet_marshal_string_wide.cpp e insira a seguinte instrução:

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

Realizar marshaling de um VARIANT para ADO.NET

Demonstra como adicionar um VARIANT nativo a um banco de dados e como realizar marshaling de um System.Object banco de dados para um VARIANT nativo.

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto DataTable ADO.NET. Observe que essa classe é uma class C++ nativa (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe do código nativo e você não pode usar tipos gerenciados no código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela diretiva #pragma managed, anterior à declaração de classe. Para obter mais informações sobre essa diretiva, consulte gerenciado, não gerenciado.

Observe o membro privado da classe DatabaseClass: gcroot<DataTable ^> table. Como os tipos nativos não podem conter tipos gerenciados, a palavra-chave gcroot é necessária. Para obter mais informações sobre gcroot, consulte Como declarar identificadores em tipos nativos.

O restante do código neste exemplo é o código C++ nativo, conforme indicado pela diretiva #pragma unmanaged anterior a main. Neste exemplo, criamos uma nova instância de DatabaseClass e chamando seus métodos para criar uma tabela e preencher algumas linhas na tabela. Observe que tipos VARIANT nativos são passados como valores para a coluna de banco de dados ObjectCol. Dentro do DatabaseClass, esses tipos VARIANT são marshaled para objetos gerenciados usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método GetObjectForNativeVariant é usado para realizar marshaling de um VARIANT em um Object e o método GetNativeVariantForObject é usado para realizar marshaling de um Object para um 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

Compilando o código

  • Para compilar o código da linha de comando, salve o exemplo de código em um arquivo chamado adonet_marshal_variant.cpp e insira a seguinte instrução:

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

Realizar marshaling de um SAFEARRAY para ADO.NET

Demonstra como adicionar um SAFEARRAY nativo a um banco de dados e como realizar marshaling de uma matriz gerenciada de um banco de dados para um nativo SAFEARRAY.

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto DataTable ADO.NET. Observe que essa classe é uma class C++ nativa (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe do código nativo e você não pode usar tipos gerenciados no código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela diretiva #pragma managed, anterior à declaração de classe. Para obter mais informações sobre essa diretiva, consulte gerenciado, não gerenciado.

Observe o membro privado da classe DatabaseClass: gcroot<DataTable ^> table. Como os tipos nativos não podem conter tipos gerenciados, a palavra-chave gcroot é necessária. Para obter mais informações sobre gcroot, consulte Como declarar identificadores em tipos nativos.

O restante do código neste exemplo é o código C++ nativo, conforme indicado pela diretiva #pragma unmanaged anterior a main. Neste exemplo, criamos uma nova instância de DatabaseClass e chamando seus métodos para criar uma tabela e preencher algumas linhas na tabela. Observe que tipos SAFEARRAY nativos são passados como valores para a coluna de banco de dados ArrayIntsCol. Dentro do DatabaseClass, esses tipos SAFEARRAY são marshaled para objetos gerenciados usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método Copy é usado para realizar marshaling de uma SAFEARRAY em uma matriz gerenciada de inteiros e o método Copy é usado para realizar marshaling de uma matriz gerenciada de inteiros em um 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

Compilando o código

  • Para compilar o código da linha de comando, salve o exemplo de código em um arquivo chamado adonet_marshal_safearray.cpp e insira a seguinte instrução:

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

Segurança do .NET Framework

Para obter informações sobre problemas de segurança envolvendo ADO.NET, consulte Proteção de aplicativos de ADO.NET.

Seção Descrição
ADO.NET Fornece uma visão geral do ADO.NET, um conjunto de classes que expõem os serviços de acesso a dados ao programador .NET.

Confira também

Programação do .NET com C++/CLI (Visual C++)

Interoperabilidade entre .NET e nativo

System.Runtime.InteropServices

Interoperabilidade