[ ... more constructor code ... ]
}
委托的iunknown接口调用的它的副本,如下
hresult queryinterface(refiid iid, void **ppv)
{
return m_punknown->queryinterface(iid, ppv);
}
using cunknown
directshow在cunknown.基类中实现了iunkown接口,directshow的其他基类也多是从cunknown类中继承过来的,所以,你也可以从这个类派生一个新类或者从其他基类派生你自己的类。
inondelegatingunknown
cunknown类还实现了一个inondelegatingunknown接口,这个接口是用来管理引用计数的。大多数情况下,你的派生类可以直接这个接口的两个管理引用计数的函数而不用做任何的修改。但是你要记住,当引用计数等于0的时候,cunknown就要销毁自己。有时候你必须要派生cunknown::nondelegatingqueryinterface函数,因为在基类中,如果它发现请求接口的id不是iid_iunknown的时候就返回e_nointerface,在你的派生类中,你就要自己来测试你所支持的接口id,看下面的例子
stdmethodimp nondelegatingqueryinterface(refiid riid, void **ppv)
{
if (riid == iid_isomeinterface)
{
return getinterface((isomeinterface*)this, ppv);
}
// default
return cunknown::nondelegatingqueryinterface(riid, ppv);
}
getinterface将接口指针设置为所请求的接口,返回s_ok,增加引用计数,缺省的情况下,要调用你继承类的nondelegatingqueryinterface
iunknown
日前所述,任何一个组件的iunknown接口的处理流程都是一样的。因为它仅仅是根据客户端的请求返回正确的接口即可。因此,为了方便,在combase.h头文件中声明了一个宏declare_iunknown,这种宏中定义了三个内联函数,如下
stdmethodimp queryinterface(refiid riid, void **ppv) {
return getowner()->queryinterface(riid,ppv);
};
stdmethodimp_(ulong) addref() {
return getowner()->addref();
};
stdmethodimp_(ulong) release() {
return getowner()->release();
};
cunknown::getowner函数返回拥有这个iunknown接口的组件,对于聚合组件,ower指的外部的组件。否则,指的就是自己。在你自己的类中的公共部分,包含declare_iunknown宏声明。
类的构造
你自己的类的构造函数一定要从基类的构造函数继承过来,如下
cmycomponent(tchar *tszname, lpunknown punk, hresult *phr)
: cunknown(tszname, punk, phr)
{
/* other initializations */
};
构造函数一般都有三个参数,这三个参数传递到cunknown类的构造函数中,其实你的构造函数基本不用作任何工作,一切都在cunknown函数的构造函数给你做好了。
tszname指定组件的名字
punk指向一个iunknown指针
phr指向一个hresult值,表示成功还是失败。
下面的例子演示了,如何派生你自己的类,这个类支持iunknown接口还有一个isomeinterface接口
class cmycomponent : public cunknown, public isomeinterface
{
public:
declare_iunknown;
stdmethodimp nondelegatingqueryinterface(refiid riid, void **ppv)
{
if( riid == iid_isomeinterface )
{
return getinterface((isomeinterface*)this, ppv);
}
return cunknown::nondelegatingqueryinterface(riid, ppv);
}
cmycomponent(tchar *tszname, lpunknown punk, hresult *phr)
: cunknown(tszname, punk, phr)
{
/* other initializations */
};
// more declarations will be added later.
};
这个例子假定了一下情况
1, cunknown类实现了iunknown接口,如何新的组件从cunknown基类继承过来,那么同时也就继承了基类所支持的接口。你可以从其他继承于cunknown的类派生你自己的类,而不一定要从cunknown派生
2, declare_iunknown宏将iunknown接口的三个函数声明为内联函数
3, cunknown类提供了inondelegatingunknown.的实现
4, 如果新的组件支持的接口不仅仅是iunknown,那么就一定要重新继承inondelegatingunknown.函数,测试新接口的id,如上代码
5, 派生类的构造函数触发了cunknown的构造函数
下一步就是使得应用程序创建组件的一个实例,这就要求了解dlls和类厂,以及构造函数的关系,请看how to create a dll.
2how to create a dll.
在客户端创建一个com对象的实例以前,它首先通过cogetclassobject方法创建一个对象类厂的实例。然后客户端调用类厂的iclassfactory::createinstance方法。类厂自动创建一个组件然后返回一个指向组件对象接口的指针。cocreateinstance方法是上面两步的综合。
下面的图演示了对象创建学习笔之十_filter开发基础/3.jpg)
图3
cogetclassobject方法调用了dll中的dllgetclassobject方法,这个方法创建了一个类厂对象,并且返回一个类厂对象的接口。directshow已经替你完成了dllgetclassobject方法 为了了解他们是如何工作,你必须了解directshow是如何实现类厂的。
类厂也是一个com对象,它可以用来创建其他的com组件。每个类厂只能用来创建特定的com组件对象。在directshow中,每一个类厂都是c++类cclassfactory的一个实例,类厂是通过一个叫做类厂模板cfactorytemplate,来实现的。每个类厂类都有一个指向类厂模板的指针,类厂模板包含了要创建的组件的信息,比如clsid,和一个指向创建对象函数的指针。
dll中定义了一个全局的类厂模板的数组,每一个动态的dll中都有这么一个全局的模版数组,当dllgetclassobject函数创建组件对象的时候,它就搜索全局数组里的匹配的clsid,如果它找到匹配的数组,它就创建一个包含一个指向匹配模板指针的类厂对象,当客户端调用iclassfactory::createinstance 方法的时候,类厂就调用模版中的函数来创建组件对象,下面的dllgetclassobject函数是我从dshow的基类中找到的,我们仔细看看吧
stdapi
dllgetclassobject(
refclsid rclsid,
refiid riid,
void **pv)
{
if (!(riid == iid_iunknown) && !(riid == iid_iclassfactory)) {
return e_nointerface;
}
// 首先在类厂模板数组中查找相应的clsid
// class id
for (int i = 0; i < g_ctemplates; i++) {
const cfactorytemplate * pt = &g_templates[i];
if (pt->isclassid(rclsid)) {
//如果找到,就用这个类厂模板作参数生成一个类厂对象
// found a template - make a class factory based on this
// template