【问题标题】:Chaining methods of ATL/COM objectsATL/COM 对象的链接方法
【发布时间】:2013-09-19 05:47:58
【问题描述】:

在 c++ 中,我们可以通过设计返回 *this 的方法轻松地在类中设置方法链接。这在 ATL/COM 设置中是否可行?假设我有一个简单的 ATL 类 MyOBj。我想知道在这种情况下是否可以进行链接,如果可以,支持链接的方法的 idl 签名是什么?简单的例子将不胜感激! (事实上​​,我的方法是从 VBA 调用 excel 的,我希望在该 VBA 上下文中进行链接,因为我们已经为标准 VBA 方法进行了链接。)

非常感谢

R

编辑:

在 .idl 文件中我有这个:

interface IRateModel : IDispatch{
    [id(1), helpstring("SETRATEMODEL")] HRESULT SETRATEMODEL( [in] VARIANT * var_in ) ;
    [id(2), helpstring("GETRATETERMSTRUCTURE")] HRESULT GETRATETERMSTRUCTURE( [in, out] VARIANT * var_in ) ;
};

interface IVolatilityModel : IDispatch{
    [id(1), helpstring("SETVOLATILITYMODEL")] HRESULT SETVOLATILITYMODEL( [in] VARIANT * var_in ) ;
    [id(2), helpstring("GETVOLATILITY")] HRESULT GETVOLATILITY( [in, out] VARIANT * var_in ) ;
};

interface IMyOption : IDispatch{
    [id(1), helpstring("SETMATURITY")] HRESULT SETMATURITY( [in] VARIANT * TheMaturity, [out,retval] IMyOption ** ret ) ;
    [id(2), helpstring("SETSTRIKE")] HRESULT SETSTRIKE( [in] VARIANT * TheStrike, [out,retval] IMyOption ** ret ) ;
    [id(3), helpstring("SETPAYOFF")] HRESULT SETPAYOFF( [in] VARIANT * ThePayoff, [out,retval] IMyOption ** ret ) ;
    [id(4), helpstring("ATTACHRATEMODEL")] HRESULT ATTACHRATEMODEL( [in] IRateModel ** TheRateModel, [out,retval] IMyOption ** ret ) ;
    [id(5), helpstring("ATTACHVOLATILITYPROCESS")] HRESULT ATTACHVOLATILITYPROCESS( [in] IVolatilityModel ** TheVolatilityModel, [out,retval] IMyOption ** ret ) ;
    [id(6), helpstring("PRICEIT")] HRESULT PRICEIT( [in, out] DOUBLE * price ) ;
};

SETRATEMODEL 的实现是:

STDMETHODIMP CRateModel::SETRATEMODEL( /*[in]*/ VARIANT * var_in )
{
    // something
    // ...

    return S_OK ;
}

自从我添加了其他接口后,这个实现没有改变。在添加它们之前,在调试时,VARIANT 为 VT_R8(来自 vba VARIANT,这个来自 excel 的 double)现在在调试时,变体是 VT_DISPATCH。

PS : 我是 ATL/COM 的新手。

