방법: ADO.NET용 SAFEARRAY 마샬링(C++/CLI)

네이티브 SAFEARRAY를 데이터베이스에 추가하는 방법과 데이터베이스에서 네이티브 SAFEARRAY로 관리되는 배열을 마샬링하는 방법을 보여 줍니다.

예제

이 예제에서는 ADO.NET DataTable 개체와 상호 작용하기 위해 DatabaseClass 클래스가 만들어집니다. 이 클래스는 네이티브 C++ class(ref class 또는 value class와 비교)입니다. 이것은 네이티브 코드에서 이 클래스를 사용하려고 하기 때문에 필요합니다. 네이티브 코드에서는 관리되는 형식을 사용할 수 없습니다. 클래스 선언 앞에 있는 #pragma managed 지시문이 나타내듯이 이 클래스는 CLR을 대상으로 컴파일됩니다. 이 지시문에 대한 자세한 내용은 managed, unmanaged를 참조하십시오.

DatabaseClass 클래스의 전용 멤버는 gcroot<DataTable ^> table입니다. 네이티브 형식에는 관리되는 형식이 포함될 수 없으므로 gcroot 키워드가 필요합니다. gcroot에 대한 자세한 내용은 방법: 네이티브 형식으로 핸들 선언을 참조하십시오.

main 앞에 있는 #pragma unmanaged 지시문이 나타내듯이 이 예제에 있는 코드의 나머지 부분은 네이티브 C++ 코드입니다. 이 예제에서는 DatabaseClass의 새 인스턴스를 만든 다음 해당 메서드를 호출하여 테이블을 만들고 테이블의 일부 행을 채웁니다. 네이티브 SAFEARRAY 형식은 데이터베이스 열 ArrayIntsCol에 대한 값으로 전달됩니다. DatabaseClass 내에서 이러한 SAFEARRAY 형식은 System.Runtime.InteropServices 네임스페이스의 마샬링 기능을 사용하여 관리되는 개체로 마샬링됩니다. 특히, SAFEARRAY를 정수 계열의 관리되는 배열로 마샬링하는 데는 Copy 메서드가 사용되고 정수 계열의 관리되는 배열을 SAFEARRAY로 마샬링하는 데는 Copy 메서드가 사용됩니다.

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

코드 컴파일

  • 명령줄에서 코드를 컴파일하려면 코드 예제를 adonet_marshal_safearray.cpp라는 파일에 저장하고 다음 문을 입력합니다.

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

보안

ADO.NET과 관련된 보안 문제에 대한 자세한 내용은 ADO.NET 응용 프로그램 보안을 참조하십시오.

참고 항목

참조

System.Runtime.InteropServices

기타 리소스

ADO.NET을 사용하여 데이터 액세스(C++/CLI)

ADO.NET

상호 운용성

네이티브 및 .NET 상호 운용성