使用 ODBC 驅動程式開發 C 與 C++ 應用程式

版本:18.7.1.1
日期:2026年9月7日

若要從 C 或 C++ 呼叫 ODBC API,請包含 sql.h、 sqlext.h和 sqltypes.h,然後連結驅動程式管理器的匯入函式庫。 若要使用 Microsoft ODBC Driver for SQL Server 在 ODBC 標準之上新增的 SQL Server 擴充功能,也請包含 msodbcsql.h,並在核心 ODBC 標頭檔之後包含它。

適用於:Windows、Linux 和 macOS 上的 Microsoft ODBC Driver 18 for SQL Server。 版本 17 使用相同的標頭名稱,並附有 170 安裝路徑和 msodbcsql17 函式庫名稱。

標頭與函式庫

該平台提供核心的 ODBC 標頭與驅動程式管理器,而非驅動程式套件。 在 Windows 上,它們是隨 Windows SDK 一起出貨的。 在 Linux 和 macOS 上,它們會隨 unix ODBC 開發套件一起出貨。 驅動程式 SDK 僅提供 msodbcsql.h 和大量複製匯入函式庫。

你在叫什麼 Headers Windows 作業系統 Linux macOS
ODBC API sql.h、sqlext.h、sqltypes.h odbc32.lib -lodbc -lodbc
ODBC API,Unicode 入口點 加上 sqlucode.h odbc32.lib -lodbc -lodbc
ODBC 安裝程式 API 加上 odbcinst.h odbccp32.lib -lodbcinst -lodbcinst
SQL Server 驅動程式擴充 加上 msodbcsql.h 沒有額外的圖書館 沒有額外的圖書館 沒有額外的圖書館
批量複製(bcp_*)函式 加上 msodbcsql.h msodbcsql18.lib -lmsodbcsql-18 -lmsodbcsql.18

不同平台的批量複製連結名稱不同,因為檔案名稱不同。 在 Linux 上,-lmsodbcsql-18 會透過位於 /usr/lib 中、指向 libmsodbcsql-18.so 的符號連結來解析,而連結器已經會搜尋該位置,因此不需要 -L。 在 macOS 上,驅動程式隨附為 libmsodbcsql.18.dylib,這與 -lmsodbcsql.18 相符,但 Homebrew 的函式庫目錄未包含在 Apple Silicon 的預設搜尋路徑中。 連結批量複製功能時再加進 -L$(brew --prefix)/lib 去。

只有批量複製函式需要驅動程式自己的函式庫。 連線屬性、語句屬性、欄位屬性及 SQL Server 類型識別碼都是巨集與型別定義,因此包含msodbcsql.h即可。

安裝程式 API 是與 ODBC API 分開的函式庫。 在 Linux 或 macOS 上,呼叫像 -lodbcinst 這樣而未使用 SQLGetPrivateProfileString 的函式時,會在連結階段因 undefined reference(未定義參考)而失敗,而非在編譯階段。

若要安裝提供 Linux 與 macOS 核心標頭的 unixODBC 開發套件,請參見 「安裝 unixODBC 驅動管理器」。

在 Linux 和 macOS 的 C 程式碼中,將 wchar.h 放在 msodbcsql.h 之前

msodbcsql.h 的 Linux 和 macOS 版本會使用 wchar_t 來宣告 Always Encrypted 金鑰存放區提供者介面,但未包含定義此類型的標頭檔。 在 C++ 中, wchar_t 是關鍵字,因此 C++ 的翻譯單元無需額外標頭即可建構。 在 C 語言中, wchar_t 是類型定義,因此你需要先在 C 翻譯單元中包含 <wchar.h> :

#include <wchar.h>

如果你不包含 <wchar.h>,編譯器會回報來自 unknown type name 'wchar_t' 內部的 msodbcsql.h 錯誤。 在 Windows 上加入 include 是無害的,所以要把它加到共用來源,而不是放在平台保護後面。

在核心 ODBC 標頭後加入 msodbcsql.h

驅動程式名稱巨集以外的所有 msodbcsql.h 定義都在一個 #ifdef ODBCVER 區塊裡,且 sql.h 定義 ODBCVER了 。 如果你先包含 msodbcsql.h ,預處理器會跳過整個區塊,標頭就不會有任何貢獻。 編譯器不會發出警告。

/* Correct order. */
#ifdef _WIN32
#include <windows.h>
#endif

#include <wchar.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <msodbcsql.h>

在 sql.h 之前包含 msodbcsql.h 會使 ODBCVER 區塊內的所有內容皆為未定義。 編譯器會在使用處回報錯誤,而不是在 include 處:

order-wrong.c(7): error C2065: 'SQL_COPT_SS_BCP': undeclared identifier

在 Windows 上,你必須在 ODBC 標頭前包含windows.h。 Windows SDK 版本的 sqltypes.h 和 sql.h 會使用 Windows 型別,例如 DWORD 和 LONG。 msodbcsql.h將其 SQL Server 結構包裝成 pshpack8.h 和 poppack.h。 沒有 windows.h,SDK 標頭本身的建置會失敗。

SDK 檔案安裝地點

平台 msodbcsql.h 批量複製函式庫
Windows 作業系統 %ProgramFiles%\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include %ProgramFiles%\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Lib\<architecture>\msodbcsql18.lib
Linux /opt/microsoft/msodbcsql18/include /opt/microsoft/msodbcsql18/lib64,並附有 /usr/lib/libmsodbcsql-18.so 符號連結
macOS $(brew --prefix msodbcsql18)/include/msodbcsql18 $(brew --prefix)/lib/libmsodbcsql.18.dylib

在 Windows 上,資料夾Lib包含安裝程式放置的每個處理器架構的子資料夾,例如 x64、 x86、 或 arm64。 將 Include 資料夾加入編譯器的 include 路徑,並將架構子資料夾加入連結器的函式庫路徑。

在 Linux 上,共享物件帶有版本號,命名方式如 libmsodbcsql-18.6.so.2.1,且不帶有 SONAME。 套件會安裝指向它的 /usr/lib/libmsodbcsql-18.so,這就是為什麼 -lmsodbcsql-18 在不需要 -L 選項的情況下也能解析。 應透過該符號連結,而不是直接指定帶版本號的檔案,以免驅動程式更新導致建置失敗。

在 macOS 上,Homebrew 會安裝在其自己的前綴路徑下;在 Apple Silicon 上為 /opt/homebrew,在 Intel 架構上則為 /usr/local。 這兩個前綴都是指向版本化 Cellar 目錄的符號連結。 在你的建置腳本中使用 brew --prefix msodbcsql18 和 brew --prefix unixodbc ,而不是硬編碼其中任何一個。

路徑中的數字對應驅動程式的主版本。 版本 17 在 Windows 上會安裝至 /opt/microsoft/msodbcsql17/,在 Linux 上會安裝至 ...\ODBC\170\SDK\,其匯入程式庫為 msodbcsql17.lib。

關於各平台的完整檔案清單,請參閱系統需求、安裝及驅動程式檔案(Windows)、在 Linux 上安裝 ODBC 驅動程式,以及在 macOS 上安裝 ODBC 驅動程式。

確認你的組裝設定

此程式會根據標頭編譯,連結驅動程式管理器,並列出驅動程式管理器能看到的驅動程式。 它無法連線,因此將建置或註冊問題與網路或憑證問題分開。

#include <stdio.h>
#include <wchar.h>

#ifdef _WIN32
#include <windows.h>
#endif

#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <msodbcsql.h>

static void PrintDiagnostics(SQLSMALLINT handleType, SQLHANDLE handle)
{
    SQLCHAR state[6];
    SQLINTEGER native;
    SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
    SQLSMALLINT length;

    for (SQLSMALLINT record = 1;
         SQL_SUCCEEDED(SQLGetDiagRec(handleType, handle, record, state, &native,
                                     message, sizeof(message), &length));
         ++record)
    {
        fprintf(stderr, "  [%s] (%ld) %s\n", state, (long)native, message);
    }
}

