首先应当明确,mfc中是通过嵌套类而不是多重继承来实现com接口的,通过接口映射机制将接口和实现该接口的嵌套类关联起来;mfc中提供一套简明的宏来实现嵌套类的定义.其次,mfc通过ccmdtarget类实现了iunknown接口.
本文首先描述创建一个com服务器的步骤和核心代码.然后说明客户程序关键代码.
此com服务器实现一个timelogserver组件,为简明起见,此组件只有一个接口itimelog,通过itimelog的方法outputlog可以将日志文本输出至日志文件.
创建一个mfc dll工程,选择支持automation(当然本程序不一定是自动化服务器,在这里这样做好处在于自动实现了几个必要的输出函数如dllgetclassobject,dllregisterserver等,否则要自己写)
第一节 com服务器
一. 声明组件和接口
1.写一个guids.h,在guids.h中声明组件和接口的guid
//声明组件guid {a433e701-e45e-11d3-97b5-52544cba7f28}
//define_guid(clsid_timelogserver,
//0xa433e701, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28);
static const iid clsid_timelogserver =
{0xa433e701, 0xe45e, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28}};
// 声明接口guid{a433e702-e45e-11d3-97b5-52544cba7f28}
//define_guid(iid_itimelog,
//0xa433e702, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28);
static const iid iid_itimelog =
{0xa433e702, 0xe45e, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28}};
2.写一个itimelogserver.h,在itimelogserver.h文件中声明组件和接口
//itimelogserver.h
#include "guids.h"
//接口itimelog的声明
declare_interface_(itimelog,iunknown)
{
stdmethod(outputlog)(bstr* varlogtext)pure;
};
说明:1.宏define_guid将组件和接口的progid与guid相关联.可以用guidgen.exe工具产生.
2.宏declare_interface_声明接口;该宏第一个参数为接口名,第二个参数为该接口的基类.声明没有基类的接口用declare_interface宏.
3.宏stdmethod声明接口中的方法.此方法的返回值为hresult.pure被解释为"=0",即此方法为纯虚函数.当方法的返回值不是hresult时,用宏stdmethod_(返回类型,函数名)(参数)pure;
二.声明组件类ctimelogserver和实现接口的嵌套类
在classwizard中添加新类ctimelogserver,其基类选择为ccmdtarget.修改其头文件timelogserver1.h,加上#include "itimelogserver.h";同时在类声明体中加上
//声明实现itimelog接口的嵌套类 begin_interface_part(timelog,itimelog)//自动声明iunknown接口的三个方法 stdmethod(outputlog)(bstr* varlogtext); end_interface_part(timelog) //声明接口映射 declare_interface_map() //声明类厂 declare_olecreate(ctimelogserver)
三.实现类厂和接口映射
在ctimelogserver的实现文件中写入:
//实现类厂
implement_olecreate(ctimelogserver,"timelogserver",
0xa433e701, 0xe45e, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0x4c, 0xba, 0x7f, 0x28);
//映射接口到相应的嵌套类
begin_interface_map(ctimelogserver,ccmdtarget)
interface_part(ctimelogserver,iid_itimelog,timelog)
end_interface_map()
四.在组件的构造和析构函数中对全局对象计数
ctimelogserver::ctimelogserver()
{
::afxolelockapp();
}
ctimelogserver::~ctimelogserver()
{
::afxoleunlockapp();
}
五.为嵌套类实现iunknown接口
//为嵌套类而实现iunknown接口
stdmethodimp_(ulong)
ctimelogserver::xtimelog::addref()
{
method_prologue(ctimelogserver,timelog)
return pthis->externaladdref();
}
stdmethodimp_(ulong)
ctimelogserver::xtimelog::release()
{
method_prologue(ctimelogserver,timelog)
return pthis->externalrelease();
}
stdmethodimp
ctimelogserver::xtimelog::queryinterface(refiid riid,void**ppvobj)
{
method_prologue(ctimelogserver,timelog)
return pthis->externalqueryinterface(&riid,ppvobj);
}
说明:虽然ccmdtarget类已经实现了iunknown接口,但是还必须通过上述代码来将嵌套类的iunknown映射到ccmdtarget支持的iunknown接口.method_prologueh宏的两个参数分别是实现组件对象的类和实现接口的嵌套类.
六.实现itimelog接口的方法outputlog
注意本组件的功能是往日志文件中输入日志.
1. 在组件类中添加一个文件指针:
// attributes public: protected: file* m_logfile;
2. 初始化和退出
首先在ctimelogserver的构造函数中进行一些初始化:
ctimelogserver::ctimelogserver()
{
::afxolelockapp();
ctime timestamp = ctime::getcurrenttime();
cstring filename;
filename.format(_t("%s.log"),timestamp.format("%y%m%d"));
m_logfile = fopen(filename,_t("a"));
if(m_logfile)
{
fprintf(m_logfile,_t("# # # # # # # # # # # # # # # # # \n"));
fprintf(m_logfile,_t("开始于:%s"),(lpctstr)timestamp.format("%y年%m月%d日%h:%m %s"));
fprintf(m_logfile,_t("\n"));
}
}
//然后在析构函数中关闭文件
ctimelogserver::~ctimelogserver()
{
::afxoleunlockapp();
if(m_logfile)
{
ctime timestamp = ctime::getcurrenttime();
fprintf(m_logfile,_t("\n"));
fprintf(m_logfile,_t("结束于:%s"),(lpctstr)timestamp.format("%y年%m月%d日%h:%m %s"));
fprintf(m_logfile,_t("\n"));
fprintf(m_logfile,_t("# # # # # # # # # # # # # # # # #\n"));
fclose(m_logfile);
}
}
3. 实现接口itimelog方法
//实现接口itimelog方法
stdmethodimp
ctimelogserver::xtimelog::outputlog(bstr* varlogtext)
{
method_prologue(ctimelogserver,timelog)
if(pthis->m_logfile)
{
ctime timestamp = ctime::getcurrenttime();
cstring nowtime = timestamp.format("%y年%m月%d日%h:%m:%s");
cstring logtext((lpcwstr)*varlogtext);
fprintf(pthis->m_logfile,"\n%s\n%s\n%",nowtime,logtext);
return noerror;
}
else
{
afxmessagebox("没有日志文件!");
return s_false;
}
}
七.完善组件服务器
在应用对象ctimelogserverapp的 实现文件中,处理instance()和exitinstance()
bool ctimelogserverapp::initinstance()
{
::afxolelockapp();
// register all ole server (factories) as running. this enables the
// ole libraries to create objects from other applications.
coleobjectfactory::registerall();
return true;
}
int ctimelogserverapp::exitinstance()
{
// todo: a