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

VC6/VC7 ATL 打造 OpenGL COM 控件:從專案建立到事件驅動渲染全流程

     編輯:VC 教學

前言

每次寫 OpenGL 都要重寫像素格式、RC 建立、訊息迴路……為何不封裝成 **ATL COM 控件**?拖拉上表單、處理 OnRender 事件即可專注繪圖邏輯。本文手把手帶你在 VC6/VC7 建立 OpenGL_ATL.ocx,支援事件回調、雙緩衝、自動清理。

專案建立步驟

  1. New Project → **ATL COM AppWizard** → 專案名 OpenGL_ATL → **Dynamic Link Library** → Finish
  2. ClassView 右鍵 → Add → **Add ATL Object** → 名稱 MyControl (Short Name 同) → **Options** 勾選:
    • ✅ **Support Connection Points** (事件必須)
    • ✅ **Support IObjectSafety** (網頁嵌入安全)
    • ✅ **Apartment Threaded** (UI 執行緒親和性)

核心代碼植入

1. 頭文件引入 OpenGL (MyControl.h)


#include 
#include 
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

class ATL_NO_VTABLE CMyControl :
    public CComObjectRootEx,
    public CComCoClass,
    public IDispatchImpl,
    public IPersistStreamInitImpl,
    public IOleControlImpl,
    public IOleObjectImpl,
    public IOleInPlaceActiveObjectImpl,
    public IViewObjectExImpl,
    public IOleInPlaceObjectWindowlessImpl,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl,
    public IPersistStorageImpl,
    public IPersistPropertyBagImpl,
    public IQuickActivateImpl,
    public IDataObjectImpl,
    public IProvideClassInfo2Impl<&CLSID_MyControl, NULL, &LIBID_OpenGL_ATLLib>,
    public CComControl
{
    HGLRC m_hRC; // OpenGL Rendering Context
    // ...

2. 像素格式設定 (SetupPixelFormat)


BOOL CMyControl::SetupPixelFormat(HDC hdc) {
    static PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR), 1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA, 24,  // 24-bit 色深
        0,0,0,0,0,0, 0,0,   // color bits ignored
        0,0,0,0, 32,        // 32-bit Z-buffer
        0,0, PFD_MAIN_PLANE, 0, 0,0,0
    };
    int pf = ChoosePixelFormat(hdc, &pfd);
    if (!pf) return FALSE;
    return SetPixelFormat(hdc, pf, &pfd);
}

3. 視窗生命週期:WM_CREATE / WM_DESTROY


// 訊息對應表
BEGIN_MSG_MAP(CMyControl)
    MESSAGE_HANDLER(WM_CREATE, OnCreate)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    CHAIN_MSG_MAP(CComControl)
END_MSG_MAP()

LRESULT CMyControl::OnCreate(UINT, WPARAM, LPARAM, BOOL&) {
    HDC hdc = GetDC();
    RECT rc; GetClientRect(&rc);
    if (SetupPixelFormat(hdc)) {
        m_hRC = wglCreateContext(hdc);
        wglMakeCurrent(hdc, m_hRC);
        // 初始化 OpenGL 狀態 (glClearColor, glEnable GL_DEPTH_TEST 等)
    }
    ReleaseDC(hdc);
    return 0;
}

LRESULT CMyControl::OnDestroy(UINT, WPARAM, LPARAM, BOOL&) {
    wglMakeCurrent(NULL, NULL);
    if (m_hRC) { wglDeleteContext(m_hRC); m_hRC = NULL; }
    return 0;
}

4. 事件定義:OnRender (IDL 編輯)

ClassView → 展開 IMyControlEvents (連接點介面) → 右鍵 Add Method → 名稱 OnRender,參數:


[id(1)] HRESULT OnRender([in] long right, [in] long left, [in] long bottom, [in] long top);

編譯 IDL (Build → Compile OpenGL_ATL.idl) → 回到 CMyControl 實作 → 點擊 **Connection Points** → 勾選 IMyControlEvents → OK (ATL 自動生成 Fire_OnRender)

5. 繪圖管線:OnDraw → 觸發事件 → SwapBuffers


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

    wglMakeCurrent(hdc, m_hRC);
    glViewport(0, 0, rc.right - rc.left, rc.bottom - rc.top);
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // 紅色背景測試
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // ★ 關鍵:觸發事件,讓容器/使用者注入繪圖代碼
    Fire_OnRender(rc.right, rc.left, rc.bottom, rc.top);
    // VC7+ 可直接呼叫 OnRender(...)

    glFinish();
    SwapBuffers(wglGetCurrentDC());
    return S_OK;
}

使用方法:容器端 (VB6/VC++/C#/網頁)

  1. 註冊控件:`regsvr32 OpenGL_ATL.dll`
  2. 工具箱右鍵 → Components → 選 MyControl Class → 拖到表單
  3. 撰寫事件處理:

// VB6 範例
Private Sub MyControl1_OnRender(ByVal right As Long, ByVal left As Long, _
                                ByVal bottom As Long, ByVal top As Long)
    ' 在此處撰寫 OpenGL 繪圖代碼
    glBegin(GL_TRIANGLES)
        glColor3f(0,1,0); glVertex3f(-0.5, -0.5, 0)
        glColor3f(0,0,1); glVertex3f( 0.5, -0.5, 0)
        glColor3f(1,1,0); glVertex3f( 0.0,  0.5, 0)
    glEnd()
End Sub

// VC++ 對話框類
void COpenGLTestDlg::OnRenderMyControl(long right, long left, long bottom, long top) {
    // 同樣在這裡寫 gl* 指令
}

避坑指南

問題原因解決
控件區域全黑/白像素格式不支援 / RC 未建立檢查 ChoosePixelFormat 回傳值、確認 wglCreateContext 成功
事件不觸發未勾選 Connection Points / IDL 未編譯重新編譯 IDL、確認 Fire_OnRender 存在於 .h
調整大小後畫面錯亂未在 OnDraw 呼叫 glViewport每幀根據 di.prcBounds 設定 viewport
記憶體洩漏WM_DESTROY 未釋放 RC務必 wglDeleteContext 並設 NULL
多執行緒存取衝突OpenGL RC 綁定執行緒ATL 控件預設 SingleThreadModel,所有呼叫自動封送至 UI 執行緒

結語

幾百行 ATL 模板代碼,換來一個 **「拖拉即用、事件驅動、雙緩衝無閃爍」** 的 OpenGL 控件。這就是 COM 元件化的威力:**將繁瑣的平台細節封裝在二進位介面後,讓使用者專注領域邏輯**。下次專案需要 3D 視覺化,直接丟上這個 OCX,連 VB6、C#、網頁都能輕鬆嵌入——**一次封裝,到處複用**。

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