COM组件(ATL篇)

时间:2021-07-23 17:29:52

目录

第1章创建进程内组件    1

1.1 目标    1

1.2 创建项目    3

1.2.1 VC++6.0    3

1.2.2 VC++2010    5

1.3 增加COM类    6

1.3.1 VC++6.0    6

1.3.2 VC++2010    8

1.3.3 项目结构    12

1.4 增加方法    13

1.4.1 VC++6.0    13

1.4.2 VC++2010    14

1.5 增加属性    16

1.5.1 VC++6.0    16

1.5.2 VC++2010    17

1.6 删除方法、属性    17

1.7 编码    17

1.7.1 增加成员变量    17

1.7.2 初始化成员变量    18

1.7.3 实现Add    18

1.7.4 实现Reset    18

1.7.5 实现get_Count    18

1.7.6 实现get_Average    19

1.7.7 实现get_StdDev    19

1.8 注册、注销    19

第2章 VC++使用组件    21

2.1 #import    21

2.2 MFC包装类    21

2.3 C语言调用    21

第1章创建进程内组件

1.1 目标

本章的目标是使用ATL创建一个进程内COM组件。在此组件里,将实现COM类CStatistic及COM接口IStatistic,用来进行统计计算。IStatistic的详细信息如下:

1、方法

void Reset();                //重新开始统计计算

void Add(double dVal);        //增加一个数据

2、属性

long    Count;                //返回数据个数

double    Average;            //返回平均值

double    StdDev;            //返回标准差

CStatistic的VC++代码如下:

#include <MATH.H>

class CStatistic

{

public:

CStatistic()            {Reset();}

public:

void Reset()        {memset(this,0,sizeof(*this));}

void Add(double dVal)

{

++m_nCount;                         //样本个数

m_dSum += dVal;             //所有样本值的和

m_dSum2 += dVal * dVal;     //所有样本值的平方和

}

public://属性定义(VC++的语法)

__declspec(property(get=GetCount)) ULONG    Count;

__declspec(property(get=GetAverage))     double        Average;

__declspec(property(get=GetStdDev))     double        StdDev;

public:

ULONG GetCount() const        {return m_nCount;}

double GetAverage() const

{

if(m_nCount)

{

return m_dSum / m_nCount;

}

return 0.0;

}

double GetStdDev() const

{

double d = 0.0;

if(m_nCount > 1)

{

d = (m_nCount * m_dSum2 - m_dSum * m_dSum)

/ (m_nCount * (m_nCount - 1));

if(d > 0.0)

{

d = sqrt(d);

}

}

return d;

}

private:

ULONG    m_nCount;     //样本个数

double        m_dSum;     //所有样本值的和

double        m_dSum2;     //所有样本值的平方和

};

测试代码如下:

ULONG    n    =    0;

double        d    =    0.0;

CStatistic    s;

s.Reset();

s.Add(1.0);

s.Add(2.0);

s.Add(3.0);

s.Add(4.0);

n = s.Count; //(1,2,3,4)的个数

d = s.Average; //(1,2,3,4)的平均值

d = s.StdDev; //(1,2,3,4)的标准差

s.Add(5.0);

n = s.Count; //(1,2,3,4,5)的个数

d = s.Average; //(1,2,3,4,5)的平均值

d = s.StdDev; //(1,2,3,4,5)的标准差

这个类的优点在于:它能实时获得样本数据的平均值、标准差,且不用把样本数据存入数组,因此可以连续的长时间工作。

1.2 创建项目

1.2.1 VC++6.0

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

COM组件(ATL篇)

图1.1

显示界面如下所示,直接单击"Finish"按钮。

COM组件(ATL篇)

图1.2

显示界面如下,单击"OK"按钮,完成项目创建。

COM组件(ATL篇)

图1.3

1.2.2 VC++2010

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

COM组件(ATL篇)

图1.4

显示创建向导,界面如下面两张图所示:

COM组件(ATL篇)

图1.5 创建向导——页面一

COM组件(ATL篇)

图1.6 创建向导——页面二

单击上图的"Finish"按钮,完成项目的创建。

1.3 增加COM类

现在,往项目里增加COM类。

1.3.1 VC++6.0

单击【Insert】【New ATL Object...】菜单项

COM组件(ATL篇)

图1.7

显示界面如下。请选中"Objects"里的"Simple Object",然后单击"Next"按钮。

COM组件(ATL篇)

图1.8

显示界面如下,一共有两个页面。

COM组件(ATL篇)

图1.9 增加COM类——页面一

下图所示界面里,Interface有两个选项:Dual表示双接口(也叫自动化接口),它派生自IDispatch;Custom表示自定义接口,它派生自IUnknown。自定义接口直接访问虚函数表,其效率较高。自动化接口效率较低,但是它支持的语言较多。

COM组件(ATL篇)

图1.10 增加COM类——页面二

1.3.2 VC++2010

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

COM组件(ATL篇)

图1.11

选中"ATL Simple Object",然后单击"Add"按钮

COM组件(ATL篇)

图1.12

显示界面如下,一共有三个页面

COM组件(ATL篇)

图1.13 增加COM类——页面一

COM组件(ATL篇)

图1.14 增加COM类——页面二

COM组件(ATL篇)

图1.15 增加COM类——页面三

1.3.3 项目结构

VC++6.0的类视图里增加了"CStatistic"和"IStatistic"。

IStatistic是COM接口,客户端程序通过它访问COM组件。

CStatistic是COM类,真正的工作由它来完成。

COM组件(ATL篇)

图1.16

同时,idl文件也发生了变化,如下表所示。增加了接口IStatistic,增加了COM类Statistic,这个COM类实现了接口IStatistic。

// comDLLatl.idl

import "oaidl.idl";

import "ocidl.idl";

[

uuid(0FEBC618-38BC-4A5B-AE09-3C56635F4D73),

version(1.0),

helpstring("comDLLatl 1.0 Type Library")

]

library COMDLLATLLib

{

importlib("stdole32.tlb");

importlib("stdole2.tlb");

[

object,

uuid(7FB59FA6-E715-4C23-9520-DE93293E0B5F),

helpstring("IStatistic Interface"),

pointer_default(unique)

]

interface IStatistic : IUnknown

{

};

[

uuid(471BF24D-B793-44A1-9205-7BF36A6EB698),

helpstring("Statistic Class")

]

coclass Statistic

{

[default] interface IStatistic;

};

};

1.4 增加方法

1.4.1 VC++6.0

鼠标右键单击接口IStatistic,弹出菜单中单击【Add Method...】菜单项

COM组件(ATL篇)

图1.17

下图就是增加方法的界面。这里增加了方法void Add(double dVal)。单击"OK"按钮,完成方法的增加。注意:对于自动化接口,Return Type只能是HRESULT。

COM组件(ATL篇)

图1.18

可使用同样的方法,增加方法void Reset()。

1.4.2 VC++2010

鼠标右键单击接口IStatistic,弹出菜单中单击【Add】【Add Method...】。

COM组件(ATL篇)

图1.19

增加方法的界面如下。与VC++6.0的大致相同。单击"Finish"按钮,完成方法void Add(DOUBLE dVal)的添加。

COM组件(ATL篇)

图1.20

可使用同样的方法,增加方法void Reset()。

1.5 增加属性

1.5.1 VC++6.0

在图1.17中,单击【Add Property...】菜单项。显示界面如下:

这里增加了属性long Count。注意:没有设置"Put function",说明这个属性是只读属性。

单击"OK"按钮,完成属性的增加。

COM组件(ATL篇)

图1.21

同样方法,可以增加属性double Average和double StdDev。

1.5.2 VC++2010

在图1.19中,单击【Add Property...】菜单项。显示界面如下:

这里增加了属性ULONG Count。注意:没有设置"Put function",说明这个属性是只读属性。

单击"Finish"按钮,完成属性的增加。

COM组件(ATL篇)

