目录
 
创建ATL项目
我们创建dll形式的ATL项目EllipseCase 如下图:
ATL7.1编写ActiveX控件_#define
 
创建ActiveX控件
ATL7.1编写ActiveX控件_ico_02
 
我打算创建一个Ellipse控件,该控件拥有背景属性,可以改变背景颜色。
该控件绘制一个笛卡尔坐标系,用户可以通过右键菜单修改X、Y、Z轴的颜色。
好啦,现就这么多了,让我上路吧。
ATL7.1编写ActiveX控件_ico_03
ATL7.1编写ActiveX控件_redis_04
ATL7.1编写ActiveX控件_控件_05
 
 
重要提醒:如果你要创建一个能够处理WM_CREATE消息的ActiveX控件,请注意:
必须------
a)       选择标准控件
b)      选中仅适用于有窗口的
c)      在构造函数中加上m_bWindowOnly=TRUE
其中的原因在于符合控件派生自CComCompositeControl<>模板类,而标准控件派生自CComControl<>类。派生自CComCompositeControl类意味着该类可以包容其它的控件,但是同时具有了对话框的特征,且拥有了自己的对话框资源。所以如果想要添加初始化代码,应该处理WM_INITDIALOG消息。如果我们想在自己的控件中包含另一个ActiveX控件,我们应该创建复合控件,并用对话框承载想要包含的ActiveX控件。
解释向导的行为
我们先停下来,了解一下ActiveX控件的基础知识,并理解向导生成的代码。
看看CEllispe的父类
class ATL_NO_VTABLE CEllispe :
     public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CEllispe, &CLSID_Ellispe>,
public IPersistStreamInitImpl<CEllispe>,
public ISpecifyPropertyPagesImpl<CEllispe>,
public IPersistStorageImpl<CEllispe>,
     public CStockPropImpl<CEllispe, IEllispe>,
     public IOleControlImpl<CEllispe>,
     public IOleObjectImpl<CEllispe>,
     public IOleInPlaceActiveObjectImpl<CEllispe>,
     public IOleInPlaceObjectWindowlessImpl<CEllispe>,
     public IViewObjectExImpl<CEllispe>,
     public ISupportErrorInfo,
     public IConnectionPointContainerImpl<CEllispe>,
public IProvideClassInfo2Impl<&CLSID_Ellispe, &__uuidof(_IEllispeEvents), &LIBID_EllipseCaseLib>,
     public CProxy_IEllispeEvents<CEllispe>,
     public IQuickActivateImpl<CEllispe>,
     public IDataObjectImpl<CEllispe>,
     public CComCompositeControl<CEllispe>
 
ActiveX控件拥有界面,所以它只能存在于单线程套间中,所以应该用CComObjectRootEx<CComSingleThreadModel>;
ActiveX控件可以被创建,所以通过CComCoClass<CEllispe, &CLSID_Ellispe>来实现自己的类厂;
ActiveX控件至少要支持流方式保存,所以使用IPersistStreamInitImpl<CEllispe>;
VB和IE要求优先以属性包的方式保存对象,所以有的ActiveX控件也要支持
ActiveX控件支持结构化存储,这样可以被嵌入到OLE文档中,IPersistStorageImpl<CEllispe>;
ActiveX控件支持标准属性,使用CStockPropImpl<CEllispe, IEllispe>可以帮助节省时间;
ActiveX控件支持标准OLE控制功能,使用IOleControlImpl<CEllispe>;
ActiveX控件支持实地激活,使用public IOleObjectImpl<CEllispe>,public IOleInPlaceActiveObjectImpl<CEllispe>
IOleInPlaceObjectWindowlessImpl<CEllispe>帮助实现快速高效的激活;
ActiveX控件支持按需呈现对象的视图,使用IViewObjectExImpl<CEllispe>;
ActiveX控件支持传递错误信息给调用方,而不是仅通过HRESULT,使用ISupportErrorInfo;
ActiveX控件支持事件,因此使用IConnectionPointContainerImpl<CEllispe>,IProvideClassInfo2Impl,CProxy_IEllispeEvents<CEllispe>,
ActiveX控件是复合控件,所以使用了CComCompositeControl<CEllispe>
 
 
由于我们在向导中选择了两个标准属性:BackGroundColor和BackGroundStyle,所以我们来看看idl文件中有些什么:
interface IEllispe : IDispatch{
     [propput, bindable, requestedit, id(DISPID_BACKCOLOR)]
     HRESULT BackColor([in]OLE_COLOR clr);
     [propget, bindable, requestedit, id(DISPID_BACKCOLOR)]
     HRESULT BackColor([out,retval]OLE_COLOR* pclr);
     [propput, bindable, requestedit, id(DISPID_BACKSTYLE)]
     HRESULT BackStyle([in]long style);
     [propget, bindable, requestedit, id(DISPID_BACKSTYLE)]
     HRESULT BackStyle([out,retval]long* pstyle);
};
IEllispe从IDispatch接口派生,这是因为控件容器要使用控件的IDispatch方法访问控件的属性和方法。
注意,所有的标准属性都有bindable和requestedit两个属性,这是因为标准属性的put方法在改变属性之前之后都要向包容器激发变化通知
 
