自修改虚拟表条目以指向具体实现
简短版本:
COM 类可以在运行时修改自己的虚拟表条目吗? (忽略线程问题)
完整版本:
我提供了许多实现 com 界面。 COM 接口是在第三方框架中定义的。
(已编辑: COM 接口的 vtable 在 Windows 平台上是标准的;但我不清楚 C++ vtable 和 COM vtable 之间的关系。)
为了将我的实现正确注入到其中框架,它需要使用两步初始化:首先创建一个不带参数的对象,然后调用带所有参数的 Initialize
方法。这是可以选择我的实现之一的时刻。
为了实现这一点,我添加了一个“resolver”类(或简称为包装类),其唯一职责是在“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;
};
Short version:
Can a COM class modify its own virtual table entries at runtime? (disregarding thread issues)
Full version:
I'm providing a number of C++ classes which implement a com interface. The COM interface is defined in a third-party framework.
(Edited: The vtable of the COM interface is standard on Windows platform; but the relationship between the C++ vtable and the COM vtable isn't clear to me.)
In order to inject my implementations correctly into that framework, it needs to use two-step initialization: first create an object with no parameters, then call an Initialize
method with all parameters. This is the moment when one of my implementations can be chosen.
To make this happen, I added a "resolver" class (or simply a wrapper class), whose sole responsibility is to choose an implementation in the Initialize
method. After that, every call to its COM methods will be forwarded to the actual implementation.
The resolver class is then injected into the framework. This gets around the framework limitation.
Now, seeing that the resolver class doesn't really have any use after initialization, I'm wondering if there is a way to get rid of the virtual method indirection cost? My idea is to copy each COM vtable entry from the concrete implementation to the vtable of the resolver class.
Will this work?
Example:
// (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;
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你无法安全地复制 vtable 条目;由于
this
指针仍会引用您的代理类,因此真正的实现将找不到其私有数据。如果基类支持 COM 聚合 ,但是您可以使用它来避免开销。构造基础对象时,您将传递外部类的 IUnknown 作为外部聚合 IUnknown。这意味着基础对象的所有派生接口上的 QueryInterface/AddRef/Release 现在将委托给您的外部对象,使其看起来像外部对象的一部分。
现在,您可以让 QueryInterface 在初始化之前返回原始对象(使用代理方法),或者在初始化完成时直接返回基础对象的接口。示例:
虽然客户端获取的初始
IInterface
指针有双重调用开销,但一旦初始化完成,他们可以重新QueryInterface
来获取更直接的指针。更重要的是,如果您可以将初始化移至不同的接口,则可以强制客户端重新QueryInterface
,从而确保它们获得直接指针。也就是说,聚合对象支持聚合协议至关重要;否则你最终会得到不一致的引用计数和其他问题。在实施之前请仔细阅读 MSDN 文档聚合。
You can't safely copy vtable entries; since the
this
pointer will still refer to your proxy class, the real implementation won't find its private data.If the base classes support COM aggregation, you can use this to avoid the overhead however. When constructing your base object, you'd pass your outer class's IUnknown as the outer aggregation IUnknown. This means that the QueryInterface/AddRef/Release on all derived interfaces of the base object will now delegate to your outer object, making it look like a part of your outer object.
Now you can have QueryInterface return your original object (with proxying methods) prior to initialization, or the base object's interface directly when initialization is complete. Example:
Although the initial
IInterface
pointer the client gets has the double-call overhead, once the initialization is complete, they can re-QueryInterface
to get a more direct pointer. What's more, if you can move the initialization to a different interface, you can force the client to re-QueryInterface
, thus ensuring they get a direct pointer.That said, it's vitally important that the aggregated objects support the aggregation protocol; otherwise you'll end up with inconsistent reference counts and other badness. Read the MSDN documentation carefully before implementing aggregation.