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

跨容器相容 ActiveX 控制項開發核心策略:授權、執行緒模型、資源共享與鍵盤導航

     編輯:關於C語言

ActiveX 控制項跨容器挑戰:標準不等於相容

雖然 ActiveX 控制項實作標準 OLE 介面 (IOleObject, IOleControl, IDispatch...),但**容器行為差異巨大**:VB6、VC++、IE、Office、Delphi、C# 各有獨特的載入時機、授權檢查、執行緒模型、訊息迴圈整合、鍵盤焦點管理。未處理差異會導致控制項在特定容器**無法載入、授權失效、死鎖、鍵盤失效、資源洩漏**。

策略一:授權機制靈活配置

設計時 vs 執行時授權

  • 授權控制項:包含 IClassFactory2、ILicenseProvider,.LIC 檔驗證。僅授權使用者可在**設計時**放入容器 (VB/VC 設計模式)。
  • 非授權控制項:任何人可設計時使用,適合免費、內部、網頁部署。

AppWizard 選項與 .LIC 檔

  • 建立專案勾選 **"Control requires a license"** → 自動生成 VerifyUserLicense、GetLicenseKey、.LIC 檔模板。
  • 修改 _szLicString[] 為唯一驗證字串 (GUID 建議)。

網頁部署授權難題:LPK 工具與樂觀授權

IE 載入授權控制項需 **.LPK 檔** (License Package):**

  1. 用 LPK_TOOL.EXE (IE SDK) 將 .LIC 加密為 .LPK。
  2. HTML 中 <PARAM NAME="LPKPath" VALUE="ctrl.lpk">。
  3. IE 讀取 .LPK 驗證授權。

替代方案:樂觀授權 — 覆寫 COleControl::GetControlFlags 移除 requiresLicense,或實作 IOleObject::GetUserClassID 等繞過。更穩健:**覆寫 AmbientUserMode 查詢**:


BOOL CMyCtrl::OptimisticAmbientUserMode()
{
    BOOL bUserMode = TRUE; // 預設為執行模式 (允許)
    // 嘗試查詢容器 Ambient UserMode
    if (!GetAmbientProperty(DISPID_AMBIENT_USERMODE, VT_BOOL, &bUserMode)) {
        // 容器不回應 (如 IE) → 樂觀假設為執行模式
        bUserMode = TRUE;
    }
    return bUserMode; // TRUE=執行模式(允許), FALSE=設計模式(需授權)
}

在 VerifyUserLicense 中呼叫此函數,若為執行模式直接返回 TRUE。

策略二:執行緒模型選擇與資源共享

單執行緒 (Single-threaded) vs 單元模型

MFC COleControl 預設**單執行緒**。改為單元模型需:

    1. 專案屬性 → Linker → **Register Output** → 勾選。 2. 覆寫 CMyCtrl::GetControlFlags() 返回含 threadingModelFree (或註冊表寫入 ThreadingModel = "Free")。 3. **所有成員變數存取、資源操作必須加鎖** (CCriticalSection)。

全域資源共享:單元模型下的同步挑戰

場景:多實例共享資料庫連線、硬體裝置句柄、GDI 物件。


// 靜態共享資源 + 關鍵區段
class CMyCtrl : public COleControl
{
    static CSharedResource* s_pResource;
    static CCriticalSection s_csResource;

    static CSharedResource* GetResource()
    {
        CSingleLock lock(&s_csResource, TRUE);
        if (!s_pResource) s_pResource = new CSharedResource;
        return s_pResource;
    }
    static void ReleaseResource()
    {
        CSingleLock lock(&s_csResource, TRUE);
        if (s_pResource && --s_pResource->m_nRefCount == 0) {
            delete s_pResource; s_pResource = nullptr;
        }
    }
};

關鍵:引用計數 + 關鍵區段保護建立/釋放。單元模型下**兩個執行緒可能同時呼叫 GetResource**,無鎖會導致重複建立或洩漏。

策略三:內容驗證

容器儲存控制項狀態時 (IPersistStream/Storage/PropertyBag),可能呼叫 IPersistPropertyBag::Load 載入屬性。惡意/損壞資料可能導致崩潰。

  • 覆寫 DoPropExchange (MFC) 或 IPersistPropertyBag::Load,**驗證每個屬性值範圍、型別、字串長度**。
  • 捕獲例外,返回 E_FAIL 而非崩潰。
  • 版本相容:m_dwVersion 機制處理舊版本資料升級。

策略四:鍵盤導航與焦點管理

容器鍵盤訊息分發機制不同:VB6 容器轉發 WM_GETDLGCODE、IE 需 IOleInPlaceActiveObject::TranslateAccelerator。

  • 對話框式控制項 (Child/Control 樣式):**必須實作 IOleInPlaceActiveObject**,MFC COleControl 已實作,但需確保對話框範本 **More Styles → Control = True**。
  • Tab/Enter/Esc 鍵:覆寫 PreTranslateMessage 或 OnGetDlgCode 返回 DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTTAB。
  • 焦點進入/離開:處理 WM_SETFOCUS/WM_KILLFOCUS,呼叫 IOleInPlaceSite::OnInPlaceActivate/Deactivate 通知容器。

策略五:其他容器差異清單

模型註冊表標記特性適用場景
單執行緒ThreadingModel = "Apartment" (或無)所有實例同一 STA 執行緒,序列化存取簡單控制項、非視覺元件、VB6 相容佳
單元模型ThreadingModel = "Free"任意執行緒可直接呼叫,**需執行緒安全**高效能、多執行緒容器 (IE、C#)、免序列化開銷
容器特殊行為應對
Internet Explorer不查詢 AmbientUserMode (永遠設計模式)、安全性區域限制、無訊息迴圈整合樂觀授權、IObjectSafety、非同步載入、避免阻塞 UI
Visual Basic 6設計時完整載入控制項、事件連接點早期建立、授權嚴格正確實作授權、設計時不執行耗時操作
Visual C++ / MFC測試容器、包裝類生成、動態建立標準流程驗證
C# / .NET (AxHost)RCW 包裝、事件轉換、執行緒封送單元模型免封送、介面簽名符合 COM Interop 規範
Office (Word/Excel)文檔儲存時序列化、印刷預覽模式正確 IPersistStream/Storage、處理 m_lpfnCloseProc

測試矩陣:最小驗證集

  1. VB6 設計時放入、設定屬性、執行時事件。
  2. VC++ 測試容器:插入、方法調用、事件日誌、屬性頁。
  3. IE 網頁:<OBJECT> 載入、腳本調用方法/屬性、事件綁定、跨域安全性。
  4. C# WinForms:工具箱加入、拖放表單、事件處理、屬性網格。
  5. 授權測試:未授權機器設計時放入 (應失敗)、授權機器設計/執行 (應成功)、網頁 .LPK 載入。
  6. 執行緒測試:多執行緒並發調用方法 (單元模型)、STA 容器調用 (單執行緒)。

總結

開發「一次編寫、到處運行」的 ActiveX 控制項,核心在於:**授權策略靈活 (LPK/樂觀授權)、執行緒模型明確選擇並做好同步 (單元模型+鎖)、資源共享引用計數化、屬性載入防禦性驗證、鍵盤焦點標準化實作、針對主流容器差異建立測試矩陣**。這不僅是技術實作,更是對 COM/OLE 生態系統深度理解的體現。

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