Com 自修改虚拟表条目以指向具体实现
短版: COM类能否在运行时修改自己的虚拟表条目?(忽略线程问题) 完整版本: <>我提供了一些C++类,它们实现了一个接口。COM接口是在第三方框架中定义的 <>(<强>编辑: Windows平台上COM接口的VTead是标准的,但是C++ VToE与COM Voice之间的关系我不清楚) 为了将我的实现正确地注入到该框架中,它需要使用两步初始化:首先创建一个没有参数的对象,然后调用一个带有所有参数的Com 自修改虚拟表条目以指向具体实现,com,visual-c++,vtable,Com,Visual C++,Vtable,短版: COM类能否在运行时修改自己的虚拟表条目?(忽略线程问题) 完整版本: 我提供了一些C++类,它们实现了一个接口。COM接口是在第三方框架中定义的 (编辑: Windows平台上COM接口的VTead是标准的,但是C++ VToE与COM Voice之间的关系我不清楚) 为了将我的实现正确地注入到该框架中,它需要使用两步初始化:首先创建一个没有参数的对象,然后调用一个带有所有参数的Initialize方法。这是可以选择我的一个实现的时刻 为了实现这一点,我添加了一个“解析器”类(或者简单
Initialize
方法。这是可以选择我的一个实现的时刻
为了实现这一点,我添加了一个“解析器”类(或者简单地说是一个包装器类),它的唯一职责是在Initialize
方法中选择一个实现。之后,对其COM方法的每个调用都将转发到实际实现
然后将解析器类注入到框架中。这绕过了框架限制
现在,看到解析器类在初始化后实际上没有任何用处,我想知道是否有办法消除虚拟方法间接开销?我的想法是将每个COM vtable条目从具体实现复制到解析器类的vtable
这样行吗
例如:
// (FYI) #define HRESULT unsigned long
struct IInterface
{
// ... the usual AddRef, Release and QI omitted
virtual HRESULT Initialize(VARIANT v) = 0; // the Initialize method, where implementation gets chosen
virtual HRESULT DoWork() = 0; // can only call after initialization.
};
class MyResolver : public IInterface
{
// ... the usual AddRef, Release and QI omitted
public:
virtual HRESULT Initialize(VARIANT v)
{
if ( /* some conditions based on v */ )
return Implem_One.Create((void**) &m_pImpl);
else
return Implem_Two.Create((void**) &m_pImpl);
"Here, can I copy the vtable entries from m_pImpl to the vtable of MyResolver (*this) ?";
for (int k = 0; k < num_virtual_methods; ++k)
this->vtbl[k] = m_pImpl->vtbl[k];
}
virtual HRESULT DoWork()
{
if (!m_pImpl) return ERROR_NOT_INITIALIZED;
m_pImpl->DoWork();
}
public:
// this creation method is injected into the framework
static HRESULT Create(void**) { /* create an instance of this class and return it */ }
private:
MyResolver() : m_pImpl(NULL) {}
virtual ~MyResolver() { if (m_pImpl) m_pImpl->Release(); }
IInterface* m_pImpl;
};
/(仅供参考)#定义HRESULT无符号长
结构界面
{
//…省略了通常的AddRef、Release和QI
virtual HRESULT Initialize(VARIANT v)=0;//选择实现的初始化方法
虚拟HRESULT DoWork()=0;//只能在初始化后调用。
};
类MyResolver:公共接口
{
//…省略了通常的AddRef、Release和QI
公众:
虚拟HRESULT初始化(变量v)
{
if(/*基于v*/的某些条件)
返回Implem_One.Create((void**)和m_pImpl);
其他的
返回Implem_Two.Create((void**)和m_pImpl);
“在这里,我可以将vtable条目从m_pImpl复制到MyResolver的vtable(*this)?”;
对于(int k=0;kvtbl[k]=m_pImpl->vtbl[k];
}
虚拟HRESULT DoWork()
{
如果(!m_pImpl)返回错误\u未初始化;
m_pImpl->DoWork();
}
公众:
//这个创建方法被注入到框架中
静态HRESULT Create(void**){/*创建此类的实例并返回它*/}
私人:
MyResolver():m_pImpl(NULL){}
virtual~MyResolver(){if(m_pImpl)m_pImpl->Release();}
界面*m_pImpl;
};
您无法安全地复制vtable条目;因为这个
指针仍然会引用代理类,所以真正的实现不会找到它的私有数据
如果基类支持,您可以使用它来避免开销。在构造基对象时,将外部类的IUnknown作为外部聚合IUnknown传递。这意味着基础对象的所有派生接口上的QueryInterface/AddRef/Release现在将委托给外部对象,使其看起来像外部对象的一部分
现在,您可以让QueryInterface在初始化之前返回原始对象(使用代理方法),或者在初始化完成时直接返回基本对象的接口。例如:
class MyProxy : public IInterface
{
long refCt;
IUnknown *pUnkInner;
IInterface *pIfaceInner;
~MyProxy() {
AddRef(); // pUnkInner may take references to us in its destruction; prevent reentrancy per aggregating object rules
if (pUnkInner) {
pUnkInner->Release();
}
}
public:
MyProxy() : refCt(1), pUnkInner(NULL), pIfaceInner(NULL) { }
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
{
if (!ppvObject) return E_POINTER;
if (riid == IID_IUnknown) {
AddRef();
*ppvObject = (void *)static_cast<IUnknown *>(this);
return S_OK;
} else if (riid == IID_IInterface && pUnkInner) {
// increments refcount of _outer_ object
return pUnkInner->QueryInterface(riid, ppvObject);
} else if (riid == IID_IInterface) {
AddRef();
*ppvObject = (void *)static_cast<IInterface *>(this);
return S_OK;
} else {
return E_NOINTERFACE;
}
}
STDMETHODIMP_(DWORD) AddRef(void)
{ return InterlockedIncrement(&refCt); }
STDMETHODIMP_(DWORD) Release(void)
{ if (!InterlockedDecrement(&refCt)) delete this; }
HRESULT Initialize(VARIANT v) {
// You can use another protocol to create the object as well as long as it supports aggregation
HRESULT res = CoCreateInstance(CLSID_InnerClass, this, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *)&pUnkInner);
if (FAILED(res)) return res;
res = pUnkInner->QueryInterface(IID_IInterface, (void **)&pIfaceInner);
if (FAILED(res)) {
pUnkInner->Release();
pUnkInner = NULL;
return res;
}
Release(); // the above QueryInterface incremented the _outer_ refcount, we don't want that.
return S_OK;
}
HRESULT DoWork() {
if (!pIfaceInner) return ERROR_NOT_INITIALIZED;
return pIfaceInner->DoWork();
}
};
类MyProxy:公共界面
{
长反射;
IUnknown*pUnkInner;
接口*接口内部;
~MyProxy(){
AddRef();//pUnkInner在其销毁过程中可能会引用我们;根据聚合对象规则防止重入
如果(潘金纳){
punkiner->Release();
}
}
公众:
MyProxy():refCt(1)、pUnkInner(NULL)、pIfaceInner(NULL){}
STDMETHODIMP查询接口(refid-riid,void**ppvObject)
{
如果(!ppvObject)返回E_指针;
如果(riid==IID_IUnknown){
AddRef();
*ppvObject=(void*)静态强制转换(this);
返回S_OK;
}else if(riid==IID_IInterface&&punkiner){
//递增_外部u对象的引用计数
返回punkiner->QueryInterface(riid,PPVOObject);
}else if(riid==IID\u界面){
AddRef();
*ppvObject=(void*)静态强制转换(this);
返回S_OK;
}否则{
返回E_NOINTERFACE;
}
}
标准方法(DWORD)添加参考(无效)
{返回联锁增量(&refCt);}
标准方法(DWORD)发布(无效)
{如果(!InterlocatedDecrement(&refCt))删除此;}
HRESULT初始化(变量v){
//您可以使用另一个协议来创建对象,只要它支持聚合
HRESULT res=CoCreateInstance(CLSID_InnerClass、this、CLSCTX_INPROC_SERVER、IID_IUnknown、(LPVOID*)和pUnkInner);
if(失败(res))返回res;
res=punkiner->QueryInterface(IID_IInterface,(void**)和pIfaceInner);
如果(失败(res)){
punkiner->Release();
pUnkInner=NULL;
返回res;
}
Release();//上面的查询接口增加了_outer_uRefCount,我们不希望这样。
返回S_OK;
}
HRESULT DoWork(){
如果(!pIfaceInner)返回错误\u未初始化;
返回pIfaceInner->DoWork();
}
};
尽管客户端获得的初始IInterface
指针具有双重调用开销,但一旦初始化完成,它们可以重新创建QueryInterface
以获得更直接的指针。更重要的是,如果您可以将初始化移动到另一个接口,那么您可以强制客户机重新进行QueryInterface
,从而确保它们获得直接指针
也就是说,聚合对象支持聚合协议至关重要;否则,您将导致引用计数不一致和其他不好的结果。在实现聚合之前,请仔细阅读本手册。@phresnel:I have r