荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: oopilix (冬眠的抽象实例), 信区: Visual
标  题: [zz]《COM技术内幕》学习笔记(3)
发信站: 荔园晨风BBS站 (Mon Oct 20 13:27:38 2003), 站内信件

原作者姓名 雷神
文章原始出处 http://www.ai361.com

介绍
当初在看《设计模式》时为什么体会不到呢?看来需要重新在去仔细研读《设计模
式》一书了。


正文
第三部分
融会贯通才是我们应该追求的学习最高境界
    第7章的标题:类厂。我一下子想到了《设计模式》的Factory模式。当我读完
这几章后,忽然觉得COM就是《设计模式》一书中多种模式的应用。继续回想《设
计模式》的23种模式,加上最近对MFC的深入探索,一下子有了些新的领悟,很显
然MFC也用到《设计模式》的多种模式。当初在看《设计模式》时为什么体会不到
呢?看来需要重新在去仔细研读《设计模式》一书了。

FAQ21:创建组件的最简单的方法是什么?
FAQ22:什么是类厂?
FAQ23:如何获得类厂的接口指针?
FAQ24:什么是IClassFactory?
FAQ25:类厂有什么特性?
FAQ26:类厂是如何被创建的?
FAQ27:类厂的其他问题?


Question:
    创建组件的最简单的方法是什么?
Answer:
    COM库包含一个用于创建组件的函数:CoCreateInstance;
    CoCreateInstance函数可以通过参数CLSID创建相应的组件的一个实例,并返
回组件实例的某个接口。
    CoCreateInstance的声明如下:
    HRESULT __stdcall CoCreateInstance(
        const CLSID& clsid,            ///待创建组件的CLSID
        IUnknown* pIUnknownOuter,    ///用于组件聚合(外部组件通过此参数
向内部组件传递IUnknow)
        DWORD dwClsContext,        ///限定所创建组件的执行环境
        const IID& iid,                ///组件上待使用接口的IID
        void ** ppv                    ///返回上一参数指定的接口指针
    );

实例说明:(创建一个组件和一个客户)
接口声明部分:
///
///IFACE.H
///
interface IX:IUnknown {virtual void __stdcall Fx()=0;}
interface IY:IUnknown {virtual void __stdcall Fy()=0;}
interface IZ:IUnknown {virtual void __stdcall Fz()=0;}
extern “c”
{
    extern const IID IID_IX;  ///接口ID定义在GUIDS.CPP中
    extern const IID IID_IY;  ///可以使用GUIDGEN.EXE工具(MSDN中有源代码
)生成一个GUID。
    extern const IID IID_IZ;  ///将所生成的GUID粘贴到一个头文件中。
}
组件实现部分:
///
///component1.cpp
///
#include <iostream.h>
#incoude <objbase.h>
#include “Iface.h”

void trace(const char* msg){cout<<”组件1”<<msg<<endl;}
class CA:public IX
{
    virtual HRESULT __stdcall QueryInterface(const IID& iid,void **ppv);
    ///查询接口指针
    virtual ULONG __stdcall AddRef();        ///接口引用记数加
    virtual ULONG __stdcall Release();        ///接口引用记数减
    virtual void __stdcall Fx(){cout<<”Fx”<<endl;}
public:
    CA():m_Ref(0){}
    ~CA(){trace(“析构自己”);}
private:
    long m_cRef;
};

HRESULT __stdcall CA::QueryInterface(const IID& iid,void **ppv)
{
    if(iid==IID_IUnknown)
    {
        trace(“返回指向IUnknown接口的指针.”);
        *ppv=static_cast<IX*>(this);
    }
    else if(iid==IID_IX)
    {
        trace(“返回指向IX接口的指针。”);
        *ppv=static_cast<IX*>(this);
    }
    else
    {
        trace(“没有可支持的接口。”);
        *ppv=NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown *>(*ppv)->AddRef();
    return S_OK;
}

ULONG __stdcall CA::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

ULONG __stdcall CA::Release()
{
    if(InterlockedIncrement(&m_cRef)==0)
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

extern “c” IUnknown* CreateInstance()
{
    IUnknown* pI=static_cast<IX*>(new CA);
    pI->AddRef();
    return pI;
}
客户代码:
///
/// Client.cpp
///
#include <iostream.h>
#include <objbase.h>
#include "Iface.h"
void trace(const char* msg) { cout << "Client: \t\t" << msg << endl ;}
int main()
{
    CoInitialize(NULL) ;    ///初始化COM库
    trace("调用CoCreateInstance ") ;
    trace("获得接口IX.") ;
    IX* pIX = NULL ;
    HRESULT hr = ::CoCreateInstance(CLSID_Component1,
                                    NULL,
                                    CLSCTX_INPROC_SERVER,
                                    IID_IX,
                                    (void**)&pIX) ;
    if (SUCCEEDED(hr))
    {
        trace("Succeeded getting IX.") ;
        pIX->Fx() ;          ///Use interface IX.
        trace("Ask for interface IY.") ;
        IY* pIY = NULL ;
        hr = pIX->QueryInterface(IID_IY, (void**)&pIY) ;
        if (SUCCEEDED(hr))
        {
            trace("Succeeded getting IY.") ;
            pIY->Fy() ;       /// Use interface IY.
            pIY->Release() ;
            trace("Release IY interface.") ;
        }
        else
        {
            trace("Could not get interface IY.") ;
        }
        trace("Ask for interface IZ.") ;
        IZ* pIZ = NULL ;
        hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ) ;
        if (SUCCEEDED(hr))
        {
            trace("Succeeded in getting interface IZ.") ;
            pIZ->Fz() ;
            pIZ->Release() ;
            trace("Release IZ interface.") ;
        }
        else
        {
            trace("Could not get interface IZ.") ;
        }
        trace("Release IX interface.") ;
        pIX->Release() ;
    }
    else
    {
        cout << "Client: \t\tCould not create component. hr = "
             << hex << hr << endl ;
    }
    CoUninitialize() ;    /// Uninitialize COM Library
    return 0 ;
}

    上面的伪码是一个最简单的纯客户和组件示例。它利用了COM库中的
CoCreateInstance函数,创建组件的过程是:传给CoCreateInstance函数一个
CLSID,然后创建相应的组件,并返回指向所请求的接口的指针。
    缺点是,利用CoCreateInstance客户无法灵活的控制创建组件的过程。想要控
制组件装载到内存中的何处或检查客户是否有权限来创建该组件已经不可能了。
    因此需要引出另一些创建组件的方法。

Question:
    什么是类厂?
Answer:
    类厂就是用来创建组件的组件。
    类厂唯一的功能就是创建其他组件。
    实际CoCreateInstance创建的不是COM组件,而是创建了类厂。
    特定的类厂将创建特定的CLSID相对应的组件。
    客户可以通过类厂支持的接口来对类厂创建组件的过程加以控制。
    创建组件的标准接口是IClassFactory.
    利用CoCreateInstance创建的组件实际上是通过IClassFactory.接口创建的。


Question:
    如何获得类厂的接口指针?
Answer:
    用COM库提供的CoGetClassObject函数。
    CoGetClassObject函数可以接收一个CLSID作为参数并返回相应类厂中某个接
口指针。
    CoGetClassObject函数的声明如下:
    HRESULT __stdcall CoGetClassObject(
        const CLSID& clsid,                ///待创建组件的CLSID
        DWORD dwClsContext,            ///限定所创建组件的执行环境
        COSERVERINFO * pServerInfo    ///DCOM用来控制远程组件的
        const IID& iid,                    ///组件上待使用接口的IID
        void ** ppv                        ///返回指向所需组件的类厂的指
针,通常是IClassFactory
    );

Question:
    什么是IClassFactory?
Answer:
    类厂所支持的用于创建组件的标准接口。
    大多数组件均可用这个接口来创建。
    IClassFactory有两个成员函数,一个是CreateInstance一个是LockServer。


Question:
    类厂有什么特性?
Answer:
    类厂的一个实例只能创建同某一个CLSID相对应的组件。
    与某一个CLSID相对应的类厂是
由组件开发人员实现的。
    类厂可以知道并且具有关于它所创建的组件的一些特殊知识。
    在实现类厂时可以使用这些特殊的知识。
    类厂的目的是知道如何创建相对应的组件,并将这一过程封装。


Question:
    类厂是如何被创建的?
Answer:
    CoGetClassObject需要DLL中的一个特定函数来创建类厂, 这个函数是
DllGetClassObject。
    客户通过调用CoGetClassObject来启动组件的创建过程。
    COM库实现CoGetClassObject函数。
    CoGetClassObject调用DLL的DllGetClassObject函数。
    DllGetClassObject函数创建类厂。
    类厂的创建方式由开发人员决定。
    类厂被创建后返回IClassFactory接口。
    通过此接口开发人员便可以控制创建组件了。

Question:
    类厂创建组件的其他问题?
Answer:
    对于组件的注册COM库提供以下函数:
    DllRegisterServer ///在注册表注册组件
    DllUnregisterServer ///在注册表注销组件
    DllMain ///DLL的MAIN
    DllMain将模块的句柄保存在全局变量g_hModule中,供注册/注销函数使用。

    类厂实例和CLSID对应关系是一对一的。
设计出色的类厂可以做到一个类厂实现完成所有的组件创建。
    COM库实现一个CoFreeUnusedLibraries的函数来释放不在需要的库所占的内存

    在程序空闲时,客户周期性的调用CoFreeUnusedLibraries函数。
    用DllCanUnloadNow函数来确定DLL是否可以被卸载掉。
    LockServer可以防止当想在某个函数作用域外使用IClassFactory指针时DLL已
被卸载掉了。
    调用LockServer(TRUE)可以锁住相应的服务器。
    LockServer(FALSE)可以对锁住的服务器解锁。





--


 ※ IP来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM oo.pi.li.x]
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 61.144.235.39]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店