处理接口指针的方法的正确实现
A correct implementation of the methods that deal with a pointer to an interface
我有一个用c++实现的COM对象。我是从一个VB6应用程序中使用这个对象的。
问题是如何实现获取和返回指向接口的指针的方法。下面是IDL的示例:
[...]
interface ICOMCvDC : IUnknown
{
HRESULT GetPen([retval][out] ICOMCvPen** ppPen);
HRESULT SetPen([in] ICOMCvPen* pPen);
};
下面是组件类对象的骨架:
class COMCvDC : public ICOMCvDC
{
public:
...
STDMETHODDECL GetPen(
/* [retval][out] */ ICOMCvPen** ppPen);
STDMETHODDECL SetPen(
/* [in] */ ICOMCvPen* pPen);
...
protected:
ICOMCvPen* m_pen;
};
...
STDMETHODIMP COMCvDC::GetPen(
/* [retval][out] */ ICOMCvPen** ppPen)
{
*ppPen = m_pen;
return S_OK;
}
STDMETHODIMP COMCvDC::SetPen(
/* [in] */ ICOMCvPen* pPen)
{
m_pen = pPen;
return S_OK;
}
我是一个初学者在COM,所以我不确定我是否做正确的方式。我觉得我需要在一些接口指针上使用QueryInterface
方法。同样有趣的是,当VB6解释如下代码时,它正在做什么:
Dim pen1 As ICOMCvPen
Set pen1 = dc1.GetPen()
是否在GetPen
方法返回的接口指针上调用AddRef
方法?
更新1
我已经实现了两个测试对象(COMCvTest和COMCvTestFactory),只是记录所有的方法被调用。然后我执行了以下VB6代码:
Dim test1 As ICOMCvTest
Set test1 = New COMCvTest
Debug.Print "Ref: " & test1.GetReferenceCounter
Set test1 = Nothing
下面是这些对象的生命周期日志:
COMCvTestFactory::COMCvTestFactory(); m_cRef = 1
COMCvTestFactory::QueryInterface() --- begin ---
IID is {00000001-0000-0000-C000-000000000046}
IID is IID_IClassFactory
COMCvTestFactory::AddRef(); m_cRef = 2 (was 1)
COMCvTestFactory::QueryInterface() ---- end ----
COMCvTestFactory::Release(); m_cRef = 1 (was 2)
COMCvTestFactory::CreateInstance() --- begin ---
COMCvTest::COMCvTest(); m_cRef = 1
COMCvTest::QueryInterface() --- begin ---
IID is {00000000-0000-0000-C000-000000000046}
IID is IID_IUnknown
COMCvTest::AddRef(); m_cRef = 2 (was 1)
COMCvTest::QueryInterface() ---- end ----
COMCvTest::Release(); m_cRef = 1 (was 2)
COMCvTestFactory::CreateInstance() ---- end ----
COMCvTest::AddRef(); m_cRef = 2 (was 1)
COMCvTest::Release(); m_cRef = 1 (was 2)
COMCvTestFactory::Release(); m_cRef = 0 (was 1); deleting object
COMCvTestFactory::~COMCvTestFactory()
COMCvTest::QueryInterface() --- begin ---
IID is {00000000-0000-0000-C000-000000000046}
IID is IID_IUnknown
COMCvTest::AddRef(); m_cRef = 2 (was 1)
COMCvTest::QueryInterface() ---- end ----
COMCvTest::QueryInterface() --- begin ---
IID is {9F660698-1950-4DE8-BB5F-C8D2D61F7367}
IID is IID_ICOMCvTest
COMCvTest::AddRef(); m_cRef = 3 (was 2)
COMCvTest::QueryInterface() ---- end ----
COMCvTest::QueryInterface() --- begin ---
IID is {7FD52380-4E07-101B-AE2D-08002B2EC713}
IID is IID_IPersistStreamInit
COMCvTest::QueryInterface() --- begin ---
IID is {37D84F60-42CB-11CE-8135-00AA004BB851}
IID is IID_IPersistPropertyBag
COMCvTest::Release(); m_cRef = 2 (was 3)
COMCvTest::Release(); m_cRef = 1 (was 2)
COMCvTest::GetReferenceCounter; m_cRef = 1
COMCvTest::Release(); m_cRef = 0 (was 1); deleting object
COMCvTest::~COMCvTest()
似乎VB6正试图从COM对象查询IPersistStreamInit
和IPersistPropertyBag
接口。为什么?我也不明白为什么在查询ICOMCvTest
接口指针之前有IUnknown
接口的查询?
当然,QueryInterface()使它成为一个干净的单行程序,它利用了内置QI的任何错误处理,并负责您必须添加的引用计数:
STDMETHODIMP COMCvDC::GetPen(ICOMCvPen** ppPen)
{
if (m_pen) return m_pen->QueryInterface(__uuidof(ICOMCvPen), (void**)ppPen);
else {
*ppPen = 0;
return E_FAIL;
}
}
STDMETHODIMP COMCvDC::SetPen(ICOMCvPen* pPen)
{
if (m_pen) m_pen->Release();
m_pen = pPen;
return S_OK;
}
不要忘记在构造函数中将m_pen初始化为NULL,并在析构函数中释放它。或者使用智能指针
接口指针管理没有什么特别的,除了一件事:你必须照顾AddRef/Release调用。这就是为什么强烈建议使用自动管理的智能指针包装器(c++伪代码):
CComPtr<ICOMCvPen> m_pen;
HRESULT Get(IPen** ppPen) { ... *ppPen = CComPtr<ICOMCvPen>(m_pen).Detach(); ... }
HRESULT Set(IPen* pPen) { ... m_pen = pPen; ... }
就是这样!如果你不使用CComPtr,你必须一直做"If (!x) x->Release()"之类的事情,这很容易出错。
Re: Update 1
- 如果
-
IUnknown
可能首先被查询;然后QueryInterface
'd感兴趣的接口。
IID_IUnknown
是VB6的CoCreateInstance的参数,IPersistStreamInit
和IPersistPropertyBag
的查询可能是VB6运行时标准对象初始化的一部分;如果这个对象加载了表单,它可以从持久数据初始化。还请注意,通常在初始化期间预先查询对象的某些接口,以便在以后需要时准备好,特别是在要重用它们时。
相关文章:
- 实现无开销push_back的最佳方法是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 这是实现图形的坏方法吗
- 在 c++ 中实现嵌套循环的更短方法吗?
- 如何实现 Front() 方法以返回模板化双向链表C++的第一个元素?
- 如何在从抽象基派生的类中实现相同的方法?
- C++数组队列实现方法错误
- 有没有一种通用的方法来实现不变量
- 'using namespace'实现细节的便捷方法(仅标头库)?
- 使用非递归插入方法实现 AVL 树
- C ++类型特征:确保子类实现方法
- 实现基于数字值(正、负、零)的条件表达式的最佳方法
- 在C++上实现高斯赛德尔迭代方法
- C++方法实现:是否可以避免每次都键入类名?
- 如何知道C2259 VS 2017错误未实现哪种方法?
- C++强制在子类中实现方法,但具有不同的签名
- 避免在每个派生类中重新实现方法调用
- C :执行模板类型实现方法
- 在C++中实现方法时继承不起作用
- 还有更好的实现方法吗?