标准属性
背景属性
背景颜色属于标准属性,CStockPropImpl类能够帮助我们。
template < class T, class InterfaceName, const IID* piid = &_ATL_IIDOF(InterfaceName), const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE CStockPropImpl : public IDispatchImpl< InterfaceName, piid, plibid, wMajor, wMinor, tihclass >
{
public:
     // Font
     HRESULT STDMETHODCALLTYPE put_Font(IFontDisp* pFont)
     {
         __if_exists(T::m_pFont)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_Font/n"));
              T* pT = (T*) this;
              if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_FONT) == S_FALSE)
                   return S_FALSE;
              pT->m_pFont = 0;
              if (pFont)
              {
                   CComQIPtr<IFont, &__uuidof(IFont)> p(pFont);
                   if (p)
                   {
                       CComPtr<IFont> pFont;
                       p->Clone(&pFont);
                       if (pFont)
                            pFont->QueryInterface(__uuidof(IFontDisp), (void**) &pT->m_pFont);
                   }
              }
              pT->m_bRequiresSave = TRUE;
              if (pT->m_nFreezeEvents == 0)
                   pT->FireOnChanged(DISPID_FONT);
              __if_exists(T::OnFontChanged)
              {
                   pT->OnFontChanged();
              }
              pT->FireViewChange();
              pT->SendOnDataChange(NULL);
         }
         return S_OK;
     }
     HRESULT STDMETHODCALLTYPE putref_Font(IFontDisp* pFont)
     {
         __if_exists(T::m_pFont)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::putref_Font/n"));
              T* pT = (T*) this;
              if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_FONT) == S_FALSE)
                   return S_FALSE;
              pT->m_pFont = pFont;
              pT->m_bRequiresSave = TRUE;
              if (pT->m_nFreezeEvents == 0)
                   pT->FireOnChanged(DISPID_FONT);
              __if_exists(T::OnFontChanged)
              {
                   pT->OnFontChanged();
              }
              pT->FireViewChange();
              pT->SendOnDataChange(NULL);
         }
         return S_OK;
     }
     HRESULT STDMETHODCALLTYPE get_Font(IFontDisp** ppFont)
     {
         __if_exists(T::m_pFont)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_Font/n"));
              ATLASSERT(ppFont != NULL);
              if (ppFont == NULL)
                   return E_POINTER;
 
              T* pT = (T*) this;
              *ppFont = pT->m_pFont;
              if (*ppFont != NULL)
                   (*ppFont)->AddRef();
         }
         return S_OK;
     }
     // Picture
     HRESULT STDMETHODCALLTYPE put_Picture(IPictureDisp* pPicture)
     {
         __if_exists(T::m_pPicture)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_Picture/n"));
              T* pT = (T*) this;
              if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_PICTURE) == S_FALSE)
                   return S_FALSE;
              pT->m_pPicture = 0;
              if (pPicture)
              {
                  CComQIPtr<IPersistStream, &__uuidof(IPersistStream)> p(pPicture);
                   if (p)
                   {
                       ULARGE_INTEGER l;
                       p->GetSizeMax(&l);
                       HGLOBAL hGlob = GlobalAlloc(GHND, l.LowPart);
                       if (hGlob)
                       {
                            CComPtr<IStream> spStream;
                            CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
                            if (spStream)
                            {
                                 if (SUCCEEDED(p->Save(spStream, FALSE)))
                                 {
                                     LARGE_INTEGER l;
                                     l.QuadPart = 0;
                                     spStream->Seek(l, STREAM_SEEK_SET, NULL);
                                     OleLoadPicture(spStream, l.LowPart, FALSE, __uuidof(IPictureDisp), (void**)&pT->m_pPicture);
                                 }
                                 spStream.Release();
                            }
                            GlobalFree(hGlob);
                       }
                   }
              }
              pT->m_bRequiresSave = TRUE;
              if (pT->m_nFreezeEvents == 0)
                   pT->FireOnChanged(DISPID_PICTURE);
              __if_exists(T::OnPictureChanged)
              {
                   pT->OnPictureChanged();
              }
              pT->FireViewChange();
              pT->SendOnDataChange(NULL);
         }
         return S_OK;
     }
     HRESULT STDMETHODCALLTYPE putref_Picture(IPictureDisp* pPicture)
     {
         __if_exists(T::m_pPicture)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::putref_Picture/n"));
              T* pT = (T*) this;
              if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_PICTURE) == S_FALSE)
                   return S_FALSE;
              pT->m_pPicture = pPicture;
              pT->m_bRequiresSave = TRUE;
              if (pT->m_nFreezeEvents == 0)
                   pT->FireOnChanged(DISPID_PICTURE);
              __if_exists(T::OnPictureChanged)
              {
                   pT->OnPictureChanged();
              }
              pT->FireViewChange();
              pT->SendOnDataChange(NULL);
         }
         return S_OK;
     }
     HRESULT STDMETHODCALLTYPE get_Picture(IPictureDisp** ppPicture)
     {
         __if_exists(T::m_pPicture)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_Picture/n"));
              ATLASSERT(ppPicture != NULL);
              if (ppPicture == NULL)
                   return E_POINTER;
 
              T* pT = (T*) this;
              *ppPicture = pT->m_pPicture;
              if (*ppPicture != NULL)
                   (*ppPicture)->AddRef();
         }
         return S_OK;
     }
     // MouseIcon
     HRESULT STDMETHODCALLTYPE put_MouseIcon(IPictureDisp* pPicture)
     {
         __if_exists(T::m_pMouseIcon)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_MouseIcon/n"));
              T* pT = (T*) this;
              if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_MOUSEICON) == S_FALSE)
                   return S_FALSE;
              pT->m_pMouseIcon = 0;
              if (pPicture)
              {
                   CComQIPtr<IPersistStream, &__uuidof(IPersistStream)> p(pPicture);
                   if (p)
                   {
                       ULARGE_INTEGER l;
                       p->GetSizeMax(&l);
                       HGLOBAL hGlob = GlobalAlloc(GHND, l.LowPart);
                       if (hGlob)
                       {
                            CComPtr<IStream> spStream;
                            CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
                            if (spStream)
                            {
                                 if (SUCCEEDED(p->Save(spStream, FALSE)))
                                 {
                                     LARGE_INTEGER l;
                                     l.QuadPart = 0;
                                     spStream->Seek(l, STREAM_SEEK_SET, NULL);
                                     OleLoadPicture(spStream, l.LowPart, FALSE, __uuidof(IPictureDisp), (void**)&pT->m_pMouseIcon);
                                 }
                                 spStream.Release();
                            }
                            GlobalFree(hGlob);
                       }
                   }
              }
              pT->m_bRequiresSave = TRUE;
              if (pT->m_nFreezeEvents == 0)
                   pT->FireOnChanged(DISPID_MOUSEICON);
              __if_exists(T::OnMouseIconChanged)
              {
                   pT->OnMouseIconChanged();
              }
              pT->FireViewChange();
              pT->SendOnDataChange(NULL);
         }
         return S_OK;
     }
     HRESULT STDMETHODCALLTYPE putref_MouseIcon(IPictureDisp* pPicture)
     {
         __if_exists(T::m_pMouseIcon)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::putref_MouseIcon/n"));
              T* pT = (T*) this;
              if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_MOUSEICON) == S_FALSE)
                   return S_FALSE;
              pT->m_pMouseIcon = pPicture;
              pT->m_bRequiresSave = TRUE;
              if (pT->m_nFreezeEvents == 0)
                   pT->FireOnChanged(DISPID_MOUSEICON);
              __if_exists(T::OnMouseIconChanged)
              {
                   pT->OnMouseIconChanged();
              }
              pT->FireViewChange();
              pT->SendOnDataChange(NULL);
         }
         return S_OK;
     }
     HRESULT STDMETHODCALLTYPE get_MouseIcon(IPictureDisp** ppPicture)
     {
         __if_exists(T::m_pMouseIcon)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_MouseIcon/n"));
              ATLASSERT(ppPicture != NULL);
              if (ppPicture == NULL)
                   return E_POINTER;
 
              T* pT = (T*) this;
              *ppPicture = pT->m_pMouseIcon;
              if (*ppPicture != NULL)
                   (*ppPicture)->AddRef();
         }
         return S_OK;
     }
     IMPLEMENT_STOCKPROP(OLE_COLOR, BackColor, clrBackColor, DISPID_BACKCOLOR)
     IMPLEMENT_STOCKPROP(OLE_COLOR, BorderColor, clrBorderColor, DISPID_BORDERCOLOR)
     IMPLEMENT_STOCKPROP(OLE_COLOR, FillColor, clrFillColor, DISPID_FILLCOLOR)
     IMPLEMENT_STOCKPROP(OLE_COLOR, ForeColor, clrForeColor, DISPID_FORECOLOR)
     IMPLEMENT_BOOL_STOCKPROP(AutoSize, bAutoSize, DISPID_AUTOSIZE)
     IMPLEMENT_BOOL_STOCKPROP(Valid, bValid, DISPID_VALID)
     IMPLEMENT_BOOL_STOCKPROP(Enabled, bEnabled, DISPID_ENABLED)
     IMPLEMENT_BOOL_STOCKPROP(TabStop, bTabStop, DISPID_TABSTOP)
     IMPLEMENT_BOOL_STOCKPROP(BorderVisible, bBorderVisible, DISPID_BORDERVISIBLE)
     IMPLEMENT_BSTR_STOCKPROP(Text, bstrText, DISPID_TEXT)
     IMPLEMENT_BSTR_STOCKPROP(Caption, bstrCaption, DISPID_CAPTION)
     HRESULT STDMETHODCALLTYPE put_Window(LONG_PTR hWnd)
     {
         return put_HWND(hWnd);
     }
     HRESULT STDMETHODCALLTYPE get_Window(LONG_PTR* phWnd)
     {
         return get_HWND(phWnd);
     }
     HRESULT STDMETHODCALLTYPE put_HWND(LONG_PTR /*hWnd*/)
     {
         ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_HWND/n"));
         return E_FAIL;
     }
     HRESULT STDMETHODCALLTYPE get_HWND(LONG_PTR* phWnd)
     {
         __if_exists(T::m_hWnd)
         {
              ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_HWND/n"));
              ATLASSERT(phWnd != NULL);
              if (phWnd == NULL)
                   return E_POINTER;
              T* pT = (T*) this;
              *phWnd = reinterpret_cast<LONG_PTR>(pT->m_hWnd);
         }
         return S_OK;
     }
     IMPLEMENT_STOCKPROP(LONG, BackStyle, nBackStyle, DISPID_BACKSTYLE)
     IMPLEMENT_STOCKPROP(LONG, BorderStyle, nBorderStyle, DISPID_BORDERSTYLE)
     IMPLEMENT_STOCKPROP(LONG, BorderWidth, nBorderWidth, DISPID_BORDERWIDTH)
     IMPLEMENT_STOCKPROP(LONG, DrawMode, nDrawMode, DISPID_DRAWMODE)
     IMPLEMENT_STOCKPROP(LONG, DrawStyle, nDrawStyle, DISPID_DRAWSTYLE)
     IMPLEMENT_STOCKPROP(LONG, DrawWidth, nDrawWidth, DISPID_DRAWWIDTH)
     IMPLEMENT_STOCKPROP(LONG, FillStyle, nFillStyle, DISPID_FILLSTYLE)
     IMPLEMENT_STOCKPROP(SHORT, Appearance, nAppearance, DISPID_APPEARANCE)
     IMPLEMENT_STOCKPROP(LONG, MousePointer, nMousePointer, DISPID_MOUSEPOINTER)
     IMPLEMENT_STOCKPROP(LONG, ReadyState, nReadyState, DISPID_READYSTATE)
};
 
我们先来看看模板参数
template < class T, class InterfaceName,
const IID* piid = &_ATL_IIDOF(InterfaceName), const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
T是控件类的名字;
InterfaceName是接口的名字,该接口拥有标准属性和方法;
piid是接口的GUID的指针;
plibid是类型库GUID的指针;
wMajor是主版本号;
wMinor是次版本号;
 
实际运用中,只需要指定前面两个参数,其余皆可以采用默认值。
CStockPropImpl类为我们实现了所有标准属性的访问方法,但是我们只需要实现两个属性,因此我们只为我们需要的标准属性提供数据成员,成员变量的名称要与CStockPropImpl类使用的名字一致。如若不信,我们看看下面的宏
IMPLEMENT_STOCKPROP(OLE_COLOR, BackColor, clrBackColor, DISPID_BACKCOLOR)
该宏的定义如下:
#define IMPLEMENT_STOCKPROP(type, fname, pname, dispid) /
    HRESULT STDMETHODCALLTYPE put_##fname(type pname) /
    { /
        __if_exists(T::m_##pname) /
    。。。。。。。。
所以CStockPropImpl类假定控件类T拥有m_clrBackColor成员变量
这很麻烦,但是不必烦恼,因为向导已经为我们定义了合适的成员变量。但是我们还是需要知道这些,万一我们不小心删除了代码,我们知道该如何恢复。
注意IMPLEMENT_STOCKPROP宏只能用于CStockPropImpl类
 
CEllispe类里边的属性声明为:
OLE_COLOR m_clrBackColor;
void OnBackColorChanged()
{
    ATLTRACE(_T("OnBackColorChanged/n"));
}
OnBackColorChanged方法会被CStockPropImpl类在属性值修改的时候调用,如果我们需要对属性的修改进行即时反映,可以在该函数里面填写代码
其他属性也是如此。
 
现在我们的控件Ellipse已经拥有了背景颜色、背景状态两种标准属性,在Activex测试容器中右键点击控件的边缘,弹出菜单,选择属性,可以看到
ATL7.1编写ActiveX控件_ico_06
我们可以设置背景颜色了。试着用用其他的功能,我们可以设置控件的环境属性,还可以把控件保存到流和结构化存储文件中。但是目前不支持属性包方式保存。
 
Unicode风格编码
我撰写本文的时候,正是Windows2000已经普及的时候,出于效率的考虑和开发程序减少从ANSI和UNICODE转换的额外负担,我在本文示例程序中全部使用Unicode风格编码,工程中设置如下:
ATL7.1编写ActiveX控件_控件_07
 
 
六 用OpenGL绘制笛卡尔坐标系
到这里忍不住提高点难度,用GDI绘制笛卡尔坐标系很简单,用OpenGL绘制三维笛卡尔坐标系才棒。如果读者不想涉及太多的OpenGL技巧,可以掠过这一节,用GDI绘图。
在stdafx.h文件中增加以下代码:
#include <gl/gl.h>
#include <gl/glu.h>
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")
#pragma warning(disable : 4100)
#include <gl/glaux.h>
#pragma comment(lib, "winmm.lib")
 
为我们的控件添加成员函数
//设置屏幕DC的像素格式,OpenGL绘图环境初始化
BOOL CEllispe::bSetupPixelFormat(HDC hdc)
{
     static PIXELFORMATDESCRIPTOR pfd =
     {
         sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
         1,                              // version number
         PFD_DRAW_TO_WINDOW |            // support window
          PFD_SUPPORT_OPENGL |          // support OpenGL
          PFD_DOUBLEBUFFER,             // double buffered
         PFD_TYPE_RGBA,                  // RGBA type
         24,                             // 24-bit color depth
         0, 0, 0, 0, 0, 0,               // color bits ignored
         0,                              // no alpha buffer
         0,                              // shift bit ignored
         0,                              // no accumulation buffer
         0, 0, 0, 0,                     // accum bits ignored
         32,                             // 32-bit z-buffer
         0,                              // no stencil buffer
         0,                              // no auxiliary buffer
         PFD_MAIN_PLANE,                 // main layer
         0,                              // reserved
         0, 0, 0                         // layer masks ignored
     };
     int pixelformat;
     if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == 0 )
     {
         ATLASSERT(FALSE);
         return FALSE;
     }
     if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE)
     {
         ATLASSERT(FALSE);
         return FALSE;
     }
     return TRUE;
}
 
