处理指向接口指针的方法的正确实现 我有一个COM对象,用C++实现。我正在使用VB6应用程序中的此对象

处理指向接口指针的方法的正确实现 我有一个COM对象,用C++实现。我正在使用VB6应用程序中的此对象,c++,com,vb6,C++,Com,Vb6,问题是如何实现获取并返回接口指针的方法。 以下是IDL的示例: [...] interface ICOMCvDC : IUnknown { HRESULT GetPen([retval][out] ICOMCvPen** ppPen); HRESULT SetPen([in] ICOMCvPen* pPen); }; 下面是component类对象的骨架: class COMCvDC : public ICOMCvDC { public: ... STDMETH

问题是如何实现获取并返回接口指针的方法。 以下是IDL的示例:

[...]
interface ICOMCvDC : IUnknown
{
    HRESULT GetPen([retval][out] ICOMCvPen** ppPen);
    HRESULT SetPen([in] ICOMCvPen* pPen);
};
下面是component类对象的骨架:

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
接口。为什么?另外,我不明白为什么在查询接口指针之前要查询
IUnknown
接口?

接口指针管理没有什么特别之处,除了一件事:您必须处理AddRef/Release调用。这就是为什么强烈建议使用执行自动管理的智能指针包装器(C++伪代码):

CComPtr mu pen;
HRESULT Get(IPen**ppPen){…*ppPen=CComPtr(m_pen).Detach();…}
HRESULT集合(IPen*pPen){…m_pen=pPen;..}
就这样!如果不使用CComPtr,就必须一直执行“If(!x)x->Release()”之类的操作,而且很容易出错

回复:更新1
  • 如果VB6的CoCreateInstance中的参数是
    IID\u IUnknown
    ,则可能首先查询
    IUnknown
    ;然后它
    QueryInterface
    “将显示感兴趣的接口
  • 查询
    IPersistStreamInit
    IPersistPropertyBag
    可能是VB6运行时标准对象初始化的一部分;如果此对象与表单一起加载,则可以从持久数据初始化它
还请注意,在初始化过程中,通常会预先查询对象上的某些接口,以便在以后需要时准备就绪,特别是在需要重用时。

当然,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,并在析构函数中释放它。或者使用智能指针

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;
}