用C实现的一个根基COM接口IFoo(二)
当前位置:以往代写 > C/C++ 教程 >用C实现的一个根基COM接口IFoo(二)
2019-06-13

用C实现的一个根基COM接口IFoo(二)

用C实现的一个根基COM接口IFoo(二)

副标题#e#

在C实现COM接口系列1中实现的com接口IFoo与利用它的客户耦合在一起,没有实此刻各自疏散的模块,因此不切合模块化编程思想。本期添加类厂支持,以使接口的实现与接口的利用相疏散。

—————————————————

类厂的浸染到底是什么?

将接口的实现与客户利用分分开来吗?

不尽然。利用CoCreateInstance,客户可以完全不必知道类厂的存在,而建设组件,获取组件实现的接口并利用。

即COM库可以完全抛开类厂的观念,而是提供一个这样的函数原型:CoCreateObject(REFID rclsid,……,REFID riid,void **ppItf);用户在挪用的时候可以对riid提供IID_Unknown可能特定于该工具的一个接口,直接获取该工具的IUnknown或特定的接口指针。

可以看到,这正是CoCreateInstance所作的工作。

1 类厂提供了间接建设类工具的方法:用户可以先获取并持有类厂接口指针,通过该指针所指向的类厂接口建设类工具。合用于需要建设多个(或反复建设)类工具的处所,淘汰了每次都要定位工具库并把工具库装入内存的开销。

2 类厂提供了担保组件库留在内存不被卸载出去的另一种要领:类厂接口函数LockServer.组件库维护一个库范畴计数器,只有该计数器为0时,组件库才答允本身被卸载出内存。(与此相对,引用计数是类工具范畴的,通过该类实现的各个接口来维护。假如一个类工具的引用计数到达0,那么该工具占有的内存就被释放,该工具上的接口指针也不再有效。)

除了挪用LockServer锁定组件库以外,当建设的组件个数大于0时,组件库也不能被卸载。也可以说,挪用一次LockServer()的浸染相当于建设了一个组件。

———————————————————————–

客户一侧:1 利用一个接口需要知道哪些信息?

备选:接口IID类工具(类厂)CLSID(或ProgID)

接口函数原型(参数个数,范例,返回值)

实现接口组件的线程模子(历程内、历程外、长途)?

范例库typelib信息?

处事一侧:2 实现一个组件和接口以供客户挪用,需要提供哪些对象?

备选:所有客户利用组件和接口所需的内容特另外尚有:

——————————————————————–

为dll添加。def文件与直接在需要导出的函数界说处指定_declspec( dllexport )有区别吗?假如有是什么区别?


#p#副标题#e#

我发此刻outdll.c中这样指定:

__declspec( dllexport ) HRESULT DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)

会发生编译错误:

1>------ Build started: Project: outside, Configuration: Debug Win32 ------
1>Compiling...
1>outdll.c
1>d:\outside-cf\outside\outdll.c(19) : error C2375: 'DllGetClassObject' : redefinition; different linkage
1> c:\program files\microsoft visual studio 8\vc\platformsdk\include\objbase.h(833) : see declaration of 'DllGetClassObject'
1>Build log was saved at "file://d:\outside-cf\outside\Debug\BuildLog.htm"
1>outside - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

c2375的表明意思是堕落的函数利用的链接指示符与之前声明的差异。

Compiler Error C2375

‘function’ : redefinition; different linkage

The function is already declared with a different linkage specifier.

objbase.h中声明白DllGetClassObject()函数:

STDAPI  DllGetClassObject(IN REFCLSID rclsid, IN REFIID riid, OUT LPVOID FAR* ppv);

而利用。def文件就没有问题。

—————————————————————————–

#p#副标题#e#

初次执行功效:

问题就是总有一个分派的内存没有释放:

用C实现的一个根本COM接口IFoo(二)

按照打印出来的内存地点可以判定,应该是先建设的类厂工具的内存没有释放。

查抄代码,main()中并没有健忘挪用Release(pCF)释放类厂工具。打印Release(pCF)的返回值,发明是1,即在类厂接口指针上少挪用了一次Release,那么,毕竟是那边少的呢?

main()函数中有关类厂工具引用计数的处所就是CoGetClassObject和Release(CreateInstance跟类厂本身的引用计数无关),这是一对增加引用计数和淘汰引用计数的对应操纵,所以,main()中应该没有问题。

那么,就只有建设类厂工具的时候了。下面看一下类厂工具是如何建设的。

首先,main挪用CoGetClassObject,该函数就挪用dll中的DllGetClassObject.由于是第一次挪用(不思量其他客户利用该dll的环境),措施执行到CreateClassFactory(……),该函数执行完后,类厂工具的引用计数是1.

#p#分页标题#e#

由于建设乐成,因此继承向下执行到QueryInterface,此时,类厂工具的引用计数酿成了2.然后,DllGetClassObject返回,com库函数CoGetClassObject也应该返回。留意,此时的类厂工具引用计数已经是2了!

#p#副标题#e#

因此,问题就出在这里。main挪用一次CoGetClassObject后,类厂工具的引用计数是2,而不是我想向中的1.于是,后头挪用一次Release也就虽然无法释放掉类场工具了。

1 HRESULT DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)

  2 {

  3     *ppv = 0;

  4     if (IsEqualCLSID (rclsid, &CLSID_Outside))

  5     {

  6

  7         if (!vpcfOutside)

  8

  9         {

  10

  11             HRESULT hr = CreateClassFactory (&CLSID_Outside, CreateOutside,

  12                                              &IID_IClassFactory, &vpcfOutside);

  13

  14                 if (hr != NOERROR)

  15

  16                     return hr;

  17         }

  18

  19         return QueryInterface (vpcfOutside, riid, ppv);

  20

  21     }

  22

  23     return E_FAIL;

  24 }

#p#副标题#e#

找到了原因,纠正就很容易了。这里我以为需要把DllGetClassObject作如下修改:

1 HRESULT DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)

  2 {

  3     *ppv = 0;

  4     if (IsEqualCLSID (rclsid, &CLSID_Outside))

  5     {

  6

  7         if (!vpcfOutside)

  8

  9         {

  10

  11             HRESULT hr = CreateClassFactory (&CLSID_Outside, CreateOutside,

  12                                              &IID_IClassFactory, &vpcfOutside);

  13

  14                 if (hr != NOERROR)

  15

  16                     return hr;

  17

  18     if(IsEqualIID(riid,&IID_IClassFactory))

  19     {

  20      *ppv = vpcfOutside;// Set *ppv to vpcfOutside directly instead of QueryInterface if first time creation

  21      return NOERROR;

  22     }

  23     else

  24     {

  25      Release(vpcfOutside);// Any interface requested (riid) other than IID_ClassFactory and IID_Unknown not support by class factory,

  26                           // call Release to free the memory.

  27      return E_FAIL;

  28     }

  29

  30         }

  31

  32         return QueryInterface (vpcfOutside, riid, ppv);

  33

  34     }

  35

  36     return E_FAIL;

  37 }

修改后在执行,内存都正常释放了。

用C实现的一个根本COM接口IFoo(二)

——————————————————————————————-

#p#副标题#e#

CreateClassFactory代码说明

1 HRESULT CreateClassFactory (REFCLSID rclsid,

  2     HRESULT (*pfnCreate)(IUnknown *, REFIID, void **),

  3     REFIID riid, void **ppv)

  4 {

  5     ClassFactory *this;

  6     HRESULT hr;

  7

  8     *ppv = 0;

  9     if (hr = Alloc (sizeof (ClassFactory), &this))

  10     return hr;

  11

  12     this->icf.lpVtbl = &vtblClassFactory;

  13     this->cRef = 1;  // After this call, cRef==1

  14

  15     this->pfnCreate = pfnCreate;

  16

  17     hr = QueryInterface (&this->icf, riid, ppv);  // After this call, cRef==2

  18     Release (&this->icf);  // Corresponds to "this->cRef = 1", ater this call, cRef==1

  19

  20     return hr;

  21 }

#p#分页标题#e#

可以看到,两行代码的结果是对引用计数增1及减1,这两行代码执行后,对引用计数的影响相互抵消,便是没有改变引用计数。那么,把这两行同时注释掉,是不是可以呢?

我的答复是:在本例中可以。因为这两行代码之间的QueryInterface老是可以执行乐成的(因为是用IDD_ClassFactory来挪用该函数的)。所以,即便把这两行代码同时注释掉,CreateClassFactory执行竣事后,类厂工具的引用计数也增了1,今后挪用Release就可以释放掉类厂工具占用的内存。

可是,假如CFQueryInterface的代码编写中除了错误,好比,像这样写:

1 static HRESULT CFQueryInterface (IClassFactory *pcf, REFIID riid, void **ppv)

  2 {

  3     ClassFactory *this = IMPL (ClassFactory, icf, pcf);

  4

  5     if (IsEqualIID (riid, &IID_IUnknown) ||

  6 //            IsEqualIID (riid, &IID_IClassFactory))   // Comment out this condition to create an error

  7         *ppv = &this->icf;

  8     else

  9     {

  10         *ppv = 0;

  11         return E_NOINTERFACE;

  12     }

  13

  14     AddRef ((IClassFactory *)*ppv);

  15

  16     return NOERROR;

  17 }

那么,这两行代码之间的QueryInterface就会执行堕落,那么类厂工具占用的内存就永远没有时机释放了。

也就是说,AddRef和Release固然在浸染上对引用计数来说彼此抵消,但Release函数提供了释放工具内存的时机(当引用计数为0时),假如不成对的挪用他们,也就失去了打点工具内存(释放工具占用的内存)的时机。

—————————————————————————

组件库outside文件说明:

IFoo.h      IFoo接口声明

outside.c   组件工具、IFoo接话柄现

cf.c        类厂工具、IClassFactory接话柄现

outdll.c    组件库导出函数实现

outside.def 组件库模块界说文件,导出函数声明

outside.reg 组件库注册文件

    关键字:

在线提交作业