新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 C/C++编程思想 』 → Microsoft组件对象模型的规则 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4552 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: Microsoft组件对象模型的规则 举报  打印  推荐  IE收藏夹 
       本主题类别: Description Logics    
     葛靖青001 美女呀,离线,快来找我吧!水瓶座1984-2-14
      
      
      等级:大三(研究MFC有点眉目了!)
      文章:168
      积分:595
      门派:XML.ORG.CN
      注册:2010/11/2

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给葛靖青001发送一个短消息 把葛靖青001加入好友 查看葛靖青001的个人资料 搜索葛靖青001在『 C/C++编程思想 』的所有贴子 点击这里发送电邮给葛靖青001 引用回复这个贴子 回复这个贴子 查看葛靖青001的博客楼主
    发贴心情 Microsoft组件对象模型的规则

    【转自互联网】


    摘要

      本文的目的是为使用和实行Microsoft的组件对象模型(COM)提供迅捷的参考。读者若想更好的理解什么是COM,以及隐藏在它的设计及体系中的动机,应该阅读开始的两章。它们是关于组件对象模型的技术说明书(MSDN库,技术说明书)。第一章是简要介绍,而第二章是提供了一个彻底的综述。此处的信息都来自于COM的技术说明书。

      规则1:必须实现Iunknown

      如果一个对象没有至少实现一个最小程度为IUnknown的接口,那它就不是Microsoft的组件对象模型(COM)。

      接口设计规则

       接口必须直接或间接地从IUnknown继承。

       接口必须有唯一的识别(IID)。

       接口是不变的。一旦分配和公布了IID,接口定义的任何因素都不能被改变。

       接口的成员函数应该有HRESULT类型的返回值,使远端结构可报告远程过程调用(RPC)错误的情况。

       接口成员函数的字符串参数应该是Unicode。

      实现 IUnknown

      对象的同一性。这要求对任何特定IUnknown接口的给定对象实例的QueryInterface调用返回相同的物理指针变量。这导致了所谓的两个接口的QueryInterface(IID_IUnknown, ...)和结果的比较,以确定它们是否为同一对象(COM对象同一性)。

      静态接口的设置。任何经由QueryInterface来访问对象的接口的设置,必须是静态而不是动态的。也就是说,假如一旦QueryInterface获得了一个给定的IID,那么它总是对相同的对象(除非有意想不到情况)调用,假如QueryInterface不能获得一个给定的IID,那么随后对相同IID的对象调用必定会失败。

      对象完整性。对于可处理的接口设置,必须有反身性,对称性和过渡性。即给定代码如下:

    IA * pA = (some function returning an IA*);
    IB * pB = NULL;
    HRESULT hr;
    hr = pA->QueryInterface(IID_IB,&pB); // line 4
    Symmetric: pA->QueryInterface(IID_IA, ...) must succeed (a>>a)
    Reflexive: If, in line 4, pB was successfully obtained, then
    pB->QueryInterface(IID_IA, ...)
    must succeed (a>>b, then b>>a).
    Transitive: If, in line 4, pB was successfully obtained, and we do
    IC * pC = NULL;
    hr = pB->QueryInterface(IID_IC, &pC); //Line 7
    and pC is successfully obtained in line 7,then
    pA->QueryInterface(IID_IC, ...)
    must succeed (a>>b, and b>>c,then a>>c).

      最小参考服务大小。我们需要实现AddRef来维护一个服务台,它足够大以便支持给定对象的所有接口的2 31 –1有出色的整体指示服务。一个32-位的无符号整型数满足要求。

      Release并不意味着失败。假如客户想知道关于资源已被释放等情况,就必须在调用Release之前使用一些对象接口中的较高的语义。

    内存管理规则

       接口指针的生命期管理总是通过建立在每个COM接口上的AddRef和Release方法来实现。(参见下面的“引用计数规则”)

       下面的规则适用于接口成员函数的参数,包括不是“按值”传递的返回值。

       对于参数来说,调用程序应分配和释放内存。

       出口参数必须由被调用程序分配,由调用程序用标准的COM内存分配程序来释放。

       出入参数首先由调用程序分配,必要时由被调用程序释放及重分配。至于出口参数,调用程序有责任释放最终返回变量。此时必须使用标准的COM内存分配程序。

       假如函数返回调用失败的代码,则通常调用者没办法清除出口和入出口参数。这导致了一些附加规则:

       错误返回时,出口参数必须可靠地被设置成可清除变量,它不能对调用程序有影响。

       此外,所有的出口指针参数(包括调用分配,被调用委任结构)必须被明显地设为NULL。最直接的方法是在函数说明项中设成NULL。

       返回错误时,所有的入出口参数必须为被调用者所搁置(这样保持为调用程序初始化的值;若调用程序没有对它初始化,则它是个出口参数,不是入出口参数),或者被明显地设为出口错误返回情况。

      参考计数规则:

       规则1:对于接口指针的每一个新的副本,AddRef必须被调用;Release在接口指针的每一个破坏时调用,除了子规则明显允许了其他情况。

       以下规则对应于规则1的非例外情况。

       规则1a:函数的入口出口参数。调用程序必须AddRef实际参数,因为当出口变量存放在它之上时,将由被调程序释放。

       规则1b:获取全局变量。从全局变量的已存在的指针副本得到的接口指针的局部副本,必须被独立地引用计数。因为存在局部副本时,被调函数会破坏全局副本。

       规则1c:新指针合成所需资源不多。函数使用内在知识合成接口指针,而不是从其他资源所得,此时必须对新指针做初始AddRef。这样的重要例子有事例生成法则,Iunknown::QueryInterface的实现,等等。

       规则1d:内部存储指针副本的返回。指针返回之后,被调程序不知道它的生命期和指针的内部存储副本如何联系。所以,被调程序必须在返回前对指针副本调用AddRef。

       规则2:对于接口指针的两个或更多的副本,它们的生命期的起始和终了的关系代码的特定知识,使AddRef/Release可以被省略。

      从COM客户的角度,引用计数是和接口对应的概念。客户不应认为对象的所有接口有同一引用计数。

      不应依赖于Addref & Release的返回值,而应用于调试目的。

      指针稳定性;参见在"Reference-Counting Rules"下的OLE帮助文件中的子部分:"Stabilizing the this Pointer and Keeping it Valid"。

      参见Douglas Hodges写的优秀的技术文章"Managing Object Lifetimes in OLE",及Kraig Brockschmidt (MSDN Library, Books)写的Inside OLE的第三章来获取更多信息。

      COM申请责任:

      以客户,服务器,对象执行者之一身份使用COM的每一进程,要对三件事负责:

       确定COM库是同COM函数CoBuildVersion一致的版本。

       在使用其他函数之前通过调用CoInitialize初始化COM库。

       在不用CoUninitialize时取消COM库的初始化。

       进程内服务器能假定载入的进程已执行了这些步骤。

    服务器规则

       进程内服务器必须输出DllGetClassObject and DllCanUnloadNow。

       进程内服务器必须支持COM自注册。

       进程内和局部服务器应该在它们的文件版本信息中提供OLESelfReg字符串。

       进程内服务器必须输出DllRegisterServer and DllUnRegisterServer。

       局部服务器应支持/RegServer and /UnRegServer命令行开关。

      生成集合对象

       生成可合计的对象是可选的,且操作简单,有诸多益处。以下规则使用于创建可合计的对象(通常称为内部对象)。

       由QueryInterface, AddRef, 和 Release对IUnknown接口的内部对象执行单独控制内部接口的引用计数,且不能授权给外部未知指针。这种IUnknown执行称为隐式IUnknown。

       内部对象执行接口的QueryInterface, AddRef, 和 Release成员的实行,除了IUnknown自己,都必须授权给外部未知指针。这些实施不能直接影响内部对象的参考计数。

       隐式Iunknown只对内部对象实施QueryInterface操作。

       集合对象在占用外部未知指针参考时,不能调用AddRef。

       如果当对象创建时,需要除Iunknown外的任一接口,创建失败同E_UBKNOWN一起。

      以下的代码段阐明了使用嵌套类来实现集合对象接口的范例:

    // CSomeObject is an aggregatable object that implements
    // IUnknown and ISomeInterface
    class CSomeObject : public IUnknown
    {
    private:
    DWORD m_cRef;// Object reference count
    IUnknown* m_pUnkOuter; //Outer unknown, no AddRef
    // Nested class to implement the ISomeInterface interface
    class CImpSomeInterface: public ISomeInterface
    {
    friend class CSomeObject ;
    private:
    private:DWORD m_cRef; //Interface ref-count, for debugging
    private:IUnknown*m_pUnkOuter; // Outerunknown, for delegation
    private:public:
    private:CImpSomeInterface() { m_cRef = 0; };
    private:~ CImpSomeInterface(void) {};
    private:// IUnknown members delegate to the outer unknown
    private:// IUnknown members do not control lifetime of object
    private:STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    private:{ return m_pUnkOuter->QueryInterface(riid,ppv);};
    private:STDMETHODIMP_(DWORD) AddRef(void)
    private:{ return m_pUnkOuter->AddRef(); };
    private:STDMETHODIMP_(DWORD) Release(void)
    private:{ return m_pUnkOuter->Release();};
    private:// ISomeInterface members
    private:STDMETHODIMP SomeMethod(void)
    private:{ return S_OK; };
    private:} ;
    private:CImpSomeInterface m_ImpSomeInterface ;
    private:public:
    private:CSomeObject(IUnknown * pUnkOuter)
    {
    m_cRef=0;
    // No AddRef necessary if non-NULL as we're aggregated.
    m_pUnkOuter=pUnkOuter;
    m_ImpSomeInterface.m_pUnkOuter=pUnkOuter;
    } ;
    // Static member function for creating new instances (don't use
    // new directly).Protects against outer objects asking for interfaces
    // other than IUnknown
    static HRESULT Create(IUnknown* pUnkOuter, REFIID riid, void **ppv)
    {
    CSomeObject* pObj;
    if (pUnkOuter != NULL && riid != IID_IUnknown)
    return CLASS_E_NOAGGREGATION;
    pObj = new CSomeObject(pUnkOuter);
    if (pObj == NULL)
    return E_OUTOFMEMORY;
    // Set up the right unknown for delegation (the non-aggregation
    case)
    if (pUnkOuter == NULL)
    pObj->m_pUnkOuter = (IUnknown*)pObj ;
    HRESULT hr;
    if (FAILED(hr = pObj->QueryInterface(riid, (void**)ppv)))
    delete pObj ;
    return hr;
    }
    // Implicit IUnknown members, non-delegating
    // Implicit QueryInterface only controls inner object
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
    *ppv=NULL;
    if (riid == IID_IUnknown)
    *ppv=this;
    if (riid == IID_ISomeInterface)
    *ppv=&m_ImpSomeInterface;
    if (NULL==*ppv)
    return ResultFromScode(E_NOINTERFACE);
    ((IUnknown*)*ppv)->AddRef();
    return NOERROR;
    } ;
    STDMETHODIMP_(DWORD)AddRef(void)
    { return++m_cRef; };
    STDMETHODIMP_(DWORD)Release(void)
    {
    if (--m_cRef != 0)
    return m_cRef;
    delete this;
    return 0;
    };
    };

      集合对象

       当在一个对象上产生另一个集合对象时,必须遵循以下规则:

       当创建一个内部对象时,外部对象必须明确的向Iunknown请求。

       外部对象必须保护重入时对破坏代码的人工引用的Release实施。

       如果外部对象查询任一内部对象接口,它必须调用自己的未知Release。释放此指针时,外部对象紧随内部对象指针调用自己的外部未知AddRef。

    // Obtaining inner object interface pointer
    pUnkInner->QueryInterface(IID_IFoo,&pIFoo);
    pUnkOuter->Release();
    // Releasing inner object interface pointer
    pUnkOuter->AddRef();
    pIFoo->Release();

      外部对象不能盲目地对内部对象的未被识别接口进行查询,除非操作是为外部对象特定目的。

      房间线程化模型

      房间模型线程化的细节实际上非常简单,但是必须小心地遵循以下规则:

       每个对象存在于单线程中(在单独的房间中)。

       所有对一对象的调用必须基于自己的线程(在自己的房间中)。直接从别的线程中调用对象是禁止的。试图用空线程方式使用对象的申请,将在操作系统的未来版本中遇上不能正确运行的问题。这条规则的含义就是在房间之间,必须安排对象的所有指针。

       为了处理从不同进程或同一进程的不同房间中的调用,在对象中的每一房间/线程必须有一个消息队列。这就意味着线程的工作函数必须有一个GetMessage/DispatchMessage循环。假如在线程之间有别的同步原语用来通信,那么Microsoft Win32的sgWaitForMultipleObjects将被用来等待消息和线程同步事件。

       基于DLL或进程内的对象必须在注册表中标记为"房间识别",通过给注册数据库的InprocServer32关键字增添名为"ThreadingModel=Apartment"的变量实现。

       房间识别对象应仔细填写DLL表项。对房间识别对象调用CoCreateInstance的每一个房间将从它的线程调用DllGetClassObject。故DllGetClassObject应能多级类对象或单线程安全对象。从任一线程调用CoFreeUnusedLibraries,都通过主房间线程来调用DllCanUnloadNow.。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    ---人之所以能,是相信能!!

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2010/12/19 11:22:00
     
     GoogleAdSense水瓶座1984-2-14
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 点击这里发送电邮给Google AdSense 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/1 13:12:36

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms