How to: Marshal a VARIANT for ADO.NET (C++/CLI)

Demonstrates how to add a native VARIANT to a database and how to marshal a Object from a database to a native VARIANT.

Example

In this example, the class DatabaseClass is created to interact with an ADO.NET DataTable object. Note that this class is a native C++ class (as compared to a ref class or value class). This is necessary because we want to use this class from native code, and you cannot use managed types in native code. This class will be compiled to target the CLR, as is indicated by the #pragma managed directive preceding the class declaration. For more information on this directive, see managed, unmanaged.

Note the private member of the DatabaseClass class: gcroot<DataTable ^> table. Since native types cannot contain managed types, the gcroot keyword is necessary. For more information on gcroot, see How to: Declare Handles in Native Types.

The rest of the code in this example is native C++ code, as is indicated by the #pragma unmanaged directive preceding main. In this example, we are creating a new instance of DatabaseClass and calling its methods to create a table and populate some rows in the table. Note that native VARIANT types are being passed as values for the database column ObjectCol. Inside DatabaseClass, these VARIANT types are marshaled to managed objects using the marshaling functionality found in the System.Runtime.InteropServices namespace. Specifically, the method GetObjectForNativeVariant is used to marshal a VARIANT to an Object, and the method GetNativeVariantForObject is used to marshal an Object to a VARIANT.

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

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

#define MAXCOLS 100

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

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

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

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

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

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

        return len;
    }

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

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

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

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

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

    }

    SysFreeString(bstr1);
    delete db;

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

Compiling the Code

  • To compile the code from the command line, save the code example in a file named adonet_marshal_variant.cpp and enter the following statement:

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

Security

For information on security issues involving ADO.NET, see Securing ADO.NET Applications.

See Also

Reference

System.Runtime.InteropServices

Other Resources

Data Access Using ADO.NET (C++/CLI)

ADO.NET

Interoperability

Native and .NET Interoperability