From 8200a779696dfe16a59f5fcc83d1b978aeff4d1b Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 11 Oct 2026 10:05:18 +0900 Subject: [PATCH] test: Add tests for explicit string `SAFEARRAY` types inside VARIANTs. Add a native COM test server and Python tests covering conversions and repetition of `SAFEARRAY(VARIANT)` and `SAFEARRAY(BSTR)` values. Verify that typed-array mismatches return `DISP_E_TYPEMISMATCH` while preserving the existing default string-array behavior. --- .../test_string_safearray_inside_variant.py | 138 +++++ .../CoStringSafearrayInsideVariantTest.cpp | 521 ++++++++++++++++++ .../CoStringSafearrayInsideVariantTest.h | 62 +++ source/CppTestSrv/MAKEFILE | 5 + source/CppTestSrv/SERVER.CPP | 7 + source/CppTestSrv/SERVER.IDL | 38 ++ 6 files changed, 771 insertions(+) create mode 100644 comtypes/test/test_string_safearray_inside_variant.py create mode 100644 source/CppTestSrv/CoStringSafearrayInsideVariantTest.cpp create mode 100644 source/CppTestSrv/CoStringSafearrayInsideVariantTest.h diff --git a/comtypes/test/test_string_safearray_inside_variant.py b/comtypes/test/test_string_safearray_inside_variant.py new file mode 100644 index 000000000..839e055c0 --- /dev/null +++ b/comtypes/test/test_string_safearray_inside_variant.py @@ -0,0 +1,138 @@ +"""Ownership and element-type tests for the native string SAFEARRAY double.""" + +import itertools +import sys +import unittest +from _ctypes import COMError + +from comtypes import BSTR, CLSCTX_LOCAL_SERVER, hresult +from comtypes.automation import VARIANT, _midlSAFEARRAY +from comtypes.client import CreateObject, GetModule + +try: + GetModule(("{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}", 1, 0, 0)) + import comtypes.gen.ComtypesCppTestSrvLib as ComtypesCppTestSrvLib + + IMPORT_SERVER_FAILED = False +except (ImportError, OSError): + IMPORT_SERVER_FAILED = True + + +def _create_tester() -> "ComtypesCppTestSrvLib.IStringSafearrayInsideVariantTest": + return CreateObject( + "Comtypes.CoStringSafearrayInsideVariantTest", + clsctx=CLSCTX_LOCAL_SERVER, + interface=ComtypesCppTestSrvLib.IStringSafearrayInsideVariantTest, + ) + + +_BstrArrType = _midlSAFEARRAY(BSTR) +_VariantArrType = _midlSAFEARRAY(VARIANT) + +PY_VER = "Python {0}.{1}.{2}".format(*sys.version_info[:3]) +# It seems to be the same root cause we discussed in https://github.com/enthought/comtypes/issues/212 +IS_SKIP_PY_VER = sys.version_info[:2] == ( + (3, 9) + or (sys.version_info[:2] == (3, 10) and sys.version_info < (3, 10, 10)) + or (sys.version_info[:2] == (3, 11) and sys.version_info < (3, 11, 2)) +) + + +@unittest.skipIf(IS_SKIP_PY_VER, f"This fails in {PY_VER}.") +@unittest.skipIf(IMPORT_SERVER_FAILED, "Requires the C++ out-of-process COM server.") +class TestVariantArrayToBstrArray(unittest.TestCase): + def test_returns_bstr_array(self): + tester = _create_tester() + for factory, values in itertools.product( + (_VariantArrType.create, _VariantArrType.from_param), + (("foo", "bar"), ["foo", "bar"]), + ): + with self.subTest(factory=factory.__name__, values=values): + self.assertEqual( + tester.VariantArrayToBstrArray(factory(values)), ("foo", "bar") + ) + + def test_raises_element_type_mismatch(self): + tester = _create_tester() + pa = _BstrArrType.from_param(["foo", "bar"]) + with self.assertRaises(COMError) as cm: + tester.VariantArrayToBstrArray(pa) + self.assertEqual(cm.exception.hresult, hresult.DISP_E_TYPEMISMATCH) + + +@unittest.skipIf(IS_SKIP_PY_VER, f"This fails in {PY_VER}.") +@unittest.skipIf(IMPORT_SERVER_FAILED, "Requires the C++ out-of-process COM server.") +class TestBstrArrayToVariantArray(unittest.TestCase): + def test_returns_variant_array(self): + tester = _create_tester() + for factory, values in itertools.product( + (_BstrArrType.create, _BstrArrType.from_param), + (("foo", "bar"), ["foo", "bar"]), + ): + with self.subTest(factory=factory.__name__, values=values): + self.assertEqual( + tester.BstrArrayToVariantArray(factory(values)), ("foo", "bar") + ) + + def test_raises_element_type_mismatch(self): + tester = _create_tester() + pa = _VariantArrType.from_param(["foo", "bar"]) + with self.assertRaises(COMError) as cm: + tester.BstrArrayToVariantArray(pa) + self.assertEqual(cm.exception.hresult, hresult.DISP_E_TYPEMISMATCH) + + +@unittest.skipIf(IS_SKIP_PY_VER, f"This fails in {PY_VER}.") +@unittest.skipIf(IMPORT_SERVER_FAILED, "Requires the C++ out-of-process COM server.") +class TestRepeatVariantArray(unittest.TestCase): + def test_returns_repeated_variant_array(self): + tester = _create_tester() + for factory, values in itertools.product( + (_VariantArrType.create, _VariantArrType.from_param), + (("foo", "bar"), ["foo", "bar"]), + ): + for repeat in range(4): + with self.subTest( + factory=factory.__name__, values=values, repeat=repeat + ): + self.assertEqual( + tester.RepeatVariantArray(factory(values), repeat), + ("foo", "bar") * repeat, + ) + + def test_raises_element_type_mismatch(self): + tester = _create_tester() + pa = _BstrArrType.from_param(["foo", "bar"]) + with self.assertRaises(COMError) as cm: + tester.RepeatVariantArray(pa, 1) + self.assertEqual(cm.exception.hresult, hresult.DISP_E_TYPEMISMATCH) + + +@unittest.skipIf(IS_SKIP_PY_VER, f"This fails in {PY_VER}.") +@unittest.skipIf(IMPORT_SERVER_FAILED, "Requires the C++ out-of-process COM server.") +class TestRepeatBstrArray(unittest.TestCase): + def test_returns_repeated_bstr_array(self): + tester = _create_tester() + for factory, values in itertools.product( + (_BstrArrType.create, _BstrArrType.from_param), + (("foo", "bar"), ["foo", "bar"]), + ): + for repeat in range(4): + with self.subTest( + factory=factory.__name__, values=values, repeat=repeat + ): + self.assertEqual( + tester.RepeatBstrArray(factory(values), repeat), + ("foo", "bar") * repeat, + ) + + def test_raises_element_type_mismatch(self): + tester = _create_tester() + pa = _VariantArrType.from_param(["foo", "bar"]) + with self.assertRaises(COMError) as cm: + tester.RepeatBstrArray(pa, 1) + self.assertEqual(cm.exception.hresult, hresult.DISP_E_TYPEMISMATCH) + + +if __name__ == "__main__": + unittest.main() diff --git a/source/CppTestSrv/CoStringSafearrayInsideVariantTest.cpp b/source/CppTestSrv/CoStringSafearrayInsideVariantTest.cpp new file mode 100644 index 000000000..c77c4d7ee --- /dev/null +++ b/source/CppTestSrv/CoStringSafearrayInsideVariantTest.cpp @@ -0,0 +1,521 @@ +#include +#include +#include +#include + +#include "Iface.h" +#include "Util.h" +#include "CUnknown.h" +#include "CFactory.h" +#include "CoStringSafearrayInsideVariantTest.h" + +static inline void trace(const char* msg) + { Util::Trace("CoStringSafearrayInsideVariantTest", msg, S_OK) ;} +static inline void trace(const char* msg, HRESULT hr) + { Util::Trace("CoStringSafearrayInsideVariantTest", msg, hr) ;} + +static HRESULT Extract1DSafeArray(const VARIANT& var, SAFEARRAY** ppsa, VARTYPE* pvtElem) +{ + SAFEARRAY* psa = NULL ; + if (var.vt & VT_BYREF) + { + if ((var.vt & ~VT_BYREF) == (VT_ARRAY | VT_BSTR) || + (var.vt & ~VT_BYREF) == (VT_ARRAY | VT_VARIANT) || + ((var.vt & ~VT_BYREF) & VT_ARRAY)) + { + psa = *(var.pparray) ; + } + else + { + return DISP_E_TYPEMISMATCH ; + } + } + else if (var.vt & VT_ARRAY) + { + psa = var.parray ; + } + else + { + return DISP_E_TYPEMISMATCH ; + } + + if (psa == NULL) + { + return DISP_E_TYPEMISMATCH ; + } + + if (SafeArrayGetDim(psa) != 1) + { + return DISP_E_TYPEMISMATCH ; + } + + VARTYPE vtElem = 0 ; + HRESULT hr = SafeArrayGetVartype(psa, &vtElem) ; + if (FAILED(hr) || vtElem == VT_EMPTY) + { + vtElem = var.vt & ~(VT_ARRAY | VT_BYREF) ; + } + + *ppsa = psa ; + *pvtElem = vtElem ; + return S_OK ; +} + +static HRESULT CheckBstrArrayValues(SAFEARRAY* psa, VARIANT_BOOL* result) +{ + long lBound = 0 ; + long uBound = -1 ; + SafeArrayGetLBound(psa, 1, &lBound) ; + SafeArrayGetUBound(psa, 1, &uBound) ; + long count = uBound - lBound + 1 ; + + if (count != 2) + { + *result = VARIANT_FALSE ; + return S_OK ; + } + + BSTR* pbstrData = NULL ; + HRESULT hr = SafeArrayAccessData(psa, reinterpret_cast(&pbstrData)) ; + if (FAILED(hr)) + { + return hr ; + } + + if (pbstrData[0] != NULL && wcscmp(pbstrData[0], L"foo") == 0 && + pbstrData[1] != NULL && wcscmp(pbstrData[1], L"bar") == 0) + { + *result = VARIANT_TRUE ; + } + else + { + *result = VARIANT_FALSE ; + } + + SafeArrayUnaccessData(psa) ; + return S_OK ; +} + +static HRESULT CheckVariantArrayValues(SAFEARRAY* psa, VARIANT_BOOL* result) +{ + long lBound = 0 ; + long uBound = -1 ; + SafeArrayGetLBound(psa, 1, &lBound) ; + SafeArrayGetUBound(psa, 1, &uBound) ; + long count = uBound - lBound + 1 ; + + if (count != 2) + { + *result = VARIANT_FALSE ; + return S_OK ; + } + + VARIANT* pvarData = NULL ; + HRESULT hr = SafeArrayAccessData(psa, reinterpret_cast(&pvarData)) ; + if (FAILED(hr)) + { + return hr ; + } + + if (pvarData[0].vt == VT_BSTR && pvarData[0].bstrVal != NULL && wcscmp(pvarData[0].bstrVal, L"foo") == 0 && + pvarData[1].vt == VT_BSTR && pvarData[1].bstrVal != NULL && wcscmp(pvarData[1].bstrVal, L"bar") == 0) + { + *result = VARIANT_TRUE ; + } + else + { + *result = VARIANT_FALSE ; + } + + SafeArrayUnaccessData(psa) ; + return S_OK ; +} + +/////////////////////////////////////////////////////////// +// +// Interface IStringSafearrayInsideVariantTest - Implementation +// + +static HRESULT GetStringArrayLength(SAFEARRAY* arr, VARTYPE elementType, + ULONG* length) +{ + if (arr == NULL || length == NULL || SafeArrayGetDim(arr) != 1) + { + return E_INVALIDARG ; + } + VARTYPE actual = 0 ; + if (FAILED(SafeArrayGetVartype(arr, &actual)) || actual != elementType) + { + return DISP_E_TYPEMISMATCH ; + } + LONG lo = 0, hi = -1 ; SafeArrayGetLBound(arr, 1, &lo); SafeArrayGetUBound(arr, 1, &hi); + *length = hi >= lo ? static_cast(hi - lo + 1) : 0; + return S_OK ; +} + +static HRESULT CreateStringArray(VARTYPE elementType, ULONG length, + SAFEARRAY** result) +{ + SAFEARRAYBOUND bound = {length, 0}; + *result = SafeArrayCreate(elementType, 1, &bound); + if (*result == NULL && bound.cElements != 0) + { + return E_OUTOFMEMORY; + } + return S_OK ; +} + +static HRESULT PrepareRepeatedStringArray(SAFEARRAY* arr, VARTYPE elementType, + LONG repeat, SAFEARRAY** result, ULONG* length) +{ + if (result == NULL || repeat < 0) + { + return E_INVALIDARG ; + } + HRESULT hr = GetStringArrayLength(arr, elementType, length) ; + if (FAILED(hr)) + { + return hr ; + } + SAFEARRAYBOUND bound = {*length * static_cast(repeat), 0}; + return CreateStringArray(elementType, bound.cElements, result) ; +} + +static HRESULT VariantArrayToBstrArrayImpl(const VARIANT& var, SAFEARRAY** result) +{ + SAFEARRAY* arr = NULL; VARTYPE elementType = 0; + HRESULT hr = Extract1DSafeArray(var, &arr, &elementType); + if (FAILED(hr) || elementType != VT_VARIANT) return DISP_E_TYPEMISMATCH; + ULONG length = 0 ; + hr = GetStringArrayLength(arr, VT_VARIANT, &length) ; + if (FAILED(hr) || result == NULL) + { + return FAILED(hr) ? hr : E_INVALIDARG ; + } + hr = CreateStringArray(VT_BSTR, length, result); + if (FAILED(hr)) + { + return hr; + } + SAFEARRAY* out = *result; + if (length != 0) + { + void* src = NULL; void* dst = NULL; hr = SafeArrayAccessData(arr, &src); + if (FAILED(hr)) + { + return hr; + } + hr = SafeArrayAccessData(out, &dst); + if (FAILED(hr)) + { + SafeArrayUnaccessData(arr); SafeArrayDestroy(out); + return hr; + } + for (ULONG i = 0; i < length; ++i) + { + static_cast(dst)[i] = SysAllocString(static_cast(src)[i].bstrVal); + } + SafeArrayUnaccessData(out); SafeArrayUnaccessData(arr); + } + return S_OK; +} + +static HRESULT BstrArrayToVariantArrayImpl(const VARIANT& var, SAFEARRAY** result) +{ + SAFEARRAY* arr = NULL; VARTYPE elementType = 0; + HRESULT hr = Extract1DSafeArray(var, &arr, &elementType); + if (FAILED(hr) || elementType != VT_BSTR) return DISP_E_TYPEMISMATCH; + ULONG length = 0 ; + hr = GetStringArrayLength(arr, VT_BSTR, &length) ; + if (FAILED(hr) || result == NULL) + { + return FAILED(hr) ? hr : E_INVALIDARG ; + } + hr = CreateStringArray(VT_VARIANT, length, result); + if (FAILED(hr)) + { + return hr; + } + SAFEARRAY* out = *result; + if (length != 0) + { + void* src = NULL; void* dst = NULL; hr = SafeArrayAccessData(arr, &src); + if (FAILED(hr)) + { + return hr; + } + hr = SafeArrayAccessData(out, &dst); + if (FAILED(hr)) + { + SafeArrayUnaccessData(arr); SafeArrayDestroy(out); + return hr; + } + for (ULONG i = 0; i < length; ++i) + { + VariantInit(&static_cast(dst)[i]); + static_cast(dst)[i].vt = VT_BSTR; + static_cast(dst)[i].bstrVal = SysAllocString(static_cast(src)[i]); + } + SafeArrayUnaccessData(out); SafeArrayUnaccessData(arr); + } + return S_OK; +} + +static HRESULT RepeatVariantArrayImpl(const VARIANT& var, LONG repeat, + SAFEARRAY** result) +{ + SAFEARRAY* arr = NULL; VARTYPE elementType = 0; + HRESULT hr = Extract1DSafeArray(var, &arr, &elementType); + if (FAILED(hr) || elementType != VT_VARIANT) return DISP_E_TYPEMISMATCH; + ULONG length = 0 ; + hr = PrepareRepeatedStringArray(arr, VT_VARIANT, repeat, result, &length) ; + if (FAILED(hr)) + { + return hr; + } + SAFEARRAY* out = *result; + SAFEARRAYBOUND bound = {length * static_cast(repeat), 0}; + if (bound.cElements != 0) + { + void* src = NULL; void* dst = NULL; hr = SafeArrayAccessData(arr, &src); + if (FAILED(hr)) + { + return hr; + } + hr = SafeArrayAccessData(out, &dst); + if (FAILED(hr)) + { + SafeArrayUnaccessData(arr); SafeArrayDestroy(out); + return hr; + } + for (ULONG i = 0; i < bound.cElements; ++i) + { + ULONG j = i % length; + VariantInit(&static_cast(dst)[i]); + VariantCopy(&static_cast(dst)[i], &static_cast(src)[j]); + } + SafeArrayUnaccessData(out); SafeArrayUnaccessData(arr); + } + return S_OK; +} + +static HRESULT RepeatBstrArrayImpl(const VARIANT& var, LONG repeat, + SAFEARRAY** result) +{ + SAFEARRAY* arr = NULL; VARTYPE elementType = 0; + HRESULT hr = Extract1DSafeArray(var, &arr, &elementType); + if (FAILED(hr) || elementType != VT_BSTR) return DISP_E_TYPEMISMATCH; + ULONG length = 0 ; + hr = PrepareRepeatedStringArray(arr, VT_BSTR, repeat, result, &length) ; + if (FAILED(hr)) + { + return hr; + } + SAFEARRAY* out = *result; + SAFEARRAYBOUND bound = {length * static_cast(repeat), 0}; + if (bound.cElements != 0) + { + void* src = NULL; void* dst = NULL; hr = SafeArrayAccessData(arr, &src); + if (FAILED(hr)) + { + return hr; + } + hr = SafeArrayAccessData(out, &dst); + if (FAILED(hr)) + { + SafeArrayUnaccessData(arr); SafeArrayDestroy(out); + return hr; + } + for (ULONG i = 0; i < bound.cElements; ++i) + { + ULONG j = i % length; + static_cast(dst)[i] = SysAllocString(static_cast(src)[j]); + } + SafeArrayUnaccessData(out); SafeArrayUnaccessData(arr); + } + return S_OK; +} + +static HRESULT WrapStringArrayResult( + HRESULT hr, SAFEARRAY* array, VARTYPE elementType, VARIANT* result) +{ + if (FAILED(hr)) + { + if (array != NULL) SafeArrayDestroy(array); + return hr; + } + if (result == NULL) + { + if (array != NULL) SafeArrayDestroy(array); + return E_INVALIDARG; + } + VariantInit(result); + result->vt = VT_ARRAY | elementType; + result->parray = array; + return S_OK; +} + +HRESULT __stdcall CStringSafearrayInsideVariantTest::VariantArrayToBstrArray(VARIANT a, VARIANT* r) +{ + SAFEARRAY* array = NULL; + return WrapStringArrayResult(VariantArrayToBstrArrayImpl(a, &array), array, VT_BSTR, r); +} +HRESULT __stdcall CStringSafearrayInsideVariantTest::BstrArrayToVariantArray(VARIANT a, VARIANT* r) +{ + SAFEARRAY* array = NULL; + return WrapStringArrayResult(BstrArrayToVariantArrayImpl(a, &array), array, VT_VARIANT, r); +} +HRESULT __stdcall CStringSafearrayInsideVariantTest::RepeatVariantArray(VARIANT a, LONG n, VARIANT* r) +{ + SAFEARRAY* array = NULL; + return WrapStringArrayResult(RepeatVariantArrayImpl(a, n, &array), array, VT_VARIANT, r); +} +HRESULT __stdcall CStringSafearrayInsideVariantTest::RepeatBstrArray(VARIANT a, LONG n, VARIANT* r) +{ + SAFEARRAY* array = NULL; + return WrapStringArrayResult(RepeatBstrArrayImpl(a, n, &array), array, VT_BSTR, r); +} + +/////////////////////////////////////////////////////////// +// +// Constructor / Destructor / QI +// + +CStringSafearrayInsideVariantTest::CStringSafearrayInsideVariantTest(IUnknown* pUnknownOuter) +: CUnknown(pUnknownOuter), + m_pITypeInfo(NULL) +{ +} + +CStringSafearrayInsideVariantTest::~CStringSafearrayInsideVariantTest() +{ + if (m_pITypeInfo != NULL) + { + m_pITypeInfo->Release() ; + } + trace("Destroy self.") ; +} + +HRESULT __stdcall CStringSafearrayInsideVariantTest::NondelegatingQueryInterface(const IID& iid, void** ppv) +{ + if (iid == IID_IStringSafearrayInsideVariantTest) + { + 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) ; + } +} + +HRESULT CStringSafearrayInsideVariantTest::Init() +{ + HRESULT hr ; + if (m_pITypeInfo == NULL) + { + ITypeLib* pITypeLib = NULL ; + hr = ::LoadRegTypeLib(LIBID_ComtypesCppTestSrvLib, + 1, 0, + 0x00, + &pITypeLib) ; + if (FAILED(hr)) + { + trace("LoadRegTypeLib Failed.", hr) ; + return hr ; + } + + hr = pITypeLib->GetTypeInfoOfGuid(IID_IStringSafearrayInsideVariantTest, + &m_pITypeInfo) ; + pITypeLib->Release() ; + if (FAILED(hr)) + { + trace("GetTypeInfoOfGuid failed.", hr) ; + return hr ; + } + } + return S_OK ; +} + +HRESULT CStringSafearrayInsideVariantTest::CreateInstance(IUnknown* pUnknownOuter, + CUnknown** ppNewComponent) +{ + if (pUnknownOuter != NULL) + { + return CLASS_E_NOAGGREGATION ; + } + *ppNewComponent = new CStringSafearrayInsideVariantTest(pUnknownOuter) ; + return S_OK ; +} + +/////////////////////////////////////////////////////////// +// +// IDispatch implementation +// + +HRESULT __stdcall CStringSafearrayInsideVariantTest::GetTypeInfoCount(UINT* pCountTypeInfo) +{ + trace("GetTypeInfoCount call succeeded.") ; + *pCountTypeInfo = 1 ; + return S_OK ; +} + +HRESULT __stdcall CStringSafearrayInsideVariantTest::GetTypeInfo( + UINT iTypeInfo, + LCID, + ITypeInfo** ppITypeInfo) +{ + *ppITypeInfo = NULL ; + if (iTypeInfo != 0) + { + trace("GetTypeInfo call failed -- bad iTypeInfo index.") ; + return DISP_E_BADINDEX ; + } + trace("GetTypeInfo call succeeded.") ; + m_pITypeInfo->AddRef() ; + *ppITypeInfo = m_pITypeInfo ; + return S_OK ; +} + +HRESULT __stdcall CStringSafearrayInsideVariantTest::GetIDsOfNames( + const IID& iid, + OLECHAR** arrayNames, + UINT countNames, + LCID, + DISPID* arrayDispIDs) +{ + if (iid != IID_NULL) + { + trace("GetIDsOfNames call failed -- bad IID.") ; + return DISP_E_UNKNOWNINTERFACE ; + } + trace("GetIDsOfNames call succeeded.") ; + return m_pITypeInfo->GetIDsOfNames(arrayNames, countNames, arrayDispIDs) ; +} + +HRESULT __stdcall CStringSafearrayInsideVariantTest::Invoke( + DISPID dispidMember, + const IID& iid, + LCID, + 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.") ; + return m_pITypeInfo->Invoke( + static_cast(this), + dispidMember, wFlags, pDispParams, + pvarResult, pExcepInfo, pArgErr) ; +} diff --git a/source/CppTestSrv/CoStringSafearrayInsideVariantTest.h b/source/CppTestSrv/CoStringSafearrayInsideVariantTest.h new file mode 100644 index 000000000..af1e835ab --- /dev/null +++ b/source/CppTestSrv/CoStringSafearrayInsideVariantTest.h @@ -0,0 +1,62 @@ +#include "Iface.h" +#include "CUnknown.h" + +class CStringSafearrayInsideVariantTest : public CUnknown, + public IStringSafearrayInsideVariantTest +{ +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 IStringSafearrayInsideVariantTest + virtual HRESULT __stdcall VariantArrayToBstrArray(VARIANT arr, VARIANT* result) ; + virtual HRESULT __stdcall BstrArrayToVariantArray(VARIANT arr, VARIANT* result) ; + virtual HRESULT __stdcall RepeatVariantArray(VARIANT arr, LONG count, VARIANT* result) ; + virtual HRESULT __stdcall RepeatBstrArray(VARIANT arr, LONG count, VARIANT* result) ; + + // Initialization + virtual HRESULT Init() ; + + // Constructor + CStringSafearrayInsideVariantTest(IUnknown* pUnknownOuter) ; + + // Destructor + ~CStringSafearrayInsideVariantTest() ; + + // Pointer to type information. + ITypeInfo* m_pITypeInfo ; +} ; diff --git a/source/CppTestSrv/MAKEFILE b/source/CppTestSrv/MAKEFILE index 4c02592dc..0c5c6afdb 100644 --- a/source/CppTestSrv/MAKEFILE +++ b/source/CppTestSrv/MAKEFILE @@ -56,6 +56,10 @@ CoNumericSafearrayTest.obj : CoNumericSafearrayTest.cpp \ CoNumericSafearrayTest.h iface.h CUnknown.h cl $(CPP_FLAGS) CoNumericSafearrayTest.cpp +CoStringSafearrayInsideVariantTest.obj : CoStringSafearrayInsideVariantTest.cpp \ + CoStringSafearrayInsideVariantTest.h iface.h registry.h CUnknown.h + cl $(CPP_FLAGS) CoStringSafearrayInsideVariantTest.cpp + # # Helper classes # @@ -86,6 +90,7 @@ SERVER_OBJS = Server.obj \ CoComtypesDispRecordParamTest.obj \ CoComtypesDispSafearrayParamTest.obj \ CoNumericSafearrayTest.obj \ + CoStringSafearrayInsideVariantTest.obj \ Registry.obj \ Cfactory.obj \ Cunknown.obj \ diff --git a/source/CppTestSrv/SERVER.CPP b/source/CppTestSrv/SERVER.CPP index 0db4c5410..5b04fb61b 100644 --- a/source/CppTestSrv/SERVER.CPP +++ b/source/CppTestSrv/SERVER.CPP @@ -11,6 +11,7 @@ #include "CoComtypesDispRecordParamTest.h" #include "CoComtypesDispSafearrayParamTest.h" #include "CoNumericSafearrayTest.h" +#include "CoStringSafearrayInsideVariantTest.h" /////////////////////////////////////////////////////////// @@ -54,6 +55,12 @@ CFactoryData g_FactoryDataArray[] = L"Comtypes.NumericSafearrayTest.1", L"Comtypes.NumericSafearrayTest", &LIBID_ComtypesCppTestSrvLib, + NULL, 0}, + {&CLSID_CoStringSafearrayInsideVariantTest, CStringSafearrayInsideVariantTest::CreateInstance, + L"String SAFEARRAY inside VARIANT Tester", + L"Comtypes.CoStringSafearrayInsideVariantTest.1", + L"Comtypes.CoStringSafearrayInsideVariantTest", + &LIBID_ComtypesCppTestSrvLib, NULL, 0} } ; int g_cFactoryDataEntries diff --git a/source/CppTestSrv/SERVER.IDL b/source/CppTestSrv/SERVER.IDL index 365071a06..25e864443 100644 --- a/source/CppTestSrv/SERVER.IDL +++ b/source/CppTestSrv/SERVER.IDL @@ -145,4 +145,42 @@ library ComtypesCppTestSrvLib { [default] interface INumericSafearrayTest ; } ; + + // Interface IStringSafearrayInsideVariantTest + [ + odl, + uuid(D621F143-5AAB-47B4-B2CF-8C4E2ED52E2B), + dual, + helpstring("Provides operations for converting and repeating one-dimensional string SAFEARRAYs passed as VARIANT values. Array results are returned through VARIANT*."), + nonextensible, + oleautomation + ] + interface IStringSafearrayInsideVariantTest : IDispatch + { + [id(0x00000001), helpstring("Convert a one-dimensional SAFEARRAY(VARIANT) to a SAFEARRAY(BSTR).")] + HRESULT VariantArrayToBstrArray( + [in] VARIANT arr, + [out, retval] VARIANT* result); + [id(0x00000002), helpstring("Convert a one-dimensional SAFEARRAY(BSTR) to a SAFEARRAY(VARIANT).")] + HRESULT BstrArrayToVariantArray( + [in] VARIANT arr, + [out, retval] VARIANT* result); + [id(0x00000003), helpstring("Repeat the elements of a one-dimensional SAFEARRAY(VARIANT) count times. *count* must be non-negative.")] + HRESULT RepeatVariantArray( + [in] VARIANT arr, [in] LONG count, + [out, retval] VARIANT* result); + [id(0x00000004), helpstring("Repeat the elements of a one-dimensional SAFEARRAY(BSTR) count times. *count* must be non-negative.")] + HRESULT RepeatBstrArray( + [in] VARIANT arr, [in] LONG count, + [out, retval] VARIANT* result); + } ; + + [ + uuid(D54643DA-37A2-4823-8812-3A9F60C29CFA), + helpstring("String SAFEARRAY inside VARIANT Tester") + ] + coclass CoStringSafearrayInsideVariantTest + { + [default] interface IStringSafearrayInsideVariantTest ; + } ; } ;