图1.22

同样方法,可以增加属性double Average和double StdDev。

1.6 删除方法、属性

使用ATL,删除属性、方法,似乎只能手动进行,相当的麻烦。

1.7 编码

1.7.1 增加成员变量

请给CStatistic增加三个成员变量

private:

ULONG    m_nCount;

double        m_dSum;

double        m_dSum2;

1.7.2 初始化成员变量

CStatistic的构造函数里,初始化这三个成员变量

CStatistic()

{

m_nCount        =    0;

m_dSum        =    0.0;

m_dSum2        =    0.0;

}

1.7.3 实现Add

STDMETHODIMP CStatistic::Add(double dVal)

{

++m_nCount;             //样本个数

m_dSum += dVal;         //所有样本值的和

m_dSum2 += dVal * dVal;     //所有样本值的平方和

return S_OK;

}

1.7.4 实现Reset

STDMETHODIMP CStatistic::Reset()

{

m_nCount = 0;

m_dSum = 0.0;

m_dSum2 = 0.0;

return S_OK;

}

1.7.5 实现get_Count

STDMETHODIMP CStatistic::get_Count(long *pVal)

{

*pVal = m_nCount;

return S_OK;

}

1.7.6 实现get_Average

STDMETHODIMP CStatistic::get_Average(double *pVal)

{

if(m_nCount)

{

*pVal = m_dSum / m_nCount;

}

else

{

*pVal = 0.0;

}

return S_OK;

}

1.7.7 实现get_StdDev

STDMETHODIMP CStatistic::get_StdDev(double *pVal)

{

*pVal = 0.0;

if(m_nCount > 1)

{

*pVal = (m_nCount * m_dSum2 - m_dSum * m_dSum)

/ (m_nCount * (m_nCount - 1));

if(*pVal > 0.0)

{

*pVal = sqrt(*pVal);

}

}

return S_OK;

}

1.8 注册、注销

编译comDLLatl,即可得到进程内COM组件comDLLatl.dll。使用它之前,需要注册。

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

regsvr32 comDLLatl.dll

Rundll32 comDLLatl.dll,DllRegisterServer

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

regsvr32 /u comDLLatl.dll

Rundll32 comDLLatl.dll,DllUnregisterServer

注意:VC++2010可以编译生成64位的COM组件。在64位操作系统上,regsvr32.exe和Rundll32.exe将自动识别COM组件是32位的还是64位的。注册信息会写入注册表的如下几个位置。注意这里的<ProgID>其实就是comDLLatl.Statistic。

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\<ProgID>

对于64位组件,注册程序直接访问上述注册表项;对于32位组件,注册程序会将上述注册表项映射至32位的注册表项。如此一来,同一个组件的32位、64位是可以同时注册在64位Windows上的,它们互不干涉。

第2章 VC++使用组件

2.1 #import

请参考《COM组件(MFC篇)》。需要注意的是:MFC创建的COM组件,其接口必定是Dual接口(图1.10、图1.15中的Interface选项),也就是派生自IDispatch的自动化接口。ATL可以创建Custom接口,这种接口将派生自IUnknown,tli文件里的函数通过虚函数实现,效率较高。Custom接口的不足之处在于:它可能不能被某些脚本语言(如:VBS)调用。

2.2 MFC包装类

如果接口是Dual接口,就可以生成MFC包装类。具体操作请参考《COM组件(MFC篇)》。

2.3 C语言调用

使用C语言也可以访问COM组件。

如果接口是Dual接口,请参考《COM组件(MFC篇)》。

如果接口是Custom接口,则可以使用编译类型库时产生的C/C++头文件。如下面这段代码:(节选自comDLLatl_i.h)

#define IStatistic_Add(This,dVal)          ((This)->lpVtbl->Add(This,dVal))

#define IStatistic_get_Count(This,pVal)    ((This)->lpVtbl->get_Count(This,pVal))

亦即:可以通过宏IStatistic_Add、IStatistic_get_Count去访问方法、属性。