手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>程序设计>C/C++>列表

COM组件设计与应用之简单调用组件

来源:互联网 作者:西部数码 时间:2008-04-09
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!
  组件的启动和释放

  在第三回中,大家用“小本本”记录了一个原则:COM 组件是运行在分布式环境中的 。于是,如何启动组件立刻就遇到了严重的问题,大家看这段代码:
      p = new 对象; 
      p->对象函数(); 
      delete p;

  这样的代码再熟悉不过了,在本地进程中运行是不会有问题的。但是你想想,如果这个对象是在“地球另一边”的计算机上,结果会如何?嘿嘿,C 在设计 new 的时候,可没有考虑远程的实现呀(计算机语言当然不会,也没必要去设计)。因此启动组件、调用接口的功能,当然就由 COM 系统来实现了。


图一 组件调用机制

  由上图可以看出,当调用组件的时候,其实是依靠代理(运行在本地)和存根(运行在远端)之间的通讯完成的。具体来说,当客户程序通过 CoCreateInstance() 函数启动组件,则代理接管该调用,它和存根通讯,存根则它所在的本地(相对于客户程序来说就是远程了)执行 new 操作加载对象。对于初学者,你可以不用理它,代理和存根对我们来说是透明的。只要大约知道是怎么一回事就一切OK了。

  问题又来了,这个远程的对象什么时候消灭呢?在第二回介绍接口概念的时候,当时我们特意忽略了两个函数,就是IUnknown::AddRef()和IUnknown::Release(),从函数名就能猜到了,一个是对内部引用记数器(Ref)加1,一个是释放(减1),当记数器减为0的时候,就是释放的机会啦。看起来很复杂,没办法,因为这是在介绍原理。其实在我们写程序的时候到比较简单,请大家遵守几个原则:

  1、启动组件得到一个接口指针(Interface)后,不要调用AddRef()。因为系统知道你得到了一个指针,所以它已经帮你调用了AddRef()函数;

  2、通过QueryInterface()得到另一个接口指针后,不要调用AddRef()。因为......和上面的道理一样;

  3、当你把接口指针赋值给(保存到)另一个变量中的时候,请调用AddRef();

  4、当不需要再使用接口指针的时候,务必执行Release()释放;

  5、当使用智能指针的时候,可以省略指针的维护工作;(注1)

  内存分配和释放

  自从学习了C语言,老师就教导我们说:对于动态内存的申请和释放,一定要遵守“谁申请,谁释放”的原则。在此原则的指导下,不仅是我、不仅是你,就连特级大师都设计了这样怪怪的函数:
  函数 说明 评论 GetWindowText(HWND,LPTSTR,int) 取得窗口标题。需要在参数中给出保存标题所使用的内存指针,和这块内存的尺寸。 晕!我又不知道窗口标题的长度,居然还要我提供尺寸?!没办法,只能估摸着给一个大一些的尺寸吧。 sprintf(char *,const char *,...) 格式化一个字符串。这个函数不用给出缓冲区的长度啦。 恩,虽然不用给出长度了,但你敢给个小尺寸吗?哼! int CListBox::GetTextLen(int)
CListBox::GetText(int,LPTSTR) 取得列表窗中子项目的标题。需要调用两个函数,先取得长度,然后分配内存,再实际取得标题内容。 真烦!

  说实在的,不但函数调用者感觉别扭,就连函数设计者心情也不会爽的,而这一切都是为了满足所谓“谁申请,谁释放”的原则。 解决这个问题最好的方式就是:函数内部根据实际需要动态申请内存,而调用者负责释放。这虽然违背了上述原则,但 COM 从方便性和效率出发,确实是这么设计的。
    C语言 C 语言 Windows 平台 COM IMalloc 接口 BSTR 申请 malloc() new GlobalAlloc() CoTaskMemAlloc() Alloc() SysAllocString() 重新申请 realloc()   GlobalReAlloc() CoTaskRealloc() Realloc() SysReAllocString() 释放 free() delete GlobalFree() CoTaskMemFree() Free() SysFreeString()

  以上这些函数必须要按类型配合使用(比如:new 申请的内存,则必须用 delete 释放)。在 COM 内部,当然你可以随便使用任何类型的内存分配释放函数,但组件如果需要与客户进行内存的交互,则必须使用上表中的后三类函数族。

  1、BSTR 内存在上回书中,已经有比较丰富的介绍了,不再重复;

  2、CoTaskXXX()函数族,其本质上就是调用C语言的函数(malloc...);

  3、IMalloc 接口又是对 CoTaskXXX() 函数族的一个包装。包装后,同时增强了一些功能,比如:IMalloc::GetSize()可以取得尺寸,使用 IMallocSpy 可以监视内存的使用;

  参数传递方向

  在C语言的函数声明中,尤其当参数为指针的时候,你是看不出它传递方向的。比如:
void fun(char * p1, int * p2); 请问,p1、p2 哪个是入参?哪个是出参?甚或都是入参或都是出参?由于牵扯到内存分配和释放等问题,COM 需要明确标注参数方向。以后我们写程序,就类似下面的样子:

      HRESULT Add([in] long n1, [in] long n2, [out] long *pnSum);  // IDL文件(注2) 
      STDMETHOD(Add)(/*[in]*/ long n1, /*[in]*/ long n2, /*[out]*/ long *pnSum);  // .h文件
  如果参数是动态分配的内存指针,那么遵守如下的规定:
  方向 申请人 释放人 提示 [in] 调用者 调用者 组件接收指针后,不能重新分配内存 [out] 组件 调用者 组件返回指针后,调用者“爱咋咋地”(注3) [in,out] 调用者 调用者 组件可以重新分配内存

  示例程序

  示例一、由 CLSID 得到 ProgID。(程序以 word 为例子。如果运行不正确,嘿嘿,你没有安装 word 吧?)

 ::CoInitialize( NULL ); 

 HRESULT hr; 
 // {000209FF-0000-0000-C000-000000000046} = word.application.9 
 CLSID clsid = {0x209ff,0,0,{0xc0,0,0,0,0,0,0,0x46}}; 
 LPOLESTR lpwProgID = NULL; 
  
 hr = ::ProgIDFromCLSID( clsid, &lpwProgID ); 
 if ( SUCCEEDED(hr) ) 
 { 
  ::MessageBoxW( NULL, lpwProgID, L"ProgID", MB_OK ); 

  IMalloc * pMalloc = NULL; 
  hr = ::CoGetMalloc( 1, &pMalloc );  // 取得 IMalloc 
  if ( SUCCEEDED(hr) ) 
  { 
   pMalloc->Free( lpwProgID );  // 释放ProgID内存 
   pMalloc->Release();          // 释放IMalloc 
  } 
 } 

 ::CoUninitialize();

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

热点关注
IDC资讯 虚拟主机 域名注册 托管租用 vps主机 智能建站
网站运营 建站经验 策划盈利 搜索优化 网站推广 免费资源
网站联盟 联盟新闻 联盟介绍 联盟点评 网赚技巧
行业资讯 业界动态 搜索引擎 网络游戏 门户动态 电子商务 广告传媒
网络编程 Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术 Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷 Internet Explorer
网页制作 FrontPages Dreamweaver Javascript css photoshop fireworks Flash
程序设计 Java技术 C/C++ VB delphi
网络知识 网络协议 网络安全 网络管理 组网方案 Cisco技术
操作系统 Win2000 WinXP Win2003 Mac OS Linux FreeBSD
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 价格总览 | 资讯中心 | 友情链接 | 网站地图 | 招贤纳士 | RSS