【问题讨论】:

    标签: methods com atl chaining idl


    【解决方案1】:

    类似这样的:

    interface IMyInterface {
      HRESULT DoSomething([in] long someParam, [out, retval] IMyInterface** ret);
      HRESULT DoSomethingElse([out, retval] IMyInterface** ret);
    };
    

    脚本客户端应该能够做到myObj.DoSomething(42).DoSomethingElse()

    【讨论】:

    • 顺便说一句,是否可以为 ATL/COM 类 1 设计方法,获取另一个 ATL/COM 类 B 的实例,或返回另一个 ATL/COM 类 C 的实例?更好的方法是什么? (我仍然处于 VBA 环境中,参见问题。)
    • 当然。你一直都在这样做——想想例如的IClassFactory::CreateInstance。请注意,在 COM 中,您使用的不是类,而是接口。您可以有一个将接口指针作为 [in] 参数的方法,或将接口指针作为 [out] 返回的方法。
    • 我在同一个 idl 中添加了三个 ATL/COM 接口,现在是三个接口之一的简单方法,签名 [id(1), helpstring("SETRATE")] HRESULT SETRATE( [in] VARIANT * var_in ) ;得到一个等于 VT_DISPATCH 的 vt 变体,而在它之前是 VT_R8,因为我在 VBA 中传递了一个双精度值。我做错了什么?...
    • 你曾经通过一个 double 并得到VT_R8。现在你传递一个接口指针,得到VT_DISPATCH。这一结果究竟让您感到惊讶的是什么?
    • 我想我应该说我是从昨天开始在 ATL/COM 中的,所以我仍然不知道事情到底是如何工作的。 (认为​​我的问题足以说明这一点,但没有...... ;-))在我通过一个变体之前(通过VBA)来自excel单元格中的双精度,它是VT_R8。现在我也这样做了,VARIANT“是”VT_DISPATCH。这是我不明白的,也是让我困惑的。我将编辑我的问题以放置 idl 签名,以便您可以看到我做了什么 - 不好。
    【解决方案2】:

    在 Igor Tandetnik 第一个回答之后,我尝试了以下 ATL/COM 的链接方法,在名为“Complex”的简单 ATL/COM 对象中,对复数进行建模:

    在 IDL 文件中:

    [id(1), helpstring("SET")] HRESULT SET( [in/*,out*/] VARIANT * var_inx, [in/*,out*/] VARIANT * var_iny ) ;
    [id(2), helpstring("SETREALPART")] HRESULT SETREALPART( [in] VARIANT * var_inx, [out, retval] IComplex** ret ) ;
    [id(3), helpstring("SETIMAGPART")] HRESULT SETIMAGPART( [in] VARIANT * var_iny, [out, retval] IComplex** ret ) ;
    [id(4), helpstring("MODULE")] HRESULT MODULE( [out, retval] VARIANT * var_out ) ;
    

    在 Complex.h 文件中:

    class ATL_NO_VTABLE CComplex :
        public CComObjectRootEx<CComSingleThreadModel>,
        public CComCoClass<CComplex, &CLSID_Complex>,
        public IDispatchImpl<IComplex, &IID_IComplex, &LIBID_ATLSimpleChainingTestLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
    {
    public:
        CComplex() ;
        CComplex( double x, double y ) ;
        CComplex & setRealPart( double x );
        CComplex & setImagPart( double y );
        void setRealPart2( double x );
        void setImagPart2( double y );
        double getRealPart( void ) ;
        double getImagPart( void ) ;
        double getModule( void ) ;
    
    private:
    
        double _RealPart ;
        double _ImagPart ;
    
    public:
    DECLARE_REGISTRY_RESOURCEID(IDR_COMPLEX)
    
    
    BEGIN_COM_MAP(CComplex)
        COM_INTERFACE_ENTRY(IComplex)
        COM_INTERFACE_ENTRY(IDispatch)
    END_COM_MAP()
    
    
    
        DECLARE_PROTECT_FINAL_CONSTRUCT()
    
        HRESULT FinalConstruct()
        {
            return S_OK;
        }
    
        void FinalRelease()
        {
        }
    
    public:
    
        STDMETHOD( SET )( /*[in]*/ VARIANT * var_inx, /*[in]*/ VARIANT * var_iny ) ;
        STDMETHOD( SETREALPART )( /*[in]*/ VARIANT * var_inx, /*[out, retval]*/ IComplex** ret ) ;
        STDMETHOD( SETIMAGPART )( /*[in]*/ VARIANT * var_iny, /*[out, retval]*/ IComplex** ret ) ;
        //STDMETHOD( SETREALPART )( /*[in]*/ VARIANT * var_inx ) ;
        //STDMETHOD( SETIMAGPART )( /*[in]*/ VARIANT * var_iny ) ;
        STDMETHOD( MODULE )( /*[out, retval]*/ VARIANT * var_out ) ;
    };
    
    OBJECT_ENTRY_AUTO(__uuidof(Complex), CComplex)
    

    在 Complex.cpp 文件中:

    // Complex.cpp : CComplex 的实现

    #include "stdafx.h"
    #include "Complex.h"
    #include <cmath>
    #include "MYVARIANT.h"
    
    // CComplex
    
    CComplex::CComplex( void )
    {
    
    }
    
    CComplex::CComplex( double x, double y )
    {
        _RealPart = x ;
        _ImagPart = y ;
    }
    
    CComplex & CComplex::setRealPart( double x )
    {
        _RealPart = x ;
        return *this ;
    }
    
    void CComplex::setRealPart2( double x )
    {
        _RealPart = x ;
    }
    
    CComplex & CComplex::setImagPart( double y )
    {
        _ImagPart = y ;
        return *this ;
    }
    
    void CComplex::setImagPart2( double y )
    {
        _ImagPart = y ;
    }
    
    double CComplex::getRealPart( void )
    {
        return _RealPart ;
    }
    
    double CComplex::getImagPart( void )
    {
        return _ImagPart ;
    }
    
    double CComplex::getModule( void )
    {
        return std::sqrt( _RealPart*_RealPart + _ImagPart*_ImagPart ) ;
    }
    
    STDMETHODIMP CComplex::SET( /*[in]*/ VARIANT * var_inx, /*[in]*/ VARIANT * var_iny )
    {
        MyVARIANT myvarx( var_inx ) ;
        MyVARIANT myvary( var_iny ) ;
        if ( myvarx.GETNBLINES()*myvarx.GETNBCOLS()*myvary.GETNBLINES()*myvary.GETNBCOLS() != 1L )
            return E_INVALIDARG ;
        ATL::CComVariant myccomvarx ;
        ATL::CComVariant myccomvary ;
        myvarx.GET(0, 0, myccomvarx ) ;
        myvary.GET(0, 0, myccomvary ) ;
        if ( ( myccomvarx.vt != VT_R8 ) || ( myccomvary.vt != VT_R8 ) )
            return E_INVALIDARG ;
        setRealPart2( myccomvarx.dblVal ) ;
        setImagPart2( myccomvary.dblVal ) ;
        return S_OK ;
    }
    
    STDMETHODIMP CComplex::SETREALPART( /*[in]*/ VARIANT * var_inx, /*[out, retval]*/ IComplex** ret )
    //STDMETHODIMP CComplex::SETREALPART( /*[in]*/ VARIANT * var_inx  )
    {
        MyVARIANT myvarx( var_inx ) ;
        if ( myvarx.GETNBLINES()*myvarx.GETNBCOLS() != 1L )
            return E_INVALIDARG ;
        ATL::CComVariant myccomvarx ;
        myvarx.GET(0, 0, myccomvarx ) ;
        if ( myccomvarx.vt != VT_R8 )
            return E_INVALIDARG ;
        setRealPart2( myccomvarx.dblVal ) ;
        return S_OK ;
    }
    
    STDMETHODIMP CComplex::SETIMAGPART( /*[in]*/ VARIANT * var_iny, /*[out, retval]*/ IComplex** ret )
    //STDMETHODIMP CComplex::SETIMAGPART( /*[in]*/ VARIANT * var_iny  )
    {
        MyVARIANT myvary( var_iny ) ;
        if ( myvary.GETNBLINES()*myvary.GETNBCOLS() != 1L )
            return E_INVALIDARG ;
        ATL::CComVariant myccomvary ;
        myvary.GET(0, 0, myccomvary ) ;
        if ( myccomvary.vt != VT_R8 )
            return E_INVALIDARG ;
        setImagPart2( myccomvary.dblVal ) ;
        return S_OK ;
    }
    
    STDMETHODIMP CComplex::MODULE( /*[out, retval]*/ VARIANT * var_out )
    {
        double mod = getModule() ;
        MyVARIANT module( &mod, 1, 1) ;
        module.ATTACH( var_out ) ;
        return S_OK ;
    }
    
    //
    

    MyVARIANT 是一个 VARIANT 包装类,可以完美运行并且经过全面回测。在

    ATL::CComVariant myccomvarx ;
    myvarx.GET(0, 0, myccomvarx ) ;
    

    GET 用 MyVARIANT myvarx 的系数 (0,0) 填充 ATL::CComVariant myccomvarx。

    很容易猜到是什么

    GETNBLINES()
    

    GETNBCOLS()
    

    方法正在做。在

    MyVARIANT module( &mod, 1, 1) ;
    module.ATTACH( var_out ) ;
    

    方法 ATTACH 用构造函数构造的 MyVARIANT“模块”“填充”VARIANT var_out

    MyVARIANT( double *, long, 1)
    

    将(在这种情况下) MyVARIANT 关联到指向 double 的指针。让我再说一遍,MyVARIANT 已经过全面回测,并且完全可以正常工作。

    现在,在 VBA for Excel 方面,我创建了以下六个函数:

    Function calcmodule11(ByRef x As Variant, ByRef y As Variant) As Variant
    
        Dim z As ATLSimpleChainingTestLib.Complex
        Set z = New ATLSimpleChainingTestLib.Complex
        Call z.SET(x, y)
        calcmodule11 = z.module()
    
    End Function
    
    Function calcmodule12(ByRef x As Variant, ByRef y As Variant) As Variant
    
        Dim z As ATLSimpleChainingTestLib.Complex
        Set z = New ATLSimpleChainingTestLib.Complex
        Dim xx As Variant
        xx = x
        Dim yy As Variant
        yy = y
        Call z.SET(xx, yy)
        calcmodule12 = z.module()
    
    End Function
    
    Function calcmodule21(ByRef x As Variant, ByRef y As Variant) As Variant
    
        Dim z As ATLSimpleChainingTestLib.Complex
        Set z = New ATLSimpleChainingTestLib.Complex
        z.SETREALPART (x)
        z.SETIMAGPART (y)
        calcmodule21 = z.module()
    
    End Function
    
    Function calcmodule22(ByRef x As Variant, ByRef y As Variant) As Variant
    
        Dim z As ATLSimpleChainingTestLib.Complex
        Set z = New ATLSimpleChainingTestLib.Complex
        Dim xx As Variant
        xx = x
        Dim yy As Variant
        yy = y
        z.SETREALPART (xx)
        z.SETIMAGPART (yy)
        calcmodule22 = z.module()
    
    End Function
    
    Function calcmodule31(ByRef x As Variant, ByRef y As Variant) As Variant
    
        Dim z As ATLSimpleChainingTestLib.Complex
        Set z = New ATLSimpleChainingTestLib.Complex
        z.SETREALPART(x).SETIMAGPART (y)
        calcmodule31 = z.module()
    
    End Function
    
    Function calcmodule32(ByRef x As Variant, ByRef y As Variant) As Variant
    
        Dim z As ATLSimpleChainingTestLib.Complex
        Set z = New ATLSimpleChainingTestLib.Complex
        Dim xx As Variant
        xx = x
        Dim yy As Variant
        yy = y
        Call z.SETREALPART(x).SETIMAGPART(y)
        calcmodule32 = z.module()
    
    End Function
    

    我在 excel 单元格中分别调用了这六个函数 F(即 F 等于 calcmodule11、或 calcmodule12 或...等),写着

    =F(B3,B4)
    

    其中 B3 和 B4 是两个包含双 1 的 Excel 单元格。以下是每个函数获得的结果:

    计算模块11 #VALUE! 计算模块12 1.414213562 计算模块21 1.414213562 calcmodule22 #值! calcmodule31 #值! calcmodule32 #VALUE!

    1.414213562 确实是预期的正确值。

    问题:

    1) 为什么我有一个#VALUE!调用 calcmodule11 吗?

    2) 由于 calcmodule12 给出了正确的值而 calcmodule11 没有,我希望这对 (calcmodule21, calcmodule22) 有相同的行为,但事实恰恰相反:calcmodule21 给出了正确的值,而 calcmodule22 没有。为什么?

    2) 正如 Igor Tandetnik 解释它对我的问题的第一个答案,我将方法链放在函数 calcmodule31 和 calcmodule32 中。而且它不起作用,它在 calcmodule21 和 calcmodule22 的情况下工作,至少对于 calcmodule21。为什么?

    Igor Tandetnik,正如我完全按照您的建议(如果我错了,请纠正我),为什么它不起作用?...

    非常感谢。

    【讨论】:

    • 似乎是 VBA 的一些怪癖,我必须承认我不是很熟悉。据我所知,calcmodule11calcmodule12 之间的唯一区别是参数先复制到局部变量,然后再传递给方法。我不知道为什么这很重要。
    • 至于我所知道的事情:我不禁注意到您采用/*[out, retval]*/ IComplex** ret 的方法实际上并未将任何内容分配给[out] 参数。你应该做类似*ret = this; InternalAddRef();
    • 1) 添加“*ret = this ;”和“InternalAddRef();”根本没有改变任何东西。 2) 在 VBA 函数签名中传递 byval 或 byref 的结论相同。 3)关于复制到VBA函数内部的局部变量,我不知道为什么会起作用,并拼命尝试。很奇怪。尽管如此,感谢 havin 试图帮助我!
    【解决方案3】:

    好的,#VALUE!是在函数 calcmodule11 中引起的,因为作为 VARIANT 传递给它的是“VARIANT/Object/Range”,其 value2 组件是 VARIANT/double,并且我没有在 MyVARIANT 包装类中处理 VARIANT/Object/Range。但是如果你通过 x.value2 (在 vba 中)做这个方法,一切都很好。这也解释了为什么技巧 dim xx 作为变体,xx = x 函数:这样做以某种方式将 x.value2 放入 xx,但我不知道为什么......对于#VALUE!在涉及方法链接的 VBA 函数中,原因是相同的,只是 VARIANT 更加复杂:指向 com obj 实例的指针...

    因此,我将不得不重写/完成我的 MyVARIANT 类以处理将出现的所有 VT_DISPATCH 情况,如“VARIANT/Object/Range”,但也更复杂的其他 VT_DISPATCH'es...

    【讨论】:

    猜你喜欢
    • 2011-01-07
    • 2016-08-12
    • 2011-01-12
    • 2014-09-20
    • 2010-12-11
    • 1970-01-01
    • 2012-10-05
    • 1970-01-01
    相关资源
    最近更新 更多