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

ADO.NET ist die .NET Framework-API für den Datenzugriff und bietet Leistung und Benutzerfreundlichkeit, die von früheren Datenzugriffslösungen nicht übereinstimmen. 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 das Hinzufügen einer systemeigenen Zeichenfolge (char *) zu einer Datenbank und das Marshallen einer System.String Datenbank in eine systemeigene 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 verwalteter, nicht verwalteter Richtlinie.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist die gcroot Schlüsselwort (keyword) erforderlich. Weitere Informationen gcrootfinden Sie unter 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 von DatabaseClass werden diese Zeichenfolgen mithilfe der Im Namespace gefundenen System.Runtime.InteropServices Marshaling-Funktionalität an verwaltete Zeichenfolgen gemarstet. Insbesondere wird die Methode PtrToStringAnsi verwendet, um ein bis char * a Stringzu marshallen, und die Methode StringToHGlobalAnsi wird verwendet, um ein String an ein char *zu marshallen.

Hinweis

Der von StringToHGlobalAnsi ihnen zugewiesene Speicher muss durch Aufrufen einer FreeHGlobal oder GlobalFreevon ihnen zugewiesen 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
    

Marshal BSTR Strings for ADO.NET

Veranschaulicht das Hinzufügen einer COM-Zeichenfolge (BSTR) zu einer Datenbank und das Marshallen einer System.String Datenbank in einer BSTRDatenbank.

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 verwalteter, nicht verwalteter Richtlinie.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist die gcroot Schlüsselwort (keyword) erforderlich. Weitere Informationen gcrootfinden Sie unter 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 COM-Zeichenfolgen als Werte für die Datenbankspalte StringCol übergeben werden. Innerhalb von DatabaseClass werden diese Zeichenfolgen mithilfe der Im Namespace gefundenen System.Runtime.InteropServices Marshaling-Funktionalität an verwaltete Zeichenfolgen gemarstet. Insbesondere wird die Methode PtrToStringBSTR verwendet, um ein bis BSTR a Stringzu marshallen, und die Methode StringToBSTR wird verwendet, um ein String an ein BSTRzu marshallen.

Hinweis

Der von StringToBSTR ihnen zugewiesene Speicher muss durch Aufrufen einer FreeBSTR oder SysFreeStringvon ihnen zugewiesen werden.

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

Marshal Unicode Strings for ADO.NET

Veranschaulicht das Hinzufügen einer systemeigenen Unicode-Zeichenfolge (wchar_t *) zu einer Datenbank und das Marshallen einer System.String Datenbank in eine systemeigene 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 verwalteter, nicht verwalteter Richtlinie.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist die gcroot Schlüsselwort (keyword) erforderlich. Weitere Informationen gcrootfinden Sie unter 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 Unicode-C++-Zeichenfolgen als Werte für die Datenbankspalte StringCol übergeben werden. Innerhalb von DatabaseClass werden diese Zeichenfolgen mithilfe der Im Namespace gefundenen System.Runtime.InteropServices Marshaling-Funktionalität an verwaltete Zeichenfolgen gemarstet. Insbesondere wird die Methode PtrToStringUni verwendet, um ein bis wchar_t * a Stringzu marshallen, und die Methode StringToHGlobalUni wird verwendet, um ein String an ein wchar_t *zu marshallen.

Hinweis

Der von StringToHGlobalUni ihnen zugewiesene Speicher muss durch Aufrufen einer FreeHGlobal oder GlobalFreevon ihnen zugewiesen werden.

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

Marshal a VARIANT für ADO.NET

Veranschaulicht das Hinzufügen einer systemeigenen VARIANT Datenbank und das Marshallen einer System.Object Datenbank aus einer Datenbank zu einer systemeigenen VARIANTDatenbank.

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 verwalteter, nicht verwalteter Richtlinie.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist die gcroot Schlüsselwort (keyword) erforderlich. Weitere Informationen gcrootfinden Sie unter 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 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 an verwaltete Objekte gemarstet. Insbesondere wird die Methode GetObjectForNativeVariant verwendet, um eine an zu VARIANT Objectmarshallen, und die Methode GetNativeVariantForObject wird verwendet, um eine zu VARIANTmarshallenObject.

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

Marshal a SAFEARRAY for ADO.NET

Veranschaulicht das Hinzufügen einer nativen SAFEARRAY Datenbank und das Marshallen eines verwalteten Arrays aus einer Datenbank zu einer systemeigenen SAFEARRAYDatenbank.

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 verwalteter, nicht verwalteter Richtlinie.

Beachten Sie das private Mitglied der DatabaseClass-Klasse: gcroot<DataTable ^> table. Da systemeigene Typen keine verwalteten Typen enthalten können, ist die gcroot Schlüsselwort (keyword) erforderlich. Weitere Informationen gcrootfinden Sie unter 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 SAFEARRAY Typen als Werte für die Datenbankspalte ArrayIntsCol übergeben werden. Innerhalb von DatabaseClass werden diese SAFEARRAY Typen mithilfe der Marshaling-Funktionalität im System.Runtime.InteropServices Namespace an verwaltete Objekte gemarstet. Insbesondere wird die Methode Copy verwendet, um ein SAFEARRAY verwaltetes Array von ganzen Zahlen zu marshallen, und die Methode Copy wird verwendet, um ein verwaltetes Array von ganzen Zahlen zu marshallen.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

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