ArcGIS Engine 开发 (三)COM技术中的QueryInterface(接口查询)的实现原理和IUnknown接口

IUnknown

IUnknown 接口是组件对象模型(COM)中的基础接口。COM规格书中规定COM对象至少要实现此一接口,而且其他所有的COM接口都需要派生自IUnknown接口。
IUnknown提供所有COM对象都支持的两种基本特性:

  • 利用引用计数来进行对象生命周期管理;(控制对象生命周期
  • 以及访问许多事先定义的接口。(接口查询

IUnknown接口会包括一个指向虚拟方法表(英语:virtual method table)的指针,虚拟方法表是一个有许多函数指针的列表,函数指针会指向许多实现IUnknown接口所宣告的函数,以和接口中宣告顺序相同的方式排列。而进程内调用产生的开销(英语:Overhead (computing))大致和C++中调用虚拟方法的开销相近。

方法


对象生存期由两个方法控制,AddRefRelease,以及内部引用计数器。每个对象都必须具有IUnknown的实现来控制其生命周期。无论何时创建或复制接口指针,都会调用AddRef方法,当客户端不再需要此指针时,将调用相应的Release方法。当引用计数达到零时,对象会自行销毁。
客户端还使用IUnknown获取对象上的其他接口。QueryInterface是客户端在需要对象上的另一个接口时调用的方法。当客户端调用QueryInterface时,该对象提供一个接口并调用AddRef。实际上,任何COM方法都有责任返回一个接口来代表调用者递增对象的引用计数。当不再需要该接口时,客户端必须调用Release方法。仅当接口重复时,客户端才会显式调用AddRef
IUnknown接口中有三个方法QueryInterface, AddRef, and Release

[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("00000000-0000-0000-C000-000000000046")]
public interface IUnknown
{
    [PreserveSig]
    int QueryInterface(ref Guid riid, out IntPtr ppvObject);
    [PreserveSig]
    uint AddRef();
    [PreserveSig]
    uint Release();
}
  • QueryInterface可以让调用此对象的程序可以确认此对象是否支持特定的接口,若是支持,则参考到此对象在特定接口下的实现。这个方法类似C++的dynamic_cast<>或是Java或是C#的casts。此方法在给定一个对应特定接口的全局唯一标识符(一般也称为接口标识符或是IID,如AE中GUID)时,可以提供一个指定特定接口的指针。若COM对象不支持此接口,会回复E_NOINTERFACE错误。
  • AddRef是在新的客户端程序要访问此对象时,让计数值加一,会回传更新后的计数值。
  • Release是在客户端程序已结束访问此对象,让计数值减一,会回传更新后的计数值,若计数值已变为零,会自动删除此一COM对象。

IUnknown本身的接口标识符为{00000000-0000-0000-C000-000000000046},IUnknown的三个方法都是纯虚拟方法(宣告时都有加上= 0),因此无法定义IUnknown类别的对象,需要由其他类别继承IUnknown,才能定义对应类别的对象。

QueryInterface


QueryInterface方法通常用首字母缩写词 QI 表示。

在开发COM对象时,开发人员必须遵守QueryInterface的规则。这些规则规定对象的接口是对称的,可传递的和反身的,并且在对象的生命周期中始终可用。对于客户端,这意味着给定对象的有效接口,通过对QueryInterface的调用,询问该对象对该对象(包括其自身)的任何其他接口始终是有效的。由于时间或安全性限制,不可能支持接口并随后拒绝访问该接口。

QueryInterface的规则规定对象的接口是自反的,对称的和可传递的。始终可以在对象上保持有效的接口指针,以获取该对象上的任何其他接口。

当请求特定接口时,QueryInterface方法可以为该请求的接口返回已分配的内存块,或者它可以分配新的内存并返回该内存。必须返回同一块内存的唯一情况是请求IUnknown接口。比较两个接口指针以查看它们是否指向同一个对象时,重要的是不执行简单的比较。要正确比较两个接口指针以查看它们是否属于同一对象,必须同时查询它们的IUnknown接口,并且必须对IUnknown指针执行比较。 通过这种方式,IUnknown接口被称为定义COM对象的标识。

由于IUnknown是所有COM对象的基础,因此通常在任何ArcObjects文档和类图中都没有对IUnknown的引用。

在AE中接口跳转方法

IMap pMap;
IActiveView pActiveView;
pMap = axMapControl1.Map;
...
pActiveView = pMap as IActiveView;//方法一
// pActiveView = (IActiveView) pMap;//方法二

重点:接口跳转原理

如果上面对QI的概念写的有点太过于书面,那我们以一个实际的例子来解释接口查询:

IPolygon pPolygon = new PolygonClass();
double Length;
Length = pPolygon.Length;
IArea pArea= pPolygon as IArea;//这里由IPolygon接口查询到IArea
double shapeArea;
shapeArea = pArea.Area;

在解释之前我们必须明白接口本身只是一种规范,是对其实现类(这里为PolygonClass),里的属性和方法调用的一种规范。

这里我们首先申明了IPolygon pPolygon,申明了一个名为pPolygon的接口变量,接口变量实质就是用于存放某个内存地址的变量,但又和一般的地址变量不同,因为在使用接口变量时必须符合该接口的规范。

接口变量pPolygon就存放了PolygonClass()对象在内存中的首地址,可以理解为pPolygon指向了某Polygon对象。并且在使用pPolygon变量时,必须符合IPolygon接口的规范,比如只有Length属性,而没有Area属性,且Length属性为只读。
pArea = pPolygon as IArea; 就是把Polygon的首地址赋给pArea,但pArea变量的使用由必须符合IArea接口的规范。

因此所谓QI就是把某对象的内存首地址赋给不同的接口变量,以便根据不同的接口规范去访问不同的属性和方法。但是转来转去,实际还是在一个对象上(PolygonClass()),或者说是在一个分配的内存上,而是否能够进行接口查询,实际上就是比较两个接口指针以查看它们是否指向同一个对象,就要借用到IUnknown接口。


参考:
ArcObjects文档:
http://desktop.arcgis.com/en/arcobjects/latest/net/webframe.htm#4a16457c-3df5-4303-bd81-e967c9bfe4d9.htm
IUnknown:
https://www.beichengjiu.com/computerscience/340351.html#
https://bbs.csdn.net/topics/380102611

你可能感兴趣的:(ArcGIS,Engine)