Démarrage rapide : Connectez-vous et interrogez avec le pilote OLE DB de Microsoft

Dans ce démarrage rapide, vous construisez une application console Windows C++ avec Visual Studio 2022 et versions ultérieures. L’application se connecte à Azure SQL Database, à une base de données SQL dans Microsoft Fabric ou à SQL Server à l’aide du pilote Microsoft OLE DB Driver 19 for SQL Server. Il exécute une requête paramétrée sur les données de l’échantillon AdventureWorksLT et vérifie le résultat.

Logiciels requis

Créer une base de données SQL

Créer ou connecter une base de données SQL sur l’une des plateformes suivantes :

Pour ce démarrage rapide, sélectionnez ou chargez les données d’échantillon AdventureWorksLT .

Pour un conteneur SQL Server, créez le conteneur et chargez les données d’exemple en une seule commande :

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

Pour une instance SQL Server existante, restaurez une AdventureWorksLT sauvegarde à partir de bases de données d’exemple AdventureWorks.

L’application cliente OLE DB dans ce démarrage rapide fonctionne sous Windows. Un conteneur SQL Server peut s’exécuter sur un autre hôte pris en charge.

Vérifiez le pilote

Ouvrez une invite de commandes des outils natifs x64 pour votre version de Visual Studio, puis exécutez la commande suivante :

Important

Les commandes de ce démarrage rapide utilisent la syntaxe de l’invite de commande. Exécutez-les dans une invite de commandes x64 Native Tools, où l’invite se termine par >. Ne les exécutez pas dans PowerShell, où l’invite commence par PS.

reg query HKCR\MSOLEDBSQL19

La commande affiche le fournisseur enregistré MSOLEDBSQL19 .

Configurez la connexion

Définissez OLEDB_CONNECTION_STRING dans l’invite de commandes des outils natifs x64. L'application lit la chaîne de connexion depuis l'environnement et ne l'affiche pas.

Pour Azure SQL Database ou SQL Database dans Fabric, utilisez l’authentification interactive de Microsoft Entra. Remplacez les placeholders par le serveur, la base de données et l’identifiant utilisateur Microsoft Entra de votre ressource 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;"

Pour la base de données SQL dans Fabric, votre identité nécessite une autorisation de lecture pour l’élément de la base de données. L’authentification SQL n’est pas prise en charge. Pour plus d’informations, consultez Authentication dans la base de données SQL dans Microsoft Fabric.

Pour une instance SQL Server existante qui accepte l’authentification Windows, utilisez Integrated 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;"

Pour une instance ou un conteneur SQL Server acceptant l’authentification SQL, utilisez Authentication=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;"

Le certificat SQL Server doit correspondre le nom du serveur et la chaîne à une autorité de certification (CA) en laquelle le client Windows a confiance. Pour un conteneur SQL Server, configurez la sécurité de la couche de transport (TLS) dans le conteneur et enregistrez l’autorité de certification émettrice sur le client Windows avant d’exécuter l’application. Pour plus d’informations, voir Chiffrer les connexions vers SQL Server sur Linux et Configurer le Moteur de base de données SQL Server pour chiffrer les connexions.

Créer l’application

  1. Créez un répertoire de projets :

    mkdir oledb-quickstart
    cd oledb-quickstart
    
  2. Créez un fichier nommé oledb-quickstart.cpp avec le code suivant :

    #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 chaîne de connexion est passée à IDataInitialize::GetDataSource. Cette API utilise les noms de mots-clés espacés Use Encryption for Data et Trust Server Certificate. Les chaînes de connexion demandent le chiffrement et nécessitent une validation des certificats.

La requête utilise un point d’interrogation comme marqueur de paramètre. L’application lie l’ID produit minimal 0 comme un int SQL Server, lit jusqu’à cinq lignes de SalesLT.Product, et affiche l’ID produit et le nom.

Générer et exécuter l’application

  1. Dans la même invite de commandes x64 Native Tools, recherchez le fichier d’en-tête du SDK OLE DB installé et stockez le répertoire correspondant dans 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. Affichez le répertoire sélectionné, et vérifiez qu’il contient l’en-tête :

    echo %OLEDB_INCLUDE%
    dir "%OLEDB_INCLUDE%\msoledbsql.h"
    
  3. Compilez l’application :

    cl /std:c++17 /EHsc /W4 /I"%OLEDB_INCLUDE%" oledb-quickstart.cpp /link ole32.lib oleaut32.lib
    
  4. Exécutez l’application :

    oledb-quickstart.exe
    
  5. Effacer la chaîne de connexion de l’invite de commande actuelle :

    set OLEDB_CONNECTION_STRING=
    set OLEDB_USER_ID=
    set OLEDB_PASSWORD=
    

Les lignes de produits peuvent varier selon la version d’AdventureWorksLT. La sortie ressemble à l’exemple suivant :

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