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 Text属性    14

1.5.2 Font属性    15

1.5.3 ColorText属性    16

1.5.4 LDownCount属性    17

1.6 事件    18

1.6.1 增加    18

1.6.2 删除    20

1.7 编码    20

1.7.1 COcxMFCCtrl添加三个成员变量    20

1.7.2 初始化成员变量    21

1.7.3 实现Show方法    21

1.7.4 实现属性存取    21

1.7.5 绘制控件    22

1.7.6 实现LDown事件    23

1.7.7 保存、恢复属性值    24

1.7.8 属性页    26

1.8 类型库    31

1.8.1 VC++6.0的一个BUG    32

1.9 控件注册与注销    32

1.9.1 命令    32

1.9.2 注册表    33

1.10 ProgID    37

1.10.1 处理重复    37

1.11 控件位图    38

1.12 AfxOleRegisterControlClass    39

2 VC++2010创建控件    41

2.1 创建项目    41

2.2 项目结构    44

2.3 增加方法    44

2.4 增加属性    47

2.5 增加事件    48

2.6 删除方法、属性、事件    50

3 VC++6.0使用控件    51

3.1 增加控件的操作    51

3.2 增加控件的实质    53

3.3 控件名称重复    54

4 VC++2010使用控件    56

4.1 增加控件    56

4.1.1 增加到对话框    56

4.1.2 增加到Toolbox    57

4.1.3 Toolbox里删除控件    58

4.2 生成包装类    59

4.2.1 直接生成    59

4.2.2 间接生成    60

4.2.3 包装类BUG    62

5 VB6.0使用控件    63

6 Office使用控件    64

 

 

1 VC++6.0创建控件

1.1 目标

本章的目标是使用MFC创建一个下图所示的ActiveX控件。

ActiveX控件(MFC篇)_第1张图片

图1.1 ActiveX控件

1.1.1 方法

将给此控件增加一个方法:

void Show(BOOL bShow);

bShowTRUE 时,显示控件;

bShowFALSE 时,隐藏控件。

1.1.2 属性

属性名

Text

控件显示的文本

Font

控件显示文本的字体

ColorText

控件显示文本的颜色

ColorInner

椭圆内的颜色

ColorOuter

椭圆外的颜色

LDownCount

控件内,鼠标左键按下的次数

1.1.3 事件

鼠标左键在控件内部按下时,会产生LDown事件:

void LDown(BOOL bInner)

bInnerTRUE 时,表示鼠标在椭圆内;

bInnerFALSE 时,表示鼠标在椭圆外。

1.2 创建项目

运行VC++6.0,新建"MFC ActiveX ControlWizard"项目,如下图所示。配置好项目名称、项目目录后,单击"OK"按钮。

ActiveX控件(MFC篇)_第2张图片

图1.2 新建ActiveX项目

VC++6.0显示创建ActiveX控件的向导,界面如下图所示。

"How many controls would you like your project to have"用来设置本项目里有几个ActiveX控件。这里取默认值1

"runtime license"表示运行时是否需要授权,这里取默认值"No runtime license",即运行时无需授权。

"Would you like source file comments to be generated?"表示代码里是否生成形如"//TODO"这样的注释。这里取默认值"Yes,please"。

"Would you like help files to be generated?"表示是否生成帮助文件,这里取默认值"NO help files"。

配置完成后,单击"Next"按钮。

ActiveX控件(MFC篇)_第3张图片

图1.3 创建ActiveX控件——第一步

VC++6.0显示向导的第二步,界面如下图所示。

OcxMFC这个下拉列表框内选择某个控件(上图有几个控件数,这里就有几个选项)。单击"Edit Names..."按钮,将显示图1.5所示的界面。用于编辑选中控件的名称。

"Activates when visible"表示控件显示时,将把焦点移到该控件上。

"Invisible at runtime"表示运行时,该控件不可见。

"Available in "Insert Object" dialog"如果希望WordExcel……这些ActiveX容器能够插入该控件,请勾中此项。

"Has an "About" box"勾中此复选框,向导会给该控件增加AboutBox方法,客户端程序调用此方法时,将显示本控件的关于对话框。

"Acts as a simple frame control"设置控件为框架控件,亦即其它控件可以放在该控件内部。框架控件移动时,它内部的控件跟着一起移动。

"Which windows class,if any,should this control subclass"用于选择一个窗口类,控件将子类化这个窗口类。

单击"Advanced..."按钮,将显示图1.6所示的界面,用于配置高级选项。

这里不做任何改动,直接单击"Finish"按钮。

ActiveX控件(MFC篇)_第4张图片

图1.4 创建ActiveX控件——第二步

ActiveX控件(MFC篇)_第5张图片

图1.5 创建ActiveX控件——编辑名称

ActiveX控件(MFC篇)_第6张图片

图1.6 创建ActiveX控件——高级选项

VC++6.0显示如下图所示。单击"OK"按钮,完成项目的创建。

ActiveX控件(MFC篇)_第7张图片

图1.7 创建ActiveX控件确认界面

1.3 项目结构

下图是创建的项目结构

ActiveX控件(MFC篇)_第8张图片

图1.8 项目结构

1.3.1 COM接口

_DOcxMFC_DOcxMFCEvents这两个COM接口分别对应于ocxMFC.odl文件中的dispinterface _DOcxMFCdispinterface _DOcxMFCEvents。前者用于客户端程序访问ActiveX控件;后者用于将控件内部的事件通知给客户端。

1.3.2 COM

COcxMFCCtrl是实现控件功能最重要的一个类,其基类依次为COleControlCWndCCmdTarget。它对应的是ocxMFC.odl文件中的coclass OcxMFC,其定义如下:

[ uuid(091AEC6D-3B16-4F1A-95FA-94E8C48E3366),

helpstring("OcxMFC Control"), control ]

coclass OcxMFC

{

[default] dispinterface _DOcxMFC;

[default, source] dispinterface _DOcxMFCEvents;

};

也就是说COMOcxMFC实现了COM接口_DOcxMFC_DOcxMFCEvents

客户端创建控件,首先要创建COMOcxMFC,其实就是实例化一个COcxMFCCtrl对象。获得COM接口_DOcxMFC之后,就可以访问控件了。控件产生的事件可以通过COM接口_DOcxMFCEvents传递给客户端。

1.3.3 属性页

下图是在Word文档中插入了一个日历控件,要想修改它的属性,如:显示的字体。可以鼠标右键单击控件,然后单击【日历对象】【属性】菜单项

ActiveX控件(MFC篇)_第9张图片

图1.9 Word中修改ActiveX控件属性

Word将显示日历控件的属性页。可在此修改控件的属性。

ActiveX控件(MFC篇)_第10张图片

图1.10 日历控件属性页

在图1.8中,COcxMFCPropPage可以实现属性页的一个页面。它的基类依次为COlePropertyPageCDialogCWnd

1.3.4 应用程序类

在图1.8中,应用程序类COcxMFCApp的基类依次为:COleControlModuleCWinApp。它提供了初始化控件模块的功能。

1.3.5 注册与注销

在图1.8中,全局函数 DllRegisterServer DllUnregisterServer 分别用于控件的注册和注销。

当执行 regsvr32 ocxMFC.ocx 时将调用DllRegisterServer函数,往注册表里写入注册信息;当执行 regsvr32 /u ocxMFC.ocx 时将调用DllUnregisterServer函数,清除注册表里ocxMFC.ocx的注册信息。

这两个函数里,第一行代码均为:

AFX_MANAGE_STATE(_afxModuleAddrThis);

这说明:MFC创建的ActiveX控件,本质上是一个MFC Regular DLL

1.4 方法

创建的项目中,已经有了一个方法AboutBox,调用此方法将显示该控件的关于对话框。其实现代码如下:

void COcxMFCCtrl::AboutBox()

{

CDialog dlgAbout(IDD_ABOUTBOX_OCXMFC);

dlgAbout.DoModal();

}

代码很简单,不过这里有意思的是:第一行代码不再是AFX_MANAGE_STATE(...);这说明:在调用此方法之前,MFC已经自动切换了模块状态。

1.4.1 增加

增加方法有两种途径:

1、鼠标右键单击"_DOcxMFC",然后鼠标左键单击弹出菜单中的【Add Method...】将显示图1.13所示的界面。注意:如果clw文件不存在,此方法会失败。请按下Ctrl+W创建clw文件之后再试一次。

ActiveX控件(MFC篇)_第11张图片

图1.11

2、按下快捷键Ctrl+W,将显示类向导对话框。进入"Automation"页面,Class name请选择"COcxMFCCtrl",然后单击"Add Method..."按钮。也将显示图1.13所示的界面。

ActiveX控件(MFC篇)_第12张图片

图1.12

下图就是"增加方法"的对话框:

External name        是客户端调用此方法时用到的名称;

Internal name        是本项目内部,此方法的名称;

Return type            是方法的返回值,这里选择void

Parameter list        表示参数列表。这里只有一个参数 bShow

                    TRUE表示显示控件,FALSE表示隐藏控件。

方法有两种类型:Stock(库存)、Custom(自定义)。库存方法由COcxMFCCtrl的基类COleControl实现;自定义方法由COcxMFCCtrl编码实现。这里,Show是一个自定义方法。

ActiveX控件(MFC篇)_第13张图片

图1.13 增加方法

1.4.2 删除

按下快捷键Ctrl+W,将显示类向导对话框。进入"Automation"页面。Class name请选择"COcxMFCCtrl"。然后在External names里选择要删除的方法或属性,单击"Delete"按钮即可删除选中项。最后请单击"OK"按钮。

下图中,External names列表中的"M"表示Method(方法);"C"表示Custom属性(自定义属性)、"S"表示Stock属性(库存属性)。

ActiveX控件(MFC篇)_第14张图片

图1.14 删除方法

1.5 属性

删除属性与删除方法的操作相同,这里只说明如何增加属性。

增加属性有两种方法:

1、鼠标右键单击"_DOcxMFC",然后鼠标左键单击弹出菜单中的【Add Property...】将显示图1.17所示的界面。注意:此方法要求clw文件已存在。

ActiveX控件(MFC篇)_第15张图片

图1.15

2、按下快捷键Ctrl+W,将显示类向导对话框。进入"Automation"页面,Class name请选择"COcxMFCCtrl",然后单击"Add Property..."按钮。也将显示图1.17所示的界面。

ActiveX控件(MFC篇)_第16张图片

图1.16

下图就是"增加属性"的对话框。External name 是客户端调用此属性时用到的名称。

ActiveX控件(MFC篇)_第17张图片

图1.17 增加属性

1.5.1 Text属性

下图是增加Text属性的界面。

注意:"Stock"单选框是可用的,说明COcxMFCCtrl的基类COleControl已经提供了Text属性。

选择"Stock"将表示Text是一个库存属性。它的存取完全由COleControl负责。

也可以选择"Get/Set methods",它将用COcxMFCCtrl::SetTextCOcxMFCCtrl::GetText这两个函数,对Text属性进行存、取。也就是说,此时Text属性改由COcxMFCCtrl负责存取。

这里选择"Stock",单击"OK"按钮,完成Text属性的增加。这个Text属性就是一个库存属性。

ActiveX控件(MFC篇)_第18张图片

图1.18 增加Text属性

1.5.2 Font属性

下图是增加Font属性的界面,它跟Text属性一样,也是库存属性。

ActiveX控件(MFC篇)_第19张图片

图1.19 增加Font属性

1.5.3 ColorText属性

下图是增加ColorText属性的界面。关于它有几点需要说明:

1、"Stock"单选框不可用,意味着它不再是库存属性;

2、因为ColorText改变后,需要立即更新控件的显示,因此在这里选择了"Get/Set methods"。在COcxMFCCtrl::SetColorText函数里,完成ColorText属性的改变及控件显示的更新。

ColorInnerColorOuter属性与ColorText属性的增加操作完全相同。

注意:下图虽然可以添加参数,但是添加参数后,属性将更改为方法。

ActiveX控件(MFC篇)_第20张图片

图1.20 增加ColorText属性

1.5.4 LDownCount属性

下图是增加LDownCount属性的界面。改变这个属性,不用更新控件显示,因此没有必要再使用"Get/Set methods",可以使用"Member variable"。

注意:Notification function,它的默认名称为On*Changed,这里就是OnLDownCountChanged。客户端修改LDownCount属性时,将调用COcxMFCCtrl::OnLDownCountChanged函数。下图的Notification function为空,表示修改LDownCount属性时不会调用任何函数。

ActiveX控件(MFC篇)_第21张图片

图1.21 增加LDownCount属性

1.6 事件

1.6.1 增加

增加事件有两种方法:

1、鼠标右键单击"_DOcxMFCEvents",然后鼠标左键单击弹出菜单中的【Add Event...】将显示图1.24所示的界面。注意:此方法要求clw文件已存在。

ActiveX控件(MFC篇)_第22张图片

1.22

2、按下快捷键Ctrl+W,将显示类向导对话框。进入"Automation"页面,Class name请选择"COcxMFCCtrl",然后单击"Add Event..."按钮。也将显示图1.24所示的界面。

ActiveX控件(MFC篇)_第23张图片

图1.23 ActiveX事件页面

下图就是"增加事件"的对话框:

ActiveX控件(MFC篇)_第24张图片

图1.24

External name        是客户端响应此事件时用到的名称。

Internal name        是本项目内部激发此事件时用到的函数名。

1.6.2 删除

在图1.23中,"External name"列表里选中要删除的事件,然后单击"Delete"按钮,最后单击"OK"按钮。

1.7 编码

1.7.1 COcxMFCCtrl添加三个成员变量

代码如下:

protected:

OLE_COLOR m_clrText;

OLE_COLOR m_clrInner;

OLE_COLOR m_clrOuter;

1.7.2 初始化成员变量

具体就是初始化成员变量:m_lDownCountm_clrTextm_clrInnerm_clrOuter

COcxMFCCtrl::COcxMFCCtrl()

{

InitializeIIDs(&IID_DOcxMFC, &IID_DOcxMFCEvents);

// TODO: Initialize your control's instance data here.

m_lDownCount = 0;

m_clrText = RGB(0,0,0);

m_clrInner = RGB(255,255,255);

m_clrOuter = RGB(0,255,0);

}

1.7.3 实现Show方法

void COcxMFCCtrl::Show(BOOL bShow)

{

ShowWindow(bShow ? SW_SHOW : SW_HIDE);

}

1.7.4 实现属性存取

OLE_COLOR COcxMFCCtrl::GetColorText()

{

return m_clrText;

}

 

void COcxMFCCtrl::SetColorText(OLE_COLOR nNewValue)

{

if(m_clrText != nNewValue)

{

m_clrText = nNewValue;

SetModifiedFlag();

InvalidateControl();

}

}

 

OLE_COLOR COcxMFCCtrl::GetColorInner()

{

return m_clrInner;

}

 

void COcxMFCCtrl::SetColorInner(OLE_COLOR nNewValue)

{

if(m_clrInner != nNewValue)

{

m_clrInner = nNewValue;

SetModifiedFlag();

InvalidateControl();

}

}

 

OLE_COLOR COcxMFCCtrl::GetColorOuter()

{

return m_clrOuter;

}

 

void COcxMFCCtrl::SetColorOuter(OLE_COLOR nNewValue)

{

if(m_clrOuter != nNewValue)

{

m_clrOuter = nNewValue;

SetModifiedFlag();

InvalidateControl();

}

}

需要说明的是:

1SetModifiedFlag说明控件的某些属性值发生了变化。ActiveX容器(如:VB6.0)在知道某个ActiveX控件的属性改变后,退出程序前会自动提示用户保存;

2InvalidateControl将使控件的显示区域无效,会导致控件的重新绘制。

1.7.5 绘制控件

绘制控件的代码应该在COcxMFCCtrl::OnDraw里完成,具体代码如下:

void COcxMFCCtrl::OnDraw(CDC* pdc, const CRect& rcBounds

,const CRect& rcInvalid)

{

pdc->FillSolidRect(rcBounds,TranslateColor(m_clrOuter));

CBrush brhInner(TranslateColor(m_clrInner));

HGDIOBJ brhOld = pdc->SelectObject(brhInner.m_hObject);

pdc->Ellipse(rcBounds);

pdc->SelectObject(brhOld);

CString s = InternalGetText();

pdc->SetBkMode(TRANSPARENT);

pdc->SetTextColor(TranslateColor(m_clrText));

RECT rc = rcBounds;

CFont* pOldFont = SelectStockFont(pdc);

pdc->DrawText(s,&rc,DT_SINGLELINE | DT_VCENTER | DT_CENTER);

pdc->SelectObject(pOldFont);

}

说明:

1TranslateColorCOleControl的成员函数,用于把OLE_COLOR转换为COLORREFOLE_COLORCOLORREF一样,都是32位整数。COLORREF其实只用到了24位,高8位始终为零。OLE_COLOR8位等于零时,它就是COLORREF,否则它表示特殊的颜色,如:0x80000001表示桌面颜色。这个时候,就需要COleControl::TranslateColorOLE_COLOR转换为COLORREF

2InternalGetTextCOleControl的成员函数,用来获得Text属性值。其返回值为CString。此外,COleControl::SetTextCOleControl::GetText也可用于存取Text属性值,不过GetText返回的是BSTR,用完之后必须SysFreeString,比InternalGetText要繁琐些;

3SelectStockFont(pdc) COleControl的成员函数,它表示pdc选用Font属性所指定的字体。

1.7.6 实现LDown事件

按下快捷键Ctrl+W,将显示类向导对话框。给COcxMFCCtrl类增加消息WM_LBUTTONDOWN的处理函数,如图1.25所示。

响应该消息的代码如下:

void COcxMFCCtrl::OnLButtonDown(UINT nFlags, CPoint point)

{

++m_lDownCount; //计数值加一

RECT rc;

GetClientRect(&rc);

double a = rc.right / 2.0;

double b = rc.bottom / 2.0;

a = (point.x - a) / a;

b = (point.y - b) / b;

FireLDown(a * a + b * b < 1.0);

COleControl::OnLButtonDown(nFlags, point);

}

注意:

1FireLDown将触发LDown事件。客户端处理完该事件后,它才返回;

2COleControl::OnLButtonDownSetCapture(hWndOcx)hWndOcx是控件窗口句柄。因此,客户端处理LDown事件时,最好不要弹出对话框。

ActiveX控件(MFC篇)_第25张图片

图1.25

1.7.7 保存、恢复属性值

完成上述步骤,即可编译本项目,生成的ocxMFC.ocx将自动注册。VB6.0里也可以使用这个控件了,如下图所示:

ActiveX控件(MFC篇)_第26张图片

图1.26 VB6.0里使用控件

控件一共有六个属性:TextFontColorTextColorInnerColorOuterLDownCount。除了TextFont外,其它属性的值都不会被保留。也就是说:使用VB6.0修改了ColorTextColorInnerColorOuterLDownCount的值,下次再打开项目时,这四个属性值又恢复成构造函数COcxMFCCtrl::COcxMFCCtrl里的值。

使用记事本打开VB6.0的窗体文件Form.frm,可以发现:它只保存了TextFont属性的值,其它四个属性值并未保存。

Begin OCXMFCLib.OcxMFC OcxMFC1

    Height            =    1695

    Left                =    1920

    TabIndex        =    3

    Top                =    960

    Width            =    2055

    _Version        =    65536

    _ExtentX        =    3625

    _ExtentY        =    2990

    _StockProps        =    20

    Text            =    "777"

    BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}

        Name            =    "新宋体"

        Size                =    15.75

        Charset            =    0

        Weight            =    400

        Underline        =    0 'False

        Italic            =    0 'False

        Strikethrough    =    0 'False

    EndProperty

End

为此,需要修改COcxMFCCtrl::DoPropExchange函数,具体代码如下:

void COcxMFCCtrl::DoPropExchange(CPropExchange* pPX)

{

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX);

// TODO: Call PX_ functions for each persistent custom property.

PX_Long (pPX,_T("LDownCount"),m_lDownCount,0);

PX_Color(pPX,_T("ColorText") ,m_clrText,RGB(0,0,0));

PX_Color(pPX,_T("ColorInner"),m_clrInner,RGB(255,255,255) );

PX_Color(pPX,_T("ColorOuter"),m_clrOuter,RGB(0,255,0));

}

增加了PX_LongPX_Color这四行代码后,ColorTextColorInnerColorOuterLDownCount的值也能够被VB6.0ActiveX容器)保存起来,以便再次载入控件时恢复属性值。

注意:

1、对于库存属性,不再需要增加PX_???代码。COleControl::DoPropExchange负责完成此项工作。

2PX_???的具体含义请查阅MSDN

1.7.8 属性页

在图1.10中,可以看到日历控件的属性页。通过属性页,可以方便的编辑控件的属性。

1.7.8.1 编辑TextLDownCount属性

鼠标右键单击COcxMFCPropPage,在弹出菜单中,单击【Go To Dialog Editor】菜单项。

ActiveX控件(MFC篇)_第27张图片

图1.27

编辑IDD_PROPPAGE_OCX_MFC界面如下:

ActiveX控件(MFC篇)_第28张图片

图1.28

按下Ctrl+W,启动类向导,为两个文本框IDC_TEXTIDC_COUNT增加变量。下图为IDC_TEXT增加了变量CString m_sText,且这个变量与Text属性绑定在一起。所谓绑定就是m_sText的值与Text属性值保持同步,一个值改变了另一个的值也跟着改变。为文本框IDC_COUNT增加变量、绑定属性值与IDC_TEXT的类似。

ActiveX控件(MFC篇)_第29张图片

为文本框分配变量,并绑定属性值,其实就是修改了函数COcxMFCPropPage::DoDataExchange。代码如下:

void COcxMFCPropPage::DoDataExchange(CDataExchange* pDX)

{

//{{AFX_DATA_MAP(COcxMFCPropPage)

DDP_Text(pDX, IDC_TEXT, m_sText, _T("Text") );

DDX_Text(pDX, IDC_TEXT, m_sText);

DDP_Text(pDX, IDC_COUNT, m_Count, _T("LDownCount") );

DDX_Text(pDX, IDC_COUNT, m_Count);

//}}AFX_DATA_MAP

DDP_PostProcessing(pDX);

}

1.7.8.2 增加库存页面

在图1.10中,页面"字体"、"颜色"是系统已预制好的页面。可以把这种库存页面增加到控件的属性页。方法就是修改如下代码:

BEGIN_PROPPAGEIDS(COcxMFCCtrl, 3)

PROPPAGEID(COcxMFCPropPage::guid)

PROPPAGEID(CLSID_CFontPropPage)         //增加"字体"库存页面

PROPPAGEID(CLSID_CColorPropPage)     //增加"颜色"库存页面

END_PROPPAGEIDS(COcxMFCCtrl)

注意上面的数字3,表示属性页将有3个页面。

1.7.8.3 运行效果

VB6.0中加载编译好的控件。在设计状态下,鼠标右键单击控件,弹出菜单中单击【Properties...】菜单项。

ActiveX控件(MFC篇)_第30张图片

图1.29

显示如下面三张图片:

ActiveX控件(MFC篇)_第31张图片

图1.30 VB6.0查看控件属性页——页面一

ActiveX控件(MFC篇)_第32张图片

图1.31 VB6.0查看控件属性页——页面二

ActiveX控件(MFC篇)_第33张图片

图1.32 VB6.0查看控件属性页——页面三

说明:

1、属性页显示过程:单击图1.29中的【Properties...】菜单项,会给控件发送消息。消息映射表如下:

BEGIN_MESSAGE_MAP(COcxMFCCtrl, COleControl)

... ... ...

ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)

END_MESSAGE_MAP()

这样就会调用OnProperties函数。在COleControl的声明中,OnProperties是一个虚函数。COcxMFCCtrl没有覆盖该函数,因此会调用COleControl::OnProperties函数。COleControl::OnProperties会根据如下代码显示一个有3个页面的属性页。

BEGIN_PROPPAGEIDS(COcxMFCCtrl, 3)

PROPPAGEID(COcxMFCPropPage::guid)

PROPPAGEID(CLSID_CFontPropPage)         //增加"字体"库存页面

PROPPAGEID(CLSID_CColorPropPage)     //增加"颜色"库存页面

END_PROPPAGEIDS(COcxMFCCtrl)

2、页面一可用来修改TextLDownCount属性。进入该页面,将调用COcxMFCPropPage::DoDataExchange函数,把属性值传给绑定的变量,再更新到页面。离开该页面或按下"确定"、"应用"按钮,也会调用COcxMFCPropPage::DoDataExchange函数,把界面的输入值传给绑定变量,再传递给属性值;

3、页面一中修改Text属性值,退出VB6.0会提示是否保存;而修改LDownCount属性值,退出VB6.0时可能不会提示保存。因为修改Text属性值会调用SetModifiedFlag函数,而修改LDownCount属性值时不会调用SetModifiedFlag函数。如果只是修改LDownCountVB6.0会认为用户对控件未做任何修改,也就不会提示用户保存;

4、页面二允许修改所有为字体类型的属性,这里就是Font属性;

5、页面三允许修改所有OLE_COLOR类型的属性,这里就是ColorTextColorInnerColorOuter三个属性。

1.8 类型库

作为C/C++程序员,要使用其他程序员编写的动态库或静态库,需要头文件。通过头文件,才能知道动态库或静态库里有什么函数,函数的参数、返回值情况……同样的,客户端程序要使用COM组件,也需要类似头文件功能的东西,这就是类型库。

可以编写文本格式的odlidl文件,然后使用midl.exe将其编译为二进制的tlb文件——这就是类型库文件。

对于ActiveX控件而言,编译完成后,类型库就已经被嵌入ocx文件。如下图所示:

ActiveX控件(MFC篇)_第34张图片

图1.33

这个类型库,是如何嵌入ocx文件的呢?使用记事本打开rc文件。可以看到如下代码。其实就是嵌入了ocxMFC.tlb文件。

图1.34

VC++6.0编译时,首先调用midl.exe编译ocxMFC.odl,生成ocxMFC.tlb。然后调用rc.exerc文件编译成res文件。最后通过link.exeres文件嵌入ocx文件。这里需要注意rc.exe,它编译时需要把ocxMFC.tlb嵌入到res文件里,那么它是如何查找ocxMFC.tlb的呢?它会首先在dsp文件所在目录进行查找,然后才是到编译目录(如:DebugRelease)去查找。所以,一定不能把ocxMFC.tlbdsp文件放在同一目录。因为编译生成的最新的ocxMFC.tlb在编译目录下,它不会被及时嵌入到ocx文件里。

1.8.1 VC++6.0的一个BUG

编辑odl文件时,该文件里的字符串尽量不要使用汉字。否则这些字符串随时可能会丢失、变成乱码。

1.9 控件注册与注销

1.9.1 命令

注册控件可使用如下任意一条命令。它们原理相同:都是载入ocxMFC.ocx,然后调用DllRegisterServer函数

regsvr32 ocxMFC.ocx

Rundll32.exe ocxMFC.ocx,DllRegisterServer

注销控件可使用如下任意一条命令。它们原理相同:都是载入ocxMFC.ocx,然后调用DllUnregisterServer函数

regsvr32 /u ocxMFC.ocx

Rundll32.exe ocxMFC.ocx,DllUnregisterServer

注意:

1regsvr32Rundll32.exe将自动判断ocxMFC.ocx32位的还是64位的;

2、在64Windows上,32位控件不能放到C:\Windows\System32目录进行注册,而应该放到C:\Windows\SysWOW64目录进行注册。C:\Windows\System32是用来存放64位控件的。

1.9.2 注册表

注册的实质其实是修改注册表,以ocxMFC.ocx为例,注册时写入的信息有六处:

1、类型库

注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}\1.0]

@="ocxMFC ActiveX Control module"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}\1.0\0\win32]

@="G:\\VC\\ocxMFC\\Release\\ocxMFC.ocx"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}\1.0\FLAGS]

@="2"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}\1.0\HELPDIR]

@="G:\\VC\\ocxMFC\\Release"

odl文件

[ uuid(DAD6A33F-6756-43C6-BCE3-E48E361127CE), version(1.0),

helpfile("ocxMFC.hlp"),

helpstring("ocxMFC ActiveX Control module"),

control ]

library OCXMFCLib

代码

const GUID CDECL BASED_CODE _tlid =

{ 0xdad6a33f, 0x6756, 0x43c6, { 0xbc, 0xe3, 0xe4, 0x8e, 0x36, 0x11, 0x27, 0xce } };

2COM接口_DOcxMFCEvents

注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{FEF5A10F-950D-4F29-B2BF-AB449A21BD65}]

@="_DOcxMFCEvents"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{FEF5A10F-950D-4F29-B2BF-AB449A21BD65}\ProxyStubClsid32]

@="{00020420-0000-0000-C000-000000000046}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{FEF5A10F-950D-4F29-B2BF-AB449A21BD65}\TypeLib]

@="{DAD6A33F-6756-43C6-BCE3-E48E361127CE}"

"Version"="1.0"

odl文件

[ uuid(FEF5A10F-950D-4F29-B2BF-AB449A21BD65),

helpstring("Event interface for OcxMFC Control") ]

dispinterface _DOcxMFCEvents

代码

const IID BASED_CODE IID_DOcxMFCEvents =

{ 0xfef5a10f, 0x950d, 0x4f29, { 0xb2, 0xbf, 0xab, 0x44, 0x9a, 0x21, 0xbd, 0x65 } };

3COM接口_DOcxMFC

注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{EC3B9652-4EAD-4FBD-AB11-5E2095423BAA}]

@="_DOcxMFC"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{EC3B9652-4EAD-4FBD-AB11-5E2095423BAA}\ProxyStubClsid32]

@="{00020420-0000-0000-C000-000000000046}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{EC3B9652-4EAD-4FBD-AB11-5E2095423BAA}\TypeLib]

@="{DAD6A33F-6756-43C6-BCE3-E48E361127CE}"

"Version"="1.0"

odl文件

[ uuid(EC3B9652-4EAD-4FBD-AB11-5E2095423BAA),

helpstring("Dispatch interface for OcxMFC Control"), hidden ]

dispinterface _DOcxMFC

代码

const IID BASED_CODE IID_DOcxMFC =

{ 0xec3b9652, 0x4ead, 0x4fbd, { 0xab, 0x11, 0x5e, 0x20, 0x95, 0x42, 0x3b, 0xaa } };

4COM

注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}]

@="OcxMFC Control"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\Control]

@=""

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\InprocServer32]

@="G:\\VC\\ocxMFC\\Release\\ocxMFC.ocx"

"ThreadingModel"="Apartment"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\MiscStatus]

@="0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\MiscStatus\1]

@="131473"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\ProgID]

@="OCXMFC.OcxMFCCtrl.1"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\ToolboxBitmap32]

@="G:\\VC\\ocxMFC\\Release\\ocxMFC.ocx, 1"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\TypeLib]

@="{DAD6A33F-6756-43C6-BCE3-E48E361127CE}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\Version]

@="1.0"

odl文件

[ uuid(091AEC6D-3B16-4F1A-95FA-94E8C48E3366),

helpstring("OcxMFC Control"), control ]

coclass OcxMFC

代码

IMPLEMENT_OLECREATE_EX(COcxMFCCtrl

,"OCXMFC.OcxMFCCtrl.1"

,0x91aec6d, 0x3b16, 0x4f1a, 0x95, 0xfa, 0x94, 0xe8, 0xc4, 0x8e, 0x33, 0x66)

5、属性页面类

注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{3AE04D9E-7833-42C3-8A28-566DE94AA395}]

@="OcxMFC Property Page"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{3AE04D9E-7833-42C3-8A28-566DE94AA395}\InprocServer32]

@="G:\\VC\\ocxMFC\\Release\\ocxMFC.ocx"

odl文件

代码

IMPLEMENT_OLECREATE_EX(COcxMFCPropPage

,"OCXMFC.OcxMFCPropPage.1"

,0x3ae04d9e, 0x7833, 0x42c3, 0x8a, 0x28, 0x56, 0x6d, 0xe9, 0x4a, 0xa3, 0x95)

6ProgID——OCXMFC.OcxMFCCtrl.1

注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\OCXMFC.OcxMFCCtrl.1]

@="OcxMFC Control"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\OCXMFC.OcxMFCCtrl.1\CLSID]

@="{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}"

odl文件

代码

IMPLEMENT_OLECREATE_EX(COcxMFCCtrl

,"OCXMFC.OcxMFCCtrl.1"

,0x91aec6d, 0x3b16, 0x4f1a, 0x95, 0xfa, 0x94, 0xe8, 0xc4, 0x8e, 0x33, 0x66)

注意:

1HKEY_LOCAL_MACHINE的某些内容会映射到HKEY_CLASSES_ROOT。如:HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}会被映射到HKEY_CLASSES_ROOT\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}。删除前者,后者也跟着消失。

1.9.2.1 64Windows

使用VC++2010,可以开发ocxMFC32位、64位两种版本。这两种版本的ocx注册后,能正常被32位、64位程序调用。显然,在64Windows下,ActiveX控件的注册是有些特殊的。

对于HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\???这样的注册表项,在64Windows下有三项。但事实上,它们只是一项的三个名字,删除任何一个,另外两个也就消失了。

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\???

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\TypeLib\???

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\TypeLib\???

对于HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\???HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\???这样的注册表项,在64Windows下也有三项。第一项只有64位程序能够访问;第二、三项其实是同一项的两个名字,它们可以被32位或64位程序访问。

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSIDInterface\???

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSIDInterface\???

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSIDInterface\???

更加明确的说明:

164位程序可直接访问HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\???32位程序访问HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\???时会被映射至HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\TypeLib\???HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\TypeLib\???。但是这三项其实是一回事,所以类型库的注册对于32位、64位程序是没有分别的;

264位程序可直接访问HKLM\SOFTWARE\Classes\CLSID\???32位程序访问HKLM\SOFTWARE\Classes\CLSID\???时会被映射至HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\???HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\???。也就是说:COM接口、COM类的注册对于32位、64位程序是有区别的。换句话说就是32位、64ActiveX控件的COM接口、COM类注册信息会写入不同的注册表项;

3、对于HKEY_LOCAL_MACHINE\SOFTWARE\Classes\OCXMFC.OcxMFCCtrl.1的访问,32位程序和64位程序没有区别。也就是说ProgID的注册,32位、64位程序也是没有分别的。

1.10 ProgID

创建ActiveX控件时,图1.5中的OCXMFC.OcxMFCCtrl.1就是控件的ProgID

下面的VB6.0代码,利用ProgID创建ActiveX控件实例。

Dim obj as Object

set obj = CreateObject("OCXMFC.OcxMFCCtrl.1")

其执行步骤:

1、根据ProgID获取COM组件的COMID

函数CLSIDFromProgID 可完成此项工作,其实质是读取注册表

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\OCXMFC.OcxMFCCtrl.1\CLSID]

@="{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}"

2、根据CLSID,加载COM组件,然后返回COM接口指针

函数CoCreateInstance可完成此项工作,它也会读取注册表:

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\InprocServer32]

@="G:\\VC\\ocxMFC\\Release\\ocxMFC.ocx"

…… …… ……

1.10.1 处理重复

使用VC++创建ActiveX控件时,有可能会发生ProgID重复的问题。图1.4中单击"Finish"按钮后,如果ProgID在注册表中已经存在,会弹出如下对话框:

ActiveX控件(MFC篇)_第35张图片

图1.35

单击"是"按钮,新建控件的COMIDCLSID)会沿用上图提示ProgIDCLSID。单击"否"按钮,新建控件的CLSID会自动生成一个全新的ID。比较稳妥的作法应该是单击"否"按钮。

VC++2010里,对于ProgID的重复处理不会再弹出对话框。而是直接生成一个全新的CLSID

1.11 控件位图

VC++6.0加入本控件后,"Controls"里将显示该控件的位图。

ActiveX控件(MFC篇)_第36张图片

图1.36

ocxMFC项目里,可以修改这个位图——IDB_OCXMFC

ActiveX控件(MFC篇)_第37张图片

图1.37

在图1.36中,位图的显示需要查询注册表:

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}\ToolboxBitmap32]

@="G:\\VC\\ocxMFC\\Release\\ocxMFC.ocx, 1"

也就是显示ocxMFC.ocxID号为1的位图。下图是使用eXeScope查看ocxMFC.ocxID号为1的位图。

ActiveX控件(MFC篇)_第38张图片

图1.38

1.12 AfxOleRegisterControlClass

在图1.4中,可以对创建的控件进行配置,其实质是配置AfxOleRegisterControlClass的参数。

如下面的代码

AfxOleRegisterControlClass(AfxGetInstanceHandle(),

    m_clsid,

    m_lpszProgID,

    IDS_OCXMFC,

    IDB_OCXMFC,

    afxRegApartmentThreading | afxRegInsertable,

    _dwOcxMFCOleMisc | OLEMISC_ACTIVATEWHENVISIBLE,

    _tlid,

    _wVerMajor,

    _wVerMinor);

afxRegInsertable 相当于勾中图1.4的"Available in "Insert Object" dialog"。这样此控件就能够被WordExcel……作为对象插入。

顺便提一下:"Available in "Insert Object" dialog"不仅仅增加了afxRegInsertable,它还增加了一行代码,使得Word插入该控件后,右键单击该控件后可以编辑它。

BEGIN_MESSAGE_MAP(COcxMFCCtrl, COleControl)

//{{AFX_MSG_MAP(COcxMFCCtrl)

//}}AFX_MSG_MAP

ON_OLEVERB(AFX_IDS_VERB_EDIT, OnEdit) //增加的代码

ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)

END_MESSAGE_MAP()

OLEMISC_ACTIVATEWHENVISIBLE 相当于勾中图1.4中的"Activates when visible"。此外,OLEMISC_INVISIBLEATRUNTIME相当于勾中图1.4中的"Invisible at runtime"更多选项请查阅MSDN

 

 

2 VC++2010创建控件

2.1 创建项目

运行VC++2010,新建"MFC ActiveX Control"项目,如下图所示。配置好项目名称、项目目录后,单击"OK"按钮。

ActiveX控件(MFC篇)_第39张图片

图2.1 新建ActiveX项目

VC++2010显示创建ActiveX控件的向导,界面如下面四张图所示。

ActiveX控件(MFC篇)_第40张图片

图2.2 创建ActiveX控件向导——页面一

ActiveX控件(MFC篇)_第41张图片

图2.3 创建ActiveX控件向导——页面二

ActiveX控件(MFC篇)_第42张图片

图2.4 创建ActiveX控件向导——页面三

ActiveX控件(MFC篇)_第43张图片

图2.5 创建ActiveX控件向导——页面四

2.2 项目结构

下图是创建的项目结构

ActiveX控件(MFC篇)_第44张图片

图2.6 项目结构

VC++6.0项目结构基本一致。最大的区别在于:增加了类型库ocxMFCLibCOMocxMFC,并把_DocxMFC_DocxMFCEvents这两个COM接口移到了类型库ocxMFCLib下。如此显示,结构更加清晰。

2.3 增加方法

鼠标右键单击"_DOcxMFC",然后鼠标左键单击弹出菜单中的【Add】【Add Method...】。

ActiveX控件(MFC篇)_第45张图片

图2.7 增加方法右键菜单

VC++2010显示如下面两张图。可见:与VC++6.0相比,数据类型更加丰富、更符合COM规范(VC++6.0里没有VARIANT_BOOL,只有BOOL),此外增加了IDL Attributes,对IDL文件的控制更加细致。

ActiveX控件(MFC篇)_第46张图片

图2.8 增加方法——页面一

ActiveX控件(MFC篇)_第47张图片

图2.9 增加方法——页面二

2.4 增加属性

与增加方法类似,只是在图2.7中单击【Add Property...】菜单项。界面显示如下面两张图。

ActiveX控件(MFC篇)_第48张图片

图2.10 增加属性——页面一

ActiveX控件(MFC篇)_第49张图片

图2.11 增加属性——页面二

2.5 增加事件

根据VC++6.0的传统,应该是鼠标右键单击"_DOcxMFCEvents",然后鼠标左键单击弹出菜单中的【Add Event...】。可惜不是这样,应该是鼠标右键单击"CocxMFCCtrl",然后再单击【Add Event...】。

ActiveX控件(MFC篇)_第50张图片

图2.12 增加事件右键菜单

增加事件的界面如下图所示:

ActiveX控件(MFC篇)_第51张图片

图2.13 增加事件

2.6 删除方法、属性、事件

VC++.NET里删除方法、属性、事件,似乎没有快捷的方法,只能手动修改ocxMFC.idlocxMFCCtrl.hocxMFCCtrl.cpp这三个文件。即便在VC++2010恢复了类向导之后,这种情况依然没有改观。

 

3 VC++6.0使用控件

3.1 增加控件的操作

单击VC++6.0的【Project】【Add To Project】【Components and controls

ActiveX控件(MFC篇)_第52张图片

图3.1

鼠标双击"Registered ActiveX Controls",进入这个目录

ActiveX控件(MFC篇)_第53张图片

图3.2

鼠标左键单击选中要插入的ActiveX控件,然后单击"Insert"按钮。

ActiveX控件(MFC篇)_第54张图片

图3.3

如果是Windows7系统,需要注意:

1、单击"Insert"按钮之前,一定要把文件名后面的".lnk"删除掉;

2、插入ActiveX控件后,请立即退出VC++6.0,然后再次运行。因为此时VC++6.0随时可能崩溃。

单击上图的"Insert"按钮,VC++6.0会提示是否插入此控件?请单击"确定"按钮。

ActiveX控件(MFC篇)_第55张图片

图3.4

VC++6.0会根据此控件的类型库信息创建COM接口_DOcxMFC的包装类。在下图中,可以修改包装类的名称及文件名称,然后单击"OK"按钮。

ActiveX控件(MFC篇)_第56张图片

至此,此控件即被插入VC++项目里。Controls窗口里增加了该控件的图标,使用它可以在对话框上增加ActiveX控件。

ActiveX控件(MFC篇)_第57张图片

图3.5

3.2 增加控件的实质

增加控件的过程中,可以看到VC++6.0为控件的COM接口_DOcxMFC生成了一个C++包装类,根据这个包装类即可访问控件的属性和方法。

还有一项工作就是在dsp文件的最后增加了如下语句

# Section Test : {EC3B9652-4EAD-4FBD-AB11-5E2095423BAA}

#     2:5:Class:COcxMFC

#     2:10:HeaderFile:ocxmfc.h

#     2:8:ImplFile:ocxmfc.cpp

