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

ATL 開發 ActiveX 控制項:輕量、高效、無 MFC 依賴的最佳實踐

     編輯:關於C語言

為何選擇 ATL?核心優勢解析

Active Template Library (ATL) 是微軟提供的輕量級 C++ 範本庫,專為建構高效能、小體積的 COM 元件而設計。相較 MFC:

  • 無執行階段依賴:不需 MFC42.DLL 等大型 DLL,控制項體積通常 < 100KB (MFC 同功能常 > 300KB)。
  • 載入/執行速度快:無 MFC 訊息映射、文檔/視圖架構等額外開銷,直接對應 Win32 API 與 CRT。
  • 彈性極高:可精確控制每個 COM 介面的實作細節,支援多執行緒模型 (Apartment/Free/Both)。
  • 學習曲線較陡:需熟悉 COM 基礎 (IUnknown, 參考計數, 編組, 介面映射表),無 Wizard 全自動生成完整控制項骨架 (VS 2008 後改善)。

ATL 專案建立與控制項新增流程 (Visual Studio 2008+)

  1. 建立 ATL Project (選擇 DLL,勾選 Allow merging of proxy/stub code)。
  2. 專案建立後,方案總管右鍵 → Add → Class → ATL Control。
  3. 屬性頁關鍵設定:
  • Short Name:ATLSam (自動產生類別 CATLSam、介面 IATLSam、事件 _IATLSamEvents)。
  • Options 索引標籤:
☑ Apartment Threaded (單元模型,最通用)
☑ Support ISupportErrorInfo (豐富錯誤資訊)
☑ Support Connection Points (事件支援)
☑ Free Threaded Marshaler (可選,提升跨執行緒效能)
☐ Support IObjectSafety (網頁安全標記,視需求勾選)
  • Misc Status:設定控制項行為旗標 (如 OLEMISC_ACTIVATEWHENVISIBLE 等)。

新增方法:IDL 定義與 C++ 實作

ATL 以 IDL (Interface Definition Language) 為核心,介面定義與實作分離。

1. 編輯 ATLSam.idl 新增方法

interface IATLSam : IDispatch
{
    [id(1), helpstring("設定標題")] HRESULT SetCaption([in] BSTR pszCaption);
    [id(2), helpstring("取得標題長度")] HRESULT GetCaptionLength([out, retval] long* pLen);
};
  • [in]:呼叫端傳入參數 (BSTR 需由呼叫端 SysAllocString,被呼叫端用完 SysFreeString)。
  • [out, retval]:傳回值,自動映射為 COM 標準 HRESULT 回傳,實際值經由指標回傳。

2. 執行 MIDL 編譯 → 產生 _IATLSamEvents_CP.h、Proxy/Stub 碼

Visual Studio 建置專案時自動執行。

3. 在 CATLSam 類別實作方法

// ATLSam.h
class ATL_NO_VTABLE CATLSam : 
    public CComObjectRootEx,
    public CComCoClass,
    public IConnectionPointContainerImpl,
    public IDispatchImpl,
    // ... 其他繼承
{
public:
    // 成員變數
    CComBSTR m_bstrCaption; // 智慧指標自動管理 BSTR 生命週期

    // 方法宣告
    STDMETHOD(SetCaption)(BSTR pszCaption);
    STDMETHOD(GetCaptionLength)(long* pLen);
};

// ATLSam.cpp
STDMETHODIMP CATLSam::SetCaption(BSTR pszCaption)
{
    if (!pszCaption) return E_POINTER;
    m_bstrCaption = pszCaption; // CComBSTR 自動複製字串內容
    FireViewChange(); // 通知容器重繪 (若支援 IViewObjectEx)
    return S_OK;
}

STDMETHODIMP CATLSam::GetCaptionLength(long* pLen)
{
    if (!pLen) return E_POINTER;
    *pLen = m_bstrCaption.Length(); // CComBSTR::Length() 回傳字元數 (不含結束符)
    return S_OK;
}

繪圖實作:OnDraw 覆寫

ATL 控制項繼承 CComControl (透過 IOleObjectImpl、IViewObjectExImpl),需覆寫 OnDraw:

