下载本文示例代码
组件的启动和释放 在第三回中,大家用“小本本”记录了一个原则: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]
调用者
调用者
组件可以重新分配内存共2页。 1 2 :
组件的启动和释放 在第三回中,大家用“小本本”记录了一个原则: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]
调用者
调用者
组件可以重新分配内存共2页。 1 2 :
下载本文示例代码
COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件COM 组件设计与应用之简单调用组件
阅读(255) | 评论(0) | 转发(0) |