Partilhar via


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

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

ADO.NET é executado sob o Common Language Runtime (CLR). Portanto, qualquer aplicativo que interaja com ADO.NET também deve ter como alvo 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 a partir de código nativo.

Marechal ANSI Strings para ADO.NET

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

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto ADO.NET DataTable . Observe que essa classe é um C++ class nativo (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe de código nativo e você não pode usar tipos gerenciados em código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela #pragma managed diretiva que precede a declaração de classe. Para obter mais informações sobre esta 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 gcroot palavra-chave é necessária. Para mais informações sobre gcroot, consulte Como: Declarar handles em tipos nativos.

O restante do código neste exemplo é código C++ nativo, como é indicado pela #pragma unmanaged diretiva anterior main. Neste exemplo, estamos criando 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 estão sendo passadas como valores para a coluna de banco de dados StringCol. Dentro de DatabaseClass, essas cadeias de caracteres são convertidas em cadeias de caracteres geridas usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método PtrToStringAnsi é usado para organizar um char * em um String, e o método StringToHGlobalAnsi é usado para organizar um String em um char *.

Observação

A memória alocada por StringToHGlobalAnsi deve ser desalocada chamando um de 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 digite a seguinte instrução:

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

Marechal BSTR Strings para ADO.NET

Demonstra como adicionar uma cadeia de caracteres COM (BSTR) a uma base de dados e como gerir um System.String de uma base de dados para um BSTR.

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto ADO.NET DataTable . Observe que essa classe é um C++ class nativo (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe de código nativo e você não pode usar tipos gerenciados em código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela #pragma managed diretiva que precede a declaração de classe. Para obter mais informações sobre esta 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 gcroot palavra-chave é necessária. Para mais informações sobre gcroot, consulte Como: Declarar handles em tipos nativos.

O restante do código neste exemplo é código C++ nativo, como é indicado pela #pragma unmanaged diretiva anterior main. Neste exemplo, estamos criando 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 estão sendo passadas como valores para a coluna de banco de dados StringCol. Dentro de DatabaseClass, essas cadeias de caracteres são convertidas em cadeias de caracteres geridas usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método PtrToStringBSTR é usado para organizar um BSTR em um String, e o método StringToBSTR é usado para organizar um String em um BSTR.

Observação

A memória alocada por StringToBSTR deve ser desalocada chamando um de 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 digite a seguinte instrução:

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

Marshal Unicode Strings para ADO.NET

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

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto ADO.NET DataTable . Observe que essa classe é um C++ class nativo (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe de código nativo e você não pode usar tipos gerenciados em código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela #pragma managed diretiva que precede a declaração de classe. Para obter mais informações sobre esta 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 gcroot palavra-chave é necessária. Para mais informações sobre gcroot, consulte Como: Declarar handles em tipos nativos.

O restante do código neste exemplo é código C++ nativo, como é indicado pela #pragma unmanaged diretiva anterior main. Neste exemplo, estamos criando 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 Unicode C++ estão sendo passadas como valores para a coluna de banco de dados StringCol. Dentro de DatabaseClass, essas cadeias de caracteres são convertidas em cadeias de caracteres geridas usando a funcionalidade de marshaling encontrada no namespace System.Runtime.InteropServices. Especificamente, o método PtrToStringUni é usado para organizar um wchar_t * em um String, e o método StringToHGlobalUni é usado para organizar um String em um wchar_t *.

Observação

A memória alocada por StringToHGlobalUni deve ser desalocada chamando um de 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 digite a seguinte instrução:

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

Organizar uma variável para ADO.NET

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

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto ADO.NET DataTable . Observe que essa classe é um C++ class nativo (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe de código nativo e você não pode usar tipos gerenciados em código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela #pragma managed diretiva que precede a declaração de classe. Para obter mais informações sobre esta 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 gcroot palavra-chave é necessária. Para mais informações sobre gcroot, consulte Como: Declarar handles em tipos nativos.

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

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

Organizar um SAFEARRAY para ADO.NET

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

Exemplo

Neste exemplo, a classe DatabaseClass é criada para interagir com um objeto ADO.NET DataTable . Observe que essa classe é um C++ class nativo (em comparação com um ref class ou value class). Isso é necessário porque queremos usar essa classe de código nativo e você não pode usar tipos gerenciados em código nativo. Essa classe será compilada para direcionar o CLR, conforme indicado pela #pragma managed diretiva que precede a declaração de classe. Para obter mais informações sobre esta 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 gcroot palavra-chave é necessária. Para mais informações sobre gcroot, consulte Como: Declarar handles em tipos nativos.

O restante do código neste exemplo é código C++ nativo, como é indicado pela #pragma unmanaged diretiva anterior main. Neste exemplo, estamos criando uma nova instância de DatabaseClass e chamando seus métodos para criar uma tabela e preencher algumas linhas na tabela. Observe que os tipos nativos SAFEARRAY estão sendo passados como valores para a coluna de banco de dados ArrayIntsCol. Dentro de DatabaseClass, esses SAFEARRAY tipos são empacotados em objetos gerenciados usando a funcionalidade de empacotamento encontrada no System.Runtime.InteropServices namespace. Especificamente, o método Copy é usado para marcar um SAFEARRAY para uma matriz gerida de inteiros, e o método Copy é usado para marcar uma matriz gerida de inteiros para 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 digite 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 que envolvem ADO.NET, consulte Protegendo aplicativos ADO.NET.

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

Ver também

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

Interoperabilidade nativa e .NET

System.Runtime.InteropServices

Interoperabilidade