int main(void)
{
    SQLHENV environment = SQL_NULL_HENV;
    SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment);

    if (!SQL_SUCCEEDED(rc))
    {
        fprintf(stderr, "SQLAllocHandle for the environment failed.\n");
        return 1;
    }

    rc = SQLSetEnvAttr(environment, SQL_ATTR_ODBC_VERSION,
                       (SQLPOINTER)SQL_OV_ODBC3_80, 0);
    if (!SQL_SUCCEEDED(rc))
    {
        fprintf(stderr, "SQLSetEnvAttr for SQL_OV_ODBC3_80 failed.\n");
        PrintDiagnostics(SQL_HANDLE_ENV, environment);
        SQLFreeHandle(SQL_HANDLE_ENV, environment);
        return 1;
    }

    printf("Driver name from msodbcsql.h: %s\n", SQLODBC_DRIVER_NAME);
    printf("Installed drivers:\n");

    SQLCHAR description[256];
    SQLSMALLINT descriptionLength = 0;
    SQLUSMALLINT direction = SQL_FETCH_FIRST;

    while (SQL_SUCCEEDED(SQLDrivers(environment, direction,
                                    description, sizeof(description), &descriptionLength,
                                    NULL, 0, NULL)))
    {
        printf("  %s\n", description);
        direction = SQL_FETCH_NEXT;
    }

    SQLFreeHandle(SQL_HANDLE_ENV, environment);
    return 0;
}

將其建置為窄字元程式。 SQLODBC_DRIVER_NAME 在定義了 UNICODE 或 _UNICODE 時會展開為寬字串,而搭配 %s 使用的 printf 無法接受它。

cl /W4 /I "%ProgramFiles%\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include" odbc-build-check.c /link odbc32.lib

在 Windows 上,/W4會從 Windows SDK 副本sqlext.h中回報兩次C4201: nonstandard extension used: nameless struct/union警告。 這些警告來自 SDK 標頭,而非你的程式碼,且建置成功。

第一行會顯示編譯進你的二進位檔中的驅動程式名稱。 其他的都是驅動程式管理員自己的清單,所以你預期會看到卻沒看到的驅動程式是註冊問題,不是建置問題。 你的清單會有所不同,包含所有已安裝的 ODBC 驅動程式,而不僅限於 SQL Server 驅動程式:

Driver name from msodbcsql.h: ODBC Driver 18 for SQL Server
Installed drivers:
  SQL Server
  ODBC Driver 17 for SQL Server
  ODBC Driver 18 for SQL Server
  Microsoft Access Driver (*.mdb, *.accdb)
  Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)
  Microsoft Access Text Driver (*.txt, *.csv)
  Microsoft Access dBASE Driver (*.dbf, *.ndx, *.mdx)

從 SQLODBC_DRIVER_NAME 建構連接字串,而不是從字串常值建構。 這個巨集會記錄你編譯時所依據的標頭檔,因此升級 SDK 就能在同一處更新驅動程式名稱。

msodbcsql.h 對 ODBC API 的新增功能

msodbcsql.h擴充標準 ODBC API 並加入 SQL Server 的特性。 每個家族都佔據一個連續的數值範圍,從一個基數開始計算。 這些範圍在不同家族之間並不獨特,所以你傳遞值給的函數就是區分它們的關鍵。

家族 基底常數 Value
SQLSetConnectAttr 的連線屬性 SQL_COPT_SS_BASE 1200
SQLSetStmtAttr 的陳述屬性 SQL_SOPT_SS_BASE 1225
SQLColAttribute 的欄屬性 SQL_CA_SS_BASE 1200
資訊類型 SQLGetInfo SQL_INFO_SS_FIRST 1199
SQLGetDiagField 的診斷欄位 SQL_DIAG_SS_BASE -1150
診斷動態功能碼 SQL_DIAG_DFC_SS_BASE -200