//初始化OpenGL绘制的环境
void CEllispe::CreateContext(HDC hdc, RECT& rc)
{
     PIXELFORMATDESCRIPTOR pfd;
     if (!bSetupPixelFormat(hdc))
         return;
     int n = ::GetPixelFormat(hdc);
     ::DescribePixelFormat(hdc, n, sizeof(pfd), &pfd);
     m_hrc = wglCreateContext(hdc);
     wglMakeCurrent(hdc, m_hrc);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
}
 
//绘制x、y、z坐标轴
void CEllispe::RenderScene(void)
{
     glClearColor(1.0f, 1.0f, 1.0f, 10.0f);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glPushMatrix();
 
     glColor3f(1.0f,1.0f,1.0f);
     //绕x轴逆时针旋转15度
     glRotatef(15.0f,1.0f,0.0f,0.0f);
     //绕y轴顺时针旋转angle度
 
     glRotatef(-30,0.0f,1.0f,0.0f);
 
     glBegin(GL_LINES);
         //绘制X轴
         glColor3f(1.0f,0.0f,0.0f);
         glVertex3f(-100.0f,0.0f,0.0f);
         glVertex3f(100.0f,0.0f,0.0f);
         glVertex3f(100.0f,0.0f,0.0f);
         glVertex3f(95.0f,0.0f,5.0f);
         glVertex3f(100.0f,0.0f,0.0f);
         glVertex3f(95.0f,0.0f,-5.0f);
         //绘制Y轴
         glColor3f(0.0f,1.0f,0.0f);
         glVertex3f(0.0f,-100.0f,0.0f);
         glVertex3f(0.0f,100.0f,0.0f);
         glVertex3f(0.0f,100.0f,0.0f);
         glVertex3f(-5.0f,95.0f,0.0f);
         glVertex3f(0.0f,100.0f,0.0f);
         glVertex3f(5.0f,95.0f,0.0f);
         //绘制Z轴
         glColor3f(0.0f,0.0f,1.0f);
         glVertex3f(0.0f,0.0f,-100.0f);
         glVertex3f(0.0f,0.0f,100.0f);
         glVertex3f(0.0f,0.0f,100.0f);
         glVertex3f(5.0f,0.0f,95.0f);
         glVertex3f(0.0f,0.0f,100.0f);
         glVertex3f(-5.0f,0.0f,95.0f);
     glEnd();
 
     glPopMatrix();
     glFinish();
     SwapBuffers(wglGetCurrentDC());
}
 
