12.1.6 使用C++(www.cppentry.com)API创建COM对象
1.问题阐述
COM的设计目的,是要能实现跨语言的调用,既然是跨语言的,那么组件的接口描述就必须在任何语言环境中都要能够认识。如何实现模块之间的调用呢?
2.实现技巧
微软使用了一个新的文件格式IDL文件(接口定义描述语言)。IDL是一个文本文件,IDL经过编译,生成二进制的等价类型库文件 TLB 提供给其他语言来使用。
调用组件程序的方法可以有#include 头文件的方法和#import 类库方法。COM对象的创建由API函数CoCreateInstance完成,其函数原型声明如下:
STDAPI CoCreateInstance( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv ); |
第一个参数是组件的CLSID,第二个参数聚合时才能用到,第三个参数为进程内的服务,第四个参数为接口的IID,第五个参数返回接口的指针。
3.实例代码
本实例演示了如何调用COM组件。首先生成一个COM组件,然后再生成一个主程序调用COM组件。
在上面的建立的COM组件中增加两个函数,其中一个完成一个简单的加法运算,另一个完成一个字符串的连接任务,代码如下。
完成加法任务的函数代码:
STDMETHODIMP CFun::Add(long a, long b, long *retval) { // TODO: 在此添加相关代码 *retval = a+b; return S_OK; }
|
完成字符串连接的代码:
STDMETHODIMP CFun::CatString(BSTR str1, BSTR str2, BSTR *pretval) { // TODO: 在此添加相关代码 int nLen1 = ::SysStringLen(str1); //计算BSTR字符串的长度 int nLen2 = ::SysStringLen(str2); *pretval = ::SysAllocStringLen(str1,nLen1+nLen2); //申请nLen1+nLen2长度的内存区域,将字符串存进去 if(nLen2>0) { ::memcpy(pretval+nLen1,str2,sizeof(WCHAR)*nLen2); //将str2复制进申请的内存缓冲区 } return S_OK; }
|
调用组件的代码,首先将组件ID、接口ID及接口的函数集包含在工程中,代码如下:
#include "..\Object\OBJECT.h" #include "..\Object\OBJECT_i.c"
|
调用过程如下所示:
/*将BSTR转换为CString*/ CString convert(BSTR b) { CString s; if(b == NULL) return s; // empty for NULL BSTR #ifdef UNICODE s = b; #else LPSTR p = s.GetBuffer(SysStringLen(b) + 1); ::WideCharToMultiByte(CP_ACP, 0, b, -1, p, SysStringLen(b)+1, NULL, NULL); s.ReleaseBuffer(); #endif return s; } void CExample1Dlg::OnExeBtn() { // TODO: 在此添加相关代码 ::CoInitialize( NULL ); UpdateData(TRUE); IFun* pFun = NULL; IUnknown* pUn = NULL; try { HRESULT hr = ::CoCreateInstance( CLSID_Fun, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID*)&pUn ); if(FAILED(hr)) { MessageBox("请您注册组件"); return; } hr = pUn->QueryInterface(IID_IFun,(LPVOID*)&pFun); if(FAILED(hr)) { MessageBox("检查接口是否存在"); return; } pFun->Add(m_add1,m_add2,&m_add3); BSTR bstr1 = m_strCat1.AllocSysString(); BSTR bstr2 = m_strCat2.AllocSysString(); BSTR bstr3; pFun->CatString(bstr1,bstr2,&bstr3); m_strCat3 = convert(bstr3); UpdateData(FALSE);
} catch ( LPCTSTR lpErr) { AfxMessageBox(lpErr); } pUn->Release(); //释放接口 pFun->Release(); //释放接口 ::CoUninitialize(); } |
使用API创建接口实例,最后不要忘记了加接口指针Realease。大部分COM的API都是以Co开头的,这个前缀是COM Object的缩写。
【责任编辑:
夏书 TEL:(010)68476606】