65.9K
CodeProject 正在变化。 阅读更多。
Home

组件类别管理器包装器类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (2投票s)

2000 年 2 月 25 日

CPOL
viewsIcon

98055

downloadIcon

1568

组件类别管理器可用于对 COM 对象进行分类。此处的代码可以更轻松地在代码中使用这些类别。

  • 下载源文件 - 38 Kb
  • 下载示例应用程序 - 113 Kb
  • 问题

    COM 对象可用于使应用程序易于扩展。这种可扩展性的一部分来自于能够编写许多符合接口的 COM 对象。应用程序使用该接口,而不关心谁实现了它。用户应该能够从可用对象的列表中选择他们想要执行工作的实际对象……幸运的是,标准的组件类别管理器使您能够将相似的 COM 对象分组到一个类别中,然后轻松地操作它们。问题在于它暴露为一系列略显粗糙的接口……


    一个解决方案

    我喜欢一次性解决一个问题,将该解决方案打包成一个易于使用的程序包,然后尽可能频繁地使用该程序包……当我开始使用组件类别管理器时,我意识到我编写了大量的重复性样板代码。我决定将其全部包装在一个或两个类中,并使其更易用。

    组件类别管理器有两个接口:注册接口和信息接口。注册接口由 COM 对象在其注册期间使用,或者由安装程序使用,以将对象注册为属于或需要某个特定类别。然后,需要使用对象类别的应用程序将使用信息接口。我编写了两个类来包装这些接口。


    CComCatRegister

    CComCatRegister 包装了 ICatRegister 接口,并提供了标准功能的精简包装。使用包装类的好处是,对于最简单的注册要求,您可以实例化它并调用单个函数来注册您的对象……

    CComCatRegister catMgr;
    catMgr.RegisterClassImplCategories(myGUID, myCATID);
    

    如果您还需要注册类别,则上述代码将变为……

    CComCatRegister catMgr;
    catMgr.RegisterCategory(myCATID, _T("This is a category"));
    catMgr.RegisterClassImplCategories(myGUID, myCATID);
    

    您还可以将该类注册为属于多个类别,或者注册该类所需的类别,而不是实现的类别。

    这就是使用注册类别管理器的大致内容。与初始化 COM、获取 ICatRegister 接口、管理其生命周期、提供类别描述的区域设置 ID 等所需的样板代码相比……它更容易!


    CComCatInformation

    第二个组件类别管理器接口是 ICatInformation。应用程序想要发现哪些对象属于哪些类别、对象需要哪些类别、它实现了哪些类别等时会使用它。

    CComCatRegister 一样,CComCatInformation 在精简包装中包装了标准的 COM 接口。这个类比 CComCatRegister 提供了更多价值,因为底层接口更复杂。使用 IEnumXXXX 迭代器包装器(解释 此处)可以很好地包装 ICatInformation 提供的所有 IEnum 接口,并使其更易于使用。

    如果您想显示实现特定类别的对象列表,那么您只需要这样做……

    CComCatInformation catMgr;
    
    CIterateGUID start = catMgr.IterateClassesOfCategory(myCATID);
    CIterateGUID end = CIterateGUID::End();
    
    for (CIterateGUID &it = begin; it != end; ++it)
    {
       LPOLESTR lpGUIDString;
    
       if (S_OK == StringFromIID(it, &lpGUIDString))
       {
          std::wcout << L" " << lpGUIDString << std::endl;
          CoTaskMemFree(lpGUIDString);
       }
    }
    

    将此代码与 IEnum 示例 中不使用组件类别管理器的代码进行比较。


    总结

    这些包装类使组件类别管理器更易于使用。值得使用它,因为它使您的应用程序更易于扩展。永远不要将您的应用程序绑定到单个对象实例,而可以相反,让它依赖于执行所需任务的对象类别。然后,您可以使用非常简单的代码允许用户更改实际对象。


    有一件事让我感到困惑……

    尽管组件类别管理器无疑是一件好事,但它有一个方面让我感到困惑。如果您查看注册表,属于类别的每个对象都将在其注册表项下列出该类别。这使得确定对象是否属于该类别变得容易。然而,似乎没有“属于类别的对象”列表,这意味着要查找属于特定类别的所有对象列表,组件类别管理器必须查看系统中的每个对象……这似乎很奇怪……但也许我遗漏了什么。


    组件类别管理器中的错误?

    标准组件类别管理器提供的 IEnumCATID 实现中似乎存在一个错误。我的机器上安装了 4.71 版的 ComCat.dll,并且在从调用 EnumImplCategoriesOfClass()EnumReqCategoriesOfClass() 获得的 IEnumCATID 接口指针上调用 Clone() 会得到一个指针,该指针似乎与您调用 Clone() 的原始指针相关联。在克隆指针或原始指针上调用 Release() 似乎会使另一个失效……这肯定不是组件类别管理器提供的其他 IEnum 接口的情况。

    可以使用下面的代码看到这个问题(下载页面提供了完整的测试程序)。

    IEnumCATID *pIEnumCatid = 0;
    hr = pICatInfo->EnumImplCategoriesOfClass(guid, &pIEnumCatid);
    
    if (SUCCEEDED(hr))
    {
       IEnumCATID *pIEnumCatidClone = 0;
    
       hr = pIEnumCatid->Clone(&pIEnumCatidClone);
    
       if (SUCCEEDED(hr))
       {
          pIEnumCatid->Release();
          pIEnumCatidClone->Release();   // Doesn't matter which order these are
                                         // the second release causes an access
                                         // violation...
       }
    }
    

    据称,VB6.0 附带的组件类别管理器有一个新版本,ComCat.dll 的版本应该是 5.0。我很想知道这个错误是否仍然存在于最新版本中。5.0 版本据称也是 IE4sr1 的一部分,但我已安装 IE4sr1,仍是 4.71。


    这对 CComCatInformation 的影响

    诚然,EnumImplCategoriesOfClass()EnumReqCategoriesOfClass() 可能是 ICatInformation 接口上使用次数最少的函数,并且对于大多数用途,您不需要从它们获得的接口指针上调用 Clone()。但是,它给我的包装类带来了问题,因为迭代器是通过值返回的,这会导致在 IEnumIterator 的复制构造函数中 Clone() 接口指针。

    如果测试程序在您的计算机上失败,请不要使用 CComCatInformation::IterateImplCategoriesOfClass()CComCatInformation::IterateReqCategoriesOfClass()

    请参阅 Len 主页上的 文章 以获取最新更新。

    © . All rights reserved.