`#import` 產生的智能指標 (`_ConnectionPtr` 等) 封裝了 COM 細節,但理解底層 `CoCreateInstance`、`QueryInterface`、`IDispatch::Invoke` 流程,有助於:除錯邊界情況、跨語言調用 (C/Assembly)、效能極致優化、不依賴 VC 編譯器擴展的環境。
#include
#include
#include
#include "adoid.h" // ADO GUID 定義
#include "adoint.h" // ADO 介面定義
// RAII COM 初始化
struct InitOle {
InitOle() { ::CoInitialize(NULL); }
~InitOle() { ::CoUninitialize(); }
} _init_InitOle_;
void main() {
HRESULT hr = S_OK;
ADORecordset* pRs = NULL; // 原始介面指標
VARIANT vSource, vConnect;
VariantInit(&vSource); VariantInit(&vConnect);
// 設定查詢 SQL (Unicode BSTR)
vSource.vt = VT_BSTR;
vSource.bstrVal = ::SysAllocString(L"SELECT * FROM Customers");
// 設定連接字串
vConnect.vt = VT_BSTR;
vConnect.bstrVal = ::SysAllocString(
L"Provider=SQLOLEDB; Data Source=127.0.0.1; Initial Catalog=Northwind; User ID=sa; Password=;");
// 1. 創建 Recordset 物件 (CLSID_CADORecordset)
hr = CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_INPROC_SERVER,
IID_IADORecordset, (LPVOID*)&pRs);
if (SUCCEEDED(hr)) {
// 2. 設定游標位置 (客戶端游標支援 RecordCount)
pRs->put_CursorLocation(adUseClient);
// 3. 開啟資料集
hr = pRs->Open(vSource, vConnect, adOpenForwardOnly, adLockReadOnly, -1);
if (SUCCEEDED(hr)) {
long nCount = 0;
pRs->get_RecordCount(&nCount); // 取得記錄數
::MessageBox(NULL, L"Success!", L"", MB_OK);
printf("Record Count: %d\n", nCount);
}
// 4. 清理
pRs->Close();
pRs->Release(); pRs = NULL;
}
// 釋放 BSTR
::SysFreeString(vSource.bstrVal);
::SysFreeString(vConnect.bstrVal);
}
| 步驟 | API/介面 | 說明 |
|---|---|---|
| 1 | CoInitialize | 初始化 COM 庫 (單執行緒單元 STA) |
| 2 | CoCreateInstance | 實例化 `CLSID_CADORecordset`,取得 `IADORecordset` 介面 |
| 3 | put_CursorLocation | 設 `adUseClient` 才能取得 `RecordCount` |
| 4 | Open (IDispatch::Invoke) | 傳入 SQL、連接字串、游標/鎖定類型,同步執行 |
| 5 | get_RecordCount | 取得記錄數 (需客戶端游標) |
| 6 | Close/Release | 釋放介面引用計數 |
| 7 | SysFreeString | 釋放 BSTR 記憶體 |
| 8 | CoUninitialize | 反初始化 COM |
| 維度 | COM API | #import 智能指標 |
|---|---|---|
| 代碼量 | 大 (手動管理引用計數、VARIANT、BSTR) | 小 (RAII 自動管理) |
| 型別安全 | 低 (介面指標轉換需 `QueryInterface`) | 高 (模板 `_com_ptr_t` 封裝) |
| 例外處理 | 手動檢查 HRESULT | 拋出 `_com_error` 例外 |
| 編譯依賴 | 僅需 `adoid.h` `adoint.h` (SDK) | 需 VC 編譯器支援 `#import` |
| 適用場景 | 驅動開發、嵌入式、跨編譯器、教學 | 一般應用開發 (推薦) |
COM API 方式雖繁瑣,但揭示了 ADO 底層運作真相:`CoCreateInstance` 創建物件 → `QueryInterface` 獲取功能介面 → `IDispatch::Invoke` 呼叫方法/屬性 → 參數封裝為 `VARIANT` → 返回 `HRESULT`。掌握此流程,對排查 `#import` 產生的神秘錯誤、編寫高效能資料存取層、移植至非 VC 環境極有幫助。實務開發仍強烈建議使用 `#import` 智能指標。