首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

VC++ 使用 ADO 連接資料庫完整指南與實戰注意事項

     編輯:ASP.NET 基礎

前言

在 Visual C++ 專案中操作資料庫,ADO (ActiveX Data Objects) 是最成熟、相容性最佳的選擇。本文詳細說明如何在 MFC 專案中正確引入 ADO、建立連線、操作 Recordset,並針對常見坑點(COM 初始化、智慧指標、資源釋放、資料類型對應)提供最佳實踐代碼範本。

專案設定與必要標頭檔

  1. 預編譯標頭 (stdafx.h) 加入:
#include <afxwin.h>         // MFC 核心
#include <afxext.h>         // MFC 擴充
#include <afxdisp.h>        // MFC 自動化 (IDispatch)
#include <afxdao.h>         // DAO (可選)
#include "ado.h"             // 自訂 ADO 智慧指標包裝
  1. ADO 智慧指標包裝 (ado.h) — 使用 #import 產生型別安全的包裝類別:
#ifndef __ADO_H__
#define __ADO_H__

#import "msado15.dll" no_namespace rename("EOF", "adoEOF") rename("BOF", "adoBOF")

// 常用智慧指標 typedef
typedef _ConnectionPtr    ADOConnectionPtr;
typedef _RecordsetPtr     ADORecordsetPtr;
typedef _CommandPtr       ADOCommandPtr;
typedef _ParameterPtr     ADOParameterPtr;
typedef FieldPtr          ADOFieldPtr;
typedef FieldsPtr         ADOFieldsPtr;

#endif

rename 避免 EOF/BOF 與 MFC 巨集衝突;no_namespace 簡化程式碼(視專案習慣決定)。

核心流程:初始化 → 連線 → 操作 → 清理

1. COM 環境初始化 (每執行緒一次)

::CoInitialize(NULL); // 或 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)

2. 建立 Connection 與 Recordset 智慧指標

ADOConnectionPtr  pConn;
ADORecordsetPtr   pRs;

HRESULT hr = pConn.CreateInstance(__uuidof(Connection));
hr = pRs.CreateInstance(__uuidof(Recordset));

3. 開啟連線 (Access MDB 範例)

_bstr_t connStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Data\\Demo.mdb;";
pConn->Open(connStr, "", "", adConnectUnspecified);

4. 執行查詢並綁定至資料表格控件

// 開啟 Recordset
pRs->Open(_bstr_t("SELECT * FROM Products"), 
          _variant_t((IDispatch*)pConn), 
          adOpenKeyset, adLockOptimistic, adCmdText);

// 綁定至 MS DataGrid ActiveX 控件
m_datagrid.ClearFields();
m_datagrid.SetRefDataSource(pRs); // 需引用 MSDATAGRIDLib

5. 正確關閉與釋放資源 (逆序原則)

// 1. 解除控件綁定
m_datagrid.SetRefDataSource(NULL);

// 2. 關閉 Recordset
if (pRs && pRs->State == adStateOpen) {
    pRs->Close();
}
pRs = NULL; // 智慧指標自動 Release()

// 3. 關閉 Connection
if (pConn && pConn->State == adStateOpen) {
    pConn->Close();
}
pConn = NULL;

// 4. 釋放 COM
::CoUninitialize();

進階主題:參數化查詢與資料類型對應

使用 Command + Parameter 防 SQL Injection

ADOCommandPtr pCmd;
pCmd.CreateInstance(__uuidof(Command));
pCmd->ActiveConnection = pConn;
pCmd->CommandText = "SELECT * FROM Users WHERE UserID = ? AND Status = ?";
pCmd->CommandType = adCmdText;

// 附加參數 (順序必須與 SQL 佔位符一致)
pCmd->Parameters->Append(pCmd->CreateParameter("UserID", adInteger, adParamInput, 4, 1001));
pCmd->Parameters->Append(pCmd->CreateParameter("Status", adVarChar, adParamInput, 20, "Active"));

ADORecordsetPtr pRs2;
pRs2 = pCmd->Execute(NULL, NULL, adCmdText);

資料類型對應表 (ADO → C++/MFC)

ADO DataTypeEnumC++/MFC 類型取值範例
adSmallInt / adIntegerlong / intpField->Value.lVal
adDouble / adNumericdoublepField->Value.dblVal
adDateDATE / COleDateTimepField->Value.date
adCurrencyCURRENCY / COleCurrencypField->Value.cyVal
adVarChar / adBSTRCString / _bstr_t(LPCTSTR)(_bstr_t)pField->Value
adBooleanVARIANT_BOOL / boolpField->Value.boolVal == VARIANT_TRUE

欄位值安全讀取範本

_variant_t val = pRs->GetCollect("FieldName"); // 或 pRs->Fields->GetItem("FieldName")->Value

if (val.vt == VT_NULL || val.vt == VT_EMPTY) {
    // 處理 NULL
} else {
    switch (val.vt) {
        case VT_I4:    int n = val.lVal; break;
        case VT_R8:    double d = val.dblVal; break;
        case VT_BSTR:  CString s = (LPCTSTR)(_bstr_t)val; break;
        case VT_DATE:  COleDateTime dt = val.date; break;
        // ... 其他類型
    }
}

常見錯誤與除錯技巧

  • 未呼叫 CoInitialize:導致 CreateInstance 返回 REGDB_E_CLASSNOTREG 或 CO_E_NOTINITIALIZED。
  • 連線字串 Provider 錯誤:Access 2007+ (.accdb) 需用 Microsoft.ACE.OLEDB.12.0,並安裝 Access Database Engine。
  • 游標類型與鎖定類型不匹配:adOpenForwardOnly 不支援 adLockOptimistic,需改 adOpenKeyset 或 adOpenStatic。
  • 智慧指標循環參考:避免在類別成員中長期持有 _ConnectionPtr 與 _RecordsetPtr 互相引用,改用 WeakPtr 模式或手動 Release()。
  • 例外處理:ADO 錯誤透過 _com_error 拋出,建議統一用 try-catch(_com_error& e) 捕獲並記錄 e.ErrorMessage()。

完整範本類別 (RAII 風格)

class AdoSession {
public:
    AdoSession() { ::CoInitialize(NULL); }
    ~AdoSession() { ::CoUninitialize(); }

    bool OpenDB(LPCTSTR connStr) {
        m_pConn.CreateInstance(__uuidof(Connection));
        return SUCCEEDED(m_pConn->Open(_bstr_t(connStr), "", "", adConnectUnspecified));
    }

    ADORecordsetPtr Query(LPCTSTR sql) {
        ADORecordsetPtr rs;
        rs.CreateInstance(__uuidof(Recordset));
        rs->Open(_bstr_t(sql), _variant_t((IDispatch*)m_pConn), 
                 adOpenStatic, adLockReadOnly, adCmdText);
        return rs; // 智慧指標自動管理生命週期
    }

    void Close() {
        if (m_pConn && m_pConn->State == adStateOpen) m_pConn->Close();
        m_pConn = NULL;
    }

private:
    ADOConnectionPtr m_pConn;
};

使用方式:

AdoSession session;
if (session.OpenDB(_T("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=App.accdb;"))) {
    ADORecordsetPtr rs = session.Query(_T("SELECT * FROM Users"));
    while (!rs->adoEOF) {
        CString name = (LPCTSTR)(_bstr_t)rs->Fields->GetItem("Name")->Value;
        // 處理資料...
        rs->MoveNext();
    }
}
session.Close();
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved