CoCreateInstance实际上并没有直接创建COM组件 ,而是创建了一个被称作是类厂的组件。而所需的组件正是由些类厂创建的。类厂组件的唯一功能就创建其他的组件。创建组件的标准接口是IClassFactory,用CoCreateInstnce创建的组件实际上是通过IClassFactory创建的。

类厂只是创建其它组件的一个简单组件。

为了创建同某个CLSID相应的类厂,需要一个与CoCreateInstance等价的,也可以接收一个CLSID作为参数并把回相应类厂中某个接口指针的函数。这个函数就是COM库中的CoGetClassObject。

CoGetClassObject声明:

STDAPI CoGetClassObject(REFCLSID rclsid,

DWORD dwClsContext,

COSERVERINFO * pServerInfo,

REFIID riid,

LPVOID * ppv);

区别1:CoGetClassObject同CoCreatInstance是非常相似的。只一个参数不同,CoCreatInstance将接收一个Iunknown指针,而CoGetClassObject则将接收一个COSERVERINFO指针。

区别2:CoGetClassObject返回的是指向类厂中某个接口的指针(客户可以用这个指针来创建所需的组件),而CoCreateInstance返回的则是指向组件中某个接口的指针。

IClassFactory:

类厂所支持的用于创建组件的标准接口是IClassFactory。大多数组件均可使用IClassFactory接口来创建。

interface IClassFactory : IUnknown

{

HRESULT _stdcall CreateInstance(IUnknown* pUnknownOuter,

const IID& iid,

void** ppv);

HRESULT _stdcall LockServer(BOOL, bLock);

};

CreateInstance

第一个参数为指向某个Iunknown接口的指针。在聚合再介绍。

其它两个参数同传给QueryInterface的参数是相同的。

CreateInstance并没有接收一个CLSID参数。这意味着些函数只能创建同某个CLSID(即传给CoGetClassObject的CLSID)相应的组件。

IClassFactory2

除了IClassFactory之外,Microsoft还定义了另外一个创建接口IClassFactory2,此接口在IClassFactory的基础上增加了许可或权限功能。此时,为使类厂能够创建所需的组件,客户必须通过IClassFactory2给类厂提供正确的关键字或许可。通过使用IClassFactory2,类厂可以保证客户只能获得它能合法访问的组件,并具有对此组件的访问授权。

CoCreateInstance与CoGetClassObject 的比较

在每次创建组件时,先创建相应的组件的类厂,然后用所获取的IClassFactory指针来创建所需的接口需要完成的工作显然比直接调用 CoCreateInstance来创建所需的组件要复杂一些。所以很多时候用CoCreateInstance来创建组件。但CoCreateInstance是能过CoGetClassObject实现的。

HRESULT CoCreateInstance(const CLSID& clsid,

IUnknown* punkonwnDuter,

DWORD dwClsContext,

const IID& iid,

void** ppv)

{

// Set the out paameter to NULL

*ppv = NULL;

// Create the class factory

// and get an IClassFactroy interface pointer.

IClassFactory* pIFactory = NULL;

HRESULT hr = CoGetClassObject(clsid,

dwClsContext,

NULL,

IID_IClassFactory,

(void**)&pIFactory);

if (SUCCEEDED(hr))

{

// create the component.

hr = pIFactory->CreateInstance(punkonwnDuter, iid, ppv);

pIFactory->Release()();

}

return hr;

}

多数情况用CoCreateInstance,何时使用CoGetClassObject

第一种情况:若想用不同于IClassFactory的某个创建接口创建组件,则必须使用CoGetClassObject。因此如果想使用IClassFactory2来创建组件,就应使用CoGetClassObject。

第二种情况:若需要创建同一个组件的多个实例,那么使用CoGetClassObject将可以获得更高的效率。因为这样只需要创建相应的类厂一次,而CoCreateInstance使得客户可以对组件的创建过程进行更多控件。

类厂的若干特性

类厂的一个实例将只能创建同某个CLSID相应的组件。

类厂和它所创建的组件是由同一个开发人员实现的。

类厂就是需要知道如何创建相应的组件并将这一点封装起来,以便客户能够尽可能地同组件所具有的特殊需要分开。

类厂的实现

DllGetClassObject的使用

CoGetClassObject需要DLL中一个特定函数来创建组件类厂。此函数的名称的为DllGetClassObject。

STDAPI DllGetClassObject(const CLSID & rclsid,

const IID & riid,

void ** ppv);

组件的创建过程

(vs2008)代码下载

本文地址:http://www.cnblogs.com/fangyukuan/archive/2010/06/12/1757417.htm

回一楼:(因为写的东西比较多,就写在这里了)


先看看CoCreateInstance(参数相关说明请看:COM笔记-CoCreateInstance和CoGetClassObject的参数。其中clsid和dwClsContext是由CoCreateInstance函数直接传给CoGetClassObject的。

接着看CoGetClassObject:(参数请参考MSDN 

HRESULT hr = CoGetClassObject(clsid,
dwClsContext,
NULL,
IID_IClassFactory,
(void**)&pIFactory);

因为CoGetClassObject源代码没有公开,所以现在只能是看msdn和从它前后的代码以及用法来推测,它在里面做了什么事情。

CoGetClassObject之前 

(可以参考我们是怎么使用com组件的。)

 
HRESULThr = CoInitialize(NULL);    //初始化COM
if(SUCCEEDED(hr))
{
hr =CoCreateInstance(CLSID_kuan,
NULL,
CLSCTX_INPROC_SERVER,
IID_Ikuan,
(void **)&IkuanATL);
if(SUCCEEDED(hr))
{
      // 使用 接口IkuanATL
   }
}
CoUninitialize();//释放COM
 

可以看到。CoInitialize初始化COM库后,就调用了CoCreateInstance,后面就是直接使用接口了。

推断:所以有一件很重要的事CoCreateInstance要做。就是把实现com的DLL加载起来。

CoGetClassObject之后

    if (SUCCEEDED(hr))
{
// create the component.
hr = pIFactory->CreateInstance(punkonwnDuter, iid, ppv);
pIFactory->Release()();
}

可见它已经得到组件的类厂接口。所以可以推断

CoGetClassObject调用了DLL的导出函数DllGetClassObject。(参考图)

STDAPI DllGetClassObject(const CLSID& clsid,
const IID& iid,
void** ppv);

 

总结

  CoGetClassObject要加载DLL,但是我们并没有给他传递DLL的路径(DLL相关请参考DLL-使用DLL)。所以它要用参数clsid(即组件id,它是从CoCreateInstance传递过来的)去注册表找到这个组件DLL路径(请见:COM笔记-Widows注册表),然后把DLL加载起来。

加载起来之后我们就可以调用DLL的导出函数DllGetClassObject。并把clsid(组件ID),IID_IclassFactory(接口ID)传递它。(可以看一下DllGetClassObject的实现,看看COM笔记-类厂源代码)。

  DllGetClassObject执行成功则返回类工厂接口pIFactory(见图)。

接着就是 类工厂接口pIFactory 调用 它的方法CreateInstance,去创建我们需要的组件,并返回我们需要的接口了。(同样可以看看COM笔记-类厂源代码

  关于参数punkonwnDuter这个没用传递CoGetClassObject,而是传递给了类厂的一个方法CreateInstance,主要是聚合方面的东西。(见:COM笔记-包容与聚合

参数dwClsContext限定所创建的组件的执行上下文。(见:COM笔记-CoCreateInstance

com这东西就是绕来绕去。不知道我说清楚了没了。

COM笔记-类厂的更多相关文章

  1. Python笔记——类定义

    Python笔记——类定义 一.类定义: class <类名>: <语句> 类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性 如果直接使用类名修改其属 ...

  2. Python编程从入门到实践笔记——类

    Python编程从入门到实践笔记——类 #coding=gbk #Python编程从入门到实践笔记——类 #9.1创建和使用类 #1.创建Dog类 class Dog():#类名首字母大写 " ...

  3. 《python基础教程(第二版)》学习笔记 类和对象(第7章)

    <python基础教程(第二版)>学习笔记 类和对象(第7章) 定义类class Person:    def setName(self,name):        self.name=n ...

  4. Objective-C学习笔记类目、协议

    不是所有的方法都可以被覆盖的!比如:intValue就不能被覆盖!! 原因正在查找中! 别人的电脑上却可以! 类目.h件 #import <Foundation/Foundation.h> ...

  5. Python 笔记 : 类和继承

    # -*- coding=  utf-8 -*- # 文件编码定义的语法规则是: coding[:=]/s*([-/w.]+) # 未指定编码将默认为 : ASCII # 同时要注意物理文件的编码也要 ...

  6. scala学习笔记——类和对象

    基础语法关于Scala程序,这是非常要注意以下几点. 区分大小写 - Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义. 类名 - 对于所有的类名的第一个字 ...

  7. Effective Java2读书笔记-类和接口(五)

    第21条:用函数对象表示策略 这一条其实也没说啥,就是策略模式.碰到这种场景时,定义一个策略接口,然后不同策略子类实现它,主类包含这个接口的引用就可以了. 第22条:优先考虑静态成员类 嵌套类是指被定 ...

  8. Effective Java2读书笔记-类和接口(四)

    第19条:接口只用于定义类型 这一条就举了一个反例,说有些接口中只包含常量.这是对接口的不良使用.要实现相同的功能,应该使用不可实例化的工具类(第4条说过). public class Physica ...

  9. Effective Java2读书笔记-类和接口(二)

    第15条:使可变性最小化 通过一个复数类来看不可变类. public final class Complex { private final double re; private final doub ...

随机推荐

  1. 『与善仁』Appium基础 — 2、常用Android模拟器的安装

    目录 1.Android Virtual Devices模拟器 2.Genymotion模拟器 (1)Genymotion模拟器下载 (2)模拟器Genymotion安装 (3)Genymotion模 ...

  2. Linux常用基础命令(二)

    Linux常用基础命令 一.-ls--列表显示目录内容 二.-alias--设置别名 三.-du--统计目录及文件空间占用情况 四.-mkdir--创建新目录 五.-touch--创建空文件 六.-l ...

  3. Java基础00-循环语句7

    1. for循环语句 1.1 循环结构 1.2 for循环语句的格式 执行流程图: 1.3 案例 (1)输出数据 (2)求和 (3)求偶数和 (4)水仙花 public static void mai ...

  4. springMVC-1-servlet回顾

    SpringMVC重点学习 项目目标:SpringMVC+Vue+SpringBoot+SpringCloud+Linux spring:IOC+AOP SpringMVC:SpringMVC的执行流 ...

  5. 队列Queue:任务间的消息读写,安排起来~

    摘要:本文通过分析鸿蒙轻内核队列模块的源码,掌握队列使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十三 消息队列Queue>,作者:zhushy . 队列(Queue)是 ...

  6. PAT乙级:1082 射击比赛 (20分)

    PAT乙级:1082 射击比赛 (20分) 题干 本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军:谁差得最远,谁就是菜鸟.本题给出一系列弹洞的平面坐标(x,y),请你编写程序找 ...

  7. SpringCloud学习之【Eureka实现服务注册与发现】

    这段时间开始整理之前的SpringCloud实践笔记,这里感谢翟永超大佬的文章SpringCloud从入门到精通的指导. 项目结构 服务注册中心 注意: 1.SpringCloud与SpringBoo ...

  8. java String转List<Device>集合

    // 从Redis中获得正常设备的数量 String success = redisService.get(RedisKey.CULTIVATION_RECORD_SUCCESS); //建立一个li ...

  9. 接口开发---basic auth接口认证

    开发中遇到了basic auth来认证的案例,这里总结一下: Basic Auth简单点说明就是每次请求API时都提供用户的username和password.[base64encode(userna ...

  10. c++ 的父类 new 重载, 子类new 对象的时候会调用父类的operater new

    子类在new 对象的 时候  父类的new 进行了重载,那么会调用父类的operater new() 函数 #include <iostream> #include <string& ...