首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

MFC ADO 連線:_ConnectionPtr 與 _RecordsetPtr 不能作為控制類成員變數

     編輯:程式設計疑難解答

錯誤現象

在非對話框類 (如 CDatabaseManager) 中宣告 _ConnectionPtr m_conn;、_RecordsetPtr m_rs;,呼叫 m_conn.CreateInstance(__uuidof(Connection)) 時拋出 _com_error 或 QueryInterface 失敗。

根本原因

MFC 的 COleControl/CWnd 衍生類別在建構時期會初始化 COM 單元模型。若智慧指標 (_com_ptr_t) 作為**非視窗類別**的成員,其建構函式可能在 **COM 未初始化 (CoInitialize 未呼叫)** 或 **錯誤的 Apartment 狀態** 下執行,導致 / 失敗。

正確模式:區域變數 / 指標 / RAII 包裝

方案一:函式內區域變數 (最簡單、最安全)

BOOL CDatabaseManager::ExecuteSQL(CString sql) {
    _ConnectionPtr conn;  // 區域智慧指標,離開作用域自動 Release
    HRESULT hr = conn.CreateInstance(__uuidof(Connection));
    if (FAILED(hr)) return FALSE;
    
    conn->Open(_bstr_t(connectionString), "", "", adConnectUnspecified);
    conn->Execute(_bstr_t(sql), NULL, adCmdText);
    return TRUE;
}

方案二:指標成員 + 手動管理

class CDatabaseManager {
    _ConnectionPtr* m_pConn;  // 指標,不自動建構
public:
    CDatabaseManager() : m_pConn(nullptr) {}
    ~CDatabaseManager() { Close(); }
    
    BOOL Open() {
        if (m_pConn) return TRUE;
        m_pConn = new _ConnectionPtr();
        HRESULT hr = (*m_pConn)->CreateInstance(__uuidof(Connection));
        if (FAILED(hr)) { delete m_pConn; m_pConn = nullptr; return FALSE; }
        (*m_pConn)->Open(...);
        return TRUE;
    }
    void Close() {
        if (m_pConn) {
            try { if ((*m_pConn)->State) (*m_pConn)->Close(); } catch(...) {}
            delete m_pConn; m_pConn = nullptr;
        }
    }
};

方案三:RAII 包裝類 (推薦現代 C++)

class AdoConnection {
    _ConnectionPtr conn;
public:
    AdoConnection() = default;
    ~AdoConnection() { if (conn) try { conn->Close(); } catch(...) {} }
    
    BOOL Open(LPCTSTR connStr) {
        HRESULT hr = conn.CreateInstance(__uuidof(Connection));
        if (FAILED(hr)) return FALSE;
        conn->Open(_bstr_t(connStr), "", "", adConnectUnspecified);
        return TRUE;
    }
    operator _ConnectionPtr&() { return conn; }
    _ConnectionPtr* operator&() { return &conn; }
};

// 使用
class CDatabaseManager {
    AdoConnection m_conn;  // 成員變數安全
public:
    BOOL Execute(LPCTSTR sql) {
        if (!m_conn) m_conn.Open(connStr); // 延遲開啟
        m_conn->Execute(_bstr_t(sql), NULL, adCmdText);
        return TRUE;
    }
};

關鍵原則

  1. **智慧指標 (_com_ptr_t) 只能在 COM 已初始化的執行緒上建構/析構**。
  2. **非視窗類別成員** 的建構時機不可控,極易在 COM 未就緒時期建構。
  3. 改用 **區域變數**、**指標延遲建構**、或 **RAII 包裝** 確保 COM 操作發生在正確時機。

總結

MFC 非對話框類中**不要直接宣告 _ConnectionPtr/_RecordsetPtr 成員**。改用區域變數、指標延遲建構或 RAII 包裝,確保 COM 操作在 CoInitialize 之後、正確 Apartment 執行緒中進行。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved