分享
 
 
 

用MFC建立COM服务器对象的框架步骤

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

用MFC建立COM服务器对象的框架步骤

任务1:建立第一个COM服务器

任务2:用VC调用COM接口

任务3:用VB调用COM接口

任务4:给接口增加新的属性和方法

任务5:增加新的自定义接口

任务6:继承接口类

例子程序在tecsp/docs/samples/comMFCDemo下

COM服务器:comMFCDemo

COM对象:coDrawObj,coDrawObjLine,coDrawObjRect

任务1:建立第一个COM服务器

操作:

1. 新建一个工程comMFCDemo,MFC AppWizard(dll),Regular DLL using shared MFC DLL,Automation.

说明:此时已建立COM服务器的基本框架,包括用于实例化和注册用的

DllCanUnloadNow(),DllGetObject(),DllRegisterServer()三个函数.

2. 在工程中加入mfcdual.h,mfcdual.cpp文件(在tecsp/comm目录中,应将该目录放在头文件查找路径中)

在stdafx.h中加入

#include "mfcdual.h"

#include "comMFCDemo_i.h"

说明:mfcdual.h中定义了一些用于简化编程的宏定义,减少MFC COM编程的重复劳动.按照本文档维护COM对象接口将非常轻松简明.

comMFCDemo_i.h是midl从odl文件中生成的,包括COM对象,接口定义,GUID定义等.

3. 编译选项

Project => Settings... => MIDL => Project Options

加入:/h comMFCDemo_i.h

MIDL将编译odl文件生成类型库comMFCDemo.tlb,头文件comMFCDemo_i.h,定义文件comMFCDemo_i.c.

任务2:新增一个COM对象

1. 用Class Wizard建立一个新类:CcoDrawObj,基类为CCmdTarget,选择Createable by type ID: comMFCDemo.coDrawObj.

说明:为避免手工添加接口定义,先选择自动化接口,将会自动生成odl文件和相应的接口定义,只需做少量的修改工作即可.选择Createable by type ID是为了能够通过类厂来创建COM对象.

2. 修改odl文件中IDualDrawObj的定义

2.1 将

[ uuid(13144AD4-ECC8-46F4-88F7-2FAC305595EA) ]

dispinterface IcoDrawObj

改成:

[ uuid(13144AD4-ECC8-46F4-88F7-2FAC305595EA),

oleautomation,

dual

]

interface IcoDrawObj : IDispatch

interface IcoDrawObj : IDispatch

说明:建议将COM对象的缺省接口定义成双接口,可以在多种环境中使用.

2.2 删除properties: 和 methods:这两行

2.3 在{}中定义属性和方法

// IcoDrawObj 属性:

[propput, helpstring("横坐标")]

HRESULT x([in] short new_x);

[propget, helpstring("横坐标")]

HRESULT x([out, retval] short* ret_x);

// IcoDrawObj 方法:

[helpstring("绘制图形")]

HRESULT Draw();

说明:上面的注释应保留以方便接口继承时使用;helpstring用于属性和方法的注释说明,应加上;只读属性删除propput定义的两行.

注意new_x和ret_x变量的书写方法,put属性使用new_前缀加上属性名,get属性使用ret_前缀加上属性名,这样是为了方便使用宏定义来自动生成属性声明和实现.

2.4 修改coclass定义

coclass coDrawObj

{

[default] dispinterface IcoDrawObj;

};

改成:

coclass coDrawObj

{

[default] interface IcoDrawObj;

};

2.5 编译odl文件

选择FileView,在comMFCDemo.odl文件上点击右键,选择Compile comMFCDemo.odl,将生成类型库comMFCDemo.tlb,头文件comMFCDemo_i.h和定义文件comMFCDemo_i.c.

将comMFCDemo_i.h和comMFCDemo_i.c加入工程中.

选择Project => Settings... => comMFCDemo_i.c,在右侧面板选择C/C++ => Category:Precompiled Headers => Not using precompiled headers

3. 修改COM对象头文件coDrawObj.h文件

3.1 加入属性对应的内部成员变量:protected,m_前缀加上属性名

short m_x;

3.2 接口声明

// ==============

// 双接口支持开始

// ==============

// ----------------------

// IcoDrawObj接口声明开始

// ----------------------

// 此处用宏定义是为了支持接口继承 ,在此处添加属性和方法

