/* ///////////////////////////////////////////////////////////////////////// * File: atlstl/automation/multiple_dispatch.hpp * * Purpose: A class template that makes the methods and properties exhibited * through multiple IDispatch interfaces visible to script clients. * * Created: 15th May 2006 * Updated: 18th June 2012 * * Home: http://stlsoft.org/ * * Copyright (c) 2007-2012, Matthew Wilson and Synesis Software * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of * any contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ////////////////////////////////////////////////////////////////////// */ /** \file atlstl/automation/multiple_dispatch.hpp * \brief [C++ only; requires ATL library] Definition of the * atlstl::IDispatchImpl2 and atlstl::IDispatchImpl3 * class templates, which make the methods and properties exhibited through * multiple IDispatch interfaces visible to scripting clients * (\ref group__library__com_automation "COM Automation" Library). */ #ifndef ATLSTL_INCL_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH #define ATLSTL_INCL_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define ATLSTL_VER_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH_MAJOR 2 # define ATLSTL_VER_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH_MINOR 1 # define ATLSTL_VER_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH_REVISION 2 # define ATLSTL_VER_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH_EDIT 17 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /* ///////////////////////////////////////////////////////////////////////// * Compatibility */ /* [Incompatibilies-start] [Incompatibilies-end] */ /* ///////////////////////////////////////////////////////////////////////// * Includes */ #ifndef ATLSTL_INCL_ATLSTL_HPP_ATLSTL # include #endif /* !ATLSTL_INCL_ATLSTL_HPP_ATLSTL */ /* ///////////////////////////////////////////////////////////////////////// * Namespace */ #ifndef _ATLSTL_NO_NAMESPACE # if defined(_STLSOFT_NO_NAMESPACE) || \ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) /* There is no stlsoft namespace, so must define ::atlstl */ namespace atlstl { # else /* Define stlsoft::atlstl_project */ namespace stlsoft { namespace atlstl_project { # endif /* _STLSOFT_NO_NAMESPACE */ #endif /* !_ATLSTL_NO_NAMESPACE */ /* ///////////////////////////////////////////////////////////////////////// * Classes */ /** \brief Class template that enables the methods and properties exhibited * through two IDispatch interfaces to be visible to scripting clients. * * \ingroup group__library__com_automation * * The class template is used in place of ATL's IDispatchImpl class in the * parent class list of a class template that support two dispinterfaces. * Consider the Open-RJ COM mapping's * Field class [IDL]: \code // openrj.com.idl [ . . . ] interface IField : IDispatch { [propget, id(1), . . .] HRESULT Name([out, retval] BSTR* pVal); [propget, id(2), . . .] HRESULT Value([out, retval] BSTR* pVal); }; [ . . . ] interface IDocumenter : IDispatch { [propget, id(1), . . .] HRESULT DocString([out, retval] BSTR* pVal); }; coclass Field { [default] interface IField; interface IDocumenter; }; \endcode * As indicated, it supports two dispinterfaces: IField and * IDocumenter. Note that each interface has a property with dispid * 1. * * By default, the ATL class template IDispatchImpl assumes that just a * single "active" dispinterface. We might envisage the following * (C++) class definition for Field: * \code class ATL_NO_VTABLE Field : public CComObjectRootEx , public CComCoClass , public atlstl::SupportErrorInfoImpl<&IID_IField> , public IDispatchImpl , public IDispatchImpl { . . . }; \endcode * Unfortunately, scripting clients, which elicit DISPIDs at runtime via an * automation server's IDispatch::GetIDsOfNames() method, will * see only those methods and properties from IField in such a case. * No parts of the IDocumenter interface will be visible or * accessible. * * This is where IDispatchImpl2 comes in. It implements GetIDsOfNames() and * Invoke(), operating over both its dispinterfaces to elicit the dispid(s) * for requested name(s) by querying each interface in turn. It is used with * the Field class as follows: * \code class ATL_NO_VTABLE Field : public CComObjectRootEx , public CComCoClass , public atlstl::SupportErrorInfoImpl<&IID_IField> , public atlstl::IDispatchImpl2 { . . . }; \endcode * * Now all members of all dispinterfaces are visible to scripting clients. * Note that the class also handles the case where the dispinterfaces have * members/properties with the same dispids. (See GetIDsOfNames() and * Invoke() for details of the mechanism.) */ // [[synesis:class: atlstl::IDispatchImpl2, IID const*, T, IID const*, GUID const*>]] template< ss_typename_param_k I0 , IID const* IID0 , ss_typename_param_k I1 , IID const* IID1 , GUID const* LibID > class IDispatchImpl2 : public IDispatchImpl , public IDispatchImpl { /// \name Member Types /// @{ public: typedef IDispatchImpl dispatch_parent_0_type; //!< \brief The type of the first dispinterface typedef IDispatchImpl dispatch_parent_1_type; //!< \brief The type of the second dispinterface /// @} /// \name IDispatch methods /// @{ protected: /** \brief Provides the required behaviour of * IDispatch::GetIDsOfNames(), by querying the two * dispinterfaces, in order, to match the name(s). * * This method operates by first determining which, if any, of the * two parent dispinterfaces can resolve the names. If successful, the * resultant dispatch Ids are then striped with a bit in their * most-significant byte(s) to record the index of the dispinterface * which has thus undertaken to interpret them. This stripe is then * detected * * \remarks Names are matched en bloc: they are either all matched by one * interface, or all by the other. It is never the case that * some part are matched by one and the remainder by the other. * * \note If a dispid returned from a successful call to one of the * underlying dispinterfaces' GetIDsOfNames() already * uses the striping bit, it is left alone. Such methods will be * successfully called in Invoke(), in its post-stripe processing. */ STDMETHOD(GetIDsOfNames)( REFIID riid , LPOLESTR* rgszNames , UINT cNames , LCID lcid , DISPID* rgdispid) { unsigned index = 1; HRESULT hr = dispatch_parent_0_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if( FAILED(hr) && DISP_E_UNKNOWNNAME == hr) { ++index; hr = dispatch_parent_1_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } // Encode interface info into the dispid if(SUCCEEDED(hr)) { DISPID dispidFlag = DISPID(0x1) << (8 * sizeof(DISPID) - 2); dispidFlag >>= (index - 1); for(UINT i = 0; i < cNames; ++i) { if(rgdispid[i] < 0) { // Leave these alone. They'll be processed on a first-come-first-serve // basis, which assumes that the GetIDsOfNames() and Invoke() of I0 and // I1 are faithfully inter-related. } else { ATLSTL_MESSAGE_ASSERT("Dispatch Id is out of range!", 0 == (dispidFlag & rgdispid[i])); rgdispid[i] |= dispidFlag; } } } return hr; } /** \brief Provides the required behaviour of * IDispatch::Invoke(), by invoking this method on the * requisite dispinterface. * * This method operates by detecting the striping bit on the dispid, * from which the appropriate dispiniterface is determined. The * stripe is then removed, and the method invoked. * * \remarks Names are matched en bloc: they are either all matched by one * interface, or all by the other. It is never the case that * some part are matched by one and the remainder by the other. * * \note If no striping is apparent, the invocation is conducted on * each interface in turn. */ STDMETHOD(Invoke)( DISPID dispidMember , REFIID riid , LCID lcid , WORD wFlags , DISPPARAMS* pdispparams , VARIANT* pvarResult , EXCEPINFO* pexcepinfo , UINT* puArgErr) { if(dispidMember >= 0) { DISPID dispidFlag = DISPID(0x1) << (8 * sizeof(DISPID) - 2); dispidFlag >>= 0; if(dispidMember & dispidFlag) { return dispatch_parent_0_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } dispidFlag >>= 1; if(dispidMember & dispidFlag) { return dispatch_parent_1_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } } HRESULT hr = dispatch_parent_0_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); if( FAILED(hr) && DISP_E_MEMBERNOTFOUND == hr) { hr = dispatch_parent_1_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } return hr; } /// @} }; /** \brief Class template that enables the methods and properties exhibited * through three IDispatch interfaces to be visible to scripting clients. * * \ingroup group__library__com_automation * * IDispatchImpl3 is used (and operates) in exactly the same way as * IDispatchImpl2, except that it supports three dispinterfaces, rather than * two. */ // [[synesis:class: atlstl::IDispatchImpl2, IID const*, T, IID const*, T, IID const*, GUID const*>]] template< ss_typename_param_k I0 , IID const* IID0 , ss_typename_param_k I1 , IID const* IID1 , ss_typename_param_k I2 , IID const* IID2 , GUID const* LibID > class IDispatchImpl3 : public IDispatchImpl , public IDispatchImpl , public IDispatchImpl { /// \name Member Types /// @{ public: typedef IDispatchImpl dispatch_parent_0_type; //!< \brief The type of the first dispinterface typedef IDispatchImpl dispatch_parent_1_type; //!< \brief The type of the second dispinterface typedef IDispatchImpl dispatch_parent_2_type; //!< \brief The type of the third dispinterface /// @} /// \name IDispatch methods /// @{ protected: /** \brief Provides the required behaviour of * IDispatch::GetIDsOfNames(), by querying the three * dispinterfaces, in order, to match the name(s). * * This method operates by first determining which, if any, of the * three parent dispinterfaces can resolve the names. If successful, the * resultant dispatch Ids are then striped with a bit in their * most-significant byte(s) to record the index of the dispinterface * which has thus undertaken to interpret them. This stripe is then * detected * * \remarks Names are matched en bloc: they are either all matched by one * interface, or all by the other. It is never the case that * some part are matched by one and the remainder by the other. * * \note If a dispid returned from a successful call to one of the * underlying dispinterfaces' GetIDsOfNames() already * uses the striping bit, it is left alone. Such methods will be * successfully called in Invoke(), in its post-stripe processing. */ STDMETHOD(GetIDsOfNames)( REFIID riid , LPOLESTR* rgszNames , UINT cNames , LCID lcid , DISPID* rgdispid) { unsigned index = 1; HRESULT hr = dispatch_parent_0_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if( FAILED(hr) && DISP_E_UNKNOWNNAME == hr) { ++index; hr = dispatch_parent_1_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } if( FAILED(hr) && DISP_E_UNKNOWNNAME == hr) { ++index; hr = dispatch_parent_2_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } // Encode interface info into the dispid if(SUCCEEDED(hr)) { DISPID dispidFlag = DISPID(0x1) << (8 * sizeof(DISPID) - 2); dispidFlag >>= (index - 1); for(UINT i = 0; i < cNames; ++i) { if(rgdispid[i] < 0) { // Leave these alone. They'll be processed on a first-come-first-serve // basis, which assumes that the GetIDsOfNames() and Invoke() of I0 and // I1 are faithfully inter-related. } else { ATLSTL_MESSAGE_ASSERT("Dispatch Id is out of range!", 0 == (dispidFlag & rgdispid[i])); rgdispid[i] |= dispidFlag; } } } return hr; } /** \brief Provides the required behaviour of * IDispatch::Invoke(), by invoking this method on the * requisite dispinterface. * * This method operates by detecting the striping bit on the dispid, * from which the appropriate dispiniterface is determined. The * stripe is then removed, and the method invoked. * * \remarks Names are matched en bloc: they are either all matched by one * interface, or all by the other. It is never the case that * some part are matched by one and the remainder by the other. * * \note If no striping is apparent, the invocation is conducted on * each interface in turn. */ STDMETHOD(Invoke)( DISPID dispidMember , REFIID riid , LCID lcid , WORD wFlags , DISPPARAMS* pdispparams , VARIANT* pvarResult , EXCEPINFO* pexcepinfo , UINT* puArgErr) { if(dispidMember >= 0) { DISPID dispidFlag = DISPID(0x1) << (8 * sizeof(DISPID) - 2); dispidFlag >>= 0; if(dispidMember & dispidFlag) { return dispatch_parent_0_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } dispidFlag >>= 1; if(dispidMember & dispidFlag) { return dispatch_parent_1_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } dispidFlag >>= 1; if(dispidMember & dispidFlag) { return dispatch_parent_2_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } } HRESULT hr = dispatch_parent_0_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); if( FAILED(hr) && DISP_E_MEMBERNOTFOUND == hr) { hr = dispatch_parent_1_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } if( FAILED(hr) && DISP_E_MEMBERNOTFOUND == hr) { hr = dispatch_parent_2_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } return hr; } /// @} }; /** \brief Class template that enables the methods and properties exhibited * through four IDispatch interfaces to be visible to scripting clients. * * \ingroup group__library__com_automation * * IDispatchImpl4 is used (and operates) in exactly the same way as * IDispatchImpl3, except that it supports four dispinterfaces, rather than * three. */ // [[synesis:class: atlstl::IDispatchImpl2, IID const*, T, IID const*, T, IID const*, T, IID const*, GUID const*>]] template< ss_typename_param_k I0 , IID const* IID0 , ss_typename_param_k I1 , IID const* IID1 , ss_typename_param_k I2 , IID const* IID2 , ss_typename_param_k I3 , IID const* IID3 , GUID const* LibID > class IDispatchImpl4 : public IDispatchImpl , public IDispatchImpl , public IDispatchImpl , public IDispatchImpl { /// \name Member Types /// @{ public: typedef IDispatchImpl dispatch_parent_0_type; //!< \brief The type of the first dispinterface typedef IDispatchImpl dispatch_parent_1_type; //!< \brief The type of the second dispinterface typedef IDispatchImpl dispatch_parent_2_type; //!< \brief The type of the third dispinterface typedef IDispatchImpl dispatch_parent_3_type; //!< \brief The type of the fourth dispinterface /// @} /// \name IDispatch methods /// @{ protected: /** \brief Provides the required behaviour of * IDispatch::GetIDsOfNames(), by querying the four * dispinterfaces, in order, to match the name(s). * * This method operates by first determining which, if any, of the * four parent dispinterfaces can resolve the names. If successful, the * resultant dispatch Ids are then striped with a bit in their * most-significant byte(s) to record the index of the dispinterface * which has thus undertaken to interpret them. This stripe is then * detected * * \remarks Names are matched en bloc: they are either all matched by one * interface, or all by the other. It is never the case that * some part are matched by one and the remainder by the other. * * \note If a dispid returned from a successful call to one of the * underlying dispinterfaces' GetIDsOfNames() already * uses the striping bit, it is left alone. Such methods will be * successfully called in Invoke(), in its post-stripe processing. */ STDMETHOD(GetIDsOfNames)( REFIID riid , LPOLESTR* rgszNames , UINT cNames , LCID lcid , DISPID* rgdispid) { unsigned index = 1; HRESULT hr = dispatch_parent_0_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if( FAILED(hr) && DISP_E_UNKNOWNNAME == hr) { ++index; hr = dispatch_parent_1_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } if( FAILED(hr) && DISP_E_UNKNOWNNAME == hr) { ++index; hr = dispatch_parent_2_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } if( FAILED(hr) && DISP_E_UNKNOWNNAME == hr) { ++index; hr = dispatch_parent_3_type::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } // Encode interface info into the dispid if(SUCCEEDED(hr)) { DISPID dispidFlag = DISPID(0x1) << (8 * sizeof(DISPID) - 2); dispidFlag >>= (index - 1); for(UINT i = 0; i < cNames; ++i) { if(rgdispid[i] < 0) { // Leave these alone. They'll be processed on a first-come-first-serve // basis, which assumes that the GetIDsOfNames() and Invoke() of I0 and // I1 are faithfully inter-related. } else { ATLSTL_MESSAGE_ASSERT("Dispatch Id is out of range!", 0 == (dispidFlag & rgdispid[i])); rgdispid[i] |= dispidFlag; } } } return hr; } /** \brief Provides the required behaviour of * IDispatch::Invoke(), by invoking this method on the * requisite dispinterface. * * This method operates by detecting the striping bit on the dispid, * from which the appropriate dispiniterface is determined. The * stripe is then removed, and the method invoked. * * \remarks Names are matched en bloc: they are either all matched by one * interface, or all by the other. It is never the case that * some part are matched by one and the remainder by the other. * * \note If no striping is apparent, the invocation is conducted on * each interface in turn. */ STDMETHOD(Invoke)( DISPID dispidMember , REFIID riid , LCID lcid , WORD wFlags , DISPPARAMS* pdispparams , VARIANT* pvarResult , EXCEPINFO* pexcepinfo , UINT* puArgErr) { if(dispidMember >= 0) { DISPID dispidFlag = DISPID(0x1) << (8 * sizeof(DISPID) - 2); dispidFlag >>= 0; if(dispidMember & dispidFlag) { return dispatch_parent_0_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } dispidFlag >>= 1; if(dispidMember & dispidFlag) { return dispatch_parent_1_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } dispidFlag >>= 1; if(dispidMember & dispidFlag) { return dispatch_parent_2_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } dispidFlag >>= 1; if(dispidMember & dispidFlag) { return dispatch_parent_3_type::Invoke(dispidMember & ~dispidFlag, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } } HRESULT hr = dispatch_parent_0_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); if( FAILED(hr) && DISP_E_MEMBERNOTFOUND == hr) { hr = dispatch_parent_1_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } if( FAILED(hr) && DISP_E_MEMBERNOTFOUND == hr) { hr = dispatch_parent_2_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } if( FAILED(hr) && DISP_E_MEMBERNOTFOUND == hr) { hr = dispatch_parent_3_type::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } return hr; } /// @} }; /* ////////////////////////////////////////////////////////////////////// */ #ifndef _ATLSTL_NO_NAMESPACE # if defined(_STLSOFT_NO_NAMESPACE) || \ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace atlstl # else } // namespace atlstl_project } // namespace stlsoft # endif /* _STLSOFT_NO_NAMESPACE */ #endif /* !_ATLSTL_NO_NAMESPACE */ /* ////////////////////////////////////////////////////////////////////// */ #endif /* !ATLSTL_INCL_ATLSTL_AUTOMATION_HPP_MULTIPLE_DISPATCH */ /* ///////////////////////////// end of file //////////////////////////// */