ActiveX控件(ATL篇)
目录
1.7.6 修改DllUnregisterServer 14
1.7.7 修改Fire_ClickIn、Fire_ClickOut 14
第1章 VC++6.0创建
本章内容根据MSDN98的ATL Tutorial翻译、整理而成。
1.1 目标
本章的目标是使用ATL创建一个下图所示的ActiveX控件。
图1.1
这个控件只有一个属性 short Sides;用于指定多边形的边数。
这个控件有两个事件:
void ClickIn([in]long x, [in] long y);
void ClickOut([in] long x, [in] long y);
当鼠标左键在多边形内部按下时将触发ClickIn事件;当鼠标左键在多边形外部按下时将触发ClickOut事件。
1.2 创建项目
运行VC++6.0,新建"ATL COM AppWizard"项目,如下图所示。配置好项目名称、项目目录后,单击"OK"按钮。
图1.2
采用默认设置,直接单击"Finish"按钮。
图1.3
单击"OK"按钮,完成项目创建。
图1.4
1.3 增加COM类
单击【Insert】【New ATL Object...】
图1.5
选中Controls里的Full Control,单击"Next"按钮
图1.6
在Names页面,请输入COM类的名称。
图1.7
在Attributes页面,请勾中Support ISupportErrorInfo(错误信息)和 Support Connection Points(连接点)这两个复选框。其中,连接点非常重要:ActiveX控件通过连接点把事件传递给客户程序。
图1.8
Miscellaneous页面的设置保持不变
图1.9
在Stock Properties页面,增加库存属性——Fill Color
图1.10
上图中,单击"确定"按钮完成COM类的添加。
1.4 属性
鼠标右键单击接口IPolyCtl,弹出菜单中单击【Add Property】。注意:不要混淆IPolyCtl和_IPolyCtlEvents,后者只是用来产生事件。
图1.11
多边形边数的Property Type(属性类型)为short,Property Name(属性名称)为Sides。单击"OK"按钮完成属性Sides的添加。
图1.12
1.5 事件
现在添加两个事件:
void ClickIn([in]long x, [in] long y);
void ClickOut([in] long x, [in] long y);
当鼠标左键在多边形内部按下时将触发ClickIn事件;当鼠标左键在多边形外部按下时将触发ClickOut事件。
鼠标右键单击_IPolyCtlEvents,弹出菜单中单击【Add Method...】
图1.13
依下图显示进行配置,单击"OK"按钮完成ClickIn的添加。
图1.14
用同样的方法添加ClickOut事件。
1.6 实现连接点
鼠标右键单击"Polygon.idl"文件,弹出菜单中单击【Compile Polygon.idl】,编译此文件。
图1.15
鼠标右键单击"CPolyCtl",弹出菜单中单击【Implement Connection Point...】
图1.16
勾中"_IPolyCtlEvents",然后单击"OK"按钮。
图1.17
1.7 编码
1.7.1 增加成员变量
给CPolyCtl增加成员变量m_nSides和m_arrPoint。可以增加到库存属性m_clrFillColor的下方:
OLE_COLOR m_clrFillColor; //填充色(库存属性) short m_nSides; //多边形边数 POINT m_arrPoint[100]; //多边形顶点坐标 |
1.7.2 初始化成员变量
CPolyCtl的构造函数里对成员变量进行初始化:
CPolyCtl() { m_nSides = 3; //初始化为三角形 m_clrFillColor = RGB(0, 0xFF, 0); //填充颜色默认为绿色 memset(m_arrPoint,0,sizeof(m_arrPoint)); //多边形顶点初始化为零 } |
1.7.3 完成属性赋值代码
STDMETHODIMP CPolyCtl::get_Sides(short *pVal) { *pVal = m_nSides; return S_OK; } STDMETHODIMP CPolyCtl::put_Sides(short newVal) { if (newVal > 2 && newVal < 101) { m_nSides = newVal; FireViewChange(); return S_OK; } return Error(_T("Shape must have between 3 and 100 sides")); } |
获取Sides属性时,将调用get_Sides函数;修改Sides属性时,将调用put_Sides函数。FireViewChange()将通知控件重新绘制。CComCoClass::Error函数将产生一个错误信息。
1.7.4 完成控件绘制代码
控件的绘制由CPolyCtl::OnDraw负责。
HRESULT OnDraw(ATL_DRAWINFO& di) { RECT& rc = *(RECT*)di.prcBounds; HDC hdc = di.hdcDraw; COLORREF colFore; HBRUSH hOldBrush, hBrush; HPEN hOldPen, hPen; //Translate m_colFore into a COLORREF type OleTranslateColor(m_clrFillColor, NULL, &colFore); //Create and select the colors to draw the circle hPen = (HPEN)GetStockObject(BLACK_PEN); hOldPen = (HPEN)SelectObject(hdc, hPen); hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH); hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); // Create and select the brush that will be used to fill the polygon hBrush = CreateSolidBrush(colFore); SelectObject(hdc, hBrush); CalcPoints(rc); Polygon(hdc, &m_arrPoint[0], m_nSides); // Select back the old pen and brush and delete the brush we created SelectObject(hdc, hOldPen); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); return S_OK; } |
上面的OleTranslateColor用于将OLE_COLOR转换为COLORREF。CalcPoints函数用于计算多边形顶点坐标至m_arrPoint,其代码如下:
void CPolyCtl::CalcPoints(const RECT& rc) { const double pi = 3.14159265358979; POINT ptCenter; double dblRadiusx = (rc.right - rc.left) / 2; double dblRadiusy = (rc.bottom - rc.top) / 2; double dblAngle = 3 * pi / 2; // Start at the top double dblDiff = 2 * pi / m_nSides; // Angle each side will make ptCenter.x = (rc.left + rc.right) / 2; ptCenter.y = (rc.top + rc.bottom) / 2; // Calculate the points for each side for (int i = 0; i < m_nSides; i++) { m_arrPoint[i].x = (long)(dblRadiusx*cos(dblAngle) + ptCenter.x + 0.5); m_arrPoint[i].y = (long)(dblRadiusy*sin(dblAngle) + ptCenter.y + 0.5); dblAngle += dblDiff; } } |
1.7.5 响应鼠标左键按下消息
鼠标右键单击"CPolyCtl",弹出菜单中单击【Add Windows Message Handler...】。
图1.18
选中"WM_LBUTTONDOWN"消息,然后依次单击"Add Handler"和"OK"按钮或直接单击"Add and Edit"按钮。
图1.19
编辑CPolyCtl::OnLButtonDown函数如下:
LRESULT OnLButtonDown(UINT uMsg ,WPARAM wParam, LPARAM lParam, BOOL& bHandled) { HRGN hRgn; WORD xPos = LOWORD(lParam); // horizontal position of cursor WORD yPos = HIWORD(lParam); // vertical position of cursor CalcPoints(m_rcPos); // Create a region from our list of points hRgn = CreatePolygonRgn(&m_arrPoint[0], m_nSides, WINDING); // If the clicked point is in our polygon then fire the ClickIn // event otherwise we fire the ClickOut event if (PtInRegion(hRgn, xPos, yPos)) Fire_ClickIn(xPos, yPos); else Fire_ClickOut(xPos, yPos); // Delete the region that we created DeleteObject(hRgn); return 0; } |
Fire_ClickIn将触发ClickIn事件给客户程序;Fire_ClickOut将触发ClickOut事件给客户程序。
1.7.6 修改DllUnregisterServer
STDAPI DllUnregisterServer(void) { HRESULT hr = _Module.UnregisterServer(); #if _WIN32_WINNT >= 0x0400 if (FAILED(hr)) return hr; // Following assumes that the type library version is 1.0 hr = UnRegisterTypeLib(LIBID_POLYGONLib, 1, 0 , LOCALE_NEUTRAL, SYS_WIN32); #endif return hr; } |
1.7.7 修改Fire_ClickIn、Fire_ClickOut
CProxy_IPolyCtlEvents的函数Fire_ClickIn和Fire_ClickOut中的如下代码有问题:
pvars[1] = x; pvars[0] = y; |
请更改为:
pvars[1].vt = VT_I4; pvars[1].lVal= x; pvars[0].vt = VT_I4; pvars[0].lVal= y; |
1.7.8 保存、恢复属性值
完成上述步骤,即可编译本项目,生成的Polygon.dll将自动注册。VB6.0里也可以使用这个控件了。但是,两个属性里FillColor可以保存,Sides却不能保存。也就是说:VB6.0里增加本控件,修改FillColor和Sides属性,下次再打开此项目时FillColor是上次修改的值,而Sides将恢复成构造函数里的数值3。
为此,需要增加下代码PROP_ENTRY("Sides",1,CLSID_NULL),如下所示:
BEGIN_PROP_MAP(CPolyCtl) PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4) PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4) PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage) PROP_ENTRY("Sides", 1, CLSID_NULL) END_PROP_MAP() |
PROP_ENTRY("Sides",1,CLSID_NULL)中的"Sides"是属性名称。1是该属性在odl文件里的顺序号。CLSID_NULL表示该属性不在任何属性页面内。
1.7.9 属性页
1.7.9.1 增加属性页面类
单击【Insert】【New ATL Object...】
图1.20
选中"Controls"里的"Property Page",单击"Next"按钮
图1.21
页面Names里输入名称
图1.22
页面Attributes采用默认设置
图1.23
页面Strings中的Title是属性页面的名称。
图1.24
上图中,单击"确定"按钮,完成属性页面类的添加。
1.7.9.2 编辑属性页面
鼠标右键单击"CPolyProp"(属性页面类),弹出菜单中单击【Go To Dialog Editor】。
图1.25
显示页面设计界面如下。增加一个文本框(IDC_SIDES)
图1.26
1.7.9.3 响应Apply
单击属性页上的Apply按钮,会调用CPolyProp::Apply函数。在这里,把属性页面上的输入值赋给属性值,代码如下:
STDMETHOD(Apply)(void) { USES_CONVERSION; ATLTRACE(_T("CPolyProp::Apply\n")); for (UINT i = 0; i < m_nObjects; i++) { CComQIPtr<IPolyCtl, &IID_IPolyCtl> pPoly(m_ppUnk[i]); short nSides = (short)GetDlgItemInt(IDC_SIDES); if FAILED(pPoly->put_Sides(nSides)) { CComPtr<IErrorInfo> pError; CComBSTR strError; GetErrorInfo(0, &pError); pError->GetDescription(&strError); MessageBox(OLE2T(strError),_T("Error") ,MB_ICONEXCLAMATION); return E_FAIL; } } m_bDirty = FALSE; return S_OK; } |
1.7.9.4 使Apply按钮可用
默认情况下,Apply按钮是不可用的。在图1.26中,修改Sides属性值后,应该让Apply按钮可用,为此需要响应文本框的消息。
鼠标右键单击"CPolyProp"(属性页面类),弹出菜单中单击【Add Windows Message Handler...】。
图1.27
先选中IDC_SIDES,然后再选中EN_CHANGE消息。最后单击"Add and Edit"按钮。
图1.28
修改CPolyProp::OnChangeSides如下:
LRESULT OnChangeSides(WORD wNotifyCode,WORD wID ,HWND hWndCtl, BOOL& bHandled) { SetDirty(TRUE); return 0; } |
SetDirty(TRUE);说明属性值改变了,按钮Apply就可使用了。
1.7.9.5 增加属性页面
修改PROP_ENTRY("Sides", 1, CLSID_NULL)中的CLSID_NULL为CLSID_PolyProp。
BEGIN_PROP_MAP(CPolyCtl) PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4) PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4) PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage) PROP_ENTRY("Sides", 1, CLSID_PolyProp) END_PROP_MAP() |
1.7.10 实现IObjectSafety接口
在IE浏览器里使用此控件,会提示如下对话框:
图1.29
为了消除上面的对话框,需要实现IObjectSafety接口。其步骤如下:
1、给CPolyCtl增加基类
class ATL_NO_VTABLE CPolyCtl : public CComObjectRootEx<CComSingleThreadModel> ... ... ... ,public IObjectSafetyImpl<CPolyCtl,INTERFACESAFE_FOR_UNTRUSTED_CALLER> { public: CPolyCtl() |
2、BEGIN_COM_MAP(CPolyCtl)与END_COM_MAP()之间增加代码:
BEGIN_COM_MAP(CPolyCtl) ... ... ... COM_INTERFACE_ENTRY(IObjectSafety) END_COM_MAP() |
1.8 注册
ATL3.0 编写的组件在注册时,如果组件所在目录包含中文,则注册后注册表中的路径会有乱码,导致无法正常使用组件。
解决方法一:使用 UNICODE,即定义宏 _UNICODE
解决方法二:
1、编译时预定义宏 _ATL_STATIC_REGISTRY
_ATL_DLL 表示动态链接 ATL.DLL
_ATL_STATIC_REGISTRY 表示注册组件时,不再使用 ATL.DLL。
不能定义 _ATL_DLL,必须定义 _ATL_STATIC_REGISTRY。这样就不会使用 ATL.DLL,也就不会产生路径乱码。
2、修改 ATL\Include\STAREG.H 文件里的 AddChar 和 AddString函数:
BOOL AddChar(const TCHAR* pch) { //if (nPos == nSize) // realloc //fix register bug with chinese path if (nPos == nSize - 1 ) { nSize *= 2; p = (LPTSTR) CoTaskMemRealloc(p, nSize*sizeof(TCHAR)); } p[nPos++] = *pch; #ifndef _UNICODE if(IsDBCSLeadByte(*pch)) { p[nPos++] = *(pch + 1); } #endif return TRUE; } BOOL AddString(LPCOLESTR lpsz) { USES_CONVERSION; LPCTSTR lpszT = OLE2CT(lpsz); while (*lpszT) { AddChar(lpszT); #ifndef _UNICODE //fix bug with chinese path if (IsDBCSLeadByte(*lpszT)) { lpszT++; } #endif lpszT++; } return TRUE; } |
1.9 BUG
使用VC++2005、2008、2010的ATL创建而成的ActiveX控件无法被VB6.0使用。解决方法:使用VC++6.0创建项目,然后使用高版本的VC++编译。
注意:高版本的VC++需要定义_WIN32_WINNT为0x0501
ActiveX控件(ATL篇)的更多相关文章
- ActiveX控件(MFC篇)
目录 第1章 VC++6.0创建控件 1 1.1 目标 1 1.1.1 方法 1 1.1.2 属性 1 1.1.3 事件 1 1.2 创建项目 2 1.3 项目结构 ...
- Ti IPNC Web网页之ActiveX控件
Ti IPNC Web网页之ActiveX控件 本篇介绍关于TI IPNC网页中播放器相关的东西. gStudio工程中添加播放器并控制播放器 打开IPNC网页时首先会自动下载ActiveX控件并安装 ...
- 用ATL写简单的ActiveX控件 .
我正在做的项目需要用读卡器来读数据,由于系统是B/S架构的所以只能把读卡器的驱动封装成一个无界面的ActiveX控件,这样web页面中的js代码才能访问读卡器其实做起来也挺简单的,我用的环境是VS20 ...
- ATL开发 ActiveX控件的 inf文件模板
ATL开发 ActiveX控件的 inf文件模板
- 如何用ATL创建ActiveX控件
演示截图: 代码简介或代码解析: 如何用ATL创建ActiveX控件 实现了一个ActiveX控件,它在一个圆内部有个正多边形,当用户在多变形内部单击将会使多边形的边数在当前的基础上+1,在多变形外部 ...
- 用ATL和MFC来创建ActiveX控件
摘要:目前MFC和ATL代表了两种框架,分别面向不同类型的基于Windows的开发.MFC代表了创建独立的Windows应用的一种简单.一致的方法:ATL提供了一种框架来实现创建COM客户机和服务器所 ...
- ATL控件签名之后页面还提示“在此页面上的Activex控件和本页上的其他部分及交互可能不安全”
ATL控件正常签名打包,然后安装之后还是会提示: 没理由啊,签名是花钱搞得正当的签名.后来查了资料才知道这还不够,需要在创建ATL控件的时候继承一个IObjectSafetyImpl 类 知道了原因, ...
- vs 2005/2008/2010 ATL ActiveX控件显示XP风格
vs 2005/2008/2010 ATL ActiveX控件在IE浏览器中控件显示的是原始的风格,要显示XP风格只要进行以下操作 一.在Stdafx.h中增加下以内容 #if defined _M_ ...
- 利用wtl的CDialogResize自动调整atl ActiveX控件布局
前言 利用atl 开发activex控件时,如果使用atl复合控件时,acitvex控件上的界面元素不会自动改变大小,如果自己在OnSize中处理每个子控件的布局是一件非常麻烦的事,我们可以借助wtl ...
随机推荐
- Intellij IDEA svn的使用记录
这里的忽略一直灰色的,可以进入 这里的版本控制里进行忽略选择 或者 这里进行添加 这里有三个选择 按照顺序 1.忽略指定的文件 2.忽略文件夹下所有文件 3.忽略符合匹配规则的文件 到Commit C ...
- css3 转换transfrom 过渡transition 和两个@
做了一个demo.用到一些css3的动画,还是不太熟练,总结了一下. 1. -webkit-font-smoothing: antialiased; -webkit-font-smoot ...
- 土豪聪要请客(stol)
土豪聪要请客(stol) 众所周知,聪哥(ndsf)是个土豪,不过你们不知道的是他的MZ和他的RMB一样滴多…… 某天土豪聪又赚了10^10000e的RMB,他比较开心,于是准备请客.他在自己在XX星 ...
- ContentProvider官方教程(2)简介、Content URIs
In this document Overview Accessing a provider Content URIs Content Provider Basics A content provid ...
- Duilib创建窗口双击标题栏禁止窗口最大化
使用Duilib创建窗口并禁止窗口最大化 第一步: XXXFrame.Create(NULL, _T("XXXFrame"), UI_WNDSTYLE_EX_FRAME, WS_E ...
- iOS framework
(一),lipo 命令 1)合并文件: lipo -create xxxX/liblibsql.a xxxx/liblibsql.a -output libsql.a 2)查看Framework 对C ...
- C#支持文件拖拽
private void listBox1_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataForm ...
- FreeSWITCH第三方库(音频)的简单介绍(一)
FreeSWITCH使用了大量的第三方库,本文档主要介绍音频相关库的信息: 视频相关库的信息介绍参考:http://www.cnblogs.com/yoyotl/p/5488890.html 其他相关 ...
- mount命令
注:硬件设备由linux系统自动识别,但必须成功挂载后才能使用 mount #查询已挂载 mount -a #依据配置文件/etc/fstab的内容自动挂载 挂载命令格式: mount [-t 文件 ...
- 比Redis更快:Berkeley DB面面观
比Redis更快:Berkeley DB面面观 Redis很火,最近大家用的多.从两年前开始,Memcached转向Redis逐渐成为潮流:而Berkeley DB可能很多朋友还很陌生,首先,我们简单 ...