#define DECLARE_INTERFACE_PART_IcoDrawObj STDMETHOD(get_x)(THIS_ short FAR* ret_x); STDMETHOD(put_x)(THIS_ short new_x); STDMETHOD(Draw)();

BEGIN_DUAL_INTERFACE_PART(coDrawObj, IcoDrawObj)

DECLARE_INTERFACE_PART_IcoDrawObj

END_DUAL_INTERFACE_PART(coDrawObj)

需要修改:

DECLARE_INTERFACE_PART_IcoDrawObj - IcoDrawObj是接口名字

BEGIN_DUAL_INTERFACE_PART(coDrawObj, IcoDrawObj) - coDrawObj是组件名字,IcoDrawObj是接口名字。

END_DUAL_INTERFACE_PART(coDrawObj) - coDrawObj是组件名字,IcoDrawObj是接口名字。

// ----------------------

// IcoDrawObj接口声明结束

// ----------------------

// ISupportErrorInfo支持

DECLARE_DUAL_ERRORINFO()

// ==============

// 双接口支持结束

// ==============

说明:需要注意的是宏定义DECLARE_INTERFACE_PART_IcoDrawObj,最后面是接口的名字.该宏主要用于接口继承时,在子接口声明中声明父接口的属性和方法.以后也在该宏定义中添加新的属性和方法的声明.

3.3 属性读写实现

说明:属性读写实现基本结构都很相似,为减少代码书写量和书写错误,特采用宏定义方式.

需要修改部份:

宏定义IMPLEMENT_PROPERTIES_IcoDrawObj是为了用于接口继承,最后面是接口的名字。

目前实现了两种属性读写:

普通属性(short, float等常规类型)

DECLARE_PUT_PROPERTY(objClass, baseClass, x)(short new_x) - 需改动部份:x 是属性名字,(short new_x) 与声明中相同。

IMPLEMENT_PUT_PROPERTY(objClass, baseClass, x) - 需改动部份:x 是属性名字。

DECLARE_GET_PROPERTY(objClass, baseClass, x)(short FAR* ret_x) - 需改动部份:x 是属性名字,(short FAR* ret_x) 与声明中相同。

IMPLEMENT_GET_PROPERTY(objClass, baseClass, x) - 需改动部份:x 是属性名字。

接口指针属性

因为接口指针的赋值需要调用AddRef方法,故特别增加此宏定义。使用的唯一不同之处在于PROPERTY后面增加_INTERFACE后缀。

// ================

// 属性读写实现开始

// ================

// ---------------------------

// IcoDrawObj 属性读写实现开始

// ---------------------------

#define IMPLEMENT_PROPERTIES_IcoDrawObj(objClass, baseClass) DECLARE_PUT_PROPERTY(objClass, baseClass, x)(short new_x) IMPLEMENT_PUT_PROPERTY(objClass, baseClass, x) DECLARE_GET_PROPERTY(objClass, baseClass, x)(short FAR* ret_x) IMPLEMENT_GET_PROPERTY(objClass, baseClass, x)

// ---------------------------

// IcoDrawObj 属性读写实现结束

// ---------------------------

// ================

// 属性读写实现结束

// ================

// ============

// 方法实现开始

// ============

3.4 方法实现

MFC采用嵌套类的形式实现多接口,方法的实现基本结构相同,故采用宏定义减少代码书写量和书写错误。

解决方法是每个接口的方法都调用嵌套父类的同名函数,这样嵌套类方法的实现全部是同一种结构,可以用宏定义生成。

对每一个方法在嵌套父类中创建一个proteced virtual的同名成员函数,建立一个以接口名字命名的实现文件IcoDrawObj.cpp,将同名成员函数的实现放在里面。这样在接口定义好后,可以让程序员只编写这个文件中的函数实现,达到统一控制接口定义的目的。

3.4.1 嵌套类方法实现

以下嵌套类方法实现的宏定义放在头文件coDrawObj.h中属性读写实现的后面。

需要修改部份:

宏定义IMPLEMENT_METHODS_IcoDrawObj用于接口继承,最后面是接口的名字。

STDMETHODIMP objClass::X##baseClass::Draw()最后的方法名字

pThis->Draw(); 最后的方法名字

// ============

// 方法实现开始

// ============

// -----------------------

// IcoDrawObj 方法实现开始

// -----------------------

#define IMPLEMENT_METHODS_IcoDrawObj(objClass, baseClass) STDMETHODIMP objClass::X##baseClass::Draw() { BEGIN_NEST_INTERFACE_FRAME(objClass, baseClass) pThis->Draw(); END_NEST_INTERFACE_FRAME }

// -----------------------

// IcoDrawObj 方法实现结束

// -----------------------

// ============

// 方法实现结束

// ============

3.4.2 嵌套父类方法实现

IcoDrawObj.cpp文件样本(接口定义完成后,程序员仅需编写此文件中的函数实现即可):

// IcoDrawObj.cpp

#include "stdafx.h"

#include "coDrawObj.h"

void CcoDrawObj::Draw()

{

AfxMessageBox( "This is coDrawObj.");

}

4. 修改coDrawObj.cpp文件

4.1 构造函数colDrawObj()

// 自动化支持

EnableAutomation();

// 聚合支持

EnableAggregation();

4.2 删除IID定义

删除类似以下这行,因为前面已在工程中加入了MIDL自动生成的comMFCDemo_i.c文件,我们可以在一个文件中管理全部的GUID定义。

static const IID IID_IcoDrawObj =

{ 0x13144ad4, 0xecc8, 0x46f4, { 0x88, 0xf7, 0x2f, 0xac, 0x30, 0x55, 0x95, 0xea } };

4.3 多接口支持定义

BEGIN_INTERFACE_MAP(CcoDrawObj, CCmdTarget)

INTERFACE_PART(CcoDrawObj, IID_IcoDrawObj, Dispatch)

END_INTERFACE_MAP()

改成:

// 多接口支持定义

BEGIN_INTERFACE_MAP(CcoDrawObj, CCmdTarget)

INTERFACE_PART(CcoDrawObj, IID_IcoDrawObj, coDrawObj)

DUAL_ERRORINFO_PART(CcoDrawObj)

END_INTERFACE_MAP()

需要修改部分:CcoDrawObj为COM对象类,IID_IcoDrawObj为接口GUID,coDrawObj为接口名字删去I前缀。

4.4 类厂实现

系统已自动加入,类似以下这行:

// {09671A2C-04FA-495E-A40C-410073A3A36E}

IMPLEMENT_OLECREATE(CcoDrawObj, "comMFCDemo.coDrawObj", 0x9671a2c, 0x4fa, 0x495e, 0xa4, 0xc, 0x41, 0x0, 0x73, 0xa3, 0xa3, 0x6e)

4.5 实现标准的基接口方法

加入以下代码:

// 实现标准的IDispatch方法

DELEGATE_DUAL_INTERFACE(CcoDrawObj, coDrawObj)

// 实现标准的IUnknown方法

// DELEGATE_IUNKNOWN_INTERFACE(CcoDrawObj, coDrawObj)

需要修改部分:CcoDrawObj为COM对象类,coDrawObj为接口名字删去I前缀。

对自动化接口使用第一个宏定义,对自定义接口使用第二个宏定义。

每个接口需要一行宏定义,修改部份为最后的coDrawObj为接口名字删去I前缀。

4.6 ISupportErrorInfo支持

加入以下一行:

// ISupportErrorInfo 支持实现

IMPLEMENT_DUAL_ERRORINFO(CcoDrawObj, IID_IcoDrawObj)

需要修改部分:CcoDrawObj为COM对象类,IID_IcoDrawObj为缺省接口GUID。

说明:目前仅支持一个接口(请使用缺省接口),以后再加入多接口支持。

4.7 接口属性实现

每个接口加入以下一行:

// 接口属性实现

IMPLEMENT_PROPERTIES_IcoDrawObj(CcoDrawObj, coDrawObj)

需要修改部份:IcoDrawObj为接口名字,CcoDrawObj为COM对象类,coDrawObj为接口名字删去I前缀。

4.8 接口方法实现

每个接口加入以下一行:

// 接口方法实现

IMPLEMENT_METHODS_IcoDrawObj(CcoDrawObj, coDrawObj)

需要修改部份:IcoDrawObj为接口名字,CcoDrawObj为COM对象类,coDrawObj为接口名字删去I前缀。

5. 注册COM服务器

好了,现在这个支持双接口的COM服务器已经完成了,赶快注册吧。

Tools => Register Control

COM服务器:comMFCDemo

自动化对象:coDrawObj,支持双接口IcoDrawObj,属性x,方法Draw

任务2:用VC调用COM接口

1. 在工程中加入新项目:vcDemo,选择Dialog Base

在项目中加入../comMFCDemo_i.h和../comMFCDemo_i.c文件

选择Project => Settings... => comMFCDemo_i.c,在右侧面板选择C/C++ => Category:Precompiled Headers => Not using precompiled headers

2. 在App文件中InitInstance函数中加入COM库初始化:

if ( !AfxOleInit() )

return FALSE;

3. 在对话框中加入一个按钮,定义它的点击消息

4. 在vcDemoDlg.cpp中

#include "../comMFCDemo_i.h"

IcoDrawObj* pIcoDrawObj = NULL;

BOOL CVcDemoDlg::OnInitDialog()

{

...

HRESULT hr;

hr = CoCreateInstance( CLSID_coDrawObj, NULL, CLSCTX_INPROC_SERVER, IID_IcoDrawObj, reinterpret_cast<void**>(&pIcoDrawObj));

if ( FAILED(hr) )

return FALSE;

...

}

BOOL CVcDemoDlg::DestroyWindow()

{

if ( pIcoDrawObj )

{

pIcoDrawObj->Release();

pIcoDrawObj = NULL;

}

return CDialog::DestroyWindow();

}

void CVcDemoDlg::OnButton1()

{

if ( pIcoDrawObj )

{

pIcoDrawObj->Draw();

}

}

5. 好了,现在可以编译运行,看看点击按钮后出现了什么?

任务3:用VB调用COM接口

我们创建的是自动化对象可以在VB中方便使用

1. 创建VB应用

2. 加入COM对象引用

Project => References... => Browse.. => ../Debug/comMFCDemo.tlb

3. 创建一个按钮,生成它的点击事件

Private Sub Command1_Click()

Dim coDrawObj As New coDrawObj

coDrawObj.Draw

End Sub

4. 好了,可以运行,点击按钮。

任务4:给接口增加新的属性和方法

1. 修改odl文件

interface IcoDrawObj : IDispatch

{

// IcoDrawObj 属性:

[propput, helpstring("横坐标")]

HRESULT x([in] short new_x);

[propget, helpstring("横坐标")]

HRESULT x([out, retval] short* ret_x);

>> [propput, helpstring("纵坐标")]

>> HRESULT y([in] short new_y);

>> [propget, helpstring("纵坐标")]

>> HRESULT y([out, retval] short* ret_y);

// IcoDrawObj 方法:

[helpstring("绘制图形")]

HRESULT Draw();

>> [helpstring("关于")]

>> HRESULT AboutMe();

};

编译odl文件

2. 在COM对象头文件coDrawObj.h中加入属性对应的内部成员变量

short m_y;

3. 在COM对象头文件coDrawObj.h中修改接口声明

#define DECLARE_INTERFACE_PART_IcoDrawObj STDMETHOD(get_x)(THIS_ short FAR* ret_x); STDMETHOD(put_x)(THIS_ short new_x); >> STDMETHOD(get_y)(THIS_ short FAR* ret_y); >> STDMETHOD(put_y)(THIS_ short new_y); STDMETHOD(Draw)(); >> STDMETHOD(AboutMe)();

4. 在COM对象头文件coDrawObj.h中修改属性读写实现

#define IMPLEMENT_PROPERTIES_IcoDrawObj(objClass, baseClass) DECLARE_PUT_PROPERTY(objClass, baseClass, x)(short new_x) IMPLEMENT_PUT_PROPERTY(objClass, baseClass, x) DECLARE_GET_PROPERTY(objClass, baseClass, x)(short FAR* ret_x) IMPLEMENT_GET_PROPERTY(objClass, baseClass, x) >> DECLARE_PUT_PROPERTY(objClass, baseClass, y)(short new_y) >> IMPLEMENT_PUT_PROPERTY(objClass, baseClass, y) >> DECLARE_GET_PROPERTY(objClass, baseClass, y)(short FAR* ret_y) >> IMPLEMENT_GET_PROPERTY(objClass, baseClass, y)

4. 在COM对象头文件coDrawObj.h中修改接口方法实现

#define IMPLEMENT_METHODS_IcoDrawObj(objClass, baseClass) STDMETHODIMP objClass::X##baseClass::Draw() { BEGIN_NEST_INTERFACE_FRAME(objClass, baseClass) pThis->Draw(); END_NEST_INTERFACE_FRAME } >> STDMETHODIMP objClass::X##baseClass::AboutMe() >> { >> BEGIN_NEST_INTERFACE_FRAME(objClass, baseClass) >> pThis->AboutMe();>> END_NEST_INTERFACE_FRAME >> }

5. 创建嵌套父类CcoDrawObj的protected virtual成员函数AboutMe

将函数实现移到IcoDrawObj.cpp中,交给程序员去完成。

void CcoDrawObj::AboutMe()

{

AfxMessageBox( "This is coDrawObj AboutMe().");

}

6. 编译,注册。现在可以在vcDemo中加入 pIcoDrawObj->AboutMe();测试一下。

任务5:增加新的自定义接口

缺省自动化接口提供对其它开发平台程序员的支持,COM对象内部之间还需要一些内部的工作需要用自定义接口实现。

1. 修改odl文件

增加新接口(接口uuid是用guidgen生成的)

[ uuid(8C1A8020-627B-4c37-AE70-51F9AD56FFBD),

object,

helpstring("coDrawObj的计算方法")

]

interface ICalc : IUnknown

{

[helpstring("计算")]

HRESULT Calc();

};

将新接口加入到COM对象类中

coclass coDrawObj

{

[default] interface IcoDrawObj;

>> interface ICalc;

};

编译odl文件

2. 修改COM对象头文件coDrawObj.h

2.1 增加自定义接口声明

// ----------------------

// ICalc接口声明开始

// ----------------------

#define DECLARE_INTERFACE_PART_ICalc STDMETHOD(Calc)();

BEGIN_INTERFACE_PART(Calc, ICalc)

DECLARE_INTERFACE_PART_ICalc

END_INTERFACE_PART(Calc)

// ----------------------

// ICalc接口声明结束

// ----------------------

需要修改:

DECLARE_INTERFACE_PART_ICalc - ICalc是接口名字

BEGIN_INTERFACE_PART(coDrawObj, ICalc) - Calc是接口名字删去I前缀,ICalc是接口名字。

END_INTERFACE_PART(Calc) - Calc是接口名字删去I前缀,ICalc是接口名字。

2.2 接口方法实现

// -----------------------

// ICalc 方法实现开始

// -----------------------

#define IMPLEMENT_METHODS_ICalc(objClass, baseClass) STDMETHODIMP objClass::X##baseClass::Calc() { BEGIN_NEST_INTERFACE_FRAME(objClass, baseClass) pThis->Calc(); END_NEST_INTERFACE_FRAME }

// -----------------------

// ICalc 方法实现结束

// -----------------------

需要修改部份:IMPLEMENT_METHODS_ICalc最后为接口名字

STDMETHODIMP objClass::X##baseClass::Calc() 最后为接口方法名字

pThis->Calc();最后为接口方法名字

2.3 创建嵌套父类CcoDrawObj的protected virtual成员函数Calc

将函数实现移到ICalc.cpp中,交给程序员去完成。

void CcoDrawObj::Calc()

{

AfxMessageBox( "This is coDrawObj Calc().");

}

3. 修改COM对象实现文件coDrawObj.cpp

加入

// 多接口支持定义

BEGIN_INTERFACE_MAP(CcoDrawObj, CCmdTarget)

INTERFACE_PART(CcoDrawObj, IID_IcoDrawObj, coDrawObj)

>> INTERFACE_PART(CcoDrawObj, IID_ICalc, Calc)

DUAL_ERRORINFO_PART(CcoDrawObj)

END_INTERFACE_MAP()

需要修改:

IID_ICalc - 接口的GUID

CcoDrawObj - COM对象类

Calc - 接口名字删去I前缀

// 实现ICalc的标准IUnknown方法

DELEGATE_IUNKNOWN_INTERFACE(CcoDrawObj, Calc)

需要修改:

CcoDrawObj - COM对象类

Calc - 接口名字删去I前缀

IMPLEMENT_METHODS_ICalc(CcoDrawObj, Calc)

需要修改:

ICalc - 接口名字

CcoDrawObj - COM对象类

Calc - 接口名字删去I前缀

4. 编译运行

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有