From 11d7651e5ed04100ca0388e9617395cfc24092cf Mon Sep 17 00:00:00 2001 From: Thomas Geppert Date: Tue, 25 Jun 2024 13:19:11 +0200 Subject: [PATCH] Implement SAFEARRAY pointers and SAFEARRAYs as method parameters of a Dispatch Interface (#569) * Implement safearray pointers as method parameters of a Dispatch Interface to get server side modifications of safearrays marshaled back to the client. * Rebase on main. Added an interface to the C++ COM server for testing pointers to safearrays as method parameres of a Dispatch Interface. The implementation of the interface cannot share the IDispatch implementation with other interfaces and requires a separate component. (see https://learn.microsoft.com/en-us/cpp/atl/multiple-dual-interfaces) Therefore the naming scheme of the existing component that implements the test interface for record pointers was also changed. * Added unittest for safearrays as Dispatch Interface method parameters. * Fixed formating issues. Fixed changes that reverted PR#561 (fix import part in automation). * Apply suggestions from code review Co-authored-by: Jun Komoda <45822440+junkmd@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: Jun Komoda <45822440+junkmd@users.noreply.github.com> --- comtypes/automation.py | 14 + comtypes/test/test_dispifc_safearrays.py | 89 +++++ source/CppTestSrv/CFACTORY.CPP | 16 +- .../CoComtypesDispSafearrayParamTest.cpp | 315 ++++++++++++++++++ .../CoComtypesDispSafearrayParamTest.h | 78 +++++ source/CppTestSrv/MAKEFILE | 5 + source/CppTestSrv/REGISTRY.CPP | 18 +- source/CppTestSrv/REGISTRY.H | 3 +- source/CppTestSrv/SERVER.CPP | 15 +- source/CppTestSrv/SERVER.IDL | 43 +++ 10 files changed, 583 insertions(+), 13 deletions(-) create mode 100644 comtypes/test/test_dispifc_safearrays.py create mode 100644 source/CppTestSrv/CoComtypesDispSafearrayParamTest.cpp create mode 100644 source/CppTestSrv/CoComtypesDispSafearrayParamTest.h diff --git a/comtypes/automation.py b/comtypes/automation.py index 5f400dd5..268e1897 100644 --- a/comtypes/automation.py +++ b/comtypes/automation.py @@ -405,6 +405,11 @@ def _set_value(self, value): ri.AddRef() self._.pRecInfo = ri self._.pvRecord = cast(value, c_void_p) + elif isinstance(ref, _Pointer) and isinstance( + ref.contents, _safearray.tagSAFEARRAY + ): + self.vt = VT_ARRAY | ref._vartype_ | VT_BYREF + self._.pparray = cast(value, POINTER(POINTER(_safearray.tagSAFEARRAY))) else: self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF elif isinstance(value, _Pointer): @@ -422,6 +427,15 @@ def _set_value(self, value): ri.AddRef() self._.pRecInfo = ri self._.pvRecord = cast(value, c_void_p) + elif isinstance(ref, _safearray.tagSAFEARRAY): + obj = _midlSAFEARRAY(value._itemtype_).create(value.unpack()) + memmove(byref(self._), byref(obj), sizeof(obj)) + self.vt = VT_ARRAY | obj._vartype_ + elif isinstance(ref, _Pointer) and isinstance( + ref.contents, _safearray.tagSAFEARRAY + ): + self.vt = VT_ARRAY | ref._vartype_ | VT_BYREF + self._.pparray = cast(value, POINTER(POINTER(_safearray.tagSAFEARRAY))) else: self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF else: diff --git a/comtypes/test/test_dispifc_safearrays.py b/comtypes/test/test_dispifc_safearrays.py new file mode 100644 index 00000000..08694d12 --- /dev/null +++ b/comtypes/test/test_dispifc_safearrays.py @@ -0,0 +1,89 @@ +# coding: utf-8 + +from ctypes import byref, c_double, pointer +import unittest + +import comtypes +import comtypes.safearray +from comtypes import CLSCTX_LOCAL_SERVER +from comtypes.client import CreateObject, GetModule + +ComtypesCppTestSrvLib_GUID = "{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}" + +try: + GetModule((ComtypesCppTestSrvLib_GUID, 1, 0, 0)) + import comtypes.gen.ComtypesCppTestSrvLib as ComtypesCppTestSrvLib + + IMPORT_FAILED = False +except (ImportError, OSError): + IMPORT_FAILED = True + + +@unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.") +class Test_DispMethods(unittest.TestCase): + """Test dispmethods with safearray and safearray pointer parameters.""" + + UNPACKED_ZERO_VALS = tuple(0.0 for _ in range(10)) + UNPACKED_CONSECUTIVE_VALS = tuple(float(i) for i in range(10)) + + def _create_dispifc(self) -> "ComtypesCppTestSrvLib.IDispSafearrayParamTest": + # Explicitely ask for the dispinterface of the component. + return CreateObject( + "Comtypes.DispSafearrayParamTest", + clsctx=CLSCTX_LOCAL_SERVER, + interface=ComtypesCppTestSrvLib.IDispSafearrayParamTest, + ) + + def _create_zero_array(self): + return comtypes.safearray._midlSAFEARRAY(c_double).create( + [c_double() for _ in range(10)] + ) + + def _create_consecutive_array(self): + return comtypes.safearray._midlSAFEARRAY(c_double).create( + [c_double(i) for i in range(10)] + ) + + def test_inout_byref(self): + dispifc = self._create_dispifc() + # Passing a safearray by reference to a method that has declared the parameter + # as [in, out] we expect modifications of the safearray on the server side to + # also change the safearray on the client side. + test_array = self._create_zero_array() + self.assertEqual(test_array.unpack(), self.UNPACKED_ZERO_VALS) + dispifc.InitArray(byref(test_array)) + self.assertEqual(test_array.unpack(), self.UNPACKED_CONSECUTIVE_VALS) + + def test_inout_pointer(self): + dispifc = self._create_dispifc() + # Passing a safearray pointer to a method that has declared the parameter + # as [in, out] we expect modifications of the safearray on the server side to + # also change the safearray on the client side. + test_array = self._create_zero_array() + self.assertEqual(test_array.unpack(), self.UNPACKED_ZERO_VALS) + dispifc.InitArray(pointer(test_array)) + self.assertEqual(test_array.unpack(), self.UNPACKED_CONSECUTIVE_VALS) + + def test_in_safearray(self): + # Passing a safearray to a method that has declared the parameter just as [in] + # we expect modifications of the safearray on the server side NOT to change + # the safearray on the client side. + # We also need to test if the safearray gets properly passed to the method on + # the server side. For this, the 'VerifyArray' method returns 'True' if + # the safearray items have values equal to the initialization values + # provided by 'InitArray'. + for sa, expected, unpacked_content in [ + (self._create_consecutive_array(), True, self.UNPACKED_CONSECUTIVE_VALS), + # Also perform the inverted test. For this, create a safearray with zeros. + (self._create_zero_array(), False, self.UNPACKED_ZERO_VALS), + ]: + with self.subTest(expected=expected, unpacked_content=unpacked_content): + # Perform the check on initialization values. + self.assertEqual(self._create_dispifc().VerifyArray(sa), expected) + # Check if the safearray is unchanged although the method + # modifies the safearray on the server side. + self.assertEqual(sa.unpack(), unpacked_content) + + +if __name__ == "__main__": + unittest.main() diff --git a/source/CppTestSrv/CFACTORY.CPP b/source/CppTestSrv/CFACTORY.CPP index a618767d..0dc23c33 100644 --- a/source/CppTestSrv/CFACTORY.CPP +++ b/source/CppTestSrv/CFACTORY.CPP @@ -207,7 +207,21 @@ HRESULT CFactory::UnregisterAll() { UnregisterServer(*(g_FactoryDataArray[i].m_pCLSID), g_FactoryDataArray[i].m_szVerIndProgID, - g_FactoryDataArray[i].m_szProgID) ; + g_FactoryDataArray[i].m_szProgID, + g_FactoryDataArray[i].m_pLIBID) ; + // We can only unregister a TypeLib once. + // So if we just unregistered a TypeLib set all pointers to the same TypeLib to NULL. + if (g_FactoryDataArray[i].m_pLIBID != NULL) + { + const GUID* libid = g_FactoryDataArray[i].m_pLIBID ; + for(int j = 0 ; j < g_cFactoryDataEntries ; j++) + { + if (g_FactoryDataArray[j].m_pLIBID != NULL && g_FactoryDataArray[j].m_pLIBID == libid) + { + g_FactoryDataArray[j].m_pLIBID = NULL ; + } + } + } } return S_OK ; } diff --git a/source/CppTestSrv/CoComtypesDispSafearrayParamTest.cpp b/source/CppTestSrv/CoComtypesDispSafearrayParamTest.cpp new file mode 100644 index 00000000..8767c38e --- /dev/null +++ b/source/CppTestSrv/CoComtypesDispSafearrayParamTest.cpp @@ -0,0 +1,315 @@ +/* + This code is based on example code to the book: + Inside COM + by Dale E. Rogerson + Microsoft Press 1997 + ISBN 1-57231-349-8 +*/ + +// +// CoComtypesDispSafearrayParamTest.cpp - Component +// +#include +#include +#include +#include + +#include "Iface.h" +#include "Util.h" +#include "CUnknown.h" +#include "CFactory.h" // Needed for module handle +#include "CoComtypesDispSafearrayParamTest.h" + +// We need to put this declaration here because we explicitely expose a dispinterface +// in parallel to the dual interface but dispinterfaces don't appear in the +// MIDL-generated header file. +EXTERN_C const IID DIID_IDispSafearrayParamTest; + +static inline void trace(const char* msg) + { Util::Trace("CoComtypesDispSafearrayParamTest", msg, S_OK) ;} +static inline void trace(const char* msg, HRESULT hr) + { Util::Trace("CoComtypesDispSafearrayParamTest", msg, hr) ;} + +/////////////////////////////////////////////////////////// +// +// Interface IDualSafearrayParamTest - Implementation +// + +HRESULT __stdcall CB::InitArray(SAFEARRAY* *pptest_array) +{ + int i ; + double *pdata = NULL ; + HRESULT hr ; + std::ostringstream sout ; + + // Display the contents of the received SAFEARRAY. + hr = SafeArrayAccessData(*pptest_array, reinterpret_cast(&pdata)) ; + if (FAILED(hr)) + { + return E_FAIL ; + } + sout << "Received SAFEARRAY contains:" << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + for (i = 0; i < (*pptest_array)->rgsabound[0].cElements; i++) + { + sout << "\n\t\t" << "Element# " << i << ": " << pdata[i] << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + } + + // Modify the SAFEARRAY. + sout << "Modifying SAFEARRAY contents." << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + for (i = 0; i < (*pptest_array)->rgsabound[0].cElements; i++) + { + pdata[i] = (double)(i) ; + } + + // Display the contents of the modifyied SAFEARRAY. + sout << "Modifyied SAFEARRAY now contains:" << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + for (i = 0; i < (*pptest_array)->rgsabound[0].cElements; i++) + { + sout << "\n\t\t" << "Element# " << i << ": " << pdata[i] << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + } + SafeArrayUnaccessData(*pptest_array) ; + + return S_OK ; +} + +HRESULT __stdcall CB::VerifyArray(SAFEARRAY* ptest_array, + VARIANT_BOOL* result) +{ + long i ; + double *pdata = NULL ; + HRESULT hr ; + std::ostringstream sout ; + + // Display and verify the contents of the received SAFEARRAY. + *result = VARIANT_TRUE ; + hr = SafeArrayAccessData(ptest_array, reinterpret_cast(&pdata)) ; + if (FAILED(hr)) + { + return E_FAIL ; + } + sout << "Received SAFEARRAY contains: " << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + for (i = 0; i < ptest_array->rgsabound[0].cElements; i++) + { + if (pdata[i] != (double)(i)) + { + *result = VARIANT_FALSE ; + } + sout << "\n\t\t" << "Element# " << i << ": " << pdata[i] << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + } + + // Modify the SAFEARRAY. + sout << "Modifying SAFEARRAY contents." << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + for (i = 0; i < ptest_array->rgsabound[0].cElements; i++) + { + pdata[i] = (double)(0.0) ; + } + + // Display the contents of the modifyied SAFEARRAY. + sout << "Modified SAFEARRAY now contains:" << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + for (i = 0; i < ptest_array->rgsabound[0].cElements; i++) + { + sout << "\n\t\t" << "Element# " << i << ": " << pdata[i] << std::ends ; + trace(sout.str().c_str()) ; + sout.str("") ; + } + SafeArrayUnaccessData(ptest_array) ; + + return S_OK ; +} + + +// +// Constructor +// +CB::CB(IUnknown* pUnknownOuter) +: CUnknown(pUnknownOuter), + m_pITypeInfo(NULL) +{ + // Empty +} + +// +// Destructor +// +CB::~CB() +{ + if (m_pITypeInfo != NULL) + { + m_pITypeInfo->Release() ; + } + + trace("Destroy self.") ; +} + +// +// NondelegatingQueryInterface implementation +// +HRESULT __stdcall CB::NondelegatingQueryInterface(const IID& iid, + void** ppv) +{ + if (iid == IID_IDualSafearrayParamTest) + { + return FinishQI(static_cast(this), ppv) ; + } + else if (iid == DIID_IDispSafearrayParamTest) + { + trace("Queried for IDispSafearrayParamTest.") ; + return FinishQI(static_cast(this), ppv) ; + } + else if (iid == IID_IDispatch) + { + trace("Queried for IDispatch.") ; + return FinishQI(static_cast(this), ppv) ; + } + else + { + return CUnknown::NondelegatingQueryInterface(iid, ppv) ; + } +} + +// +// Load and register the type library. +// +HRESULT CB::Init() +{ + HRESULT hr ; + + // Load TypeInfo on demand if we haven't already loaded it. + if (m_pITypeInfo == NULL) + { + ITypeLib* pITypeLib = NULL ; + hr = ::LoadRegTypeLib(LIBID_ComtypesCppTestSrvLib, + 1, 0, // Major/Minor version numbers + 0x00, + &pITypeLib) ; + if (FAILED(hr)) + { + trace("LoadRegTypeLib Failed.", hr) ; + return hr ; + } + + // Get type information for the interface of the object. + hr = pITypeLib->GetTypeInfoOfGuid(IID_IDualSafearrayParamTest, + &m_pITypeInfo) ; + pITypeLib->Release() ; + if (FAILED(hr)) + { + trace("GetTypeInfoOfGuid failed.", hr) ; + return hr ; + } + } + return S_OK ; +} + +/////////////////////////////////////////////////////////// +// +// Creation function used by CFactory +// +HRESULT CB::CreateInstance(IUnknown* pUnknownOuter, + CUnknown** ppNewComponent ) +{ + if (pUnknownOuter != NULL) + { + // Don't allow aggregation (just for the heck of it). + return CLASS_E_NOAGGREGATION ; + } + + *ppNewComponent = new CB(pUnknownOuter) ; + return S_OK ; +} + +/////////////////////////////////////////////////////////// +// +// IDispatch implementation +// +HRESULT __stdcall CB::GetTypeInfoCount(UINT* pCountTypeInfo) +{ + trace("GetTypeInfoCount call succeeded.") ; + *pCountTypeInfo = 1 ; + return S_OK ; +} + +HRESULT __stdcall CB::GetTypeInfo( + UINT iTypeInfo, + LCID, // This object does not support localization. + ITypeInfo** ppITypeInfo) +{ + *ppITypeInfo = NULL ; + + if(iTypeInfo != 0) + { + trace("GetTypeInfo call failed -- bad iTypeInfo index.") ; + return DISP_E_BADINDEX ; + } + + trace("GetTypeInfo call succeeded.") ; + + // Call AddRef and return the pointer. + m_pITypeInfo->AddRef() ; + *ppITypeInfo = m_pITypeInfo ; + return S_OK ; +} + +HRESULT __stdcall CB::GetIDsOfNames( + const IID& iid, + OLECHAR** arrayNames, + UINT countNames, + LCID, // Localization is not supported. + DISPID* arrayDispIDs) +{ + if (iid != IID_NULL) + { + trace("GetIDsOfNames call failed -- bad IID.") ; + return DISP_E_UNKNOWNINTERFACE ; + } + + trace("GetIDsOfNames call succeeded.") ; + HRESULT hr = m_pITypeInfo->GetIDsOfNames(arrayNames, + countNames, + arrayDispIDs) ; + return hr ; +} + +HRESULT __stdcall CB::Invoke( + DISPID dispidMember, + const IID& iid, + LCID, // Localization is not supported. + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pvarResult, + EXCEPINFO* pExcepInfo, + UINT* pArgErr) +{ + if (iid != IID_NULL) + { + trace("Invoke call failed -- bad IID.") ; + return DISP_E_UNKNOWNINTERFACE ; + } + + ::SetErrorInfo(0, NULL) ; + + trace("Invoke call succeeded.") ; + HRESULT hr = m_pITypeInfo->Invoke( + static_cast(this), + dispidMember, wFlags, pDispParams, + pvarResult, pExcepInfo, pArgErr) ; + return hr ; +} diff --git a/source/CppTestSrv/CoComtypesDispSafearrayParamTest.h b/source/CppTestSrv/CoComtypesDispSafearrayParamTest.h new file mode 100644 index 00000000..159c33bf --- /dev/null +++ b/source/CppTestSrv/CoComtypesDispSafearrayParamTest.h @@ -0,0 +1,78 @@ +/* + This code is based on example code to the book: + Inside COM + by Dale E. Rogerson + Microsoft Press 1997 + ISBN 1-57231-349-8 +*/ + +// +// CoComtypesDispSafearrayParamTest.cpp - Component +// + +#include "Iface.h" +#include "CUnknown.h" + +/////////////////////////////////////////////////////////// +// +// Component B +// +class CB : public CUnknown, + public IDualSafearrayParamTest +{ +public: + // Creation + static HRESULT CreateInstance(IUnknown* pUnknownOuter, + CUnknown** ppNewComponent ) ; + +private: + // Declare the delegating IUnknown. + DECLARE_IUNKNOWN + + // IUnknown + virtual HRESULT __stdcall NondelegatingQueryInterface(const IID& iid, + void** ppv) ; + + // IDispatch + virtual HRESULT __stdcall GetTypeInfoCount(UINT* pCountTypeInfo) ; + + virtual HRESULT __stdcall GetTypeInfo( + UINT iTypeInfo, + LCID, // Localization is not supported. + ITypeInfo** ppITypeInfo) ; + + virtual HRESULT __stdcall GetIDsOfNames( + const IID& iid, + OLECHAR** arrayNames, + UINT countNames, + LCID, // Localization is not supported. + DISPID* arrayDispIDs) ; + + virtual HRESULT __stdcall Invoke( + DISPID dispidMember, + const IID& iid, + LCID, // Localization is not supported. + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pvarResult, + EXCEPINFO* pExcepInfo, + UINT* pArgErr) ; + + // Interface IDualSafearrayParamTest + virtual HRESULT __stdcall InitArray(SAFEARRAY* *test_array) ; + virtual HRESULT __stdcall VerifyArray( + SAFEARRAY *test_array, + VARIANT_BOOL* result) ; + + // Initialization + virtual HRESULT Init() ; + + // Constructor + CB(IUnknown* pUnknownOuter) ; + + // Destructor + ~CB() ; + + // Pointer to type information. + ITypeInfo* m_pITypeInfo ; +} ; diff --git a/source/CppTestSrv/MAKEFILE b/source/CppTestSrv/MAKEFILE index 5a3239bd..d23f37a1 100644 --- a/source/CppTestSrv/MAKEFILE +++ b/source/CppTestSrv/MAKEFILE @@ -48,6 +48,10 @@ CoComtypesDispRecordParamTest.obj : CoComtypesDispRecordParamTest.cpp \ CoComtypesDispRecordParamTest.h iface.h registry.h CUnknown.h cl $(CPP_FLAGS) CoComtypesDispRecordParamTest.cpp +CoComtypesDispSafearrayParamTest.obj : CoComtypesDispSafearrayParamTest.cpp \ + CoComtypesDispSafearrayParamTest.h iface.h registry.h CUnknown.h + cl $(CPP_FLAGS) CoComtypesDispSafearrayParamTest.cpp + # # Helper classes # @@ -76,6 +80,7 @@ outproc.obj : outproc.cpp CFactory.h CUnknown.h SERVER_OBJS = Server.obj \ CoComtypesDispRecordParamTest.obj \ + CoComtypesDispSafearrayParamTest.obj \ Registry.obj \ Cfactory.obj \ Cunknown.obj \ diff --git a/source/CppTestSrv/REGISTRY.CPP b/source/CppTestSrv/REGISTRY.CPP index 10fe020d..aed34984 100644 --- a/source/CppTestSrv/REGISTRY.CPP +++ b/source/CppTestSrv/REGISTRY.CPP @@ -152,7 +152,8 @@ HRESULT RegisterServer(HMODULE hModule, // DLL module handle // LONG UnregisterServer(const CLSID& clsid, // Class ID LPCWSTR szVerIndProgID, // Programmatic - LPCWSTR szProgID) // IDs + LPCWSTR szProgID, // IDs + const GUID* libid) // Library ID { // Convert the CLSID into a char. wchar_t szCLSID[GUID_STRING_SIZE] ; @@ -190,12 +191,15 @@ LONG UnregisterServer(const CLSID& clsid, // Class ID (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist. } - // Unregister the Type Library. - HRESULT hr = UnRegisterTypeLib(LIBID_ComtypesCppTestSrvLib, - 1, 0, // Major/Minor version numbers - 0x00, - SYS_WIN64) ; - assert(hr == S_OK) ; + // Unregister the Type Library if it's still registered. + if (libid != NULL) + { + HRESULT hr = UnRegisterTypeLib(*libid, + 1, 0, // Major/Minor version numbers + 0x00, + SYS_WIN64) ; + assert(hr == S_OK) ; + } return S_OK ; } diff --git a/source/CppTestSrv/REGISTRY.H b/source/CppTestSrv/REGISTRY.H index cf3f807d..9465a985 100644 --- a/source/CppTestSrv/REGISTRY.H +++ b/source/CppTestSrv/REGISTRY.H @@ -26,6 +26,7 @@ HRESULT RegisterServer(HMODULE hModule, // call this function from their DllUnregisterServer function. HRESULT UnregisterServer(const CLSID& clsid, LPCWSTR szVerIndProgID, - LPCWSTR szProgID) ; + LPCWSTR szProgID, + const GUID* libid) ; #endif \ No newline at end of file diff --git a/source/CppTestSrv/SERVER.CPP b/source/CppTestSrv/SERVER.CPP index 4d93648e..a7476157 100644 --- a/source/CppTestSrv/SERVER.CPP +++ b/source/CppTestSrv/SERVER.CPP @@ -9,6 +9,7 @@ #include "CFactory.h" #include "Iface.h" #include "CoComtypesDispRecordParamTest.h" +#include "CoComtypesDispSafearrayParamTest.h" /////////////////////////////////////////////////////////// @@ -35,11 +36,17 @@ // CFactoryData g_FactoryDataArray[] = { - {&CLSID_CoComtypesDispRecordParamTest, CA::CreateInstance, + {&CLSID_CoComtypesDispRecordParamTest, CA::CreateInstance, L"Comtypes component for dispinterface record parameter tests", // Friendly Name - L"Comtypes.DispRecordParamTest.1", // ProgID - L"Comtypes.DispRecordParamTest", // Version-independent ProgID - &LIBID_ComtypesCppTestSrvLib, // Type Library ID + L"Comtypes.DispRecordParamTest.1", // ProgID + L"Comtypes.DispRecordParamTest", // Version-independent ProgID + &LIBID_ComtypesCppTestSrvLib, // Type Library ID + NULL, 0}, + {&CLSID_CoComtypesDispSafearrayParamTest, CB::CreateInstance, + L"Comtypes component for dispinterface Safearray parameter tests", // Friendly Name + L"Comtypes.DispSafearrayParamTest.1", // ProgID + L"Comtypes.DispSafearrayParamTest", // Version-independent ProgID + &LIBID_ComtypesCppTestSrvLib, // Type Library ID NULL, 0} } ; int g_cFactoryDataEntries diff --git a/source/CppTestSrv/SERVER.IDL b/source/CppTestSrv/SERVER.IDL index b2b09b87..6b3df39c 100644 --- a/source/CppTestSrv/SERVER.IDL +++ b/source/CppTestSrv/SERVER.IDL @@ -46,6 +46,36 @@ dispinterface IDispRecordParamTest } ; +// Interface IDualSafearrayParamTest +[ + odl, + uuid(1F4F3B8B-D07E-4BB6-8D2C-D79B375696DA), + dual, + helpstring("IDualSafearrayParamTest Interface"), + nonextensible, + oleautomation +] +interface IDualSafearrayParamTest : IDispatch +{ + [id(0x00000001)] + HRESULT InitArray([in, out] SAFEARRAY(double)* test_array) ; + [id(0x00000002)] + HRESULT VerifyArray( + [in] SAFEARRAY(double) test_array, + [out, retval] VARIANT_BOOL* result); +} ; + + +// Interface IDispSafearrayParamTest +[ + uuid(4097A6D0-A111-40E2-BD0B-177B775A9496) +] +dispinterface IDispSafearrayParamTest +{ + interface IDualSafearrayParamTest; +} ; + + // // Component and type library descriptions // @@ -70,4 +100,17 @@ library ComtypesCppTestSrvLib interface IDualRecordParamTest ; dispinterface IDispRecordParamTest ; } ; + + + // CoComtypesDispSafearrayParamTest + // Component that implements interfaces used for dispinterface Safearray parameter tests. + [ + uuid(091D762E-FF4B-4532-8B24-23807FE873C3), + helpstring("Comtypes component for dispinterface Safearray parameter tests.") + ] + coclass CoComtypesDispSafearrayParamTest + { + interface IDualSafearrayParamTest ; + dispinterface IDispSafearrayParamTest ; + } ; } ;