OnCreate里面添加如下代码:
LRESULT CEllispe::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    HDC hdc = GetDC();
    RECT rc;
    GetClientRect(&rc);
    CreateContext(hdc, rc);
    return 0;
}
OnDestroy里面添加如下代码:
LRESULT CEllispe::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    ::wglMakeCurrent(NULL, NULL);
    if (m_hrc)
    {
        ::wglDeleteContext(m_hrc);
        m_hrc = NULL;
    }
    return 0;
}
在OnDraw里面添加如下代码:
HRESULT CEllispe::OnDraw(ATL_DRAWINFO& di)
{
     HDC hdc = di.hdcDraw;
     wglMakeCurrent(hdc, m_hrc);
     RenderScene();
     return __super::OnDraw(di);
}
 
最后一定要在CEllipse头文件中加上宏
BEGIN_MSG_MAP(CEllispe)
    MESSAGE_HANDLER(WM_PAINT, OnPaint)
    MESSAGE_HANDLER(WM_SIZE, OnSize)
    MESSAGE_HANDLER(WM_CREATE, OnCreate)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    CHAIN_MSG_MAP(CComCompositeControl<CEllispe>)
END_MSG_MAP()
 
效果如图:
ATL7.1编写ActiveX控件_#define_08
 
另外发现一个问题glut库函数在ATL7.1中使用会导致工程生成的控件注册失败。
 
