Inicio rápido: Conecta y consulta con el controlador OLE DB de Microsoft

En este inicio rápido, creas una aplicación de consola para Windows C++ con Visual Studio 2022 y versiones posteriores. La aplicación se conecta a Azure SQL Database, SQL database en Microsoft Fabric o SQL Server con Microsoft OLE DB Driver 19 para SQL Server. Ejecuta una consulta parametrizada contra los AdventureWorksLT datos de muestra y verifica el resultado.

Prerequisites

Creación de una base de datos SQL

Crea o conéctate a una base de datos SQL en una de las siguientes plataformas:

Para este inicio rápido, selecciona o carga los datos de muestra AdventureWorksLT .

Para un contenedor de SQL Server, crea el contenedor y carga los datos de muestra en un solo comando:

sqlcmd create mssql --accept-eula --using https://aka.ms/AdventureWorksLT.bak

Para una instancia existente de SQL Server, restaura una AdventureWorksLT copia de seguridad desde bases de datos de ejemplo de AdventureWorks.

La aplicación cliente de OLE DB en este inicio rápido funciona en Windows. Un contenedor de SQL Server puede ejecutarse en otro host compatible.

Verifica el controlador

Abre un x64 Native Tools Command Prompt correspondiente a tu versión de Visual Studio y, a continuación, ejecuta el siguiente comando:

Importante

Los comandos de este inicio rápido usan la sintaxis del símbolo del prompt. Ejecutalos en un símbolo de Herramientas Nativas x64, donde el indicador termina con >. No los ejecutes en PowerShell, donde el prompt empieza con PS.

reg query HKCR\MSOLEDBSQL19

El comando muestra el proveedor registrado MSOLEDBSQL19 .

Configurar la conexión

Configurado OLEDB_CONNECTION_STRING en el símbolo de comandos de Native Tools x64. La aplicación lee la cadena de conexión del entorno y no la muestra.

Para Azure SQL Database o base de datos SQL en Fabric, utiliza la autenticación interactiva de Microsoft Entra. Sustituye los marcadores de posición por el servidor, la base de datos y el ID de usuario de Microsoft Entra de tu recurso SQL:

set "OLEDB_CONNECTION_STRING=Provider=MSOLEDBSQL19;Data Source=tcp:<server>,1433;Initial Catalog=<database>;Authentication=ActiveDirectoryInteractive;User ID=<user_id>;Use Encryption for Data=Mandatory;Trust Server Certificate=false;"

Para la base de datos SQL en Fabric, tu identidad necesita permiso de lectura para el elemento de la base de datos. No se soporta autenticación SQL. Para obtener más información, consulte el artículo sobre la autenticación en la base de datos SQL de Microsoft Fabric.

Para una instancia existente de SQL Server que acepta autenticación de Windows, utilizeIntegrated Security=SSPI:

set "OLEDB_CONNECTION_STRING=Provider=MSOLEDBSQL19;Data Source=tcp:<server>,1433;Initial Catalog=<database>;Integrated Security=SSPI;Use Encryption for Data=Mandatory;Trust Server Certificate=false;"

Para una instancia o contenedor de SQL Server que acepte autenticación SQL, utilizaAuthentication=SqlPassword:

set "OLEDB_USER_ID=<user_id>"
set "OLEDB_PASSWORD=<password>"
set "OLEDB_CONNECTION_STRING=Provider=MSOLEDBSQL19;Data Source=tcp:<server>,1433;Initial Catalog=<database>;Authentication=SqlPassword;User ID=%OLEDB_USER_ID%;Password=%OLEDB_PASSWORD%;Use Encryption for Data=Mandatory;Trust Server Certificate=false;"

El certificado de SQL Server debe coincidir con el nombre del servidor y la cadena con una autoridad de certificación (CA) en la que el cliente de Windows confíe. Para un contenedor de SQL Server, configura la Seguridad de la Capa de Transporte (TLS) en el contenedor y registra la CA emisora en el cliente de Windows antes de ejecutar la aplicación. Para más información, consulte Encriptar conexiones a SQL Server en Linux y Configurar Motor de base de datos de SQL Server para cifrar conexiones.