標頭同時宣告:

  • 認證屬性,包括 SQL_COPT_SS_AUTHENTICATION 和 SQL_COPT_SS_ACCESS_TOKEN,攜帶 Microsoft Entra ID 設定與存取權杖。
  • 對於 ODBC 未定義的 SQL Server 類型,其 SQL 類型識別碼介於 -150 到 -199:SQL_SS_VARIANT、SQL_SS_TIME2、SQL_SS_VECTOR、SQL_SS_UDT、SQL_SS_XML、SQL_SS_TABLE及 SQL_SS_TIMESTAMPOFFSET。 這些參數會命名 SQL 型別,所以你會在 ODBC 預期有 SQL 型別的地方傳遞它們,例如 ParameterType 的 SQLBindParameter參數。
  • 緩衝區側有三種相符的 C 類型:SQL_C_SS_TIME2、、 SQL_C_SS_TIMESTAMPOFFSETSQL_C_SS_VECTOR和 。 其他 SQL Server 類型會對應到標準 ODBC C 類型,例如 SQL_C_BINARY 或 SQL_C_WCHAR,因此沒有對應的 SQL_C_SS_*。
  • SQL_C_SS_* 型別繫結的結構為:SQL_SS_TIME2_STRUCT、SQL_SS_TIMESTAMPOFFSET_STRUCT 和 SQL_SS_VECTOR_STRUCT。
  • 批量複製原型與巨集,包括 bcp_init、bcp_bind、bcp_sendrow、bcp_batch 和 bcp_done。 BCP_ENCRYPT_OFF、 BCP_ENCRYPT_ON和 BCP_ENCRYPT_STRICT 選項只在 Windows 標頭裡。

每個平台都隨附自己的 msodbcsql.h 副本,而且它們宣告的符號也不盡相同。 SQLPERF 結構以及用來填入該結構的效能連線屬性(例如 SQL_COPT_SS_PERF_DATA 和 SQL_COPT_SS_PERF_QUERY),僅存在於 Windows 標頭檔中。 Linux 和 macOS 的標頭不會宣告這些功能,驅動程式也不會收集這些平台的效能資料。 請參閱程式設計指引(Linux 與 macOS)。

關於這些屬性對應的 連接字串 關鍵字,請參見 DSN 與 連接字串 關鍵字與屬性。 關於 Microsoft Entra ID 的設定,請參見使用 Microsoft Entra ID 搭配 ODBC 驅動程式。 關於 向量 類型,請參見 向量資料型別。

在非同步執行與執行緒之間選擇

部分 ODBC 函式可同步或非同步執行。 在同步模式下,驅動程式直到伺服器回應後才會回傳控制權。 在非同步模式下,驅動程式會立即回傳 SQL_STILL_EXECUTING ,應用程式會重複相同呼叫並使用相同的參數,直到得到不同的回傳碼。 任何其他回傳碼,包括 SQL_ERROR,表示操作已完成。

非同步模式有兩種形式,你會使用其中一種。 使用 SQL_ASYNC_MODE 呼叫 SQLGetInfo,以找出驅動程式支援哪一個。 如果驅動程式支援每個陳述式控制,它會回傳 SQL_AM_CONNECTION;如果該設定適用於整個連線,則回傳 SQL_AM_NONE;如果驅動程式完全不會以非同步方式執行函式,則回傳 SQL_AM_STATEMENT。

該陳述式會對一個陳述句柄開啟非同步模式。 連線上的其他陳述式都會維持同步,因此你可以同時執行這兩種類型:

SQLSetStmtAttr(hStmt, SQL_ATTR_ASYNC_ENABLE,
               (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER);

如果 SQL_ASYNC_MODE 傳回 SQL_AM_CONNECTION,則該陳述屬性為唯讀,且此呼叫會傳回 SQL_ERROR,SQLSTATE 為 HYC00。 請改用連線表單。

連線表單會對之後在該連線上配置的每個陳述式控制代碼啟用非同步模式。 是否同時影響已存在的句柄則由驅動程式定義,因此在分配任何語句前請先設定:

SQLSetConnectAttr(hDbc, SQL_ATTR_ASYNC_ENABLE,
                  (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER);

如果某個函式仍在該連線的陳述式上以非同步方式執行,則此呼叫會回傳 HY010,並帶有 SQLSTATE SQL_ERROR。 游標本身開啟不會阻擋通話。 傳遞 SQL_ASYNC_ENABLE_OFF 會讓連線上的每個語句回到同步模式。

要查詢驅動程式在同一連線同時支援多少非同步語句,請呼叫 SQLGetInfoSQL_MAX_ASYNC_CONCURRENT_STATEMENTS。 Microsoft ODBC Driver 18 for SQL Server 會傳回 1,因此請預期每個連線只能有一個尚未完成的非同步作業;如果需要超過這個數量,請開啟更多連線或使用執行緒。 參見非同步執行(輪詢方法)。

執行緒則是另一種讓多個操作持續運作的方式。 ODBC 要求多執行緒作業系統的驅動程式必須是執行緒安全的,因此一個執行緒可以呼叫阻塞 ODBC,而其他執行緒則繼續運作。 這樣可以避免非同步模式所需的輪詢迴圈和重複的函式呼叫。 為每個執行緒指定各自的陳述式控制代碼。 驅動程式很可能會同時序列化兩個使用相同句柄的執行緒,因此共用一個會讓你失去並行性。 參見多執行緒。 對於新程式碼,應優先使用執行緒;而對於已經正常運作的非同步程式碼,則應先評估自己的工作負載,再決定是否轉換。

在 Windows 上,驅動程式管理員也支援通知方式,移除輪詢迴圈。 你會將 Win32 事件與連線或語句句柄關聯起來。 函式仍會立即回傳 SQL_STILL_EXECUTING ,驅動程式管理器會在操作完成時通知事件。 此模式下會停用輪詢:再次呼叫原始函式會回傳 SQL_ERROR,並附帶 SQLSTATE IM017。 改為呼叫 SQLCompleteAsync 以取得結果。 這需要驅動管理員版本 ODBC 3.81 及以上版本,且驅動程式也必須支援它。 使用 SQL_ASYNC_NOTIFICATION 呼叫 SQLGetInfo 來檢查。 你取得的值取決於應用程式所宣告的 ODBC 版本:使用 Microsoft ODBC Driver 18 for SQL Server 時,將 SQL_OV_ODBC3 設為 SQL_ASYNC_NOTIFICATION_NOT_CAPABLE 的應用程式會取得 SQL_ASYNC_NOTIFICATION_CAPABLE,而宣告 SQL_OV_ODBC3_80 的應用程式則會從同一個驅動程式取得 SQL_ATTR_ODBC_VERSION。 在分配連線前先聲明 SQL_OV_ODBC3_80 。 請參見 非同步執行(通知方法) 及 通知方法範例。

取消一項未完成的行動

SQLCancel 取消仍在執行於語句柄上的操作。 從另一個執行緒中呼叫它,或在輪詢迴圈中呼叫它,並傳入該尚未完成呼叫的控制代碼。

只用 SQLCancel 來做這個。 若要放棄您不想再讀取的結果集,請改為呼叫 SQLCloseCursor 或 SQLMoreResults。

從 sqlncli.h 遷移到 msodbcsql.h

SQL Server 原生客戶端已經退休,使用它的應用程式應該轉移到 Microsoft 的 SQL Server ODBC 驅動程式。 API 是同一個 ODBC API,所以大部分工作都是重新命名建置輸入和 連接字串 中的驅動名稱。

SQL Server 原生用戶端 Microsoft ODBC Driver 18 for SQL Server
sqlncli.h msodbcsql.h
sqlncli11.lib msodbcsql18.lib
sqlncli11.dll msodbcsql18.dll
Driver={SQL Server Native Client 11.0} Driver={ODBC Driver 18 for SQL Server}
SQLNCLI_VER SQLODBC_VER

msodbcsql.h 標頭檔仍然定義了 SQLNCLI_* 名稱巨集,因此使用這些巨集的原始碼仍可繼續編譯。 這些定義會受到 #ifndef __sqlncli_h__保護,這表示你不能在同一個翻譯單元中同時包含兩個標頭。 移除 sqlncli.h include。

有兩件事無法延續:

  • 那些會回傳連結伺服器清單及其目錄的分散式查詢元資料 API 函式,並未在 msodbcsql.h中宣告。 它們是專門針對 SQL Server 原生客戶端的。
  • 版本 18 預設加密連線並驗證伺服器憑證。 Native Client 則沒有。 原本可在 Native Client 中正常運作的連線字串,在首次連線時可能會失敗,直到你修正憑證信任設定或明確設定 Encrypt 為止。 請參見 連線加密故障排除。

關於版本 17 到 18 的其他變更,請參見 主要版本差異。