七自定义属性
我要为我的控件添加自己的属性,XColor,用来保存X轴的颜色。右键点击IEllipse接口,选择添加属性,
ATL7.1编写ActiveX控件_#define_09
ATL7.1编写ActiveX控件_ico_10
 
向导在idl文件中添加如下代码:
    [propget, id(1), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([out, retval] OLE_COLOR* pVal);
    [propput, id(1), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([in] OLE_COLOR newVal);
修改idl文件中的代码:
    const int DISPID_XCOLOR=1;
     [propget, id(DISPID_XCOLOR), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([out, retval] OLE_COLOR* pVal);
     [propput, id(DISPID_XCOLOR), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([in] OLE_COLOR newVal);
向导在.h文件中增加如下代码:
    STDMETHOD(get_XColor)(OLE_COLOR* pVal);
    STDMETHOD(put_XColor)(OLE_COLOR newVal);
向导在.cpp文件中增加如下代码:
STDMETHODIMP CEllispe::get_XColor(OLE_COLOR* pVal)
{
    // TODO: 在此添加实现代码
    return S_OK;
}
 
STDMETHODIMP CEllispe::put_XColor(OLE_COLOR newVal)
{
    // TODO: 在此添加实现代码
    return S_OK;
}
 
现在我们来实现添加自己的代码实现属性:
第一步,先添加私有成员变量 OLE_COLOR m_XColor;
第二步,在get和put函数内部填写代码
STDMETHODIMP CEllispe::get_XColor(OLE_COLOR* pVal)
{
     if(pVal==NULL)
         return E_POINTER;
     *pVal=m_XColor;
     return S_OK;
}
 
STDMETHODIMP CEllispe::put_XColor(OLE_COLOR newVal)
{
     if(m_XColor==newVal)
         return S_OK;
     if(!m_nFreezeEvents)//m_nFreezeEvents是从父类CComCompositeControl继承下来的,只有为0时才可以设置属性
     {
         if(FireOnRequestEdit(DISPID_XCOLOR)==S_FALSE)//通知容器控件准备改变属性,容器许可将返回S_OK
              return S_FALSE;
     }
     //////////////////////////////
     //真正本属性需要的代码就此一行
     m_XColor=newVal;
     ///////////////////////////////
     m_bRequiresSave=TRUE;//m_bRequiresSave从CComControlBase类继承,为TRUE表示属性已被修改,需要保存
     if(!m_nFreezeEvents)
     {
         FireOnChanged(DISPID_XCOLOR);//通知容器属性值已经修改
     }
     FireViewChange();//通知容器控件窗口需要重绘,用于绘制图形的地方,不是所有场合都必须
     SendOnDataChange(NULL);//通知所有建立连接的通报接收器控件的属性已经改变
     return S_OK;
}
真得非常简单,如你所看到的,其实COM对象的属性实质是一对方法,只是VB语言让你以为“=”运算符就可以获取/设置属性值,内部还是调用了两个方法
 
 
 
自定义方法
有了自定义属性的基础,编写自定义方法非常简单,在这里我就不再赘述了,可以参考我以前的文章<<ATL7.1创建房屋对象>>。
唯一的区别是不需要考虑通知容器。
标准方法
ActiveX控件有标准方法。遗憾的是我还没有找到ATL7.1中通过向导自动生成他们代码的方法,或许根本就没有。下面列出的是(可能是)ATL7.1支持的ActiveX控件的所有的标准方法ID。
#define DISPID_REFRESH                  (-550)
#define DISPID_DOCLICK                  (-551)
#define DISPID_ABOUTBOX                 (-552)
#define DISPID_ADDITEM                  (-553)
#define DISPID_CLEAR                    (-554)
#define DISPID_REMOVEITEM               (-555)
 
#define DISPID_CLICK                    (-600)
#define DISPID_DBLCLICK                 (-601)
#define DISPID_KEYDOWN                  (-602)
#define DISPID_KEYPRESS                 (-603)
#define DISPID_KEYUP                    (-604)
#define DISPID_MOUSEDOWN                (-605)
#define DISPID_MOUSEMOVE                (-606)
#define DISPID_MOUSEUP                  (-607)
#define DISPID_ERROREVENT               (-608)
#define DISPID_READYSTATECHANGE         (-609)
#define DISPID_CLICK_VALUE              (-610)
#define DISPID_RIGHTTOLEFT              (-611)
#define DISPID_TOPTOBOTTOM              (-612)
#define DISPID_THIS                     (-613)
 
现在我们手动实现其中的一个AboutBox方法,该方法弹出AboutBox对话框。我们可以用它显示我们的一些信息。
首先在idl文件中加上如下代码:
    [id(DISPID_ABOUTBOX)] HRESULT AboutBox();
然后在.h中声明函数原型,然后在.cpp文件中
STDMETHODIMP CEllispe::AboutBox()
{
    MessageBox(L"这是我们的ATL编写ActivX控件的教程,属于飞天公司内部资料");
    return S_OK;
}
好了,标准方法也添加完毕。
注意,我们的控件是复合控件,它包含了一个对话框资源IDD_ELLISPE,如果我们在该对话框资源上加上控件,程序运行时会自动看见,你可以把我们的控件想象成对话框程序。
我们这里并不想在该对话框上添加任何东西。如果我们想弹出对话框,请添加新的对话框资源。ATL中使用对话框涉及到其他知识,这里不涉及。
 
标准事件
`ATL7.1似乎不支持标准事件
十一自定义事件