Com 自修改虚拟表条目以指向具体实现

Com 自修改虚拟表条目以指向具体实现,com,visual-c++,vtable,Com,Visual C++,Vtable,短版: COM类能否在运行时修改自己的虚拟表条目?(忽略线程问题) 完整版本: 我提供了一些C++类,它们实现了一个接口。COM接口是在第三方框架中定义的 (编辑: Windows平台上COM接口的VTead是标准的,但是C++ VToE与COM Voice之间的关系我不清楚) 为了将我的实现正确地注入到该框架中,它需要使用两步初始化:首先创建一个没有参数的对象,然后调用一个带有所有参数的Initialize方法。这是可以选择我的一个实现的时刻 为了实现这一点,我添加了一个“解析器”类(或者简单

短版:

COM类能否在运行时修改自己的虚拟表条目?(忽略线程问题)

完整版本:

<>我提供了一些C++类,它们实现了一个接口。COM接口是在第三方框架中定义的

<>(<强>编辑: Windows平台上COM接口的VTead是标准的,但是C++ VToE与COM Voice之间的关系我不清楚) 为了将我的实现正确地注入到该框架中,它需要使用两步初始化:首先创建一个没有参数的对象,然后调用一个带有所有参数的
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