ActiveX控件(ATL篇)

目录

第1章 VC++6.0创建    2

1.1 目标    2

1.2 创建项目    2

1.3 增加COM类    4

1.4 属性    7

1.5 事件    8

1.6 实现连接点    9

1.7 编码    11

1.7.1 增加成员变量    11

1.7.2 初始化成员变量    11

1.7.3 完成属性赋值代码    11

1.7.4 完成控件绘制代码    11

1.7.5 响应鼠标左键按下消息    13

1.7.6 修改DllUnregisterServer    14

1.7.7 修改Fire_ClickIn、Fire_ClickOut    14

1.7.8 保存、恢复属性值    14

1.7.9 属性页    15

1.7.10 实现IObjectSafety接口    20

1.8 注册    21

1.9 BUG    22

第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

时间: 2024-08-04 18:17:20

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 项目结构    6 1.3.1 COM接口    6 1.3.2 COM类    7 1.3.3 属性页    7 1.3.4 应用程序类    8 1.3.5 注册与注销    8 1.4 方法    9 1.4.1 增加    9 1.4.2 删除    11 1.5 属性    12 1.5.1 Te

编写 ATL ActiveX 控件

一直想写一些ATL.ActiveX的东西,但是一直都没有下定决心去写,一来是自己对这方面的东西不太了解,写不出什么名堂:二来就是懒,懒得思考,懒得动手.这篇文章只是记录一下ATL ActiveX控件的一个大概写法跟使用方法,方便有这方面需求的同学快速上手. 一.ATL ActiveX控件编写 我做的是一个简单的图表控件,提供扇形图跟柱状图两种表现方式. 我们先简单列一下图表的成员属性: 1.图表样式:柱状图还是扇形图. typedef enum _em_chart_type { ctNull,

[activeX]使用ATL开发ActiveX控件[转]

本文描述了使用ATL开发一个ActiveX控件的完整过程. 一.创建项目 单击起始页中的"New Project-",选择"ATL"分类下的"ATL Project"项目,项目名称为"Calculator".在随后出现的项目向导中,使用默认配置即可. 二.添加控件 在解决方案管理器中的项目上右击,依次选择"Add"."Class",在添加类对话框中选择ATL分类下的ATL Control

剖析ActiveX控件安全问题

Dissect ActiveX Control Safety 1.介绍 如果你曾经在网页或者ASP中使用过com对象,你可能会发现,有时候会出现这样讨厌的对话框     这是因为你的控件没有被标记为安全的,对于初始化不安全或者对于脚本不安全,甚至兼而有之.你每打开一次这样的网页,这种情况就会发生一次,你怎么办?当然,这可以通过设置IE本身的安全等级为low来解决这样的问题,但是如果你要制作一个可发布的控件,你能想象到每一位用户在使用你制作的控件时都要且列抱怨这种强制行为:或者如果你是其中一个使用

基于MFC的ActiveX控件开发教程------------浏览器插件之ActiveX开发

一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务数据交互.需插件才能实现的与本地设备的交互等)的情况下可以酌情慎用. 浏览器插件总体可以划分为两大阵营,即IE支持的插件以及非IE支持的插件.本来在Netscape时代,对于浏览器插件是有公用的规范的(NPAPI),一开始所有浏览器都支持该规范,包括IE.后来出于商业原因,微软的IE不再支持NPAPI,改而自己开发了一套基于COM的Ac

C#开发ActiveX控件

昨天写了篇博客<Winform 程序嵌入WPF程序 并发送消息>,没有说明为什么要嵌入WPF程序,那么今天就来唠叨唠叨其中的一个使用场景,开发ActiveX控件 首先,新建一个类库工程HuaYun.ActiveX,右键工程属性,在“应用程序”页,点击“程序集信息”按钮,在弹出的窗体里勾选“使程序集COM可见”,具体操作如下图 第二步,切换到“生成”的选项卡,勾选“为COM互操作注册”,如下图 第三步,在AssemblyInfo.cs里添加[assembly: AllowPartiallyTru

【转载】基于MFC的ActiveX控件开发(1)

原文:http://iysm.net/?p=114 ActiveX 控件是基于组件对象模型 (COM) 的可重用软件组件,广泛应用于桌面及Web应用中.在VC下ActiveX控件的开发可以分为三种,一种是直接用COM的API来开发,这样做显然非常的麻烦,对程序员要求也非常高,因此一般是不予考虑的:一种是基于传统的MFC,采用面向对象的方式将COM的基本功能封装在若干MFC的C++类中,开发者通过继承这些类得到COM支持功能.MFC为广大VC程序员所熟悉,易于上手学习,但缺点是MFC封装的东西比较

.NET : 开发ActiveX控件(转载)

我估计有些朋友不清楚ActiveX控件,但这篇博客不是来解释这些概念的.如果你对ActiveX的概念不清楚,请参考这里: http://baike.baidu.com/view/28141.htm 这篇博客也不是讨论ActiveX是否有必要用的,因为这历来也是有些争议的.anyway, 我们这里只是探讨一下如何在.NET中也编写ActiveX控件,我用一个实例记录一下该过程 1. 创建一个类库项目,设置 Comvisible为true,设置AllowPartiallyTrustedCallers

ActiveX控件

什么是ActiveX控件:一个进程内服务器,支持多种的COM接口.(可以理解为,一个COM接口是一个纯抽象基类,你实现了它,并且它支持自注册,就是一个ActiveX控件了)可以把ActiveX控件看做是一个极小的服务器应用程序,它不能独立运行,必须嵌入到某个容器程序中,与该容器一起运行. 容器应用程序是可以嵌入或链接对象的应用程序. 服务器应用程序是创建对象并且当对象被双击时,可以被启动的应用程序. 我们常用的word就是一个容器应用程序,例如,若在word文档中可以嵌入或链接一个Excel表格