HRESULT CATLSam::OnDraw(ATL_DRAWINFO& di)
{
    RECT& rc = *(RECT*)di.prcBounds;
    HDC hdc = di.hdcDraw;

    // 1. 準備 GDI 物件
    HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
    HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
    COLORREF crOldText = SetTextColor(hdc, RGB(0,0,0));
    int nOldMode = SetBkMode(hdc, TRANSPARENT);

    // 2. 繪製標題文字
    USES_CONVERSION; // ATL 巨集:轉換字串編碼
    LPCTSTR pszText = OLE2CT(m_bstrCaption); // BSTR -> TCHAR* (視 _UNICODE 定義)
    if (pszText && *pszText)
        TextOut(hdc, rc.left, rc.top, pszText, lstrlen(pszText));
    else
        TextOut(hdc, rc.left, rc.top, _T(""), 14);

    // 3. 還原 DC
    SetTextColor(hdc, crOldText);
    SetBkMode(hdc, nOldMode);
    SelectObject(hdc, hOldFont);

    return S_OK;
}

事件觸發機制:Connection Points

勾選 Support Connection Points 後,Wizard 在 _IATLSamEvents_CP.h 產生事件介面 Proxy 類別。觸發事件範例:

// 定義事件介面 (在 .idl 中)
dispinterface _IATLSamEvents
{
    properties:
    methods:
    [id(1)] void OnCaptionChange(BSTR newCaption);
};

// 觸發程式碼 (在 CATLSam 方法中)
void CATLSam::FireOnCaptionChange(BSTR newCaption)
{
    CComVariant varResult;
    DISPPARAMS params = { &CComVariant(newCaption), NULL, 1, 0 };
    // 透過 Connection Point 容器廣播
    FireEvent(1, ¶ms, &varResult); // 1 為 DISPID
}

登錄腳本 (.rgs) 與部署

ATL 使用 Registrar Script (.rgs) 宣告式註冊資訊,建置時自動編譯為資源並由 DllRegisterServer 執行。關鍵區段:

HKCR
{
    NoRemove CLSID
    {
        ForceRemove {YOUR_CLSID} = s 'ATLSam Control'
        {
            InprocServer32 = s '%MODULE%'
            {
                val ThreadingModel = s 'Apartment'
            }
            ProgID = s 'ATLLib.ATLSam.1'
            VersionIndependentProgID = s 'ATLLib.ATLSam'
            TypeLib = s '{YOUR_LIBID}'
            Control = s ''
            Version = s '1.0'
            // 關鍵:標記為安全 (視需求)
            // Implemented Categories
            // {
            //     {7DD95801-9882-11CF-9FA9-00AA006C42C4} = s '' // CATID_SafeForScripting
            //     {7DD95802-9882-11CF-9FA9-00AA006C42C4} = s '' // CATID_SafeForInitializing
            // }
        }
    }
}

ATL vs MFC 選型決策表

維度ATLMFC
二進位大小極小 (~50-100KB)較大 (~300KB+ 需 MFC DLL)
載入速度極快較慢 (載入 MFC DLL)
開發效率需手寫 IDL、映射表、COM 樣板碼Wizard/ClassWizard 高度自動化
視窗/無視窗支援完整支援,手動控制封裝完善,COleControl 統一處理
適用場景網頁下載控制項、高效能元件、無 MFC 環境企業內部豐富 UI 控制項、快速原型、MFC 既有專案

進階技巧:混合 MFC 與 ATL

若專案主體為 MFC,但需開發極小型控制項:可在 MFC 專案中加入 ATL 類別 (#include <atlbase.h>),但需注意:

  • MFC 模組狀態 (AfxGetModuleState) 與 ATL 模組 (_Module) 共存需小心管理資源句柄。
  • 建議純 ATL 專案輸出 .ocx,MFC 專案透過 #import 或動態建立方式使用。

總結

ATL 是開發高效能、輕量級 ActiveX 控制項的利器。雖起步需較深 COM 功力,但掌握 CComObjectRootEx、IDispatchImpl、IConnectionPointContainerImpl、CComControl 等核心範本類別,配合 IDL 定義介面、.rgs 註冊、OnDraw 繪圖、FireEvent 觸發事件,即可打造出體積極小、效能極佳、符合 COM 標準的專業級控制項。

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