Creación de la aplicación

  1. Crea un directorio de proyectos:

    mkdir oledb-quickstart
    cd oledb-quickstart
    
  2. Cree un archivo denominado oledb-quickstart.cpp con el código siguiente:

    #include <windows.h>
    #include <oledb.h>
    #include <msdasc.h>
    #include <msoledbsql.h>
    
    #include <cstddef>
    #include <iomanip>
    #include <iostream>
    #include <string>
    
    template <typename T>
    void Release(T*& pointer)
    {
        if (pointer != nullptr)
        {
            pointer->Release();
            pointer = nullptr;
        }
    }
    
    struct ParameterData
    {
        DBSTATUS status;
        DBLENGTH length;
        LONG value;
    };
    
    constexpr std::size_t productNameCharacters = 51;
    
    struct RowData
    {
        DBSTATUS productIdStatus;
        DBLENGTH productIdLength;
        LONG productId;
        DBSTATUS nameStatus;
        DBLENGTH nameLength;
        wchar_t name[productNameCharacters];
    };
    
    std::wstring ReadEnvironmentVariable(const wchar_t* name)
    {
        const DWORD length = GetEnvironmentVariableW(name, nullptr, 0);
        if (length == 0)
            return {};
    
        std::wstring value(length, L'\0');
        const DWORD copied = GetEnvironmentVariableW(
            name,
            value.data(),
            length);
        if (copied == 0 || copied >= length)
            return {};
    
        value.resize(copied);
        return value;
    }
    
    int wmain()
    {
        const std::wstring connectionString =
            ReadEnvironmentVariable(L"OLEDB_CONNECTION_STRING");
        if (connectionString.empty())
        {
            std::wcerr << L"Set OLEDB_CONNECTION_STRING before running.\n";
            return 1;
        }
    
        HRESULT result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
        if (FAILED(result))
        {
            std::wcerr << L"COM initialization failed: 0x"
                       << std::hex << result << L'\n';
            return 1;
        }
    
        IDataInitialize* dataInitialize = nullptr;
        IDBInitialize* dbInitialize = nullptr;
        IDBCreateSession* createSession = nullptr;
        IDBCreateCommand* createCommand = nullptr;
        ICommandText* commandText = nullptr;
        ICommandWithParameters* commandParameters = nullptr;
        IAccessor* parameterAccessor = nullptr;
        IRowset* rowset = nullptr;
        IAccessor* rowAccessor = nullptr;
        HACCESSOR parameterHandle = DB_NULL_HACCESSOR;
        HACCESSOR rowHandle = DB_NULL_HACCESSOR;
        HROW* rows = nullptr;
        DBCOUNTITEM rowCount = 0;
        bool initialized = false;
    
        do
        {
            result = CoCreateInstance(
                CLSID_MSDAINITIALIZE,
                nullptr,
                CLSCTX_INPROC_SERVER,
                IID_IDataInitialize,
                reinterpret_cast<void**>(&dataInitialize));
            if (FAILED(result))
                break;
    
            result = dataInitialize->GetDataSource(
                nullptr,
                CLSCTX_INPROC_SERVER,
                connectionString.c_str(),
                IID_IDBInitialize,
                reinterpret_cast<IUnknown**>(&dbInitialize));
            if (FAILED(result))
                break;
    
            result = dbInitialize->Initialize();
            if (FAILED(result))
                break;
            initialized = true;
    
            result = dbInitialize->QueryInterface(
                IID_IDBCreateSession,
                reinterpret_cast<void**>(&createSession));
            if (FAILED(result))
                break;
    
            result = createSession->CreateSession(
                nullptr,
                IID_IDBCreateCommand,
                reinterpret_cast<IUnknown**>(&createCommand));
            if (FAILED(result))
                break;
    
            result = createCommand->CreateCommand(
                nullptr,
                IID_ICommandText,
                reinterpret_cast<IUnknown**>(&commandText));
            if (FAILED(result))
                break;
    
            result = commandText->SetCommandText(
                DBGUID_DBSQL,
                const_cast<wchar_t*>(
                    L"SELECT TOP (5) ProductID, Name "
                    L"FROM SalesLT.Product "
                    L"WHERE ProductID > ? "
                    L"ORDER BY ProductID;"));
            if (FAILED(result))
                break;
    
            result = commandText->QueryInterface(
                IID_ICommandWithParameters,
                reinterpret_cast<void**>(&commandParameters));
            if (FAILED(result))
                break;
    
            DB_UPARAMS parameterOrdinal = 1;
            wchar_t parameterType[] = L"int";
            DBPARAMBINDINFO parameterInfo = {};
            parameterInfo.pwszDataSourceType = parameterType;
            parameterInfo.ulParamSize = sizeof(LONG);
            parameterInfo.dwFlags = DBPARAMFLAGS_ISINPUT;
            parameterInfo.bPrecision = 10;
    
            result = commandParameters->SetParameterInfo(
                1,
                &parameterOrdinal,
                &parameterInfo);
            if (FAILED(result))
                break;
    
            result = commandText->QueryInterface(
                IID_IAccessor,
                reinterpret_cast<void**>(&parameterAccessor));
            if (FAILED(result))
                break;
    
            DBBINDING parameterBinding = {};
            parameterBinding.iOrdinal = 1;
            parameterBinding.obStatus = offsetof(ParameterData, status);
            parameterBinding.obLength = offsetof(ParameterData, length);
            parameterBinding.obValue = offsetof(ParameterData, value);
            parameterBinding.dwPart = DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
            parameterBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
            parameterBinding.eParamIO = DBPARAMIO_INPUT;
            parameterBinding.cbMaxLen = sizeof(LONG);
            parameterBinding.wType = DBTYPE_I4;
            parameterBinding.bPrecision = 10;
    
            DBBINDSTATUS parameterBindStatus = DBBINDSTATUS_OK;
            result = parameterAccessor->CreateAccessor(
                DBACCESSOR_PARAMETERDATA,
                1,
                &parameterBinding,
                sizeof(ParameterData),
                &parameterHandle,
                &parameterBindStatus);
            if (FAILED(result) || parameterBindStatus != DBBINDSTATUS_OK)
            {
                if (SUCCEEDED(result))
                    result = E_FAIL;
                break;
            }
    
            ParameterData parameter = {
                DBSTATUS_S_OK,
                sizeof(LONG),
                0
            };
            DBPARAMS parameters = {
                &parameter,
                1,
                parameterHandle
            };
    
            result = commandText->Execute(
                nullptr,
                IID_IRowset,
                &parameters,
                nullptr,
                reinterpret_cast<IUnknown**>(&rowset));
            if (FAILED(result))
                break;
    
            result = rowset->QueryInterface(
                IID_IAccessor,
                reinterpret_cast<void**>(&rowAccessor));
            if (FAILED(result))
                break;
    
            DBBINDING rowBindings[2] = {};
            rowBindings[0].iOrdinal = 1;
            rowBindings[0].obStatus = offsetof(RowData, productIdStatus);
            rowBindings[0].obLength = offsetof(RowData, productIdLength);
            rowBindings[0].obValue = offsetof(RowData, productId);
            rowBindings[0].dwPart =
                DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
            rowBindings[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
            rowBindings[0].eParamIO = DBPARAMIO_NOTPARAM;
            rowBindings[0].cbMaxLen = sizeof(LONG);
            rowBindings[0].wType = DBTYPE_I4;
            rowBindings[0].bPrecision = 10;
    
            rowBindings[1].iOrdinal = 2;
            rowBindings[1].obStatus = offsetof(RowData, nameStatus);
            rowBindings[1].obLength = offsetof(RowData, nameLength);
            rowBindings[1].obValue = offsetof(RowData, name);
            rowBindings[1].dwPart =
                DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
            rowBindings[1].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
            rowBindings[1].eParamIO = DBPARAMIO_NOTPARAM;
            rowBindings[1].cbMaxLen =
                productNameCharacters * sizeof(wchar_t);
            rowBindings[1].wType = DBTYPE_WSTR;
    
            DBBINDSTATUS rowBindStatus[2] = {
                DBBINDSTATUS_OK,
                DBBINDSTATUS_OK
            };
            result = rowAccessor->CreateAccessor(
                DBACCESSOR_ROWDATA,
                2,
                rowBindings,
                sizeof(RowData),
                &rowHandle,
                rowBindStatus);
            if (FAILED(result) ||
                rowBindStatus[0] != DBBINDSTATUS_OK ||
                rowBindStatus[1] != DBBINDSTATUS_OK)
            {
                if (SUCCEEDED(result))
                    result = E_FAIL;
                break;
            }
    
            DBCOUNTITEM productsPrinted = 0;
            while (true)
            {
                result = rowset->GetNextRows(
                    DB_NULL_HCHAPTER,
                    0,
                    1,
                    &rowCount,
                    &rows);
                if (FAILED(result) || rowCount == 0)
                    break;
    
                RowData row = {};
                result = rowset->GetData(rows[0], rowHandle, &row);
                if (FAILED(result) ||
                    row.productIdStatus != DBSTATUS_S_OK ||
                    row.productIdLength != sizeof(LONG) ||
                    row.nameStatus != DBSTATUS_S_OK ||
                    row.nameLength == 0 ||
                    row.nameLength % sizeof(wchar_t) != 0 ||
                    row.nameLength >= sizeof(row.name))
                {
                    result = E_FAIL;
                    break;
                }
                row.name[row.nameLength / sizeof(wchar_t)] = L'\0';
    
                if (productsPrinted == 0)
                {
                    std::wcout << L"Connected with MSOLEDBSQL19.\n\n";
                    std::wcout << std::left
                               << std::setw(12) << L"Product ID"
                               << L"Name\n";
                    std::wcout << std::setw(12) << L"----------"
                               << L"----\n";
                }
    
                std::wcout << std::left
                           << std::setw(12) << row.productId
                           << row.name << L'\n';
                ++productsPrinted;
    
                result = rowset->ReleaseRows(
                    rowCount,
                    rows,
                    nullptr,
                    nullptr,
                    nullptr);
                CoTaskMemFree(rows);
                rows = nullptr;
                rowCount = 0;
                if (FAILED(result))
                    break;
            }
    
            if (FAILED(result))
                break;
            if (productsPrinted == 0)
            {
                result = E_FAIL;
                break;
            }
        }
        while (false);
    
        if (rows != nullptr)
        {
            if (rowset != nullptr && rowCount != 0)
                rowset->ReleaseRows(rowCount, rows, nullptr, nullptr, nullptr);
            CoTaskMemFree(rows);
        }
        if (rowHandle != DB_NULL_HACCESSOR && rowAccessor != nullptr)
            rowAccessor->ReleaseAccessor(rowHandle, nullptr);
        if (parameterHandle != DB_NULL_HACCESSOR && parameterAccessor != nullptr)
            parameterAccessor->ReleaseAccessor(parameterHandle, nullptr);
    
        Release(rowAccessor);
        Release(rowset);
        Release(parameterAccessor);
        Release(commandParameters);
        Release(commandText);
        Release(createCommand);
        Release(createSession);
        if (initialized)
            dbInitialize->Uninitialize();
        Release(dbInitialize);
        Release(dataInitialize);
        CoUninitialize();
    
        if (FAILED(result))
        {
            std::wcerr << L"OLE DB operation failed: 0x"
                       << std::hex << result << L'\n';
            return 1;
        }
    
        return 0;
    }
    

La cadena de conexión se pasa a IDataInitialize::GetDataSource. Esta API utiliza los nombres de palabras clave con espacios Use Encryption for Data y Trust Server Certificate. Las cadenas de conexión solicitan cifrado y requieren validación de certificados.

La consulta utiliza un signo de interrogación como marcador de parámetro. La aplicación vincula el ID mínimo del producto 0 como un int de SQL Server, lee hasta cinco filas de SalesLT.Product e imprime el ID y el nombre del producto.

Compilación y ejecución de la aplicación

  1. En el mismo x64 Native Tools Command Prompt, busque el archivo de encabezado del SDK de OLE DB instalado y almacene su directorio en OLEDB_INCLUDE:

    for /f "delims=" %i in ('where /r "%ProgramFiles%\Microsoft SQL Server\Client SDK\OLEDB" msoledbsql.h') do for %j in ("%~dpi.") do set "OLEDB_INCLUDE=%~fj"
    
  2. Muestra el directorio seleccionado y verifica que contiene el encabezado:

    echo %OLEDB_INCLUDE%
    dir "%OLEDB_INCLUDE%\msoledbsql.h"
    
  3. Compile la aplicación:

    cl /std:c++17 /EHsc /W4 /I"%OLEDB_INCLUDE%" oledb-quickstart.cpp /link ole32.lib oleaut32.lib
    
  4. Ejecute la aplicación:

    oledb-quickstart.exe
    
  5. Borrar la cadena de conexión del Command Prompt actual:

    set OLEDB_CONNECTION_STRING=
    set OLEDB_USER_ID=
    set OLEDB_PASSWORD=
    

Las filas de productos pueden variar según la versión de AdventureWorksLT. La salida se parece a la del ejemplo siguiente:

Connected with MSOLEDBSQL19.

Product ID  Name
----------  ----
680         HL Road Frame - Black, 58
706         HL Road Frame - Red, 58
707         Sport-100 Helmet, Red
708         Sport-100 Helmet, Black
709         Mountain Bike Socks, M