# End Section

# Section Test : {091AEC6D-3B16-4F1A-95FA-94E8C48E3366}

#     2:21:DefaultSinkHeaderFile:ocxmfc.h

#     2:16:DefaultSinkClass:COcxMFC

# End Section

{EC3B9652-4EAD-4FBD-AB11-5E2095423BAA}COM接口_DOcxMFCGUID,这一段表示:_DOcxMFC的包装类是COcxMFC,并指明了包装类的头文件和实现文件。

{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}COM类的GUID。这一段表示:控件发出的事件由COcxMFC负责接收,并指明了COcxMFC的头文件。

3.3 控件名称重复

插入控件时,列表列出的是控件的名称。即图3.3里的"OcxMFC Control"就是插入控件的名称。

控件名称在注册表中的位置如下表所示。注意:{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}是控件COM类的GUID

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}]

@="OcxMFC Control"

创建ActiveX项目时,可在图1.5中修改控件名称。

编译ActiveX项目时,可在odl文件中修改控件名称。

[ uuid(091AEC6D-3B16-4F1A-95FA-94E8C48E3366),

helpstring("OcxMFC Control"), control ]

coclass OcxMFC

因为ActiveX控件可能不是同一人开发,所以名称重复在所难免。此时,会发生什么情况?

在控件名称重复的情况下,图3.3的列表只能列出重名控件中的一个。插入控件时需要仔细检查控件的全路径名称(即Path to control),如果这个全路径名称不正确,就需要修改注册表,修改某些控件的名称。

 

 

4 VC++2010使用控件

4.1 增加控件

4.1.1 增加到对话框

Resource View 里,鼠标左键双击某个对话框,如下图的IDD_TEST_DIALOG。这个对话框将被显示出来,此时鼠标右键单击该对话框,弹出菜单中单击【Insert ActiveX Control...】菜单项。

ActiveX控件(MFC篇)_第58张图片

图4.1

VC++2010将显示如下界面

ActiveX控件(MFC篇)_第59张图片

图4.2

选择要增加的控件,然后单击"OK"按钮,即可完成控件的增加。也可以直接鼠标左键双击某个控件完成增加工作。

这里笔者有个疑问:上图中,控件的Path为什么没有显示出来?

4.1.2 增加到Toolbox

如果需要频繁的增加某种控件,上一节的增加方法就显得效率比较低。这一节里,将把控件增加到Toolbox。这样,增加控件到对话框,就会比较高效一些。

鼠标右键单击Toolbox,弹出菜单中单击【Choose Items...】菜单项

ActiveX控件(MFC篇)_第60张图片

图4.3

显示如下界面。请进入"COM Components"页面,列表里勾中要添加的控件(可以勾中多个)。单击"OK"按钮即可将控件添加到Toolbox。此时,可以通过Toolbox往对话框里增加控件,效率会提高很多。

ActiveX控件(MFC篇)_第61张图片

图4.4

4.1.3 Toolbox里删除控件

鼠标右键单击某控件,弹出菜单中单击【Delete】菜单项,即可完成删除。

ActiveX控件(MFC篇)_第62张图片

图4.5

上图中还有【Rename Item】菜单项,可用来更名。

4.2 生成包装类

增加了COM控件的接口包装类后,客户端程序才能访问控件。这里将说明生成包装类的两种方法。

4.2.1 直接生成

单击【Project】【Add Class...】菜单项

ActiveX控件(MFC篇)_第63张图片

图4.6

选中"MFC Class From ActiveX Control",单击"Add"按钮。

ActiveX控件(MFC篇)_第64张图片

图4.7

下拉列表框内选择控件,然后依次单击">>"和"Finish"按钮。

ActiveX控件(MFC篇)_第65张图片

图4.8

上图中,单击"File"单选框,就可以通过一个ocx文件来生成包装类了。这种方式会多出一个事件接口,可以不用为事件接口添加包装类。

4.2.2 间接生成

可以给ActiveX控件添加变量。添加变量时如果没有发现包装类,VC++2010会自动生成包装类。操作如下:

鼠标右键单击控件,弹出菜单中单击【Add Variable...】菜单项

ActiveX控件(MFC篇)_第66张图片

图4.9

显示如下界面:

ActiveX控件(MFC篇)_第67张图片

图4.10

"Variable type"就是包装类的名称。如果ocxMFC这种控件已经生成了包装类,则此项将无法更改。

"Variable name"表示绑定该控件的变量名称。

单击"Finish"按钮,完成包装类的创建以及控件与变量的绑定。

4.2.3 包装类BUG

比较下面两张图,就能发现:使用VC++200520082010生成的包装类,竟然没有属性!

ActiveX控件(MFC篇)_第68张图片

图4.11 VC++2005、2008、2010生成的包装类

ActiveX控件(MFC篇)_第69张图片

图4.12 VC++6.0生成的包装类

 

5 VB6.0使用控件

VB6.0里增加ActiveX控件,如下图所示。

ActiveX控件(MFC篇)_第70张图片

图5.1

列出的是控件的类型库信息。也就是说"ocxMFC ActiveX Control module"在注册表中的位置如下所示

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{DAD6A33F-6756-43C6-BCE3-E48E361127CE}\1.0]

@="ocxMFC ActiveX Control module"

控件加入VB6.0,类型库也会被缓存至oca文件,这个缓存文件和ocx文件在同一目录下。

 

6 Office使用控件

在图1.4中,勾中"Available in "Insert Object" dialog"。创建出来的控件就可以被WordExcel做为对象插入。如下图所示:

ActiveX控件(MFC篇)_第71张图片

图6.1

上图列出的是HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID下可插入的COM类。显示的文本"OcxMFC Control"在注册表中的位置见下表。

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{091AEC6D-3B16-4F1A-95FA-94E8C48E3366}]

@="OcxMFC Control"

Word 2003里插入一个控件后,该控件的类型库将被缓存至C:\Users\Administrator\AppData\Local\Temp\Word8.0目录,文件后缀名为exd。下次再插入同样的控件,会使用这个exd文件里的类型库,而不是控件嵌入的类型库。这就意味着:一旦控件COM接口有所更新,Word里并不会反应这些变化。此时,必须删除相应的exd文件。

你可能感兴趣的:(VC++)