📄 vc下com的server和client编程实现 .txt
字号:
介绍
这里主要是对COM在VC下的实现过程作个介绍,使大家对用VC来进行COM编程有个初步的了解。本例子以银行一个用户(Account)的存钱(Deposit)和取钱(Withdraw)简单模型用COM来具体实现。
正文
VC++ 下 COM 编程
对COM规范在这儿就不介绍了,这里主要是对COM在VC下的实现过程作个介绍,使大家对用VC来进行COM编程有个初步的了解。本例子以银行一个用户(Account)的存钱(Deposit)和取钱(Withdraw)简单模型用COM来具体实现。本例子中用到的一个IAccount接口,这个接口的成员函数有四个:
1、Deposit(double x)-用来对一个账户存钱;
2、Withdraw(double x)-用来对一个账户取钱;
3、CurrentAccount(double newVal) –设置当前账户的余额;
4、CurrentAccount(double *pVal)- 获取当前账户的余额;
其中函数3和4是当你在定义一个接口属性时生成的两个函数,3表示设置属性值,4表示获取属性值;
1、组件对象的编程(即对象服务器端)
1.1 逻辑实现步骤
组件对象的编程目的:就是定义对象的接口,并实现接口中定义的成员函数,为客户提供服务。
实现的步骤为:
a、 ATL COM AppWizard新建一个工程;
b、在工程中插入一个对象(object),定义接口的名字;
c、定义接口函数;
d、在类中对接口函数编写服务代码,使接口在被客户访问时提供服务;
1.2 具体实现步骤
a、用ATL COM AppWizard新建一个工程,工程名为:Bank(图一)
图一、新建工程的界面
b、选择组件提供服务时所用的类型(.dll 或 exe),本例实现Bank.exe(图二)
图二、选择组件提供服务的类型
说明:
进程内组件:组件对象和客户进程在同一进程,客户进程在同一进程内调用组件对象提供的服务;
进程外组件:组件对象和客户进程分别在不同的进程,客户可以跨进程调用组件对象提供的服务,也可以作为远程进程组件;
远程进程外组件:组件对象和客户进程运行在不同的机器上,客户进程通过RPC调用远程机器上组件对象提供的服务;
c、在工程插入一个Object, 在菜单Insert->New ATL Object…,选中Simple Objects(图三)
图三、新建一个组件对象界面
d、在Short Name中输入Account类的名字,其它项(如接口等)自动产生(图四)
图四、对组件对象命名
至此完成了组件对象的定义,接口的定义,如果此时编译的的话的到一个不提供任何服务的组件。因为我们还没有定义接口的成员函数,也没有为成员函数编写服务代码。
E、定义接口的成员函数,在接口Iaccount处点击鼠标右键(图五)
图五、定义接口函数
然后点击 出现增加接口函数的对话框(图六)
图六、定义接口函数的具体信息
f、重复过程e直到把所有的接口函数全部加入到接口中(图七)
图七、定义完接口的情况
说明:
需要指出的是,在增加属性到接口时,会出现两个借口函数;原因是属性是接口修改对象状态的,以表示组件提供服务时同一个类所产生的对象的不同,所以在定义属性时会自动产生两个函数,分别用来获取对象属性和修改对象属性;
g、对接口函数在类中编写其服务代码,我们以Withdraw来简单说明一下其过程(图八)
图八、接口函数在类中的具体实现
说明:
其中CurrentValue为类的一个变量表示当然对象中账户的值,本例中在类构造函数中对其初始化为CurrentValue = 0;当然对于一个真实的银行账户来说,需要在创建一个账户对象时需要从一个数据库中读出账户当前值,而不能是0;除非是初次创建一个新的账户时才可以设置CurrentValue为0本例子是简化了。
全部代码的简单实现(图九)
图九、接口函数的全部实现
h、至此我们已经完成一个组件的代码编写,编译后就可以为客户提供服务了,注意组件服务端编译后会生成类和接口的GUID对我们编写客户端是必需的,这儿生成CLSID和IID为:
const IID IID_IAccount = {0xEF327845,0x40A8,0x4AE0,{0x90,0x50,0xEA,0x7F,0xAD,0x02,0x75,0xEA}};
const IID LIBID_BANKLib = {0x98ECF356,0x06EF,0x4F72,{0xA3,0xA0,0x6E,0xCA,0x37,0x25,0x50,0x9E}};
const CLSID CLSID_Account = {0xEB914BA1,0x3C9C,0x4321,{0xBB,0x87,0xD2,0xF8,0xE3,0xEF,0xEB,0x09}};
对客户端编程必需的代码文件还有一个就是描述接口的头文件,这里为Bank.h在编写客户端时需要将该文件拷贝到客户端程序目录下,下面将描述客户端程序的编写过程。
2、 客户端编程
2.1逻辑实现步骤
a、 COM库的初始化;
b、 创建组件对象实例,返回IUnknown指针;
c、 通过IUnkonwn指针去查询接口指针,如果接口存在,返回IAccount指针;
d、 调用接口服务函数;
e、 释放接口指针;
f、 释放COM库;
2.2 具体实现
客户端的实现其实和普通的编程没很大的区别,主要就是要引用组件对象提供的服务。
本例的客户端用基于对话框MFC来实现对组件服务的调用。客户端实现的是账户的存钱(Deposit)和取钱(Withdrw)和对账户的当前余额的度取和设置。
a、 用MFC AppWinzard新建一个基于对话框的程序(图一、二)
图一、建立客户端程序的向导界面
图二、选择基于对话框向导
b、 设计对话框界面(本例很简单,图三)
图三、对话框界面设计
c、 设置和对话上控件对应的成员变量(图四)
图四、增加对话框成员变量
说明:
m_Currrent表示当前账户对象的余额;
m_Value:表示交易的钱数;
以上是客户端的界面设计部分;
d、 将在编写组件时产生的Bank.h(接口说明文件)拷贝到本工程下,在TestBankComDlg.h(客户端对话框头文件)中增加代码:
#define _WIN32_DCOM //宏定义
#include "Bank.h"//组件端产生的接口描述文件
在对话框类中增加几个变量:
HRESULT hr;//调用函数的返回信息
IAccount *pAccount;//接口指针
IUnknown *pUnknown;//Iunknown指针
添加对象类CLSID和接口的IID:
const IID IID_IAccount = {0xEF327845,0x40A8,0x4AE0,{0x90,0x50,0xEA,0x7F,0xAD,0x02,0x75,0xEA}};
const IID LIBID_BANKLib = {0x98ECF356,0x06EF,0x4F72,{0xA3,0xA0,0x6E,0xCA,0x37,0x25,0x50,0x9E}};
const CLSID CLSID_Account = {0xEB914BA1,0x3C9C,0x4321,{0xBB,0x87,0xD2,0xF8,0xE3,0xEF,0xEB,0x09}};
这些128位数是由VC自动生成;
在对话框OnInitDialog()中添加初始代码:
////////////////////////////////////////////////////////////////////////////////////////////////////////
//组件库初始化
hr = CoInitialize(NULL);
if(FAILED(hr))
{
MessageBox("组件初始化失败!");
return false;
}
//创建对象实例,并返回IUnknown 指针
hr = CoCreateInstance(CLSID_Account, NULL,
CLSCTX_LOCAL_SERVER , IID_IUnknown, (void**)&pUnknown);
if(FAILED(hr))
{
MessageBox("创建对象实例失败!");
return false;
}
//通过IUnkonwn指针去查询接口指针,返回IAccount指针
hr = pUnknown->QueryInterface(IID_IAccount,(void**)&pAccount);
if(FAILED(hr))
{
MessageBox("没有查找的接口指针!");
return false;
}
//Iunknown接口释放
pUnknown->Release();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
以上代码产生一个组件对象,并通过Iunknown接口查询了对象的接口Iaccount
e、 编写调用对象服务的代码
在“存钱”对应的消息映射函数OnDeposit()中编写代码:
//////////////////////////////////////////////////////////////////////////////////////////////////////
UpdateData(true);
pAccount->Deposit(this->m_Value);//存钱
pAccount->get_CurrentAccount(&(this->m_CurrentValue));//取得当前账户值
UpdateData(false);
//////////////////////////////////////////////////////////////////////////////////////////////////////
在“取钱”按钮对应的消息映射函数OnWithdraw()中编写代码
//////////////////////////////////////////////////////////////////////////////////////////////////////
UpdateData(true);
double currentvalue;//局部变量,表示对象当前的余额
pAccount->get_CurrentAccount(¤tvalue);//得到对象当前的余额
currentvalue = currentvalue-this->m_Value;//判断取了前后的余额
if(currentvalue>=0)//取钱少于账户值
{
pAccount->Withdraw(this->m_Value);//取钱
pAccount->get_CurrentAccount(&(this->m_CurrentValue));// 得到对象当前的余额
UpdateData(false);
}else
{
MessageBox("余额不够!");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
f、 在OnDestroy()中编写代码,对组件库和接口的释放
//////////////////////////////////////////////////////////////////////////////////////////////////////
//账户接口释放
pAccount->Release();
///组件释放
CoUninitialize();
//////////////////////////////////////////////////////////////////////////////////////////////////////
g、 编译成功后,即可完成客户端的实现,我们就可以利用组件对象提供的服务了(图五为存入100元,图六取10元)
图五、存入100元情形 图六、取10元情形
总结:
本文通过对在VC下如何实现COM编程作了一个实例性的介绍,并在windows 2000/98下编译通过。虽然实现的功能很简单,但是对组件编写和对客户端编写的描述却是很详细的,希望对大家有所帮助,同时希望大家提出好的想法和我一起交流。
mail: hietour@sina.com 联系地址:江苏自动化所(连云港)一分箱803房 邮编:222006 海涛
正文完
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -