このクイックスタートでは、Visual Studio 2022以降のバージョンでWindows C++コンソールアプリケーションを構築します。 アプリケーションはAzure SQL Database、Microsoft FabricのSQLデータベース、またはSQL ServerのOLE DBドライバー19でMicrosoft SQL Serverに接続できます。
AdventureWorksLTサンプルデータに対してパラメータ化されたクエリを実行し、結果を検証します。
Prerequisites
- Visual Studio 2022以降のバージョンで、C++を用いたデスクトップ開発ワークロードも含まれます。 このワークロードは、このクイックスタートで使われるx64 Native Toolsコマンドプロンプトをインストールします。 開く手順については、「 64ビットホスト開発者コマンドプロンプトショートカットを使う」をご覧ください。
- Microsoft OLE DB Driver 19 for SQL Server. x64ドライバーをインストールし、クライアントコンポーネントとソフトウェア開発キット(SDK)の両方を選択します。
SQL データベースを作成する
以下のいずれかのプラットフォームでSQLデータベースを作成または接続してください:
このクイックスタートでは、 AdventureWorksLT サンプルデータを選択するか読み込みください。
SQL Serverコンテナの場合は、コンテナを作成し、サンプルデータを1つのコマンドで読み込みます。
sqlcmd create mssql --accept-eula --using https://aka.ms/AdventureWorksLT.bak
既存のSQL Serverインスタンスについては、AdventureWorksのサンプルデータベースからAdventureWorksLTバックアップを復元してください。
このクイックスタートのOLE DBクライアントアプリケーションはWindows上で動作します。 SQL Serverコンテナは、他のサポートされているホスト上で動作可能です。
運転手の確認を
Visual Studioバージョン用のx64 Native Toolsコマンドプロンプトを開き、次のコマンドを実行します:
Important
このクイックスタートのコマンドはコマンドプロンプトの構文を使用しています。 x64 Native Toolsコマンドプロンプトで実行し、プロンプトの最後に「 >」で終わらせます。 プロンプトが PSで始まるPowerShellで実行しないでください。
reg query HKCR\MSOLEDBSQL19
コマンドは登録済み MSOLEDBSQL19 プロバイダーを表示します。
接続の設定
x64のネイティブツールコマンドプロンプトで OLEDB_CONNECTION_STRING を設定してください。 アプリケーションは環境から接続文字列を読み込み、表示しません。
Azure SQL DatabaseやFabricのSQLデータベースの場合は、Microsoft Entraのインタラクティブ認証をご利用ください。 プレースホルダーをサーバー、データベース、SQLリソースのMicrosoft EntraユーザーIDに置き換えます:
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;"
Fabric の SQL データベースについては、使用している ID にデータベース アイテムに対する Read 権限が必要です。 SQL認証はサポートされていません。 詳細については、Microsoft Fabric の SQL データベースにおける認証を参照してください。
既存の SQL Server インスタンスで Windows 認証を使用する場合は、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;"
SQL認証を受け入れるSQL Serverインスタンスやコンテナの場合は、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;"
SQL Serverの証明書は、サーバー名とチェーンをWindowsクライアントが信頼する認証機関(CA)と一致しなければなりません。 SQL Serverコンテナの場合は、コンテナ内でトランスポート層セキュリティ(TLS)を設定し、アプリケーションを実行する前にWindowsクライアントで発行CAを登録してください。 詳細については、「Linux上のSQL Server on Linuxへの接続の暗号化」および「接続の暗号化のためのSQL Server データベース エンジンの設定」をご覧ください。
アプリケーションを作成する
プロジェクトディレクトリを作成する:
mkdir oledb-quickstart cd oledb-quickstart次のコードを使用して、
oledb-quickstart.cppという名前のファイルを作成します。#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, ¶meterOrdinal, ¶meterInfo); if (FAILED(result)) break; result = commandText->QueryInterface( IID_IAccessor, reinterpret_cast<void**>(¶meterAccessor)); 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, ¶meterBinding, sizeof(ParameterData), ¶meterHandle, ¶meterBindStatus); if (FAILED(result) || parameterBindStatus != DBBINDSTATUS_OK) { if (SUCCEEDED(result)) result = E_FAIL; break; } ParameterData parameter = { DBSTATUS_S_OK, sizeof(LONG), 0 }; DBPARAMS parameters = { ¶meter, 1, parameterHandle }; result = commandText->Execute( nullptr, IID_IRowset, ¶meters, 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; }
接続文字列はIDataInitialize::GetDataSourceに引き継がれます。 このAPIは、スペース付きのキーワード名 Use Encryption for Data と Trust Server Certificateを使用します。 接続文字列は暗号化を要求し、証明書検証を必要とします。
クエリはパラメータマーカーとしてクエスチョンマークを使用します。 アプリケーションは最小製品ID0をSQL Server intとしてバインドし、SalesLT.Productから最大5行を読み取り、製品IDと名前を印刷します。
アプリケーションの構築と実行
同じx64ネイティブツールコマンドプロンプト内で、インストールされているOLE DB SDKヘッダーを見つけて、そのディレクトリを
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"選択したディレクトリを表示し、ヘッダーが以下のものであることを確認します:
echo %OLEDB_INCLUDE% dir "%OLEDB_INCLUDE%\msoledbsql.h"アプリケーションをコンパイルします。
cl /std:c++17 /EHsc /W4 /I"%OLEDB_INCLUDE%" oledb-quickstart.cpp /link ole32.lib oleaut32.libアプリケーションを実行します。
oledb-quickstart.exe現在のコマンドプロンプトから接続文字列を消してください:
set OLEDB_CONNECTION_STRING= set OLEDB_USER_ID= set OLEDB_PASSWORD=
商品列はAdventureWorksLTのバージョンによって異なる場合があります。 出力は次の例のようになります。
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