Freigeben über


Datenzugriff mit ADO.NET (C++/CLI)

ADO.NET ist die API des .NET Frameworks für den Datenzugriff und bietet eine Leistung und Benutzerfreundlichkeit, die von früheren Datenzugriffslösungen beispiellos ist. In diesem Abschnitt werden einige der Probleme beschrieben, die ADO.NET betreffen, die für Visual C++-Benutzer eindeutig sind, z. B. das Marshallen systemeigener Typen.

ADO.NET wird unter der Common Language Runtime (CLR) ausgeführt. Daher muss jede Anwendung, die mit ADO.NET interagiert, auch auf die CLR abzielen. Das bedeutet jedoch nicht, dass systemeigene Anwendungen ADO.NET nicht verwenden können. In diesen Beispielen wird die Interaktion mit einer ADO.NET Datenbank aus systemeigenem Code veranschaulicht.

Marshal ANSI-Zeichenfolgen für ADO.NET

Veranschaulicht, wie eine systemeigene Zeichenfolge (char *) zu einer Datenbank hinzugefügt wird und wie ein System.String aus einer Datenbank in eine systemeigene Zeichenfolge umgewandelt wird.

Beispiel

In diesem Beispiel wird die Klasse DatabaseClass erstellt, um mit einem ADO.NET-Objekt DataTable zu interagieren. Beachten Sie, dass es sich bei dieser Klasse um ein systemeigenes C++ class handelt (im Vergleich zu einem ref class oder value class). Dies ist erforderlich, da wir diese Klasse aus systemeigenem Code verwenden möchten, und Sie können keine verwalteten Typen in systemeigenem Code verwenden. Diese Klasse wird so kompiliert, dass sie auf die CLR ausgerichtet ist, wie durch die #pragma managed Direktive vor der Klassendeklaration angegeben. Weitere Informationen zu dieser Direktive finden Sie unter verwaltet, nicht verwaltet.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist das gcroot Schlüsselwort erforderlich. Weitere Informationen zu gcroot finden Sie in "How to: Declare Handles in Native Types".

Der rest des Codes in diesem Beispiel ist systemeigener C++-Code, wie durch die #pragma unmanaged vorangehende mainDirektive angegeben. In diesem Beispiel erstellen wir eine neue Instanz von DatabaseClass und rufen die zugehörigen Methoden auf, um eine Tabelle zu erstellen und einige Zeilen in der Tabelle aufzufüllen. Beachten Sie, dass systemeigene C++-Zeichenfolgen als Werte für die Datenbankspalte StringCol übergeben werden. Innerhalb der DatabaseClass werden diese Zeichenfolgen mithilfe der Marshaling-Funktionalität im System.Runtime.InteropServices-Namespace an verwaltete Zeichenfolgen übertragen. Insbesondere wird die Methode PtrToStringAnsi verwendet, um ein char * in ein String zu marshallen, und die Methode StringToHGlobalAnsi wird verwendet, um ein String in ein char * zu marshallen.

Hinweis

Der von StringToHGlobalAnsi zugewiesene Speicher muss durch Aufrufen von FreeHGlobal oder GlobalFree freigegeben werden.

// 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.

Kompilieren des Codes

  • Um den Code aus der Befehlszeile zu kompilieren, speichern Sie das Codebeispiel in einer Datei namens adonet_marshal_string_native.cpp, und geben Sie die folgende Anweisung ein:

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

Marshalling von-BSTR Zeichenfolgen für ADO.NET

Veranschaulicht, wie man eine COM-Zeichenfolge (BSTR) zu einer Datenbank hinzufügt und wie man ein System.String von einer Datenbank zu einem BSTR marshallen kann.

Beispiel

In diesem Beispiel wird die Klasse DatabaseClass erstellt, um mit einem ADO.NET-Objekt DataTable zu interagieren. Beachten Sie, dass es sich bei dieser Klasse um ein systemeigenes C++ class handelt (im Vergleich zu einem ref class oder value class). Dies ist erforderlich, da wir diese Klasse aus systemeigenem Code verwenden möchten, und Sie können keine verwalteten Typen in systemeigenem Code verwenden. Diese Klasse wird so kompiliert, dass sie auf die CLR ausgerichtet ist, wie durch die #pragma managed Direktive vor der Klassendeklaration angegeben. Weitere Informationen zu dieser Direktive finden Sie unter Managed, Unmanaged.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist das gcroot Schlüsselwort erforderlich. Weitere Informationen zu gcroot finden Sie unter So geht's: Griffe in nativen Typen deklarieren.

Der rest des Codes in diesem Beispiel ist systemeigener C++-Code, wie durch die #pragma unmanaged vorangehende mainDirektive angegeben. In diesem Beispiel erstellen wir eine neue Instanz von DatabaseClass und rufen die zugehörigen Methoden auf, um eine Tabelle zu erstellen und einige Zeilen in der Tabelle aufzufüllen. Beachten Sie, dass COM-Zeichenfolgen als Werte für die Datenbankspalte StringCol übergeben werden. Innerhalb der DatabaseClass werden diese Zeichenfolgen mit der in dem System.Runtime.InteropServices-Namespace enthaltenen Marshaling-Funktionalität in verwaltete Zeichenfolgen umgewandelt. Insbesondere wird die Methode PtrToStringBSTR verwendet, um ein BSTR zu einem String zu übergeben, und die Methode StringToBSTR wird verwendet, um ein String zu einem BSTR zu übergeben.

Hinweis

Der von StringToBSTR zugewiesene Speicher muss freigegeben werden, indem entweder FreeBSTR oder SysFreeString aufgerufen wird.

// 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.

Kompilieren des Codes

  • Um den Code aus der Befehlszeile zu kompilieren, speichern Sie das Codebeispiel in einer Datei namens adonet_marshal_string_native.cpp, und geben Sie die folgende Anweisung ein:

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

Marshalling von Unicode-Zeichenfolgen für ADO.NET

Veranschaulicht das Hinzufügen einer nativen Unicode-Zeichenfolge (wchar_t *) zu einer Datenbank und das Marshallen eines System.String aus einer Datenbank in eine native Unicode-Zeichenfolge.

Beispiel

In diesem Beispiel wird die Klasse DatabaseClass erstellt, um mit einem ADO.NET-Objekt DataTable zu interagieren. Beachten Sie, dass es sich bei dieser Klasse um ein systemeigenes C++ class handelt (im Vergleich zu einem ref class oder value class). Dies ist erforderlich, da wir diese Klasse aus systemeigenem Code verwenden möchten, und Sie können keine verwalteten Typen in systemeigenem Code verwenden. Diese Klasse wird so kompiliert, dass sie auf die CLR ausgerichtet ist, wie durch die #pragma managed Direktive vor der Klassendeklaration angegeben. Weitere Informationen zu dieser Direktive finden Sie unter Managed, Unmanaged.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist das gcroot Schlüsselwort erforderlich. Weitere Informationen zu gcroot finden Sie unter So geht's: Griffe in nativen Typen deklarieren.

Der rest des Codes in diesem Beispiel ist systemeigener C++-Code, wie durch die #pragma unmanaged vorangehende mainDirektive angegeben. In diesem Beispiel erstellen wir eine neue Instanz von DatabaseClass und rufen die zugehörigen Methoden auf, um eine Tabelle zu erstellen und einige Zeilen in der Tabelle aufzufüllen. Beachten Sie, dass Unicode-C++-Zeichenfolgen als Werte für die Datenbankspalte StringCol übergeben werden. Innerhalb der DatabaseClass werden diese Zeichenfolgen mit der in dem System.Runtime.InteropServices-Namespace enthaltenen Marshaling-Funktionalität in verwaltete Zeichenfolgen umgewandelt. Insbesondere wird die Methode PtrToStringUni verwendet, um ein wchar_t * zu einem String zu übergeben, und die Methode StringToHGlobalUni wird verwendet, um ein String zu einem wchar_t * zu übergeben.

Hinweis

Der von StringToHGlobalUni zugewiesene Speicher muss freigegeben werden, indem entweder FreeHGlobal oder GlobalFree aufgerufen wird.

// 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.

Kompilieren des Codes

  • Um den Code aus der Befehlszeile zu kompilieren, speichern Sie das Codebeispiel in einer Datei namens adonet_marshal_string_wide.cpp, und geben Sie die folgende Anweisung ein:

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

"Ein VARIANT für ADO.NET arrangieren"

Veranschaulicht das Hinzufügen eines systemeigenen VARIANT zu einer Datenbank und das Marshallen eines System.Object aus einer Datenbank in eine systemeigene VARIANT.

Beispiel

In diesem Beispiel wird die Klasse DatabaseClass erstellt, um mit einem ADO.NET-Objekt DataTable zu interagieren. Beachten Sie, dass es sich bei dieser Klasse um ein systemeigenes C++ class handelt (im Vergleich zu einem ref class oder value class). Dies ist erforderlich, da wir diese Klasse aus systemeigenem Code verwenden möchten, und Sie können keine verwalteten Typen in systemeigenem Code verwenden. Diese Klasse wird so kompiliert, dass sie auf die CLR ausgerichtet ist, wie durch die #pragma managed Direktive vor der Klassendeklaration angegeben. Weitere Informationen zu dieser Direktive finden Sie unter Managed, Unmanaged.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist das gcroot Schlüsselwort erforderlich. Weitere Informationen zu gcroot finden Sie unter So geht's: Griffe in nativen Typen deklarieren.

Der rest des Codes in diesem Beispiel ist systemeigener C++-Code, wie durch die #pragma unmanaged vorangehende mainDirektive angegeben. In diesem Beispiel erstellen wir eine neue Instanz von DatabaseClass und rufen die zugehörigen Methoden auf, um eine Tabelle zu erstellen und einige Zeilen in der Tabelle aufzufüllen. Beachten Sie, dass systemeigene VARIANT Typen als Werte für die Datenbankspalte ObjectCol übergeben werden. Innerhalb von DatabaseClass werden diese VARIANT Typen mithilfe der Marshaling-Funktionalität im System.Runtime.InteropServices Namespace-Bereich an verwaltete Objekte übertragen. Insbesondere wird die Methode GetObjectForNativeVariant verwendet, um ein VARIANT für ein Object zu marshallen, und die Methode GetNativeVariantForObject wird verwendet, um ein Object für ein VARIANT zu marshallen.

// 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

Kompilieren des Codes

  • Um den Code aus der Befehlszeile zu kompilieren, speichern Sie das Codebeispiel in einer Datei namens adonet_marshal_variant.cpp, und geben Sie die folgende Anweisung ein:

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

Marshalling eines SAFEARRAY für ADO.NET

Veranschaulicht, wie man ein natives SAFEARRAY zu einer Datenbank hinzufügt und ein verwaltetes Array von einer Datenbank in ein natives SAFEARRAY umwandelt.

Beispiel

In diesem Beispiel wird die Klasse DatabaseClass erstellt, um mit einem ADO.NET-Objekt DataTable zu interagieren. Beachten Sie, dass es sich bei dieser Klasse um ein systemeigenes C++ class handelt (im Vergleich zu einem ref class oder value class). Dies ist erforderlich, da wir diese Klasse aus systemeigenem Code verwenden möchten, und Sie können keine verwalteten Typen in systemeigenem Code verwenden. Diese Klasse wird so kompiliert, dass sie auf die CLR ausgerichtet ist, wie durch die #pragma managed Direktive vor der Klassendeklaration angegeben. Weitere Informationen zu dieser Direktive finden Sie unter Managed, Unmanaged.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist das gcroot Schlüsselwort erforderlich. Weitere Informationen zu gcroot finden Sie unter So geht's: Griffe in nativen Typen deklarieren.

Der rest des Codes in diesem Beispiel ist systemeigener C++-Code, wie durch die #pragma unmanaged vorangehende mainDirektive angegeben. In diesem Beispiel erstellen wir eine neue Instanz von DatabaseClass und rufen die zugehörigen Methoden auf, um eine Tabelle zu erstellen und einige Zeilen in der Tabelle aufzufüllen. Beachten Sie, dass systemeigene SAFEARRAY Typen als Werte für die Datenbankspalte ArrayIntsCol übergeben werden. Innerhalb der DatabaseClass werden diese SAFEARRAY Typen mithilfe der Marshaling-Funktionalität im System.Runtime.InteropServices Namespace in verwaltete Objekte gemarshallt. Insbesondere wird die Methode Copy verwendet, um ein SAFEARRAY in ein verwaltetes Array von ganzen Zahlen zu marshallen, und die Methode Copy wird verwendet, um ein verwaltetes Array von ganzen Zahlen in ein SAFEARRAY zu marshallen.

// 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

Kompilieren des Codes

  • Um den Code aus der Befehlszeile zu kompilieren, speichern Sie das Codebeispiel in einer Datei namens adonet_marshal_safearray.cpp, und geben Sie die folgende Anweisung ein:

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

.NET Framework-Sicherheit

Informationen zu Sicherheitsproblemen, die ADO.NET betreffen, finden Sie unter Sichern von ADO.NET Anwendungen.

Abschnitt Beschreibung
ADO.NET Bietet eine Übersicht über ADO.NET, eine Reihe von Klassen, die Datenzugriffsdienste für .NET-Programmierer verfügbar machen.

Siehe auch

.NET Programming with C++/CLI (Visual C++) (.NET-Programmierung mit C++/CLI (Visual C++))

Interoperabilität von nativem Code und .NET

System.Runtime.InteropServices

Interoperabilität