AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > VC编程

COM技术纵横谈

51自学网 2015-08-30 http://www.wanshiok.com

  二:什么是接口

  前面已经提到过,COM组件与客户大家打交道的唯一办法是通过接口。在C++的实现中,我们一般用抽象基类来定义接口,然后利用C++类的多重继承实现该组件。下面给出一个简单的示意:

////////////////
// iface.h
////////////////

#ifndef IFACE_H
#define IFACE_H 1

#define interface class

interface IA
{
public:
    virtual func1() = 0;
    virtual func2() = 0;
};

interface IB
{
public:
    virtual func3() = 0;
    virtual func4() = 0;
};

#endif

//////--iface.h end--//////


////////////////
// test.c
////////////////

#include "iface.h"

class Ca : public IA, IB
{
public:
    Ca(int i) : m_Count(i) {}
    virtual func1() { cout << "IA::func1 is " << m_Count * 1 << endl; } virtual func2() { cout << "IA::func2 is " << m_Count * 2 << endl; } virtual func3() { cout << "IB::func3 is " << m_Count * 3 << endl; } virtual func4() { cout << "IB::func4 is " << m_Count * 4 << endl; } int m_Count; }; main() { IA* pIa; IB* pIb; Ca* pCa="new" Ca(2); pIa="pCa;" pIa> func1();
    pIa -> func2();
    pIb -> func3();
    pIb -> func4();
    delete pCa;
}

//////--test.c end--//////

  上例中,定义了IA,IB两个接口,你可以注意到他们所有的成员函数都被声明为virtual,并且在函数末尾用 = 0 做了结束。类似这样的函数我们在C++中称之为纯虚函数,如果整个的类都由纯虚函数组成,那么这个类就叫做抽象基类。抽象基类本身由于没有实体函数与变量,所以并不分配内存。一般它的用途是为派生类指定内存结构。打个比方来说,就好像把房子分割成很多小间,规定以后哪些小间应该放什么(函数的实体)但具体的东西则要等派生类来填放。

  这里有一个概念需要说明一下:组件并不是类,上面我们用一个类就实现了两组接口,同样我们也可以用它来实现更多接口。组件本身其实只是一个接口集及其实现的集合。一个组件可能包含了多个接口,每一个接口都有各自的实现。同时,接口并非总是继承的,COM规范没有要求实现某个接口的类必须从那个接口继承。这是因为客户并不了解COM组件的继承关系。对接口的继承只不过是一种实现细节而已。

  下面将介绍QueryInterface函数。这个函数被用来查询其他接口。客户于组件之间的通讯是通过接口完成的。哪怕是客户查询其他一个组件时,也需要通过一个接口(换而言之,如果一个组件不支持这个接口,那他一定不是一个COM组件)这个接口的名字叫IUnknown,它有三个函数,如下所示:

interface IUnknown
{
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) = 0;
    virtual ULONG __stdcall AddRef() = 0;
    virtual ULONG __stdcall Release() = 0;
};

  COM组件的所有接口都继承了IUnknown,这样一来,每一个接口的前三个函数都是QueryInterface,这就是的所有的COM接口都可以被当成是IUnknown来处理。客户只要通过一个CoCreateInstance函数就可以创建该组件的实例并且获取其IUnknown*。

HRESULT __stdcall CoCreateInstance(
            const CLSID& clsid,
            IUnknown* pIUnknownOuter,
            DWORD dwClsContext,
            const IID& iid,
            void** ppv
         );
下面的CODE演示创建一个组件:

extern "C" const GUID CLSID_COM1 = (
        0x32bb8230, 0xb41b1 0x11cf, 0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82 );

extern "C" const GUID IID_IX = (
        0x32bb8230, 0xb591c 0x11ff, 0xc1, 0xb0, 0xc7, 0xf8, 0x21, 0x35, 0x1c, 0x2f );

CoInitialize();

Ia* pIx = NULL;
HRESULT hr = ::CoCreateInstance(
                CLSID_COM1,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IX,
                (void**) &pIx
            );

if (SUCCEEDED(hr))
{
    pIx -> Fx();
    pIx -> Release();
}

  extern "C" const GUID其实是所谓的"全局唯一标示符"(Globally Unique Identifier)。我们规定用它来表示不同的接口。换而言之,如果你发现有两个GUID完全相同,你完全有理由相信他们标示的是同一个接口。(有专门的算法来产生该结构,确保它在时间和空间上都是唯一的。) 接下来的CoInitialize函数初始化COM库。这一步是非常重要的,如果没有初始化,以后进行的操作都将失败。

 

 
 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。

上一篇:用VC进行COM编程所必须掌握的理论知识  下一篇:通过COM技术实现Windows外壳编程