diff --git a/src/System.Windows.Forms.Design/src/Resources/SR.resx b/src/System.Windows.Forms.Design/src/Resources/SR.resx index eac5cc0aa9..1e5cd836f5 100644 --- a/src/System.Windows.Forms.Design/src/Resources/SR.resx +++ b/src/System.Windows.Forms.Design/src/Resources/SR.resx @@ -133,12 +133,6 @@ Read-Only - - Double cannot be converted to a date. - - - Integer cannot be converted to a float. - Unhandled VT: {0}. diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf index f2984f56ce..6c71d78e97 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.cs.xlf @@ -406,16 +406,6 @@ Nezpracovaná hodnota VT: {0}. - - Double cannot be converted to a date. - Nelze převést typ Double na typ Date. - - - - Integer cannot be converted to a float. - Nelze převést typ Integer na typ Float. - - Alignment Picker Výběr přiřazení diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf index 912855a523..c48b784ed9 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.de.xlf @@ -406,16 +406,6 @@ Unbehandelter VT: {0}. - - Double cannot be converted to a date. - "Double" kann nicht in "Date" konvertiert werden. - - - - Integer cannot be converted to a float. - "Integer" kann nicht in "Float" konvertiert werden. - - Alignment Picker Ausrichtungsauswahl diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf index cba42ab044..6f42ee1c61 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.es.xlf @@ -406,16 +406,6 @@ VT no controlado: {0}. - - Double cannot be converted to a date. - No se puede convertir double en date. - - - - Integer cannot be converted to a float. - No se puede convertir integer en float. - - Alignment Picker Selector de alineación diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf index 261f051244..4d0e524842 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.fr.xlf @@ -406,16 +406,6 @@ VT non géré : {0}. - - Double cannot be converted to a date. - Impossible de convertir une valeur double en date. - - - - Integer cannot be converted to a float. - Impossible de convertir une valeur integer en float. - - Alignment Picker Sélecteur d'alignement diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf index 1716476468..5dfc120191 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.it.xlf @@ -406,16 +406,6 @@ VT non gestito: {0}. - - Double cannot be converted to a date. - Impossibile convertire un tipo double in un tipo date. - - - - Integer cannot be converted to a float. - Impossibile convertire un numero intero in un valore float. - - Alignment Picker Selezione allineamento diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf index 421aa4ff8f..f733326c05 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ja.xlf @@ -406,16 +406,6 @@ ハンドルされていない VT: {0} - - Double cannot be converted to a date. - double を date に変換できません。 - - - - Integer cannot be converted to a float. - integer を float に変換できません。 - - Alignment Picker 配置ピッカー diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf index 5d3dc736ce..20d4603916 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ko.xlf @@ -406,16 +406,6 @@ 처리되지 않은 VT입니다. {0} - - Double cannot be converted to a date. - double 형식을 date 형식으로 변환할 수 없습니다. - - - - Integer cannot be converted to a float. - integer 형식을 float 형식으로 변환할 수 없습니다. - - Alignment Picker 맞춤 선택 diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf index ac1608ca58..7e6c90957f 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pl.xlf @@ -406,16 +406,6 @@ Nieobsługiwany VT: {0}. - - Double cannot be converted to a date. - Nie można przekonwertować typu double na typ date. - - - - Integer cannot be converted to a float. - Nie można przekonwertować typu integer na typ float. - - Alignment Picker Selektor wyrównania diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf index 655e0cda24..aa04e91e8d 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.pt-BR.xlf @@ -406,16 +406,6 @@ VT não tratado: {0}. - - Double cannot be converted to a date. - Não é possível converter duplo em data. - - - - Integer cannot be converted to a float. - Não é possível converter inteiro em flutuante. - - Alignment Picker Seletor de Alinhamento diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf index 2a1e47e3cb..c0c0b5b107 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.ru.xlf @@ -406,16 +406,6 @@ Необработанный VT: {0}. - - Double cannot be converted to a date. - Тип double нельзя привести к типу date. - - - - Integer cannot be converted to a float. - Тип integer нельзя привести к типу float. - - Alignment Picker Выбор выравнивания diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf index 3e257f5e0a..7b6a1ad098 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.tr.xlf @@ -406,16 +406,6 @@ İşlenmemiş VT: {0}. - - Double cannot be converted to a date. - Double tarihe dönüştürülemez. - - - - Integer cannot be converted to a float. - Tam sayı kayan noktalıya dönüştürülemez. - - Alignment Picker Hizalama Seçici diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf index cb761b2ccc..c3a188e0d2 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hans.xlf @@ -406,16 +406,6 @@ 未处理的 VT: {0}。 - - Double cannot be converted to a date. - 无法将 double 转换为 date。 - - - - Integer cannot be converted to a float. - 无法将 integer 转换为 float。 - - Alignment Picker 对齐方式选择器 diff --git a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf index 230df74512..14329a2561 100644 --- a/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Design/src/Resources/xlf/SR.zh-Hant.xlf @@ -406,16 +406,6 @@ 未處理的 VT: {0}。 - - Double cannot be converted to a date. - Double 無法轉換為 Date。 - - - - Integer cannot be converted to a float. - Integer 無法轉換為 Float。 - - Alignment Picker 對齊選擇器 diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Interop.HRESULT.cs b/src/System.Windows.Forms.Primitives/src/Interop/Interop.HRESULT.cs index cc4ba9f8e3..6304d0579b 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/Interop.HRESULT.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/Interop.HRESULT.cs @@ -18,6 +18,7 @@ internal static partial class Interop DISP_E_UNKNOWNNAME = unchecked((int)0x80020006), DISP_E_EXCEPTION = unchecked((int)0x80020009), DISP_E_UNKNOWNLCID = unchecked((int)0x8002000C), + DISP_E_DIVBYZERO = unchecked((int)0x80020012), TYPE_E_BADMODULEKIND = unchecked((int)0x800288BD), E_NOTIMPL = unchecked((int)0x80004001), E_NOINTERFACE = unchecked((int)0x80004002), diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Interop.Libraries.cs b/src/System.Windows.Forms.Primitives/src/Interop/Interop.Libraries.cs index 613380c753..150b5e5bec 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/Interop.Libraries.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/Interop.Libraries.cs @@ -17,6 +17,7 @@ internal static partial class Interop public const string Oleacc = "oleacc.dll"; public const string Oleaut32 = "oleaut32.dll"; public const string Powrprof = "Powrprof.dll"; + public const string Propsys = "Propsys.dll"; public const string RichEdit41 = "MsftEdit.DLL"; public const string SHCore = "SHCore.dll"; public const string Shell32 = "shell32.dll"; diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Kernel32/Interop.FILETIME.cs b/src/System.Windows.Forms.Primitives/src/Interop/Kernel32/Interop.FILETIME.cs new file mode 100644 index 0000000000..4ae3bb7761 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/Kernel32/Interop.FILETIME.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + public struct FILETIME + { + public FILETIME(DateTime date) + { + long ft = date.ToFileTime(); + dwLowDateTime = (uint)(ft & 0xFFFFFFFF); + dwHighDateTime = (uint)(ft >> 32); + } + + public uint dwLowDateTime; + public uint dwHighDateTime; + + public DateTime ToDateTime() + { + return DateTime.FromFileTime(((long)dwHighDateTime << 32) + dwLowDateTime); + } + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.PropVariantClear.cs b/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.PropVariantClear.cs new file mode 100644 index 0000000000..c42718172c --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.PropVariantClear.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static Interop.Oleaut32; + +internal partial class Interop +{ + internal static partial class Ole32 + { + [DllImport(Libraries.Ole32, ExactSpelling = true)] + public unsafe static extern HRESULT PropVariantClear(VARIANT* pvarg); + + public unsafe static HRESULT PropVariantClear(ref VARIANT varg) + { + fixed (VARIANT* pvarg = &varg) + { + return PropVariantClear(pvarg); + } + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.DECIMAL.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.DECIMAL.cs new file mode 100644 index 0000000000..40beacebd5 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.DECIMAL.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class Oleaut32 + { + public struct DECIMAL + { + public ushort wReserved; + public byte scale; + public byte sign; + public uint Hi32; + public uint Lo32; + public uint Mid32; + + public decimal ToDecimal() + { + return new decimal((int)Lo32, (int)Mid32, (int)Hi32, sign == 0x80, scale); + } + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.FADF.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.FADF.cs new file mode 100644 index 0000000000..1545b9a5e3 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.FADF.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + [Flags] + public enum FADF : ushort + { + AUTO = 0x0001, + STATIC = 0x0002, + EMBEDDED = 0x0004, + FIXEDSIZE = 0x0010, + RECORD = 0x0020, + HAVEIID = 0x0040, + HAVEVARTYPE = 0x0080, + BSTR = 0x0100, + UNKNOWN = 0x0200, + DISPATCH = 0x0400, + VARIANT = 0x0800 + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IPerPropertyBrowsing.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IPerPropertyBrowsing.cs index 514deb8cab..ae6efe1885 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IPerPropertyBrowsing.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IPerPropertyBrowsing.cs @@ -28,8 +28,8 @@ internal partial class Interop [PreserveSig] HRESULT GetPredefinedStrings( DispatchID dispID, - CA* pCaStringsOut, - CA* pCaCookiesOut); + Ole32.CA* pCaStringsOut, + Ole32.CA* pCaCookiesOut); [PreserveSig] HRESULT GetPredefinedValue( diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IRecordInfo.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IRecordInfo.cs new file mode 100644 index 0000000000..18793dab51 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IRecordInfo.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Oleaut32 + { + [ComImport] + [Guid("0000002F-0000-0000-C000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public unsafe interface IRecordInfo + { + [PreserveSig] + HRESULT RecordInit( + void* pvNew); + + [PreserveSig] + HRESULT RecordClear( + void* pvExisting); + + [PreserveSig] + HRESULT RecordCopy( + void* pvExisting, + void* pvNew); + + [PreserveSig] + HRESULT GetGuid( + Guid* pguid); + + [PreserveSig] + HRESULT GetName( + BSTR* pbstrName); + + [PreserveSig] + HRESULT GetSize( + uint* pcbSize); + + [PreserveSig] + HRESULT GetTypeInfo( + out ITypeInfo ppTypeInfo); + + [PreserveSig] + HRESULT GetField( + void* pvData, + [MarshalAs(UnmanagedType.LPWStr)] out string szFieldName, + VARIANT* pvarField); + + [PreserveSig] + HRESULT GetFieldNoCopy( + void* pvData, + [MarshalAs(UnmanagedType.LPWStr)] out string szFieldName, + VARIANT* pvarField, + void* ppvDataCArray); + + [PreserveSig] + HRESULT PutField( + Ole32.INVOKEKIND wFlags, + void* pvData, + [MarshalAs(UnmanagedType.LPWStr)] out string szFieldName, + VARIANT* pvarField); + + [PreserveSig] + HRESULT PutFieldNoCopy( + Ole32.INVOKEKIND wFlags, + void* pvData, + [MarshalAs(UnmanagedType.LPWStr)] out string szFieldName, + VARIANT* pvarField); + + [PreserveSig] + HRESULT GetFieldNames( + uint* pcNames, + BSTR* rgBstrNames); + + [PreserveSig] + BOOL IsMatchingType( + ref IRecordInfo pRecordInfo); + + [PreserveSig] + void* RecordCreate(); + + [PreserveSig] + HRESULT RecordCreateCopy( + void* pvSource, + void** ppvDest); + + [PreserveSig] + HRESULT RecordDestroy( + void* pvRecord); + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SAFEARRAY.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SAFEARRAY.cs new file mode 100644 index 0000000000..a48236974b --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SAFEARRAY.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using static Interop.Ole32; + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + public unsafe struct SAFEARRAY + { + public ushort cDims; + public FADF fFeatures; + public uint cbElements; + public uint cLocks; + public void* pvData; + public SAFEARRAYBOUND _rgsabound; + + public ReadOnlySpan rgsabound => TrailingArray.GetBuffer(ref _rgsabound, cDims); + + public VARENUM VarType + { + get + { + // Match CLR behaviour. + FADF hardwiredType = fFeatures & (FADF.BSTR | FADF.UNKNOWN | FADF.DISPATCH | FADF.VARIANT); + if (hardwiredType == FADF.BSTR && cbElements == sizeof(char*)) + { + return VARENUM.BSTR; + } + else if (hardwiredType == FADF.UNKNOWN && cbElements == sizeof(IntPtr)) + { + return VARENUM.UNKNOWN; + } + else if (hardwiredType == FADF.DISPATCH && cbElements == sizeof(IntPtr)) + { + return VARENUM.DISPATCH; + } + else if (hardwiredType == FADF.VARIANT && cbElements == sizeof(VARIANT)) + { + return VARENUM.VARIANT; + } + + // Call native API. + VARENUM vt = VARENUM.EMPTY; + fixed (SAFEARRAY* pThis = &this) + { + SafeArrayGetVartype(pThis, &vt); + return vt; + } + } + } + + public T GetValue(Span indices) + { + // SAFEARRAY is laid out in column-major order. + // See https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/array-manipulation-functions + int indicesIndex = 0; + int c1 = indices[indicesIndex++]; + uint dimensionSize = 1; + + ReadOnlySpan bounds = rgsabound; + int boundIndex = cDims - 1; + + uint cell = 0; + for (ushort dim = 1; dim < cDims; dim++) + { + dimensionSize *= bounds[boundIndex--].cElements; + + int diff = (indices[indicesIndex++] - bounds[boundIndex].lLbound); + cell += (uint)diff * dimensionSize; + } + + cell += (uint)(c1 - bounds[cDims - 1].lLbound); + + void* v = Unsafe.Add(pvData, (int)cell); + return Unsafe.AsRef(v); + } + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SAFEARRAYBOUND.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SAFEARRAYBOUND.cs new file mode 100644 index 0000000000..ee2e26fd20 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SAFEARRAYBOUND.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + public struct SAFEARRAYBOUND + { + public uint cElements; + public int lLbound; + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetElemsize.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetElemsize.cs new file mode 100644 index 0000000000..94137b95de --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetElemsize.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + public unsafe static extern uint SafeArrayGetElemsize(SAFEARRAY* psa); + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetRecordInfo.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetRecordInfo.cs new file mode 100644 index 0000000000..6a3b4cef58 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetRecordInfo.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Oleaut32 + { + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + public unsafe static extern HRESULT SafeArrayGetRecordInfo(SAFEARRAY* psa, out IRecordInfo pRecInfo); + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetVartype.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetVartype.cs new file mode 100644 index 0000000000..4c86455b19 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayGetVartype.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + public unsafe static extern HRESULT SafeArrayGetVartype(SAFEARRAY* psa, Ole32.VARENUM* pvt); + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VariantClear.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayLock.cs similarity index 70% rename from src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VariantClear.cs rename to src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayLock.cs index 43f8aa2199..5123ec48c2 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VariantClear.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayLock.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -10,6 +10,6 @@ internal partial class Interop internal static partial class Oleaut32 { [DllImport(Libraries.Oleaut32, ExactSpelling = true)] - public unsafe static extern HRESULT VariantClear(VARIANT* pvarg); + public unsafe static extern HRESULT SafeArrayLock(SAFEARRAY* psa); } } diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayUnlock.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayUnlock.cs new file mode 100644 index 0000000000..470e198fce --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.SafeArrayUnlock.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + public unsafe static extern HRESULT SafeArrayUnlock(SAFEARRAY* psa); + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT.cs index 14f748c1fe..b30e4e60d6 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows.Forms.Primitives.Resources; using static Interop.Ole32; @@ -22,10 +24,7 @@ internal static partial class Interop public short reserved1; public short reserved2; public short reserved3; - - public IntPtr data1; - - public IntPtr data2; + public VARIANTUnion data; public VARENUM Type => (vt & VARENUM.TYPEMASK); @@ -33,177 +32,960 @@ internal static partial class Interop public void Clear() { - fixed (VARIANT* pThis = &this) - { - VariantClear(pThis); - } - + PropVariantClear(ref this); vt = VARENUM.EMPTY; - data1 = IntPtr.Zero; - data2 = IntPtr.Zero; + data = new VARIANTUnion(); } public void Dispose() => Clear(); public object? ToObject() { - IntPtr val = data1; - long longVal; + fixed (VARIANT* thisVariant = &this) + { + void* data = &thisVariant->data; + if (Byref) + { + data = *((void**)data); + // CLR allows VT_EMPTY/NULL | VT_BYREF to have no data. + // In other cases, the variant is invalid. + if (data == null && !(Type == VARENUM.EMPTY || Type == VARENUM.NULL)) + { + throw new ArgumentException("Invalid Variant"); + } + } + else if (vt == VARENUM.DECIMAL) + { + data = thisVariant; + } - switch (Type) + // Note that the following check also covers VT_ILLEGAL. + if ((vt & ~(VARENUM.BYREF | VARENUM.ARRAY | VARENUM.VECTOR)) >= (VARENUM)0x80) + { + throw new InvalidOleVariantTypeException(); + } + + if ((vt & VARENUM.VECTOR) != 0) + { + return ToVector(thisVariant->data.ca, vt); + } + if ((vt & VARENUM.ARRAY) != 0) + { + return ToArray(*(SAFEARRAY**)data, vt); + } + + return ToObject(Type, Byref, data); + } + } + + private static object? ToObject(VARENUM type, bool byRef, void* data) + { + switch (type) { case VARENUM.EMPTY: + if (byRef) + { + // CLR returns VT_EMPTY | VT_BYREF data as nuint. + if (IntPtr.Size == 8) + { + return (ulong)data; + } + + return (uint)data; + } + return null; case VARENUM.NULL: return Convert.DBNull; - case VARENUM.I1: - if (Byref) - { - val = (IntPtr)Marshal.ReadByte(val); - } - return (sbyte)(0xFF & (sbyte)val); - + return *((sbyte*)data); case VARENUM.UI1: - if (Byref) - { - val = (IntPtr)Marshal.ReadByte(val); - } - - return (byte)(0xFF & (byte)val); - + return *((byte*)data); case VARENUM.I2: - if (Byref) - { - val = (IntPtr)Marshal.ReadInt16(val); - } - return (short)(0xFFFF & (short)val); - + return *((short*)data); case VARENUM.UI2: - if (Byref) - { - val = (IntPtr)Marshal.ReadInt16(val); - } - return (ushort)(0xFFFF & (ushort)val); - + return *((ushort*)data); case VARENUM.I4: case VARENUM.INT: - if (Byref) - { - val = (IntPtr)Marshal.ReadInt32(val); - } - return (int)val; - + case VARENUM.ERROR: + case VARENUM.HRESULT: + return *((int*)data); case VARENUM.UI4: case VARENUM.UINT: - if (Byref) - { - val = (IntPtr)Marshal.ReadInt32(val); - } - return (uint)val; - + return *((uint*)data); case VARENUM.I8: + return *((long*)data); case VARENUM.UI8: - if (Byref) - { - longVal = Marshal.ReadInt64(val); - } - else - { - longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); - } - - if (vt == VARENUM.I8) - { - return (long)longVal; - } - else - { - return (ulong)longVal; - } - } - - if (Byref) - { - val = GetRefInt(val); - } - - switch (Type) - { + return *((ulong*)data); case VARENUM.R4: + return *((float*)data); case VARENUM.R8: - - // can I use unsafe here? - throw new FormatException(SR.CannotConvertIntToFloat); - + return *((double*)data); case VARENUM.CY: - // internally currency is 8-byte int scaled by 10,000 - longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); - return new decimal(longVal); + long cyVal = *((long*)data); + return decimal.FromOACurrency(cyVal); case VARENUM.DATE: - throw new FormatException(SR.CannotConvertDoubleToDate); - + double date = *((double*)data); + return DateTime.FromOADate(date); case VARENUM.BSTR: case VARENUM.LPWSTR: - return Marshal.PtrToStringUni(val); - + return Marshal.PtrToStringUni(*(IntPtr*)data); case VARENUM.LPSTR: - return Marshal.PtrToStringAnsi(val); - + return Marshal.PtrToStringAnsi(*(IntPtr*)data); case VARENUM.DISPATCH: case VARENUM.UNKNOWN: + IntPtr pInterface = *(IntPtr*)data; + if (pInterface == IntPtr.Zero) { - return Marshal.GetObjectForIUnknown(val); + return null; } - case VARENUM.HRESULT: - return val; - + return Marshal.GetObjectForIUnknown(pInterface); case VARENUM.DECIMAL: - longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); - return new decimal(longVal); - + return ((DECIMAL*)data)->ToDecimal(); case VARENUM.BOOL: - return (val != IntPtr.Zero); - + return (*(VARIANT_BOOL*)data) != VARIANT_BOOL.FALSE; case VARENUM.VARIANT: - VARIANT* pVariant = (VARIANT*)val; + // We only support VT_VARIANT | VT_BYREF. + if (!byRef) + { + break; + } + + // BYREF VARIANTS are not allowed to be nested. + VARIANT* pVariant = (VARIANT*)data; + if (pVariant->Byref) + { + throw new InvalidOleVariantTypeException(); + } + return pVariant->ToObject(); - case VARENUM.CLSID: - Guid guid = Marshal.PtrToStructure(val); - return guid; + // We only support VT_CLSID. + // This is the type of InitPropVariantFromCLSID. + if (byRef) + { + break; + } + return **((Guid**)data); case VARENUM.FILETIME: - longVal = ((uint)data1 & 0xffffffff) | ((uint)data2 << 32); - return new DateTime(longVal); + // We only support VT_FILETIME. + // This is the type of InitPropVariantFromFILETIME. + if (byRef) + { + break; + } - case VARENUM.USERDEFINED: - throw new ArgumentException(string.Format(SR.COM2UnhandledVT, "USERDEFINED")); - - case VARENUM.ARRAY: + return (*(Kernel32.FILETIME*)data).ToDateTime(); case VARENUM.VOID: - case VARENUM.PTR: - case VARENUM.SAFEARRAY: - case VARENUM.CARRAY: - + return null; case VARENUM.RECORD: - case VARENUM.BLOB: - case VARENUM.STREAM: - case VARENUM.STORAGE: - case VARENUM.STREAMED_OBJECT: - case VARENUM.STORED_OBJECT: - case VARENUM.BLOB_OBJECT: - case VARENUM.CF: - case VARENUM.BSTR_BLOB: - case VARENUM.VECTOR: - case VARENUM.BYREF: + { + VARIANTRecord* record = (VARIANTRecord*)data; + if (record->pRecInfo == IntPtr.Zero) + { + throw new ArgumentException("Specified OLE variant is invalid."); + } + if (record->pvRecord == null) + { + return null; + } + + // TODO: cast IntPtr to IRecordInfo. Not that much of a concern + // as .NET Core doesn't support records anyway. + // Type recordType = GetRecordElementType(record->pvRecord); + throw new ArgumentException("Record marshalling doesn't actually work in .NET Core. Matching that behaviour."); + } + } + + throw new ArgumentException(string.Format(SR.COM2UnhandledVT, type)); + } + + private static Type GetRecordElementType(IRecordInfo record) + { + Guid guid; + HRESULT hr = record.GetGuid(&guid); + if (!hr.Succeeded()) + { + throw Marshal.GetExceptionForHR((int)hr)!; + } + + Type? t = System.Type.GetTypeFromCLSID(guid); + if (t == null || !t.IsValueType) + { + throw new ArgumentException("The specified record cannot be mapped to a managed value class."); + } + + return t; + } + + private static object? ToArray(SAFEARRAY* psa, VARENUM vt) + { + if (psa == null) + { + return null; + } + + VARENUM arrayType = vt & ~VARENUM.ARRAY; + Array array = CreateArrayFromSafeArray(psa, arrayType); + + HRESULT hr = SafeArrayLock(psa); + Debug.Assert(hr == HRESULT.S_OK); + + try + { + if (array.Rank == 1) + { + switch (arrayType) + { + case VARENUM.I1: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.UI1: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.I2: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.UI2: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.I4: + case VARENUM.INT: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.UI4: + case VARENUM.UINT: + case VARENUM.ERROR: // Not explicitly mentioned in the docs but trivial to implement. + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.I8: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.UI8: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.R4: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.R8: + new Span(psa->pvData, array.Length) + .CopyTo(GetSpan(array)); + break; + case VARENUM.BOOL: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + result[i] = data[i] != VARIANT_BOOL.FALSE; + } + break; + } + case VARENUM.DECIMAL: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + result[i] = data[i].ToDecimal(); + } + break; + } + case VARENUM.CY: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + result[i] = decimal.FromOACurrency(data[i]); + } + break; + } + case VARENUM.DATE: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + result[i] = DateTime.FromOADate(data[i]); + } + break; + } + case VARENUM.BSTR: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + result[i] = Marshal.PtrToStringUni(data[i]); + } + break; + } + case VARENUM.DISPATCH: + case VARENUM.UNKNOWN: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + if (data[i] == IntPtr.Zero) + { + result[i] = null; + } + else + { + result[i] = Marshal.GetObjectForIUnknown(data[i]); + } + } + break; + } + case VARENUM.VARIANT: + { + var data = new Span(psa->pvData, array.Length); + var result = GetSpan(array); + for (int i = 0; i < data.Length; i++) + { + result[i] = data[i].ToObject(); + } + break; + } + case VARENUM.RECORD: + throw new NotImplementedException(); + default: + throw new ArgumentException(string.Format(SR.COM2UnhandledVT, vt)); + } + } + else if (array.Length != 0) + { + // CLR arrays are laid out in row-major order. + // See CLI 8.9.1: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf + // However, SAFEARRAYs are laid out in column-major order. + // See https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/array-manipulation-functions + // Therefore, we need to transpose data. + TransposeArray(psa, array, arrayType); + } + } + finally + { + hr = SafeArrayUnlock(psa); + Debug.Assert(hr == HRESULT.S_OK); + } + + return array; + } + + private static void TransposeArray(SAFEARRAY* psa, Array array, VARENUM arrayType) + { + if (array.Rank <= 32) + { + StackTransposeArray(psa, array, arrayType); + } + else + { + Debug.Fail("The CLR should not support arrays with more than 32 dimensions."); + HeapTransposeArray(psa, array, arrayType); + } + + static void StackTransposeArray(SAFEARRAY* psa, Array array, VARENUM arrayType) + { + Span indices = stackalloc int[array.Rank]; + Span lower = stackalloc int[array.Rank]; + Span upper = stackalloc int[array.Rank]; + InternalTransposeArray(psa, array, arrayType, indices, lower, upper); + } + + static void HeapTransposeArray(SAFEARRAY* psa, Array array, VARENUM arrayType) + { + var indices = new int[array.Rank]; + var lower = new int[array.Rank]; + var upper = new int[array.Rank]; + InternalTransposeArray(psa, array, arrayType, indices, lower, upper); + } + + static void InternalTransposeArray(SAFEARRAY* psa, Array array, VARENUM arrayType, Span indices, Span lower, Span upper) + { + int lastIndex = array.Rank - 1; + int i; + for (i = 0; i < array.Rank; i++) + { + indices[i] = lower[i] = array.GetLowerBound(i); + upper[i] = array.GetUpperBound(i); + } + + // Loop through all the indices. + while (true) + { + BeginMainLoop: + + SetArrayValue(psa, array, indices, lower, arrayType); + + for (i = lastIndex; i > 0;) + { + if (++indices[i] <= upper[i]) + { + goto BeginMainLoop; + } + + indices[i] = lower[i]; + --i; + } + + // Special case for the first index, it must be enumerated only once + if (++indices[0] > upper[0]) + { + break; + } + } + } + } + + private static void SetArrayValue(SAFEARRAY* psa, Array array, Span indices, Span lowerBounds, VARENUM arrayType) + { + static void SetValue(Array array, T value, Span indices, Span lowerBounds) + { + // CLR arrays are laid out in row-major order. + // See CLI 8.9.1: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf + var span = GetSpan(array); + int offset = 0; + int multiplier = 1; + for (int i = array.Rank; i >= 1; i--) + { + int diff = indices[i - 1] - lowerBounds[i - 1]; + offset += diff * multiplier; + multiplier *= array.GetLength(i - 1); + } + + span[offset] = value; + } + + switch (arrayType) + { + case VARENUM.I1: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.UI1: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.I2: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.UI2: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.I4: + case VARENUM.INT: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.UI4: + case VARENUM.UINT: + case VARENUM.ERROR: // Not explicitly mentioned in the docs but trivial to implement. + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.I8: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.UI8: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.R4: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.R8: + SetValue(array, psa->GetValue(indices), indices, lowerBounds); + break; + case VARENUM.BOOL: + { + VARIANT_BOOL data = psa->GetValue(indices); + SetValue(array, data != VARIANT_BOOL.FALSE, indices, lowerBounds); + break; + } + case VARENUM.DECIMAL: + { + DECIMAL data = psa->GetValue(indices); + SetValue(array, data.ToDecimal(), indices, lowerBounds); + break; + } + case VARENUM.CY: + { + long data = psa->GetValue(indices); + SetValue(array, decimal.FromOACurrency(data), indices, lowerBounds); + break; + } + case VARENUM.DATE: + { + double data = psa->GetValue(indices); + SetValue(array, DateTime.FromOADate(data), indices, lowerBounds); + break; + } + case VARENUM.BSTR: + { + IntPtr data = psa->GetValue(indices); + SetValue(array, Marshal.PtrToStringUni(data), indices, lowerBounds); + break; + } + case VARENUM.DISPATCH: + case VARENUM.UNKNOWN: + { + IntPtr data = psa->GetValue(indices); + if (data == IntPtr.Zero) + { + SetValue(array, null, indices, lowerBounds); + } + else + { + SetValue(array, Marshal.GetObjectForIUnknown(data), indices, lowerBounds); + } + break; + } + case VARENUM.VARIANT: + { + VARIANT data = psa->GetValue(indices); + SetValue(array, data.ToObject(), indices, lowerBounds); + break; + } + case VARENUM.RECORD: + throw new NotImplementedException(); default: + throw new ArgumentException(string.Format(SR.COM2UnhandledVT, arrayType)); + } + } + + private static Array CreateArrayFromSafeArray(SAFEARRAY* psa, VARENUM vt) + { + Type elementType; + if (vt == VARENUM.EMPTY) + { + throw new InvalidOleVariantTypeException(); + } + if (vt == VARENUM.RECORD) + { + HRESULT hr = SafeArrayGetRecordInfo(psa, out IRecordInfo record); + if (!hr.Succeeded()) + { + throw Marshal.GetExceptionForHR((int)hr)!; + } + + elementType = GetRecordElementType(record); + } + + VARENUM arrayVarType = psa->VarType; + if (arrayVarType == VARENUM.EMPTY) + { + if (psa->cbElements != GetElementSizeForVarType(vt)) + { + throw new SafeArrayTypeMismatchException(); + } + } + // Allow limited conversion between arrays of different but related types. + else if (arrayVarType != vt + && !(vt == VARENUM.INT && arrayVarType == VARENUM.I4) + && !(vt == VARENUM.UINT && arrayVarType == VARENUM.UI4) + && !(vt == VARENUM.I4 && arrayVarType == VARENUM.INT) + && !(vt == VARENUM.UI4 && arrayVarType == VARENUM.UINT) + && !(vt == VARENUM.UNKNOWN && arrayVarType == VARENUM.DISPATCH) + && !(arrayVarType == VARENUM.RECORD)) + { + // To match CLR behaviour. + throw new SafeArrayTypeMismatchException(); + } + + switch (vt) + { + case VARENUM.I1: + elementType = typeof(sbyte); + break; + case VARENUM.UI1: + elementType = typeof(byte); + break; + case VARENUM.I2: + elementType = typeof(short); + break; + case VARENUM.UI2: + elementType = typeof(ushort); + break; + case VARENUM.I4: + case VARENUM.INT: + elementType = typeof(int); + break; + case VARENUM.I8: + elementType = typeof(long); + break; + case VARENUM.UI8: + elementType = typeof(ulong); + break; + case VARENUM.UI4: + case VARENUM.UINT: + case VARENUM.ERROR: + elementType = typeof(uint); + break; + case VARENUM.R4: + elementType = typeof(float); + break; + case VARENUM.R8: + elementType = typeof(double); + break; + case VARENUM.BOOL: + elementType = typeof(bool); + break; + case VARENUM.DECIMAL: + case VARENUM.CY: + elementType = typeof(decimal); + break; + case VARENUM.DATE: + elementType = typeof(DateTime); + break; + case VARENUM.BSTR: + elementType = typeof(string); + break; + case VARENUM.DISPATCH: + case VARENUM.UNKNOWN: + case VARENUM.VARIANT: + elementType = typeof(object); + break; + default: + throw new ArgumentException(string.Format(SR.COM2UnhandledVT, vt)); + } + + if (psa->cDims == 1 && psa->rgsabound[0].lLbound == 0) + { + // SZArray. + return Array.CreateInstance(elementType, (int)psa->rgsabound[0].cElements); + } + + var lengths = new int[psa->cDims]; + var bounds = new int[psa->cDims]; + int counter = 0; + // Copy the lower bounds and count of elements for the dimensions. These + // need to copied in reverse order. + for (int i = psa->cDims - 1; i >= 0; i--) + { + lengths[counter] = (int)psa->rgsabound[i].cElements; + bounds[counter] = (int)psa->rgsabound[i].lLbound; + counter++; + } + + return Array.CreateInstance(elementType, lengths, bounds); + } + + private static uint GetElementSizeForVarType(VARENUM vt) + { + switch (vt) + { + case VARENUM.EMPTY: + case VARENUM.NULL: + case VARENUM.VOID: + return 0; + case VARENUM.I1: + case VARENUM.UI1: + return 1; + case VARENUM.I2: + case VARENUM.UI2: + case VARENUM.BOOL: + return 2; + case VARENUM.I4: + case VARENUM.UI4: + case VARENUM.INT: + case VARENUM.UINT: + case VARENUM.R4: + case VARENUM.HRESULT: + case VARENUM.ERROR: + return 4; + case VARENUM.I8: + case VARENUM.UI8: + case VARENUM.CY: + case VARENUM.R8: + case VARENUM.DATE: + return 8; + case VARENUM.DECIMAL: + return (uint)sizeof(DECIMAL); + case VARENUM.VARIANT: + return (uint)sizeof(VARIANT); + case VARENUM.BSTR: + case VARENUM.LPSTR: + case VARENUM.LPWSTR: + case VARENUM.UNKNOWN: + case VARENUM.DISPATCH: + case VARENUM.USERDEFINED: + case VARENUM.CARRAY: + case VARENUM.SAFEARRAY: + case VARENUM.PTR: + return (uint)IntPtr.Size; + default: + if ((vt & VARENUM.ARRAY) != 0) + { + return (uint)sizeof(SAFEARRAY*); + } + + return 0; + } + } + + private static object ToVector(in CA ca, VARENUM vectorType) + { + VARENUM vt = vectorType & ~VARENUM.VECTOR; + switch (vt) + { + case VARENUM.I1: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.UI1: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.I2: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.UI2: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.BOOL: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new bool[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = data[i] != VARIANT_BOOL.FALSE; + } + return result; + } + case VARENUM.I4: + case VARENUM.INT: // Not explicitly mentioned in the docs but trivial to implement. + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.UI4: + case VARENUM.ERROR: + case VARENUM.UINT: // Not explicitly mentioned in the docs but trivial to implement. + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.I8: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.UI8: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.R4: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.R8: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.CY: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new decimal[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = decimal.FromOACurrency(data[i]); + } + return result; + } + case VARENUM.DATE: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new DateTime[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = DateTime.FromOADate(data[i]); + } + return result; + } + case VARENUM.FILETIME: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new DateTime[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = data[i].ToDateTime(); + } + return result; + } + case VARENUM.CLSID: + return new Span(ca.pElems, (int)ca.cElems).ToArray(); + case VARENUM.BSTR: + case VARENUM.LPWSTR: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new string?[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = Marshal.PtrToStringUni(data[i]); + } + return result; + } + case VARENUM.LPSTR: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new string?[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = Marshal.PtrToStringAnsi(data[i]); + } + return result; + } + case VARENUM.VARIANT: + { + var data = new Span(ca.pElems, (int)ca.cElems); + var result = new object?[data.Length]; + for (int i = 0; i < data.Length; i++) + { + result[i] = data[i].ToObject(); + } + return result; + } + case VARENUM.CF: // Not implemented. + case VARENUM.BSTR_BLOB: // System use only. + default: // Documentation does not specify any other types that are supported. throw new ArgumentException(string.Format(SR.COM2UnhandledVT, vt)); } } - private static IntPtr GetRefInt(IntPtr value) => Marshal.ReadIntPtr(value); + private static Span GetSpan(Array arr) + => MemoryMarshal.CreateSpan(ref Unsafe.AsRef(Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0).ToPointer()), arr.Length); + + [StructLayout(LayoutKind.Explicit)] + public struct VARIANTUnion + { + [FieldOffset(0)] + public long llVal; + + [FieldOffset(0)] + public int lVal; + + [FieldOffset(0)] + public byte bVal; + + [FieldOffset(0)] + public short iVal; + + [FieldOffset(0)] + public float fltVal; + + [FieldOffset(0)] + public double dblVal; + + [FieldOffset(0)] + public short boolVal; + + [FieldOffset(0)] + public int scode; + + [FieldOffset(0)] + public long cyVal; + + [FieldOffset(0)] + public double date; + + [FieldOffset(0)] + public IntPtr bstrVal; + + [FieldOffset(0)] + public IntPtr punkVal; + + [FieldOffset(0)] + public IntPtr pdispVal; + + [FieldOffset(0)] + public SAFEARRAY* parray; + + [FieldOffset(0)] + public byte* pbVal; + + [FieldOffset(0)] + public short* piVal; + + [FieldOffset(0)] + public int* plVal; + + [FieldOffset(0)] + public long* pllVal; + + [FieldOffset(0)] + public float* pfltVal; + + [FieldOffset(0)] + public double* pdblVal; + + [FieldOffset(0)] + public short* pboolVal; + + [FieldOffset(0)] + public int* pscode; + + [FieldOffset(0)] + public long* pcyVal; + + [FieldOffset(0)] + public double* pdate; + + [FieldOffset(0)] + public IntPtr* pbstrVal; + + [FieldOffset(0)] + public IntPtr* ppunkVal; + + [FieldOffset(0)] + public IntPtr* ppdispVal; + + [FieldOffset(0)] + public SAFEARRAY** pparray; + + [FieldOffset(0)] + public VARIANT* pvarVal; + + [FieldOffset(0)] + public void* Byref; + + [FieldOffset(0)] + public sbyte cVal; + + [FieldOffset(0)] + public ushort uiVal; + + [FieldOffset(0)] + public uint ulVal; + + [FieldOffset(0)] + public ulong ullVal; + + [FieldOffset(0)] + public DECIMAL* pdecVal; + + [FieldOffset(0)] + public sbyte* pcVal; + + [FieldOffset(0)] + public ushort* puiVal; + + [FieldOffset(0)] + public uint* pulVal; + + [FieldOffset(0)] + public ulong* pullVal; + + [FieldOffset(0)] + public int* pintVal; + + [FieldOffset(0)] + public uint* puintVal; + + [FieldOffset(0)] + public VARIANTRecord recordVal; + + [FieldOffset(0)] + public Guid* puuid; + + [FieldOffset(0)] + public Kernel32.FILETIME filetime; + + [FieldOffset(0)] + public CA ca; + } + + public struct VARIANTRecord + { + public void* pvRecord; + public IntPtr pRecInfo; + } } } } diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT_BOOL.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT_BOOL.cs new file mode 100644 index 0000000000..eb3a2fe8d0 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.VARIANT_BOOL.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +internal partial class Interop +{ + internal static partial class Oleaut32 + { + internal enum VARIANT_BOOL : short + { + FALSE = 0, + TRUE = -1, + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/TrailingArray.cs b/src/System.Windows.Forms.Primitives/src/Interop/TrailingArray.cs new file mode 100644 index 0000000000..56bb3bb273 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/TrailingArray.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +/// +/// Used for trailing native unsized (ANYSIZE) arrays of . Native example: +/// UCHAR UniqueId[1]; +/// +/// +/// Accessing the values is only safe when you have a pointer to the containing struct in +/// a buffer. If you have an actual struct (Foo, not Foo*), the trailing array will have been +/// truncated as the values aren't actually part of the struct. +/// +internal readonly struct TrailingArray where T : unmanaged +{ + private readonly T _firstItem; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static ReadOnlySpan GetBuffer(ref T first, uint count, uint offset = 0) + => Unsafe.As>(ref first).GetBuffer(count, offset); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static ReadOnlySpan GetBufferInBytes(ref T first, uint countInBytes, uint offsetInBytes = 0) + => Unsafe.As>(ref first).GetBuffer(countInBytes / (uint)sizeof(T), offsetInBytes / (uint)sizeof(T)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe ReadOnlySpan GetBuffer(uint count, uint offset = 0) + { + if (count == 0) + { + return new ReadOnlySpan(); + } + + fixed (T* t = &_firstItem) + { + return new ReadOnlySpan(t + offset, (int)(count)); + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Resources/SR.resx b/src/System.Windows.Forms.Primitives/src/Resources/SR.resx index 8da2d9d8e1..ee84d5c2a6 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/SR.resx +++ b/src/System.Windows.Forms.Primitives/src/Resources/SR.resx @@ -117,12 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Integer cannot be converted to a float. - - - Double cannot be converted to a date. - Unhandled VT: {0}. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.cs.xlf index 57b14e237c..e73ff09d33 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.cs.xlf @@ -7,16 +7,6 @@ Nezpracovaná hodnota VT: {0}. - - Double cannot be converted to a date. - Nelze převést typ Double na typ Date. - - - - Integer cannot be converted to a float. - Nelze převést typ Integer na typ Float. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Nejméně jedna položka není platná v parametru IDictionary. Ověřte, zda všechny hodnoty odpovídají vlastnostem objektu. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.de.xlf index a35ff04ee2..efb807cdad 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.de.xlf @@ -7,16 +7,6 @@ Unbehandelter VT: {0}. - - Double cannot be converted to a date. - "Double" kann nicht in "Date" konvertiert werden. - - - - Integer cannot be converted to a float. - "Integer" kann nicht in "Float" konvertiert werden. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Mindestens ein Eintrag im IDictionary-Parameter ist ungültig. Stellen Sie sicher, dass alle Werte den Eigenschaften des Objekts entsprechen. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.es.xlf index 320092e759..cfabfcbdf5 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.es.xlf @@ -7,16 +7,6 @@ VT no controlado: {0}. - - Double cannot be converted to a date. - No se puede convertir double en date. - - - - Integer cannot be converted to a float. - No se puede convertir integer en float. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Una o más entradas no son válidas en el parámetro IDictionary. Compruebe que todos los valores coinciden con las propiedades del objeto. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.fr.xlf index ce45ccc99b..f63e6f37f1 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.fr.xlf @@ -7,16 +7,6 @@ VT non géré : {0}. - - Double cannot be converted to a date. - Impossible de convertir une valeur double en date. - - - - Integer cannot be converted to a float. - Impossible de convertir une valeur integer en float. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Le paramètre IDictionary contient au moins une entrée non valide. Vérifiez que toutes les valeurs correspondent aux propriétés de l'objet. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.it.xlf index 490cacf84f..bf41e36ee8 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.it.xlf @@ -7,16 +7,6 @@ VT non gestito: {0}. - - Double cannot be converted to a date. - Impossibile convertire un tipo double in un tipo date. - - - - Integer cannot be converted to a float. - Impossibile convertire un numero intero in un valore float. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Una o più voci non valide nel parametro IDictionary. Verificare che tutti i valori corrispondano alle proprietà dell'oggetto. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ja.xlf index 0850740290..fa20df62c0 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ja.xlf @@ -7,16 +7,6 @@ ハンドルされていない VT: {0} - - Double cannot be converted to a date. - double を date に変換できません。 - - - - Integer cannot be converted to a float. - integer を float に変換できません。 - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. 1 つ以上の有効ではないエントリが IDictionary パラメーターにあります。すべての値がオブジェクトのプロパティに一致していることを確認してください。 diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ko.xlf index 9efbafb79a..b97b2c9653 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ko.xlf @@ -7,16 +7,6 @@ 처리되지 않은 VT입니다. {0} - - Double cannot be converted to a date. - double 형식을 date 형식으로 변환할 수 없습니다. - - - - Integer cannot be converted to a float. - integer 형식을 float 형식으로 변환할 수 없습니다. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. IDictionary 매개 변수에 잘못된 항목이 하나 이상 있습니다. 모든 값이 개체의 속성과 일치하는지 확인하십시오. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pl.xlf index 29fc0e457b..59d141aef1 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pl.xlf @@ -7,16 +7,6 @@ Nieobsługiwany VT: {0}. - - Double cannot be converted to a date. - Nie można przekonwertować typu double na typ date. - - - - Integer cannot be converted to a float. - Nie można przekonwertować typu integer na typ float. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Jeden lub kilka wpisów jest nieprawidłowych w parametrze IDictionary. Sprawdź, czy wszystkie wartości pasują do właściwości obiektu. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pt-BR.xlf index 24ba52ecd9..4d9c16d9fe 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.pt-BR.xlf @@ -7,16 +7,6 @@ VT não tratado: {0}. - - Double cannot be converted to a date. - Não é possível converter duplo em data. - - - - Integer cannot be converted to a float. - Não é possível converter inteiro em flutuante. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Uma ou mais entradas não são válidas no parâmetro IDictionary. Verifique se todos os valores correspondem às propriedades do objeto. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ru.xlf index 4c27386b68..ff6937a2ec 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.ru.xlf @@ -7,16 +7,6 @@ Необработанный VT: {0}. - - Double cannot be converted to a date. - Тип double нельзя привести к типу date. - - - - Integer cannot be converted to a float. - Тип integer нельзя привести к типу float. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. Недействительны одна или больше записей в параметре IDictionary. Проверьте, все ли значения соответствуют свойствам этого объекта. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.tr.xlf index 8a8e90b2f8..81bcf3c1f0 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.tr.xlf @@ -7,16 +7,6 @@ İşlenmemiş VT: {0}. - - Double cannot be converted to a date. - Double tarihe dönüştürülemez. - - - - Integer cannot be converted to a float. - Tam sayı kayan noktalıya dönüştürülemez. - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. IDictionary parametresindeki bir veya daha fazla girdi geçerli değil. Tüm değerlerin nesnenin özellikleriyle eşleştiğini doğrulayın. diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hans.xlf index 780c4c0ff9..81f74cff1c 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hans.xlf @@ -7,16 +7,6 @@ 未处理的 VT: {0}。 - - Double cannot be converted to a date. - 无法将 double 转换为 date。 - - - - Integer cannot be converted to a float. - 无法将 integer 转换为 float。 - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. 在 IDictionary 参数中有一个或多个无效项。请验证所有值均与对象的属性匹配。 diff --git a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hant.xlf index 686780ca8e..50bf38889c 100644 --- a/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms.Primitives/src/Resources/xlf/SR.zh-Hant.xlf @@ -7,16 +7,6 @@ 未處理的 VT: {0}。 - - Double cannot be converted to a date. - Double 無法轉換為 Date。 - - - - Integer cannot be converted to a float. - Integer 無法轉換為 Float。 - - One or more entries are not valid in the IDictionary parameter. Verify that all values match up to the object's properties. IDictionary 參數中有一或多個無效的項目。請確認所有值都符合物件的屬性。 diff --git a/src/System.Windows.Forms.Primitives/tests/Interop/Oleaut32/VARIANTTests.cs b/src/System.Windows.Forms.Primitives/tests/Interop/Oleaut32/VARIANTTests.cs deleted file mode 100644 index abf73f645c..0000000000 --- a/src/System.Windows.Forms.Primitives/tests/Interop/Oleaut32/VARIANTTests.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using Xunit; -using static Interop; -using static Interop.Kernel32; -using static Interop.Ole32; -using static Interop.Oleaut32; - -namespace System.Windows.Forms.Tests.Interop.Oleaut32 -{ - public unsafe class VARIANTTests : IClassFixture - { - [ConditionalFact(typeof(ArchitectureDetection), nameof(ArchitectureDetection.Is32bit))] - public void VARIANT_Sizeof_InvokeX86_ReturnsExpected() - { - Assert.Equal(16, Marshal.SizeOf()); - Assert.Equal(16, sizeof(VARIANT)); - } - - [ConditionalFact(typeof(ArchitectureDetection), nameof(ArchitectureDetection.Is64bit))] - public void VARIANT_Sizeof_InvokeX64_ReturnsExpected() - { - Assert.Equal(24, Marshal.SizeOf()); - Assert.Equal(24, sizeof(VARIANT)); - } - - [WinFormsTheory] - [InlineData((ushort)VARENUM.EMPTY, false)] - [InlineData((ushort)VARENUM.BOOL, false)] - [InlineData((ushort)(VARENUM.BYREF), true)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF), true)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF | VARENUM.ARRAY), true)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF | VARENUM.VECTOR), true)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.ARRAY), false)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.VECTOR), false)] - public void VARIANT_Byref_Get_ReturnsExpected(ushort vt, bool expected) - { - using var variant = new VARIANT - { - vt = (VARENUM)vt - }; - Assert.Equal(expected, variant.Byref); - } - - [WinFormsTheory] - [InlineData((ushort)VARENUM.EMPTY, (ushort)VARENUM.EMPTY)] - [InlineData((ushort)VARENUM.BOOL, (ushort)VARENUM.BOOL)] - [InlineData((ushort)(VARENUM.BYREF), (ushort)VARENUM.EMPTY)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF), (ushort)VARENUM.BOOL)] - public void VARIANT_Type_Get_ReturnsExpected(ushort vt, ushort expected) - { - using var variant = new VARIANT - { - vt = (VARENUM)vt - }; - Assert.Equal((VARENUM)expected, variant.Type); - } - - [WinFormsTheory] - [InlineData((ushort)VARENUM.EMPTY)] - [InlineData((ushort)(VARENUM.EMPTY | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.UNKNOWN)] - [InlineData((ushort)(VARENUM.UNKNOWN | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.DISPATCH)] - [InlineData((ushort)(VARENUM.DISPATCH | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.BSTR)] - [InlineData((ushort)(VARENUM.BSTR | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.BOOL)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF))] - public void VARIANT_Clear_InvokeDefault_Success(ushort vt) - { - using var variant = new VARIANT - { - vt = (VARENUM)vt - }; - variant.Clear(); - Assert.Equal(VARENUM.EMPTY, variant.vt); - Assert.Equal(IntPtr.Zero, variant.data1); - Assert.Equal(IntPtr.Zero, variant.data2); - } - - [WinFormsFact] - public void VARIANT_Clear_InvokeCustom_Success() - { - using var variant = new VARIANT - { - vt = VARENUM.BOOL, - data1 = (IntPtr)1 - }; - variant.Clear(); - Assert.Equal(VARENUM.EMPTY, variant.vt); - Assert.Equal(IntPtr.Zero, variant.data1); - Assert.Equal(IntPtr.Zero, variant.data2); - } - - [WinFormsFact] - public void VARIANT_Clear_InvokeBSTR_Success() - { - IntPtr data = Marshal.StringToBSTR("abc"); - using var variant = new VARIANT - { - vt = VARENUM.BSTR, - data1 = data - }; - variant.Clear(); - Assert.Equal(VARENUM.EMPTY, variant.vt); - Assert.Equal(IntPtr.Zero, variant.data1); - Assert.Equal(IntPtr.Zero, variant.data2); - } - - [WinFormsTheory] - [InlineData((ushort)VARENUM.EMPTY)] - [InlineData((ushort)(VARENUM.EMPTY | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.UNKNOWN)] - [InlineData((ushort)(VARENUM.UNKNOWN | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.DISPATCH)] - [InlineData((ushort)(VARENUM.DISPATCH | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.BSTR)] - [InlineData((ushort)(VARENUM.BSTR | VARENUM.BYREF))] - [InlineData((ushort)VARENUM.BOOL)] - [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF))] - public void VARIANT_Dispose_InvokeDefault_Success(ushort vt) - { - using var variant = new VARIANT - { - vt = (VARENUM)vt - }; - variant.Dispose(); - Assert.Equal(VARENUM.EMPTY, variant.vt); - Assert.Equal(IntPtr.Zero, variant.data1); - Assert.Equal(IntPtr.Zero, variant.data2); - } - - [WinFormsFact] - public void VARIANT_Dispose_InvokeCustom_Success() - { - using var variant = new VARIANT - { - vt = VARENUM.BOOL, - data1 = (IntPtr)1 - }; - variant.Dispose(); - Assert.Equal(VARENUM.EMPTY, variant.vt); - Assert.Equal(IntPtr.Zero, variant.data1); - Assert.Equal(IntPtr.Zero, variant.data2); - } - - [WinFormsFact] - public void VARIANT_Dispose_InvokeBSTR_Success() - { - IntPtr data = Marshal.StringToBSTR("abc"); - using var variant = new VARIANT - { - vt = VARENUM.BSTR, - data1 = data - }; - variant.Dispose(); - Assert.Equal(VARENUM.EMPTY, variant.vt); - Assert.Equal(IntPtr.Zero, variant.data1); - Assert.Equal(IntPtr.Zero, variant.data2); - } - } -} diff --git a/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Kernel32/FILETIMETests.cs b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Kernel32/FILETIMETests.cs new file mode 100644 index 0000000000..20fe747680 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Kernel32/FILETIMETests.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using Xunit; +using static Interop.Kernel32; + +namespace System.Windows.Forms.Tests.Interop.Kernel32 +{ + // NB: doesn't require thread affinity + public class FILETIMETests + { + [Fact] + public unsafe void FILETIME_Sizeof_Invoke_ReturnsExpected() + { + Assert.Equal(8, Marshal.SizeOf()); + Assert.Equal(8, sizeof(FILETIME)); + } + + [Fact] + public void FILETIME_Ctor_Default() + { + var ft = new FILETIME(); + Assert.Equal(0u, ft.dwLowDateTime); + Assert.Equal(0u, ft.dwHighDateTime); + } + + [Fact] + public void FILETIME_Ctor_DateTime() + { + var dt = new DateTime(2020, 05, 13, 13, 3, 12); + var ft = new FILETIME(dt); + Assert.Equal(3680495616u, ft.dwLowDateTime); + Assert.Equal(30812454u, ft.dwHighDateTime); + } + + [Fact] + public void FILETIME_ToDateTime_Invoke_ReturnsExpected() + { + var ft = new FILETIME() + { + dwLowDateTime = 3680495616u, + dwHighDateTime = 30812454u + }; + Assert.Equal(new DateTime(2020, 05, 13, 13, 3, 12), ft.ToDateTime()); + } + } +} diff --git a/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/DECIMALTests.cs b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/DECIMALTests.cs new file mode 100644 index 0000000000..1357d9d8ea --- /dev/null +++ b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/DECIMALTests.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using Xunit; +using static Interop; +using static Interop.Oleaut32; + +namespace System.Windows.Forms.Tests.Interop.Oleaut32 +{ + // NB: doesn't require thread affinity + public class DECIMALTests + { + [Fact] + public unsafe void DECIMAL_Sizeof_Invoke_ReturnsExpected() + { + Assert.Equal(16, Marshal.SizeOf()); + Assert.Equal(16, sizeof(DECIMAL)); + } + + [Fact] + public void DECIMAL_ToDecimal_InvokeEmpty_ReturnsExpected() + { + var dec = new DECIMAL(); + Assert.Equal(0m, dec.ToDecimal()); + } + + [Theory] + [InlineData((double)int.MinValue)] + [InlineData(-1.2)] + [InlineData(0)] + [InlineData(1.2)] + [InlineData((double)int.MaxValue)] + public void DECIMAL_ToDecimal_InvokeCustom_ReturnsExpected(double value) + { + HRESULT hr = VarDecFromR8(value, out DECIMAL dec); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal((decimal)value, dec.ToDecimal()); + } + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static extern HRESULT VarDecFromR8(double dblIn, out DECIMAL pdecOut); + } +} diff --git a/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/SAFEARRAYTests.cs b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/SAFEARRAYTests.cs new file mode 100644 index 0000000000..4ab7f9203d --- /dev/null +++ b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/SAFEARRAYTests.cs @@ -0,0 +1,416 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Xunit; +using static Interop; +using static Interop.Ole32; +using static Interop.Oleaut32; + +namespace System.Windows.Forms.Tests.Interop.Oleaut32 +{ + public unsafe class SAFEARRAYTests + { + [ConditionalFact(typeof(ArchitectureDetection), nameof(ArchitectureDetection.Is32bit))] + public void SAFEARRAY_Sizeof_InvokeX86_ReturnsExpected() + { + Assert.Equal(24, Marshal.SizeOf()); + Assert.Equal(24, sizeof(SAFEARRAY)); + } + + [ConditionalFact(typeof(ArchitectureDetection), nameof(ArchitectureDetection.Is64bit))] + public void SAFEARRAY_Sizeof_InvokeX64_ReturnsExpected() + { + Assert.Equal(32, Marshal.SizeOf()); + Assert.Equal(32, sizeof(SAFEARRAY)); + } + + public static IEnumerable Create_TestData() + { + yield return new object[] { VARENUM.I4, FADF.HAVEVARTYPE, 4 }; + yield return new object[] { VARENUM.I8, FADF.HAVEVARTYPE, 8 }; + yield return new object[] { VARENUM.BSTR, FADF.HAVEVARTYPE | FADF.BSTR, IntPtr.Size }; + yield return new object[] { VARENUM.UNKNOWN, FADF.HAVEIID | FADF.UNKNOWN, IntPtr.Size }; + yield return new object[] { VARENUM.DISPATCH, FADF.HAVEIID | FADF.DISPATCH, IntPtr.Size }; + } + + [StaTheory] + [MemberData(nameof(Create_TestData))] + public void SAFEARRAY_CreateSingleDimension_GetProperties_Success(ushort vt, ushort expectedFeatures, uint expectedCbElements) + { + var saBound = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = 1 + }; + SAFEARRAY *psa = SafeArrayCreate((VARENUM)vt, 1, &saBound); + Assert.True(psa != null); + + try + { + Assert.Equal(1u, psa->cDims); + Assert.Equal((FADF)expectedFeatures, psa->fFeatures); + Assert.Equal((uint)expectedCbElements, psa->cbElements); + Assert.Equal(0u, psa->cLocks); + Assert.True(psa->pvData != null); + Assert.Equal(10u, psa->rgsabound[0].cElements); + Assert.Equal(1, psa->rgsabound[0].lLbound); + + VARENUM arrayVt = VARENUM.EMPTY; + HRESULT hr = SafeArrayGetVartype(psa, &arrayVt); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal((VARENUM)vt, arrayVt); + } + finally + { + HRESULT hr = SafeArrayDestroy(psa); + Assert.Equal(HRESULT.S_OK, hr); + } + } + + [StaFact] + public void SAFEARRAY_CreateSingleDimensionRECORD_GetProperties_Success() + { + var saBound = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = 1 + }; + var record = new CustomRecordInfo(); + IntPtr pRecord = Marshal.GetComInterfaceForObject(record); + try + { + SAFEARRAY *psa = SafeArrayCreateEx(VARENUM.RECORD, 1, &saBound, pRecord); + Assert.True(psa != null); + + try + { + Assert.Equal(1u, psa->cDims); + Assert.Equal(FADF.RECORD, psa->fFeatures); + Assert.Equal((uint)sizeof(int), psa->cbElements); + Assert.Equal(0u, psa->cLocks); + Assert.True(psa->pvData != null); + Assert.Equal(10u, psa->rgsabound[0].cElements); + Assert.Equal(1, psa->rgsabound[0].lLbound); + + VARENUM arrayVt = VARENUM.EMPTY; + HRESULT hr = SafeArrayGetVartype(psa, &arrayVt); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.RECORD, arrayVt); + } + finally + { + HRESULT hr = SafeArrayDestroy(psa); + Assert.Equal(HRESULT.S_OK, hr); + } + } + finally + { + Marshal.Release(pRecord); + } + } + + private class CustomRecordInfo : IRecordInfo + { + HRESULT IRecordInfo.RecordInit(void* pvNew) => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordClear(void* pvExisting) => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordCopy(void* pvExisting, void* pvNew) => throw new NotImplementedException(); + + public Func<(Guid, HRESULT)> GetGuidAction { get; set; } + + HRESULT IRecordInfo.GetGuid(Guid* pguid) + { + (Guid guid, HRESULT hr) = GetGuidAction(); + *pguid = guid; + return hr; + } + + HRESULT IRecordInfo.GetName(BSTR* pbstrName) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetSize(uint* pcbSize) + { + *pcbSize = (uint)sizeof(int); + return HRESULT.S_OK; + } + + HRESULT IRecordInfo.GetTypeInfo(out ITypeInfo ppTypeInfo) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetField(void* pvData, out string szFieldName, VARIANT* pvarField) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetFieldNoCopy(void* pvData, out string szFieldName, VARIANT* pvarField, void* ppvDataCArray) => throw new NotImplementedException(); + + HRESULT IRecordInfo.PutField(INVOKEKIND wFlags, void* pvData, out string szFieldName, VARIANT* pvarField) => throw new NotImplementedException(); + + HRESULT IRecordInfo.PutFieldNoCopy(INVOKEKIND wFlags, void* pvData, out string szFieldName, VARIANT* pvarField) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetFieldNames(uint* pcNames, BSTR* rgBstrNames) => throw new NotImplementedException(); + + BOOL IRecordInfo.IsMatchingType(ref IRecordInfo pRecordInfo) => throw new NotImplementedException(); + + void* IRecordInfo.RecordCreate() => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordCreateCopy(void* pvSource, void** ppvDest) => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordDestroy(void* pvRecord) => throw new NotImplementedException(); + } + + [StaTheory] + [MemberData(nameof(Create_TestData))] + public void SAFEARRAY_CreateMultipleDimensions_GetProperties_Success(ushort vt, ushort expectedFeatures, uint expectedCbElements) + { + SAFEARRAYBOUND* saBounds = stackalloc SAFEARRAYBOUND[2]; + saBounds[0] = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = 1 + }; + saBounds[1] = new SAFEARRAYBOUND + { + cElements = 20, + lLbound = 0 + }; + SAFEARRAY *psa = SafeArrayCreate((VARENUM)vt, 2, saBounds); + Assert.True(psa != null); + + try + { + Assert.Equal(2u, psa->cDims); + Assert.Equal((FADF)expectedFeatures, psa->fFeatures); + Assert.Equal(expectedCbElements, psa->cbElements); + Assert.Equal(0u, psa->cLocks); + Assert.True(psa->pvData != null); + Assert.Equal(20u, psa->rgsabound[0].cElements); + Assert.Equal(0, psa->rgsabound[0].lLbound); + Assert.Equal(10u, psa->rgsabound[1].cElements); + Assert.Equal(1, psa->rgsabound[1].lLbound); + + VARENUM arrayVt = VARENUM.EMPTY; + HRESULT hr = SafeArrayGetVartype(psa, &arrayVt); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal((VARENUM)vt, arrayVt); + } + finally + { + HRESULT hr = SafeArrayDestroy(psa); + Assert.Equal(HRESULT.S_OK, hr); + } + } + + [StaFact] + public void SAFEARRAY_GetValue_InvokeSingleDimensional_ReturnsExpected() + { + var saBound = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = 0 + }; + SAFEARRAY *psa = SafeArrayCreate(VARENUM.I4, 1, &saBound); + Assert.True(psa != null); + + try + { + Span indices1 = stackalloc int[] { 0 }; + Span indices2 = stackalloc int[] { 1 }; + + fixed (int* pIndices1 = indices1) + fixed (int* pIndices2 = indices2) + { + int value1 = 1; + HRESULT hr = SafeArrayPutElement(psa, pIndices1, &value1); + Assert.Equal(HRESULT.S_OK, hr); + + int value2 = 2; + hr = SafeArrayPutElement(psa, pIndices2, &value2); + Assert.Equal(HRESULT.S_OK, hr); + + int result = -1; + hr = SafeArrayGetElement(psa, pIndices1, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(1, result); + + hr = SafeArrayGetElement(psa, pIndices2, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(2, result); + } + + Assert.Equal(1, psa->GetValue(indices1)); + Assert.Equal(2, psa->GetValue(indices2)); + } + finally + { + SafeArrayDestroy(psa); + } + } + + [StaFact] + public void SAFEARRAY_GetValue_InvokeSingleDimensionalNonZeroLowerBound_ReturnsExpected() + { + var saBound = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = -5 + }; + SAFEARRAY *psa = SafeArrayCreate(VARENUM.I4, 1, &saBound); + Assert.True(psa != null); + + try + { + Span indices1 = stackalloc int[] { -5 }; + Span indices2 = stackalloc int[] { -4 }; + + fixed (int* pIndices1 = indices1) + fixed (int* pIndices2 = indices2) + { + int value1 = 1; + HRESULT hr = SafeArrayPutElement(psa, pIndices1, &value1); + Assert.Equal(HRESULT.S_OK, hr); + + int value2 = 2; + hr = SafeArrayPutElement(psa, pIndices2, &value2); + Assert.Equal(HRESULT.S_OK, hr); + + int result = -1; + hr = SafeArrayGetElement(psa, pIndices1, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(1, result); + + hr = SafeArrayGetElement(psa, pIndices2, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(2, result); + } + + Assert.Equal(1, psa->GetValue(indices1)); + Assert.Equal(2, psa->GetValue(indices2)); + } + finally + { + SafeArrayDestroy(psa); + } + } + + [StaFact] + public void SAFEARRAY_GetValue_InvokeMultiDimensional_ReturnsExpected() + { + SAFEARRAYBOUND* saBounds = stackalloc SAFEARRAYBOUND[2]; + saBounds[0] = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = 0 + }; + saBounds[1] = new SAFEARRAYBOUND + { + cElements = 20, + lLbound = 0 + }; + SAFEARRAY *psa = SafeArrayCreate(VARENUM.I4, 2, saBounds); + Assert.True(psa != null); + + try + { + Span indices1 = stackalloc int[] { 0, 0 }; + Span indices2 = stackalloc int[] { 1, 2 }; + + fixed (int* pIndices1 = indices1) + fixed (int* pIndices2 = indices2) + { + int value1 = 1; + HRESULT hr = SafeArrayPutElement(psa, pIndices1, &value1); + Assert.Equal(HRESULT.S_OK, hr); + + int value2 = 2; + hr = SafeArrayPutElement(psa, pIndices2, &value2); + Assert.Equal(HRESULT.S_OK, hr); + + int result = -1; + hr = SafeArrayGetElement(psa, pIndices1, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(1, result); + + hr = SafeArrayGetElement(psa, pIndices2, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(2, result); + } + + Assert.Equal(1, psa->GetValue(indices1)); + Assert.Equal(2, psa->GetValue(indices2)); + } + finally + { + SafeArrayDestroy(psa); + } + } + + [StaFact] + public void SAFEARRAY_GetValue_InvokeMultiDimensionalNonZeroLowerBound_ReturnsExpected() + { + SAFEARRAYBOUND* saBounds = stackalloc SAFEARRAYBOUND[2]; + saBounds[0] = new SAFEARRAYBOUND + { + cElements = 10, + lLbound = -5 + }; + saBounds[1] = new SAFEARRAYBOUND + { + cElements = 20, + lLbound = -4 + }; + SAFEARRAY *psa = SafeArrayCreate(VARENUM.I4, 2, saBounds); + Assert.True(psa != null); + + try + { + Span indices1 = stackalloc int[] { -5, -4 }; + Span indices2 = stackalloc int[] { -4, -3 }; + + fixed (int* pIndices1 = indices1) + fixed (int* pIndices2 = indices2) + { + int value1 = 1; + HRESULT hr = SafeArrayPutElement(psa, pIndices1, &value1); + Assert.Equal(HRESULT.S_OK, hr); + + int value2 = 2; + hr = SafeArrayPutElement(psa, pIndices2, &value2); + Assert.Equal(HRESULT.S_OK, hr); + + int result = -1; + hr = SafeArrayGetElement(psa, pIndices1, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(1, result); + + hr = SafeArrayGetElement(psa, pIndices2, &result); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(2, result); + } + + Assert.Equal(1, psa->GetValue(indices1)); + Assert.Equal(2, psa->GetValue(indices2)); + } + finally + { + SafeArrayDestroy(psa); + } + } + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static unsafe extern SAFEARRAY* SafeArrayCreate(VARENUM vt, uint cDims, SAFEARRAYBOUND* rgsabound); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static unsafe extern SAFEARRAY* SafeArrayCreateEx(VARENUM vt, uint cDims, SAFEARRAYBOUND* rgsabound, IntPtr pvExtra); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static unsafe extern HRESULT SafeArrayDestroy(SAFEARRAY* psa); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private unsafe static extern HRESULT SafeArrayPutElement(SAFEARRAY* psa, int* rgIndices, void* pv); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private unsafe static extern HRESULT SafeArrayGetElement(SAFEARRAY* psa, int* rgIndices, void* pv); + } +} diff --git a/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/VARIANTTests.cs b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/VARIANTTests.cs new file mode 100644 index 0000000000..56eff86f97 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/tests/UnitTests/Interop/Oleaut32/VARIANTTests.cs @@ -0,0 +1,6111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Xunit; +using static Interop; +using static Interop.Kernel32; +using static Interop.Ole32; +using static Interop.Oleaut32; + +namespace System.Windows.Forms.Tests.Interop.Oleaut32 +{ + public unsafe class VARIANTTests + { + [ConditionalFact(typeof(ArchitectureDetection), nameof(ArchitectureDetection.Is32bit))] + public void VARIANT_Sizeof_InvokeX86_ReturnsExpected() + { + Assert.Equal(16, Marshal.SizeOf()); + Assert.Equal(16, sizeof(VARIANT)); + } + + [ConditionalFact(typeof(ArchitectureDetection), nameof(ArchitectureDetection.Is64bit))] + public void VARIANT_Sizeof_InvokeX64_ReturnsExpected() + { + Assert.Equal(24, Marshal.SizeOf()); + Assert.Equal(24, sizeof(VARIANT)); + } + + [StaTheory] + [InlineData((ushort)VARENUM.EMPTY, false)] + [InlineData((ushort)VARENUM.BOOL, false)] + [InlineData((ushort)(VARENUM.BYREF), true)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF), true)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF | VARENUM.ARRAY), true)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF | VARENUM.VECTOR), true)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.ARRAY), false)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.VECTOR), false)] + public void VARIANT_Byref_Get_ReturnsExpected(ushort vt, bool expected) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + Assert.Equal(expected, variant.Byref); + } + + [StaTheory] + [InlineData((ushort)VARENUM.EMPTY, (ushort)VARENUM.EMPTY)] + [InlineData((ushort)VARENUM.BOOL, (ushort)VARENUM.BOOL)] + [InlineData((ushort)(VARENUM.BYREF), (ushort)VARENUM.EMPTY)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF), (ushort)VARENUM.BOOL)] + public void VARIANT_Type_Get_ReturnsExpected(ushort vt, ushort expected) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + Assert.Equal((VARENUM)expected, variant.Type); + } + + [StaTheory] + [InlineData((ushort)VARENUM.EMPTY)] + [InlineData((ushort)(VARENUM.EMPTY | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.UNKNOWN)] + [InlineData((ushort)(VARENUM.UNKNOWN | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.DISPATCH)] + [InlineData((ushort)(VARENUM.DISPATCH | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.BSTR)] + [InlineData((ushort)(VARENUM.BSTR | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.BOOL)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF))] + public void VARIANT_Clear_InvokeDefault_Success(ushort vt) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + variant.Clear(); + Assert.Equal(VARENUM.EMPTY, variant.vt); + Assert.Equal(IntPtr.Zero, variant.data.punkVal); + } + + [StaFact] + public void VARIANT_Clear_InvokeCustom_Success() + { + using var variant = new VARIANT + { + vt = VARENUM.BOOL, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)1, + } + }; + variant.Clear(); + Assert.Equal(VARENUM.EMPTY, variant.vt); + Assert.Equal(IntPtr.Zero, variant.data.punkVal); + } + + [StaFact] + public void VARIANT_Clear_InvokeBSTR_Success() + { + IntPtr data = Marshal.StringToBSTR("abc"); + using var variant = new VARIANT + { + vt = VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + variant.Clear(); + Assert.Equal(VARENUM.EMPTY, variant.vt); + Assert.Equal(IntPtr.Zero, variant.data.punkVal); + } + + [StaTheory] + [InlineData((ushort)VARENUM.EMPTY)] + [InlineData((ushort)(VARENUM.EMPTY | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.UNKNOWN)] + [InlineData((ushort)(VARENUM.UNKNOWN | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.DISPATCH)] + [InlineData((ushort)(VARENUM.DISPATCH | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.BSTR)] + [InlineData((ushort)(VARENUM.BSTR | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.BOOL)] + [InlineData((ushort)(VARENUM.BOOL | VARENUM.BYREF))] + public void VARIANT_Dispose_InvokeDefault_Success(ushort vt) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + variant.Dispose(); + Assert.Equal(VARENUM.EMPTY, variant.vt); + Assert.Equal(IntPtr.Zero, variant.data.punkVal); + } + + [StaFact] + public void VARIANT_Dispose_InvokeCustom_Success() + { + using var variant = new VARIANT + { + vt = VARENUM.BOOL, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)1, + } + }; + variant.Dispose(); + Assert.Equal(VARENUM.EMPTY, variant.vt); + Assert.Equal(IntPtr.Zero, variant.data.punkVal); + } + + [StaFact] + public void VARIANT_Dispose_InvokeBSTR_Success() + { + IntPtr data = Marshal.StringToBSTR("abc"); + using var variant = new VARIANT + { + vt = VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + variant.Dispose(); + Assert.Equal(VARENUM.EMPTY, variant.vt); + Assert.Equal(IntPtr.Zero, variant.data.punkVal); + } + + public static IEnumerable ToObject_TestData() + { + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.I1, (IntPtr)long.MinValue, (sbyte)0 }; + } + yield return new object[] { VARENUM.I1, (IntPtr)int.MinValue, (sbyte)0 }; + yield return new object[] { VARENUM.I1, (IntPtr)short.MinValue, (sbyte)0 }; + yield return new object[] { VARENUM.I1, (IntPtr)sbyte.MinValue, sbyte.MinValue }; + yield return new object[] { VARENUM.I1, (IntPtr)(-10), (sbyte)(-10) }; + yield return new object[] { VARENUM.I1, (IntPtr)0, (sbyte)0 }; + yield return new object[] { VARENUM.I1, (IntPtr)10, (sbyte)10 }; + yield return new object[] { VARENUM.I1, (IntPtr)sbyte.MaxValue, sbyte.MaxValue }; + yield return new object[] { VARENUM.I1, (IntPtr)short.MaxValue, (sbyte)(-1) }; + yield return new object[] { VARENUM.I1, (IntPtr)int.MaxValue, (sbyte)(-1) }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.I1, (IntPtr)long.MaxValue, (sbyte)(-1) }; + } + + yield return new object[] { VARENUM.UI1, (IntPtr)(-10), (byte)246 }; + yield return new object[] { VARENUM.UI1, (IntPtr)0, (byte)0 }; + yield return new object[] { VARENUM.UI1, (IntPtr)10, (byte)10 }; + yield return new object[] { VARENUM.UI1, (IntPtr)byte.MaxValue, byte.MaxValue }; + yield return new object[] { VARENUM.UI1, (IntPtr)ushort.MaxValue, byte.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.UI1, (IntPtr)uint.MaxValue, byte.MaxValue }; + } + yield return new object[] { VARENUM.UI1, (IntPtr)(-1), byte.MaxValue }; + + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.I2, (IntPtr)long.MinValue, (short)0 }; + } + yield return new object[] { VARENUM.I2, (IntPtr)int.MinValue, (short)0 }; + yield return new object[] { VARENUM.I2, (IntPtr)short.MinValue, short.MinValue }; + yield return new object[] { VARENUM.I2, (IntPtr)sbyte.MinValue, (short)sbyte.MinValue }; + yield return new object[] { VARENUM.I2, (IntPtr)(-10), (short)(-10) }; + yield return new object[] { VARENUM.I2, (IntPtr)0, (short)0 }; + yield return new object[] { VARENUM.I2, (IntPtr)10, (short)10 }; + yield return new object[] { VARENUM.I2, (IntPtr)sbyte.MaxValue, (short)sbyte.MaxValue }; + yield return new object[] { VARENUM.I2, (IntPtr)short.MaxValue, short.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.I2, (IntPtr)long.MaxValue, (short)(-1) }; + } + + yield return new object[] { VARENUM.UI2, (IntPtr)(-10), (ushort)65526 }; + yield return new object[] { VARENUM.UI2, (IntPtr)0, (ushort)0 }; + yield return new object[] { VARENUM.UI2, (IntPtr)10, (ushort)10 }; + yield return new object[] { VARENUM.UI2, (IntPtr)byte.MaxValue, (ushort)byte.MaxValue }; + yield return new object[] { VARENUM.UI2, (IntPtr)ushort.MaxValue, ushort.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.UI2, (IntPtr)uint.MaxValue, ushort.MaxValue }; + } + yield return new object[] { VARENUM.UI2, (IntPtr)(-1), ushort.MaxValue }; + + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.I4, (IntPtr)long.MinValue, 0 }; + } + yield return new object[] { VARENUM.I4, (IntPtr)int.MinValue, int.MinValue }; + yield return new object[] { VARENUM.I4, (IntPtr)short.MinValue, (int)short.MinValue }; + yield return new object[] { VARENUM.I4, (IntPtr)sbyte.MinValue, (int)sbyte.MinValue }; + yield return new object[] { VARENUM.I4, (IntPtr)(-10), -10 }; + yield return new object[] { VARENUM.I4, (IntPtr)0, 0 }; + yield return new object[] { VARENUM.I4, (IntPtr)10, 10 }; + yield return new object[] { VARENUM.I4, (IntPtr)sbyte.MaxValue, (int)sbyte.MaxValue }; + yield return new object[] { VARENUM.I4, (IntPtr)short.MaxValue, (int)short.MaxValue }; + yield return new object[] { VARENUM.I4, (IntPtr)int.MaxValue, int.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.I4, (IntPtr)long.MaxValue, -1 }; + } + + yield return new object[] { VARENUM.UI4, (IntPtr)(-10), (uint)4294967286 }; + yield return new object[] { VARENUM.UI4, (IntPtr)0, (uint)0 }; + yield return new object[] { VARENUM.UI4, (IntPtr)10, (uint)10 }; + yield return new object[] { VARENUM.UI4, (IntPtr)byte.MaxValue, (uint)byte.MaxValue }; + yield return new object[] { VARENUM.UI4, (IntPtr)ushort.MaxValue, (uint)ushort.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.UI4, (IntPtr)uint.MaxValue, uint.MaxValue }; + } + yield return new object[] { VARENUM.UI4, (IntPtr)(-1), uint.MaxValue }; + + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.INT, (IntPtr)long.MinValue, 0 }; + } + yield return new object[] { VARENUM.INT, (IntPtr)int.MinValue, int.MinValue }; + yield return new object[] { VARENUM.INT, (IntPtr)short.MinValue, (int)short.MinValue }; + yield return new object[] { VARENUM.INT, (IntPtr)sbyte.MinValue, (int)sbyte.MinValue }; + yield return new object[] { VARENUM.INT, (IntPtr)(-10), -10 }; + yield return new object[] { VARENUM.INT, (IntPtr)0, 0 }; + yield return new object[] { VARENUM.INT, (IntPtr)10, 10 }; + yield return new object[] { VARENUM.INT, (IntPtr)sbyte.MaxValue, (int)sbyte.MaxValue }; + yield return new object[] { VARENUM.INT, (IntPtr)short.MaxValue, (int)short.MaxValue }; + yield return new object[] { VARENUM.INT, (IntPtr)int.MaxValue, int.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.INT, (IntPtr)long.MaxValue, -1 }; + } + + yield return new object[] { VARENUM.UINT, (IntPtr)(-10), (uint)4294967286 }; + yield return new object[] { VARENUM.UINT, (IntPtr)0, (uint)0 }; + yield return new object[] { VARENUM.UINT, (IntPtr)10, (uint)10 }; + yield return new object[] { VARENUM.UINT, (IntPtr)byte.MaxValue, (uint)byte.MaxValue }; + yield return new object[] { VARENUM.UINT, (IntPtr)ushort.MaxValue, (uint)ushort.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.UINT, (IntPtr)uint.MaxValue, uint.MaxValue }; + } + yield return new object[] { VARENUM.UINT, (IntPtr)(-1), uint.MaxValue }; + + yield return new object[] { VARENUM.BOOL, (IntPtr)(-1), true }; + yield return new object[] { VARENUM.BOOL, (IntPtr)0, false }; + yield return new object[] { VARENUM.BOOL, (IntPtr)1, true }; + + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.ERROR, (IntPtr)long.MinValue, 0 }; + } + yield return new object[] { VARENUM.ERROR, (IntPtr)int.MinValue, int.MinValue }; + yield return new object[] { VARENUM.ERROR, (IntPtr)short.MinValue, (int)short.MinValue }; + yield return new object[] { VARENUM.ERROR, (IntPtr)sbyte.MinValue, (int)sbyte.MinValue }; + yield return new object[] { VARENUM.ERROR, (IntPtr)(-10), -10 }; + yield return new object[] { VARENUM.ERROR, (IntPtr)0, 0 }; + yield return new object[] { VARENUM.ERROR, (IntPtr)10, 10 }; + yield return new object[] { VARENUM.ERROR, (IntPtr)sbyte.MaxValue, (int)sbyte.MaxValue }; + yield return new object[] { VARENUM.ERROR, (IntPtr)short.MaxValue, (int)short.MaxValue }; + yield return new object[] { VARENUM.ERROR, (IntPtr)int.MaxValue, int.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { VARENUM.ERROR, (IntPtr)long.MaxValue, -1 }; + } + } + + [StaTheory] + [MemberData(nameof(ToObject_TestData))] + public void VARIANT_ToObject_Invoke_ReturnsExpected(ushort vt, IntPtr data, object expected) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)data + } + }; + AssertToObjectEqual(expected, variant); + } + + [StaTheory] + [MemberData(nameof(ToObject_TestData))] + public void VARIANT_ToObject_InvokeBYREF_ReturnsExpected(ushort vt, IntPtr data, object expected) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)(&data) + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable BYREFNoData_TestData() + { + yield return new object[] { VARENUM.I2 }; + yield return new object[] { VARENUM.I4 }; + yield return new object[] { VARENUM.R4 }; + yield return new object[] { VARENUM.R8 }; + yield return new object[] { VARENUM.CY }; + yield return new object[] { VARENUM.DATE }; + yield return new object[] { VARENUM.BSTR }; + yield return new object[] { VARENUM.DISPATCH }; + yield return new object[] { VARENUM.ERROR }; + yield return new object[] { VARENUM.BOOL }; + yield return new object[] { VARENUM.VARIANT }; + yield return new object[] { VARENUM.UNKNOWN }; + yield return new object[] { VARENUM.DECIMAL }; + yield return new object[] { VARENUM.I1 }; + yield return new object[] { VARENUM.UI1 }; + yield return new object[] { VARENUM.UI2 }; + yield return new object[] { VARENUM.UI4 }; + yield return new object[] { VARENUM.I8 }; + yield return new object[] { VARENUM.UI8 }; + yield return new object[] { VARENUM.INT }; + yield return new object[] { VARENUM.UINT }; + yield return new object[] { VARENUM.VOID }; + yield return new object[] { VARENUM.HRESULT }; + yield return new object[] { VARENUM.PTR }; + yield return new object[] { VARENUM.SAFEARRAY }; + yield return new object[] { VARENUM.CARRAY }; + yield return new object[] { VARENUM.USERDEFINED }; + yield return new object[] { VARENUM.LPSTR }; + yield return new object[] { VARENUM.LPWSTR }; + yield return new object[] { VARENUM.RECORD }; + yield return new object[] { VARENUM.INT_PTR }; + yield return new object[] { VARENUM.UINT_PTR }; + yield return new object[] { VARENUM.FILETIME }; + yield return new object[] { VARENUM.BLOB }; + yield return new object[] { VARENUM.STREAM }; + yield return new object[] { VARENUM.STORAGE }; + yield return new object[] { VARENUM.STREAMED_OBJECT }; + yield return new object[] { VARENUM.STORED_OBJECT }; + yield return new object[] { VARENUM.BLOB_OBJECT }; + yield return new object[] { VARENUM.CF }; + yield return new object[] { VARENUM.CLSID }; + yield return new object[] { VARENUM.VERSIONED_STREAM }; + yield return new object[] { VARENUM.BSTR_BLOB }; + } + + [StaTheory] + [MemberData(nameof(BYREFNoData_TestData))] + public void VARIANT_Toobject_BYREFNoData_Throws(ushort vt) + { + using var variant = new VARIANT + { + vt = VARENUM.BYREF | (VARENUM)vt, + }; + AssertToObjectThrows(variant); + } + + public static IEnumerable ToObject_I8_TestData() + { + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)long.MinValue, long.MinValue }; + yield return new object[] { (IntPtr)int.MinValue, (long)int.MinValue }; + yield return new object[] { (IntPtr)short.MinValue, (long)short.MinValue }; + yield return new object[] { (IntPtr)sbyte.MinValue, (long)sbyte.MinValue }; + yield return new object[] { (IntPtr)(-10), (long)(-10) }; + } + yield return new object[] { (IntPtr)0, (long)0 }; + yield return new object[] { (IntPtr)10, (long)10 }; + yield return new object[] { (IntPtr)sbyte.MaxValue, (long)sbyte.MaxValue }; + yield return new object[] { (IntPtr)short.MaxValue, (long)short.MaxValue }; + yield return new object[] { (IntPtr)int.MaxValue, (long)int.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)long.MaxValue, long.MaxValue }; + } + } + + [Theory] + [MemberData(nameof(ToObject_I8_TestData))] + public void VARIANT_ToObject_I8_ReturnsExpected(IntPtr data, long expected) + { + using var variant = new VARIANT + { + vt = VARENUM.I8, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable ToObject_I8BYREF_TestData() + { + yield return new object[] { long.MinValue }; + yield return new object[] { int.MinValue }; + yield return new object[] { short.MinValue }; + yield return new object[] { sbyte.MinValue }; + yield return new object[] { -10, }; + yield return new object[] { 0, }; + yield return new object[] { 10, }; + yield return new object[] { sbyte.MaxValue }; + yield return new object[] { short.MaxValue }; + yield return new object[] { int.MaxValue }; + yield return new object[] { long.MaxValue }; + } + + [Theory] + [MemberData(nameof(ToObject_I8BYREF_TestData))] + public void VARIANT_ToObject_I8BYREF_ReturnsExpected(long data) + { + using var variant = new VARIANT + { + vt = VARENUM.I8 | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)(&data) + } + }; + AssertToObjectEqual(data, variant); + } + + public static IEnumerable ToObject_UI8_TestData() + { + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)(-10), (ulong)18446744073709551606 }; + } + yield return new object[] { (IntPtr)0, (ulong)0 }; + yield return new object[] { (IntPtr)10, (ulong)10 }; + yield return new object[] { (IntPtr)byte.MaxValue, (ulong)byte.MaxValue }; + yield return new object[] { (IntPtr)ushort.MaxValue, (ulong)ushort.MaxValue }; + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)uint.MaxValue, (ulong)uint.MaxValue }; + yield return new object[] { (IntPtr)ulong.MaxValue, ulong.MaxValue }; + } + } + + [Theory] + [MemberData(nameof(ToObject_UI8_TestData))] + public void VARIANT_ToObject_UI8_ReturnsExpected(IntPtr data, ulong expected) + { + using var variant = new VARIANT + { + vt = VARENUM.UI8, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable ToObject_UI8BYREF_TestData() + { + yield return new object[] { 0 }; + yield return new object[] { 10 }; + yield return new object[] { byte.MaxValue }; + yield return new object[] { ushort.MaxValue}; + yield return new object[] { uint.MaxValue }; + yield return new object[] { ulong.MaxValue }; + } + + [Theory] + [MemberData(nameof(ToObject_UI8BYREF_TestData))] + public void VARIANT_ToObject_UI8BYREF_ReturnsExpected(ulong data) + { + using var variant = new VARIANT + { + vt = VARENUM.UI8 | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)(&data) + } + }; + AssertToObjectEqual(data, variant); + } + + public static IEnumerable ToObject_CY_TestData() + { + yield return new object[] { (IntPtr)0, 0.0m }; + yield return new object[] { (IntPtr)10, 0.001m }; + yield return new object[] { (IntPtr)10000, 1m }; + yield return new object[] { (IntPtr)123456, 12.3456m }; + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)(-10), -0.001m }; + yield return new object[] { (IntPtr)(-10000), -1m }; + yield return new object[] { (IntPtr)(-123456), -12.3456m }; + } + } + + [Theory] + [MemberData(nameof(ToObject_CY_TestData))] + public void VARIANT_ToObject_CY_ReturnsExpected(IntPtr data, decimal expected) + { + using var variant = new VARIANT + { + vt = VARENUM.CY, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable ToObject_CYBYREF_TestData() + { + yield return new object[] { 0, 0.0m }; + yield return new object[] { 10, 0.001m }; + yield return new object[] { 10000, 1m }; + yield return new object[] { 123456, 12.3456m }; + yield return new object[] { -10, -0.001m }; + yield return new object[] { -10000, -1m }; + yield return new object[] { -123456, -12.3456m }; + } + + [Theory] + [MemberData(nameof(ToObject_CYBYREF_TestData))] + public void VARIANT_ToObject_CYBYREF_ReturnsExpected(long data, decimal expected) + { + using var variant = new VARIANT + { + vt = VARENUM.CY | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)(&data) + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable ToObject_R4_TestData() + { + yield return new object[] { (IntPtr)0, 0.0f }; + yield return new object[] { (IntPtr)1067030938, 1.2f }; + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)3214514586, -1.2f }; + yield return new object[] { (IntPtr)4290772992, float.NaN }; + } + } + + [Theory] + [MemberData(nameof(ToObject_R4_TestData))] + public void VARIANT_ToObject_R4_ReturnsExpected(IntPtr data, float expected) + { + using var variant = new VARIANT + { + vt = VARENUM.R4, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable ToObject_R4BYREF_TestData() + { + yield return new object[] { 0.0f }; + yield return new object[] { 1.2f }; + yield return new object[] { -1.2f }; + yield return new object[] { float.NaN }; + } + + [Theory] + [MemberData(nameof(ToObject_R4BYREF_TestData))] + public void VARIANT_ToObject_R4BYREF_ReturnsExpected(float data) + { + using var variant = new VARIANT + { + vt = VARENUM.R4 | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)(&data) + } + }; + AssertToObjectEqual(data, variant); + } + + public static IEnumerable ToObject_R8_TestData() + { + yield return new object[] { (IntPtr)0, 0.0 }; + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)4608083138725491507, 1.2 }; + yield return new object[] { (IntPtr)(-4615288898129284301), -1.2 }; + yield return new object[] { (IntPtr)(-2251799813685248), double.NaN }; + } + } + + [Theory] + [MemberData(nameof(ToObject_R8_TestData))] + public void VARIANT_ToObject_R8_ReturnsExpected(IntPtr data, double expected) + { + using var variant = new VARIANT + { + vt = VARENUM.R8, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(expected, variant); + } + + public static IEnumerable ToObject_R8BYREF_TestData() + { + yield return new object[] { 0.0 }; + yield return new object[] { 1.2 }; + yield return new object[] { -1.2 }; + yield return new object[] { double.NaN }; + } + + [Theory] + [MemberData(nameof(ToObject_R8BYREF_TestData))] + public void VARIANT_ToObject_R8BYREF_ReturnsExpected(double data) + { + using var variant = new VARIANT + { + vt = VARENUM.R8 | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + punkVal = (IntPtr)(&data) + } + }; + AssertToObjectEqual(data, variant); + } + + public static IEnumerable NULL_TestData() + { + yield return new object[] { IntPtr.Zero }; + yield return new object[] { (IntPtr)1 }; + } + + [StaTheory] + [MemberData(nameof(NULL_TestData))] + public void VARIANT_ToObject_NULL_Sucess(IntPtr data) + { + using var variant = new VARIANT + { + vt = VARENUM.BYREF | VARENUM.NULL, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(Convert.DBNull, variant); + } + + [StaTheory] + [MemberData(nameof(NULL_TestData))] + public void VARIANT_ToObject_NULLBYREFData_Sucess(IntPtr data) + { + using var variant = new VARIANT + { + vt = VARENUM.BYREF | VARENUM.NULL, + data = new VARIANT.VARIANTUnion + { + ppunkVal = &data + } + }; + AssertToObjectEqual(Convert.DBNull, variant); + } + + [StaFact] + public void VARIANT_ToObject_NULLBYREFNoData_Sucess() + { + using var variant = new VARIANT + { + vt = VARENUM.BYREF | VARENUM.NULL + }; + AssertToObjectEqual(Convert.DBNull, variant); + } + + public static IEnumerable EMPTY_TestData() + { + yield return new object[] { IntPtr.Zero }; + yield return new object[] { (IntPtr)1 }; + } + + [StaTheory] + [MemberData(nameof(EMPTY_TestData))] + public void VARIANT_ToObject_EMPTY_Sucess(IntPtr data) + { + using var variant = new VARIANT + { + vt = VARENUM.EMPTY, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqual(null, variant); + } + + [StaTheory] + [MemberData(nameof(EMPTY_TestData))] + public void VARIANT_ToObject_EMPTYBYREFData_Sucess(IntPtr data) + { + using var variant = new VARIANT + { + vt = VARENUM.BYREF | VARENUM.EMPTY, + data = new VARIANT.VARIANTUnion + { + ppunkVal = &data + } + }; + AssertToObject(variant, value => + { + if (IntPtr.Size == 8) + { + Assert.Equal((ulong)(IntPtr)variant.data.ppunkVal, value); + } + else + { + Assert.Equal((uint)(IntPtr)variant.data.ppunkVal, value); + } + }); + } + + [StaFact] + public void VARIANT_ToObject_EMPTYBYREFNoData_Sucess() + { + using var variant = new VARIANT + { + vt = VARENUM.BYREF | VARENUM.EMPTY + }; + AssertToObject(variant, value => + { + if (IntPtr.Size == 8) + { + Assert.Equal((ulong)0, value); + } + else + { + Assert.Equal((uint)0, value); + } + }); + } + + public static IEnumerable HRESULT_TestData() + { + yield return new object[] { (IntPtr)int.MinValue, int.MinValue }; + yield return new object[] { (IntPtr)short.MinValue, (int)short.MinValue }; + yield return new object[] { (IntPtr)sbyte.MinValue, (int)sbyte.MinValue }; + yield return new object[] { (IntPtr)(-10), -10 }; + yield return new object[] { (IntPtr)0, 0 }; + yield return new object[] { (IntPtr)10, 10 }; + yield return new object[] { (IntPtr)sbyte.MaxValue, (int)sbyte.MaxValue }; + yield return new object[] { (IntPtr)short.MaxValue, (int)short.MaxValue }; + yield return new object[] { (IntPtr)int.MaxValue, int.MaxValue }; + if (IntPtr.Size == 8) + { + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)long.MinValue, 0 }; + } + if (IntPtr.Size == 8) + { + yield return new object[] { (IntPtr)long.MaxValue, -1 }; + } + } + } + + [StaTheory] + [MemberData(nameof(HRESULT_TestData))] + public void VARIANT_ToObject_HRESULT_Success(IntPtr data, int expected) + { + using var variant = new VARIANT + { + vt = VARENUM.HRESULT, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + AssertToObjectEqualExtension(expected, variant); + } + + [StaTheory] + [MemberData(nameof(HRESULT_TestData))] + public void VARIANT_ToObject_HRESULTBYREF_Success(IntPtr data, int expected) + { + using var variant = new VARIANT + { + vt = VARENUM.HRESULT | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + ppunkVal = &data + } + }; + AssertToObjectEqualExtension(expected, variant); + } + + [StaFact] + public void VARIANT_ToObject_FILETIME_Success() + { + using var variant = new VARIANT(); + var dt = new DateTime(2020, 05, 13, 13, 3, 12); + var ft = new FILETIME(dt); + HRESULT hr = InitPropVariantFromFileTime(&ft, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.FILETIME, variant.Type); + + AssertToObjectEqualExtension(new DateTime(2020, 05, 13, 13, 3, 12), variant); + } + + [StaTheory] + [InlineData(-10)] + public void VARIANT_ToObject_InvalidFILETIME_ThrowsArgumentOutOfRangeException(int value) + { + using var variant = new VARIANT + { + vt = VARENUM.FILETIME, + data = new VARIANT.VARIANTUnion + { + cyVal = value + } + }; + Assert.Throws("fileTime", () => variant.ToObject()); + } + + [StaFact] + public void VARIANT_ToObject_DateFromFILETIME_Success() + { + using var variant = new VARIANT(); + var dt = new DateTime(2020, 05, 13, 13, 3, 12); + var ft = new FILETIME(dt); + HRESULT hr = InitVariantFromFileTime(&ft, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.DATE, variant.Type); + + AssertToObjectEqual(new DateTime(2020, 05, 13, 13, 3, 12), variant); + } + + [StaFact] + public void VARIANT_ToObject_Date_Success() + { + var dt = new DateTime(2020, 05, 13, 13, 3, 12); + double date = dt.ToOADate(); + using var variant = new VARIANT + { + vt = VARENUM.DATE, + data = new VARIANT.VARIANTUnion + { + date = date + } + }; + AssertToObjectEqual(new DateTime(2020, 05, 13, 13, 3, 12), variant); + } + + [StaFact] + public void VARIANT_ToObject_DateEmpty_Success() + { + using var variant = new VARIANT + { + vt = VARENUM.DATE + }; + AssertToObjectEqual(new DateTime(1899, 12, 30), variant); + } + + [StaFact] + public void VARIANT_ToObject_DateBYREF_Success() + { + var dt = new DateTime(2020, 05, 13, 13, 3, 12); + double date = dt.ToOADate(); + using var variant = new VARIANT + { + vt = VARENUM.DATE | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pdate = &date + } + }; + AssertToObjectEqual(new DateTime(2020, 05, 13, 13, 3, 12), variant); + } + + [StaFact] + public void VARIANT_ToObject_DateBYREFEmpty_Success() + { + double date = 0; + using var variant = new VARIANT + { + vt = VARENUM.DATE | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pdate = &date + } + }; + AssertToObjectEqual(new DateTime(1899, 12, 30), variant); + } + + [StaTheory] + [InlineData(null)] + [InlineData("")] + [InlineData("text")] + public void VARIANT_ToObject_BSTR_ReturnsExpected(string text) + { + IntPtr ptr = Marshal.StringToBSTR(text); + using var variant = new VARIANT + { + vt = VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + bstrVal = ptr + } + }; + AssertToObjectEqual(text, variant); + } + + [StaFact] + public void VARIANT_ToObject_BSTRNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.BSTR + }; + AssertToObjectEqual(null, variant); + } + + [StaTheory] + [InlineData("")] + [InlineData("text")] + public void VARIANT_ToObject_BSTRBYREF_ReturnsExpected(string text) + { + IntPtr ptr = Marshal.StringToBSTR(text); + try + { + using var variant = new VARIANT + { + vt = VARENUM.BSTR | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pbstrVal = &ptr + } + }; + AssertToObjectEqual(text, variant); + } + finally + { + Marshal.FreeBSTR(ptr); + } + } + + [StaTheory] + [InlineData(null)] + [InlineData("")] + [InlineData("text")] + public void VARIANT_ToObject_LPWSTR_ReturnsExpected(string text) + { + IntPtr ptr = Marshal.StringToCoTaskMemUni(text); + using var variant = new VARIANT + { + vt = VARENUM.LPWSTR, + data = new VARIANT.VARIANTUnion + { + bstrVal = ptr + } + }; + AssertToObjectEqualExtension(text, variant); + } + + [StaFact] + public void VARIANT_ToObject_LPWSTRNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.LPWSTR + }; + AssertToObjectEqualExtension(null, variant); + } + + [StaTheory] + [InlineData("")] + [InlineData("text")] + public void VARIANT_ToObject_LPWSTRBYREF_ReturnsExpected(string text) + { + IntPtr ptr = Marshal.StringToCoTaskMemUni(text); + try + { + using var variant = new VARIANT + { + vt = VARENUM.LPWSTR | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pbstrVal = &ptr + } + }; + AssertToObjectEqualExtension(text, variant); + } + finally + { + Marshal.FreeCoTaskMem(ptr); + } + } + + [StaTheory] + [InlineData(null)] + [InlineData("")] + [InlineData("text")] + public void VARIANT_ToObject_LPSTR_ReturnsExpected(string text) + { + IntPtr ptr = Marshal.StringToCoTaskMemAnsi(text); + using var variant = new VARIANT + { + vt = VARENUM.LPSTR, + data = new VARIANT.VARIANTUnion + { + bstrVal = ptr + } + }; + AssertToObjectEqualExtension(text, variant); + } + + [StaFact] + public void VARIANT_ToObject_LPSTRNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.LPSTR + }; + AssertToObjectEqualExtension(null, variant); + } + + [StaTheory] + [InlineData("")] + [InlineData("text")] + public void VARIANT_ToObject_LPSTRBYREF_ReturnsExpected(string text) + { + IntPtr ptr = Marshal.StringToCoTaskMemAnsi(text); + try + { + using var variant = new VARIANT + { + vt = VARENUM.LPSTR | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pbstrVal = &ptr + } + }; + AssertToObjectEqualExtension(text, variant); + } + finally + { + Marshal.FreeCoTaskMem(ptr); + } + } + + [StaFact] + public void VARIANT_ToObject_Dispatch_ReturnsExpected() + { + var o = new object(); + IntPtr pUnk = Marshal.GetIUnknownForObject(o); + using var variant = new VARIANT + { + vt = VARENUM.DISPATCH, + data = new VARIANT.VARIANTUnion + { + pdispVal = pUnk + } + }; + AssertToObjectEqual(o, variant); + } + + [StaFact] + public void VARIANT_ToObject_DispatchNoData_ReturnsNull() + { + using var variant = new VARIANT + { + vt = VARENUM.DISPATCH + }; + AssertToObjectEqual(null, variant); + } + + [StaFact] + public void VARIANT_ToObject_DispatchBYREF_ReturnsExpected() + { + var o = new object(); + IntPtr pUnk = Marshal.GetIUnknownForObject(o); + using var variant = new VARIANT + { + vt = VARENUM.DISPATCH | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + ppdispVal = &pUnk + } + }; + AssertToObjectEqual(o, variant); + } + + [StaFact] + public void VARIANT_ToObject_DispatchBYREFNullData_ReturnsNull() + { + IntPtr pUnk = IntPtr.Zero; + using var variant = new VARIANT + { + vt = VARENUM.DISPATCH | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + ppunkVal = &pUnk + } + }; + AssertToObjectEqual(null, variant); + } + + [StaFact] + public void VARIANT_ToObject_UNKNOWN_ReturnsExpected() + { + var o = new object(); + IntPtr pUnk = Marshal.GetIUnknownForObject(o); + using var variant = new VARIANT + { + vt = VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + punkVal = pUnk + } + }; + AssertToObjectEqual(o, variant); + } + + [StaFact] + public void VARIANT_ToObject_UNKNOWNNoData_ReturnsNull() + { + using var variant = new VARIANT + { + vt = VARENUM.UNKNOWN + }; + AssertToObjectEqual(null, variant); + } + + [StaFact] + public void VARIANT_ToObject_UNKNOWNBYREF_ReturnsExpected() + { + var o = new object(); + IntPtr pUnk = Marshal.GetIUnknownForObject(o); + using var variant = new VARIANT + { + vt = VARENUM.UNKNOWN | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + ppunkVal = &pUnk + } + }; + AssertToObjectEqual(o, variant); + } + + [StaFact] + public void VARIANT_ToObject_UNKNOWNBYREFNullData_ReturnsNull() + { + IntPtr pUnk = IntPtr.Zero; + using var variant = new VARIANT + { + vt = VARENUM.UNKNOWN | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + ppunkVal = &pUnk + } + }; + AssertToObjectEqual(null, variant); + } + + [StaFact] + public void VARIANT_ToObject_I4VARIANTBYREF_ReturnsExpected() + { + using var target = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 10 + } + }; + using var variant = new VARIANT + { + vt = VARENUM.VARIANT | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pvarVal = &target + } + }; + AssertToObjectEqual(10, variant); + } + + [StaFact] + public void VARIANT_ToObject_BSTRVARIANTBYREF_ReturnsExpected() + { + IntPtr ptr = Marshal.StringToBSTR("test"); + using var target = new VARIANT + { + vt = VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + bstrVal = ptr + } + }; + using var variant = new VARIANT + { + vt = VARENUM.VARIANT | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pvarVal = &target + } + }; + AssertToObjectEqual("test", variant); + } + + [StaFact] + public void VARIANT_ToObject_EMPTYVARIANTBYREF_ThrowsInvalidOleVariantTypeException() + { + using var target = new VARIANT + { + vt = VARENUM.EMPTY, + }; + using var variant = new VARIANT + { + vt = VARENUM.VARIANT | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pvarVal = &target + } + }; + AssertToObjectEqual(null, variant); + } + + [StaFact] + public void VARIANT_ToObject_BYREFVARIANTBYREF_ThrowsInvalidOleVariantTypeException() + { + int lval = 10; + using var target = new VARIANT + { + vt = VARENUM.BYREF | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + plVal = &lval + } + }; + using var variant = new VARIANT + { + vt = VARENUM.VARIANT | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pvarVal = &target + } + }; + AssertToObjectThrows(variant); + } + + [StaFact] + public void VARIANT_ToObject_VARIANT_ThrowsArgumentException() + { + using var variant = new VARIANT + { + vt = VARENUM.VARIANT + }; + AssertToObjectThrows(variant); + } + + public static IEnumerable Decimal_I8_TestData() + { + yield return new object[] { long.MinValue, (decimal)long.MinValue }; + yield return new object[] { -123, -123m }; + yield return new object[] { 0, 0m }; + yield return new object[] { 123, 123m }; + yield return new object[] { long.MaxValue, (decimal)long.MaxValue }; + } + + [StaTheory] + [MemberData(nameof(Decimal_I8_TestData))] + public void VARIANT_ToObject_DecimalI8_ReturnsExpected(long i8, decimal expected) + { + VarDecFromI8(i8, out DECIMAL d); + VARIANT_ToObject_Decimal_ReturnsExpected(d, expected); + VARIANT_ToObject_DecimalBYREF_ReturnsExpected(d, expected); + } + + public static IEnumerable Decimal_R8_TestData() + { + yield return new object[] { -10e12, -10e12m }; + yield return new object[] { -123.456, -123.456m }; + yield return new object[] { 0.0, 0m }; + yield return new object[] { 123.456, 123.456m }; + yield return new object[] { 10e12, 10e12m }; + } + + [StaTheory] + [MemberData(nameof(Decimal_R8_TestData))] + public void VARIANT_ToObject_DecimalR8_ReturnsExpected(double r8, decimal expected) + { + VarDecFromR8(r8, out DECIMAL d); + VARIANT_ToObject_Decimal_ReturnsExpected(d, expected); + VARIANT_ToObject_DecimalBYREF_ReturnsExpected(d, expected); + } + + public static IEnumerable Decimal_TestData() + { + yield return new object[] { new DECIMAL(), 0.0m }; + } + + [StaTheory] + [MemberData(nameof(Decimal_TestData))] + public void VARIANT_ToObject_Decimal_ReturnsExpected(object d, decimal expected) + { + var variant = new VARIANT(); + *(DECIMAL*)(&variant) = (DECIMAL)d; + variant.vt = VARENUM.DECIMAL; + AssertToObjectEqual(expected, variant); + } + + [StaTheory] + [MemberData(nameof(Decimal_TestData))] + public void VARIANT_ToObject_DecimalBYREF_ReturnsExpected(object d, decimal expected) + { + DECIMAL asD = (DECIMAL)d; + using var variant = new VARIANT + { + vt = VARENUM.DECIMAL | VARENUM.BYREF, + data = new VARIANT.VARIANTUnion + { + pdecVal = &asD + } + }; + AssertToObjectEqual(expected, variant); + } + + [StaFact] + public void VARIANT_ToObject_CLSID_ReturnsExpected() + { + var guid = Guid.NewGuid(); + using var variant = new VARIANT(); + HRESULT hr = InitPropVariantFromCLSID(&guid, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.CLSID, variant.Type); + + AssertToObjectEqualExtension(guid, variant); + } + + public static IEnumerable VOID_TestData() + { + yield return new object[] { IntPtr.Zero }; + yield return new object[] { (IntPtr)1 }; + } + + [StaTheory] + [MemberData(nameof(VOID_TestData))] + public void VARIANT_ToObject_VOID_ReturnsExpected(IntPtr data) + { + using var variant = new VARIANT + { + vt = VARENUM.VOID, + data = new VARIANT.VARIANTUnion + { + punkVal = data + } + }; + IntPtr pv = (IntPtr)(&variant); + Assert.Null(Marshal.GetObjectForNativeVariant(pv)); + } + + [StaTheory] + [InlineData((ushort)VARENUM.USERDEFINED)] + [InlineData((ushort)(VARENUM.USERDEFINED | VARENUM.BYREF))] + public void VARIANT_ToObject_USERDATA_ThrowsArgumentException(ushort vt) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [InlineData((ushort)(VARENUM.VOID | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.PTR)] + [InlineData((ushort)(VARENUM.PTR | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.SAFEARRAY)] + [InlineData((ushort)(VARENUM.SAFEARRAY | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.CARRAY)] + [InlineData((ushort)(VARENUM.CARRAY | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.RECORD)] + [InlineData((ushort)(VARENUM.RECORD | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.BLOB)] + [InlineData((ushort)(VARENUM.BLOB | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.STREAM)] + [InlineData((ushort)(VARENUM.STREAM | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.STORAGE)] + [InlineData((ushort)(VARENUM.STORAGE | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.STREAMED_OBJECT)] + [InlineData((ushort)(VARENUM.STREAMED_OBJECT | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.STORED_OBJECT)] + [InlineData((ushort)(VARENUM.STORED_OBJECT | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.BLOB_OBJECT)] + [InlineData((ushort)(VARENUM.BLOB_OBJECT | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.CF)] + [InlineData((ushort)(VARENUM.CF | VARENUM.BYREF))] + [InlineData((ushort)(VARENUM.BSTR_BLOB | VARENUM.BYREF))] + [InlineData((ushort)VARENUM.ILLEGAL)] + [InlineData((ushort)VARENUM.INT_PTR)] + [InlineData((ushort)VARENUM.UINT_PTR)] + [InlineData(127)] + [InlineData(0x000F)] + [InlineData(0x0020)] + [InlineData(0x0021)] + [InlineData(0x0022)] + [InlineData(0x0023)] + [InlineData(0x0024)] + public void VARIANT_ToObject_CantConvert_ThrowsArgumentException(ushort vt) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [InlineData(128)] + [InlineData(129)] + [InlineData((ushort)VARENUM.BSTR_BLOB)] + public void VARIANT_ToObject_Illegal_ThrowsInvalidOleVariantTypeException(ushort vt) + { + using var variant = new VARIANT + { + vt = (VARENUM)vt + }; + AssertToObjectThrows(variant); + } + + public static IEnumerable VectorI1_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new sbyte[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorI1_TestData))] + public void VARIANT_ToObject_VECTORI1_ReturnsExpected(sbyte[] result) + { + var variant = new VARIANT(); + try + { + fixed (sbyte* pResult = result) + { + HRESULT hr = InitPropVariantFromBuffer(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI1, variant.vt); + } + + // I1 and UI1 have the same size. + variant.vt = VARENUM.VECTOR | VARENUM.I1; + AssertToObjectEqualExtension(result, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORI1NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.I1 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorUI1_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new byte[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorUI1_TestData))] + public void VARIANT_ToObject_VECTORUI1_ReturnsExpected(byte[] result) + { + using var variant = new VARIANT(); + fixed (byte* pResult = result) + { + HRESULT hr = InitPropVariantFromBuffer(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI1, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORUI1NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.UI1 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorI2_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new short[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorI2_TestData))] + public void VARIANT_ToObject_VECTORI2_ReturnsExpected(short[] result) + { + using var variant = new VARIANT(); + fixed (short* pResult = result) + { + HRESULT hr = InitPropVariantFromInt16Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I2, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORI2NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.I2 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorUI2_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new ushort[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorUI2_TestData))] + public void VARIANT_ToObject_VECTORUI2_ReturnsExpected(ushort[] result) + { + using var variant = new VARIANT(); + fixed (ushort* pResult = result) + { + HRESULT hr = InitPropVariantFromUInt16Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI2, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORUI2NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.UI2 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorBOOL_TestData() + { + yield return new object[] { Array.Empty(), Array.Empty() }; + yield return new object[] { new BOOL[] { BOOL.TRUE, BOOL.FALSE, BOOL.TRUE }, new bool[] { true, false, true } }; + } + + [StaTheory] + [MemberData(nameof(VectorBOOL_TestData))] + public void VARIANT_ToObject_VECTORBOOL_ReturnsExpected(object result, bool[] expected) + { + using var variant = new VARIANT(); + BOOL[] boolResult = (BOOL[])result; + fixed (BOOL* pResult = boolResult) + { + HRESULT hr = InitPropVariantFromBooleanVector(pResult, (uint)boolResult.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.BOOL, variant.vt); + } + + AssertToObjectEqualExtension(expected, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORBOOLNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.BOOL + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorI4_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new int[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorI4_TestData))] + public void VARIANT_ToObject_VECTORI4_ReturnsExpected(int[] result) + { + using var variant = new VARIANT(); + fixed (int* pResult = result) + { + HRESULT hr = InitPropVariantFromInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I4, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORI4NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.I4 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorUI4_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new uint[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorUI4_TestData))] + public void VARIANT_ToObject_VECTORUI4_ReturnsExpected(uint[] result) + { + using var variant = new VARIANT(); + fixed (uint* pResult = result) + { + HRESULT hr = InitPropVariantFromUInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI4, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORUI4NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.UI4 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorINT_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new int[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorINT_TestData))] + public void VARIANT_ToObject_VECTORINT_ReturnsExpected(int[] result) + { + var variant = new VARIANT(); + try + { + fixed (int* pResult = result) + { + HRESULT hr = InitPropVariantFromInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I4, variant.vt); + } + + // I4 and INT have the same size. + variant.vt = VARENUM.VECTOR | VARENUM.INT; + AssertToObjectEqualExtension(result, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORINTNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.INT + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorUINT_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new uint[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorUINT_TestData))] + public void VARIANT_ToObject_VECTORUINT_ReturnsExpected(uint[] result) + { + var variant = new VARIANT(); + try + { + fixed (uint* pResult = result) + { + HRESULT hr = InitPropVariantFromUInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI4, variant.vt); + } + + // UI4 and UINT have the same size. + variant.vt = VARENUM.VECTOR | VARENUM.UINT; + AssertToObjectEqualExtension(result, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORUINTNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.UINT + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorI8_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new long[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorI8_TestData))] + public void VARIANT_ToObject_VECTORI8_ReturnsExpected(long[] result) + { + using var variant = new VARIANT(); + fixed (long* pResult = result) + { + HRESULT hr = InitPropVariantFromInt64Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I8, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORI8NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.I8 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorUI8_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new ulong[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorUI8_TestData))] + public void VARIANT_ToObject_VECTORUI8_ReturnsExpected(ulong[] result) + { + using var variant = new VARIANT(); + fixed (ulong* pResult = result) + { + HRESULT hr = InitPropVariantFromUInt64Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI8, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORUI8NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.UI8 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorR4_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new float[] { 1.1f, 2.2f, 3.3f } }; + } + + [StaTheory] + [MemberData(nameof(VectorR4_TestData))] + public void VARIANT_ToObject_VECTORR4_ReturnsExpected(float[] result) + { + var variant = new VARIANT(); + try + { + fixed (float* pResult = result) + { + HRESULT hr = InitPropVariantFromInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I4, variant.vt); + } + + // I4 and R4 are the same size. + variant.vt = VARENUM.VECTOR | VARENUM.R4; + AssertToObjectEqualExtension(result, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORR4NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.R4 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorR8_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new double[] { 1.1, 2.2, 3.3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorR8_TestData))] + public void VARIANT_ToObject_VECTORR8_ReturnsExpected(double[] result) + { + using var variant = new VARIANT(); + fixed (double* pResult = result) + { + HRESULT hr = InitPropVariantFromDoubleVector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.R8, variant.vt); + } + AssertToObjectEqualExtension(result, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORR8NoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.R8 + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorERROR_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new uint[] { 1, 2, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorERROR_TestData))] + public void VARIANT_ToObject_VECTORERROR_ReturnsExpected(uint[] result) + { + var variant = new VARIANT(); + try + { + fixed (uint* pResult = result) + { + HRESULT hr = InitPropVariantFromUInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI4, variant.vt); + } + + // UI4 and ERROR are the same size. + variant.vt = VARENUM.VECTOR | VARENUM.ERROR; + AssertToObjectEqualExtension(result, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORERRORNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.ERROR + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorCY_TestData() + { + yield return new object[] { Array.Empty(), Array.Empty() }; + yield return new object[] { new long[] { 11000, 22000, 30000 } , new decimal[] { 1.1m, 2.2m, 3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorCY_TestData))] + public void VARIANT_ToObject_VECTORCY_ReturnsExpected(long[] result, decimal[] expected) + { + var variant = new VARIANT(); + try + { + fixed (long* pResult = result) + { + HRESULT hr = InitPropVariantFromInt64Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I8, variant.vt); + } + + // I8 and CY have the same size. + variant.vt = VARENUM.VECTOR | VARENUM.CY; + AssertToObjectEqualExtension(expected, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORCYNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.CY + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorDATE_TestData() + { + yield return new object[] { Array.Empty(), Array.Empty() }; + + var d1 = new DateTime(2020, 05, 13, 13, 3, 12); + var d2 = new DateTime(2020, 05, 13, 13, 3, 11); + var d3 = new DateTime(2020, 3, 13, 13, 3, 12); + yield return new object[] { new double[] { d1.ToOADate(), d2.ToOADate(), d3.ToOADate() }, new DateTime[] { d1, d2, d3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorDATE_TestData))] + public void VARIANT_ToObject_VECTORDATE_ReturnsExpected(double[] result, DateTime[] expected) + { + var variant = new VARIANT(); + try + { + fixed (double* pResult = result) + { + HRESULT hr = InitPropVariantFromDoubleVector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.R8, variant.vt); + } + + // R8 and DATE have the same size. + variant.vt = VARENUM.VECTOR | VARENUM.DATE; + AssertToObjectEqualExtension(expected, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORDATENoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.DATE + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorFILETIME_TestData() + { + yield return new object[] { Array.Empty(), Array.Empty() }; + + var d1 = new DateTime(2020, 05, 13, 13, 3, 12); + var d2 = new DateTime(2020, 05, 13, 13, 3, 11); + var d3 = new DateTime(2020, 3, 13, 13, 3, 12); + yield return new object[] { new FILETIME[] { new FILETIME(d1), new FILETIME(d2), new FILETIME(d3) }, new DateTime[] { d1, d2, d3 } }; + } + + [StaTheory] + [MemberData(nameof(VectorFILETIME_TestData))] + public void VARIANT_ToObject_VECTORFILETIME_ReturnsExpected(object result, DateTime[] expected) + { + using var variant = new VARIANT(); + FILETIME[] fileTimeResult = (FILETIME[])result; + fixed (FILETIME* pResult = fileTimeResult) + { + HRESULT hr = InitPropVariantFromFileTimeVector(pResult, (uint)fileTimeResult.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.FILETIME, variant.vt); + } + + AssertToObjectEqualExtension(expected, variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORFILETIMENoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.FILETIME + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + public static IEnumerable VectorCLSID_TestData() + { + yield return new object[] { Array.Empty() }; + yield return new object[] { new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() } }; + } + + [StaTheory] + [MemberData(nameof(VectorCLSID_TestData))] + public void VARIANT_ToObject_VECTORCLSID_ReturnsExpected(Guid[] result) + { + var variant = new VARIANT(); + try + { + fixed (Guid* pResult = result) + { + HRESULT hr = InitPropVariantFromBuffer(pResult, (uint)(result.Length * sizeof(Guid)), &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI1, variant.vt); + } + + variant.data.ca.cElems = (uint)(variant.data.ca.cElems / sizeof(Guid)); + variant.vt = VARENUM.VECTOR | VARENUM.CLSID; + AssertToObjectEqualExtension(result, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORCLSIDNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.CLSID + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORBSTR_ReturnsExpected() + { + var variant = new VARIANT(); + IntPtr ptr1 = Marshal.StringToBSTR("text"); + IntPtr ptr2 = Marshal.StringToBSTR(""); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + fixed (IntPtr* pResult = result) + { + if (IntPtr.Size == 4) + { + HRESULT hr = InitPropVariantFromInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I4, variant.vt); + } + else + { + HRESULT hr = InitPropVariantFromInt64Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I8, variant.vt); + } + } + + // I4/I8 and BSTR have same size. + variant.vt = VARENUM.VECTOR | VARENUM.BSTR; + AssertToObjectEqualExtension(new string[] { null, "text", "" }, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORBSTRNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.BSTR + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORLPWSTR_ReturnsExpected() + { + var variant = new VARIANT(); + IntPtr ptr1 = Marshal.StringToCoTaskMemUni("text"); + IntPtr ptr2 = Marshal.StringToCoTaskMemUni(""); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + fixed (IntPtr* pResult = result) + { + if (IntPtr.Size == 4) + { + HRESULT hr = InitPropVariantFromInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I4, variant.vt); + } + else + { + HRESULT hr = InitPropVariantFromInt64Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I8, variant.vt); + } + } + + // I4/I8 and LPWSTR have same size. + variant.vt = VARENUM.VECTOR | VARENUM.LPWSTR; + AssertToObjectEqualExtension(new string[] { null, "text", "" }, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORLPWSTRNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.LPWSTR + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORLPSTR_ReturnsExpected() + { + var variant = new VARIANT(); + IntPtr ptr1 = Marshal.StringToCoTaskMemAnsi("text"); + IntPtr ptr2 = Marshal.StringToCoTaskMemAnsi(""); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + fixed (IntPtr* pResult = result) + { + if (IntPtr.Size == 4) + { + HRESULT hr = InitPropVariantFromInt32Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I4, variant.vt); + } + else + { + HRESULT hr = InitPropVariantFromInt64Vector(pResult, (uint)result.Length, &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.I8, variant.vt); + } + } + + // I4/I8 and LPSTR have same size. + variant.vt = VARENUM.VECTOR | VARENUM.LPSTR; + AssertToObjectEqualExtension(new string[] { null, "text", "" }, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORLPSTRNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.LPSTR + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + [StaFact] + public void VARIANT_ToObject_VECTORVARIANT_ReturnsExpected() + { + var variant = new VARIANT(); + try + { + var variant1 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + lVal = 1 + } + }; + var variant2 = new VARIANT + { + vt = VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + ulVal = 2 + } + }; + var result = new VARIANT[] { variant1, variant2 }; + fixed (VARIANT* pResult = result) + { + HRESULT hr = InitPropVariantFromBuffer(pResult, (uint)(result.Length * sizeof(VARIANT)), &variant); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.VECTOR | VARENUM.UI1, variant.vt); + } + + variant.data.ca.cElems = (uint)(variant.data.ca.cElems / sizeof(VARIANT)); + variant.vt = VARENUM.VECTOR | VARENUM.VARIANT; + AssertToObjectEqualExtension(new object[] { 1, 2u }, variant); + } + finally + { + variant.Dispose(); + } + } + + [StaFact] + public void VARIANT_ToObject_VECTORVARIANTNoData_ReturnsExpected() + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | VARENUM.VARIANT + }; + AssertToObjectEqualExtension(Array.Empty(), variant); + } + + [StaTheory] + [InlineData((ushort)VARENUM.EMPTY)] + [InlineData((ushort)VARENUM.DECIMAL)] + [InlineData((ushort)VARENUM.UNKNOWN)] + [InlineData((ushort)VARENUM.DISPATCH)] + [InlineData((ushort)VARENUM.NULL)] + [InlineData((ushort)VARENUM.CF)] + [InlineData((ushort)VARENUM.VOID)] + [InlineData((ushort)VARENUM.PTR)] + [InlineData((ushort)VARENUM.SAFEARRAY)] + [InlineData((ushort)VARENUM.CARRAY)] + [InlineData((ushort)VARENUM.RECORD)] + [InlineData((ushort)VARENUM.BLOB)] + [InlineData((ushort)VARENUM.STREAM)] + [InlineData((ushort)VARENUM.STORAGE)] + [InlineData((ushort)VARENUM.STREAMED_OBJECT)] + [InlineData((ushort)VARENUM.STORED_OBJECT)] + [InlineData((ushort)VARENUM.BLOB_OBJECT)] + [InlineData(127)] + [InlineData(0x000F)] + [InlineData(0x0020)] + [InlineData(0x0021)] + [InlineData(0x0022)] + [InlineData(0x0023)] + [InlineData(0x0024)] + public void VARIANT_ToObject_VECTORInvalidType_ThrowsArgumentException(ushort vt) + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | (VARENUM)vt + }; + Assert.Throws(null, () => variant.ToObject()); + } + + [StaTheory] + [InlineData(128)] + [InlineData(129)] + [InlineData((ushort)VARENUM.BSTR_BLOB)] + public void VARIANT_ToObject_VECTORInvalidTypeNoData_ThrowsInvalidOleVariantTypeException(ushort vt) + { + using var variant = new VARIANT + { + vt = VARENUM.VECTOR | (VARENUM)vt + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [MemberData(nameof(VectorUI1_TestData))] + public void VARIANT_ToObject_ARRAYUI1SingleDimension_ReturnsExpected(byte[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI1, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI1_TestData))] + public void VARIANT_ToObject_ARRAYUI1SingleDimensionNonZeroLowerBounds_ReturnsExpected(byte[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI1, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(byte).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionUI1_TestData() + { + yield return new object[] { new byte[0,0] }; + yield return new object[] { new byte[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI1_TestData))] + public void VARIANT_ToObject_ARRAYUI1MultiDimension_ReturnsExpected(byte[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI1, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(byte).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI1_TestData))] + public void VARIANT_ToObject_ARRAYUI1MultiDimensionNonZeroLowerBound_ReturnsExpected(byte[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI1, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(byte).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI1_TestData))] + public void VARIANT_ToObject_ARRAYI1SingleDimension_ReturnsExpected(sbyte[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I1, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI1_TestData))] + public void VARIANT_ToObject_ARRAYI1SingleDimensionNonZeroLowerBounds_ReturnsExpected(sbyte[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I1, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(sbyte).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionI1_TestData() + { + yield return new object[] { new sbyte[0,0] }; + yield return new object[] { new sbyte[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI1_TestData))] + public void VARIANT_ToObject_ARRAYI1MultiDimension_ReturnsExpected(sbyte[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I1, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(sbyte).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI1_TestData))] + public void VARIANT_ToObject_ARRAYI1MultiDimensionNonZeroLowerBound_ReturnsExpected(sbyte[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I1, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I1, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(sbyte).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI2_TestData))] + public void VARIANT_ToObject_ARRAYI2SingleDimension_ReturnsExpected(short[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I2, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI2_TestData))] + public void VARIANT_ToObject_ARRAYI2SingleDimensionNonZeroLowerBounds_ReturnsExpected(short[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I2, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(short).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionI2_TestData() + { + yield return new object[] { new short[0,0] }; + yield return new object[] { new short[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI2_TestData))] + public void VARIANT_ToObject_ARRAYI2MultiDimension_ReturnsExpected(short[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I2, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(short).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI2_TestData))] + public void VARIANT_ToObject_ARRAYI2MultiDimensionNonZeroLowerBound_ReturnsExpected(short[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I2, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(short).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI2_TestData))] + public void VARIANT_ToObject_ARRAYUI2SingleDimension_ReturnsExpected(ushort[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI2, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI2_TestData))] + public void VARIANT_ToObject_ARRAYUI2SingleDimensionNonZeroLowerBounds_ReturnsExpected(ushort[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI2, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(ushort).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionUI2_TestData() + { + yield return new object[] { new ushort[0,0] }; + yield return new object[] { new ushort[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI2_TestData))] + public void VARIANT_ToObject_ARRAYUI2MultiDimension_ReturnsExpected(ushort[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI2, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(ushort).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI2_TestData))] + public void VARIANT_ToObject_ARRAYUI2MultiDimensionNonZeroLowerBound_ReturnsExpected(ushort[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI2, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI2, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(ushort).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI4_TestData))] + public void VARIANT_ToObject_ARRAYI4SingleDimension_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI4_TestData))] + public void VARIANT_ToObject_ARRAYI4SingleDimensionNonZeroLowerBounds_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionI4_TestData() + { + yield return new object[] { new int[0,0] }; + yield return new object[] { new int[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI4_TestData))] + public void VARIANT_ToObject_ARRAYI4MultiDimension_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI4_TestData))] + public void VARIANT_ToObject_ARRAYI4MultiDimensionNonZeroLowerBound_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI4_TestData))] + public void VARIANT_ToObject_INTArrayI4SingleDimension_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI4_TestData))] + public void VARIANT_ToObject_INTArrayI4SingleDimensionNonZeroLowerBounds_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI4_TestData))] + public void VARIANT_ToObject_INTArrayI4MultiDimension_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI4_TestData))] + public void VARIANT_ToObject_INTArrayI4MultiDimensionNonZeroLowerBound_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI4_TestData))] + public void VARIANT_ToObject_ARRAYUI4SingleDimension_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI4_TestData))] + public void VARIANT_ToObject_ARRAYUI4SingleDimensionNonZeroLowerBounds_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionUI4_TestData() + { + yield return new object[] { new uint[0,0] }; + yield return new object[] { new uint[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI4_TestData))] + public void VARIANT_ToObject_ARRAYUI4MultiDimension_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI4_TestData))] + public void VARIANT_ToObject_ARRAYUI4MultiDimensionNonZeroLowerBound_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI4_TestData))] + public void VARIANT_ToObject_UINTArrayUI4SingleDimension_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI4_TestData))] + public void VARIANT_ToObject_UINTArrayUI4SingleDimensionNonZeroLowerBounds_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI4_TestData))] + public void VARIANT_ToObject_UINTArrayUI4MultiDimension_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI4_TestData))] + public void VARIANT_ToObject_UINTArrayUI4MultiDimensionNonZeroLowerBound_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorINT_TestData))] + public void VARIANT_ToObject_ARRAYINTSingleDimension_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorINT_TestData))] + public void VARIANT_ToObject_ARRAYINTSingleDimensionNonZeroLowerBounds_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionINT_TestData() + { + yield return new object[] { new int[0,0] }; + yield return new object[] { new int[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionINT_TestData))] + public void VARIANT_ToObject_ARRAYINTMultiDimension_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionINT_TestData))] + public void VARIANT_ToObject_ARRAYINTMultiDimensionNonZeroLowerBound_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.INT, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorINT_TestData))] + public void VARIANT_ToObject_I4ArrayINTSingleDimension_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorINT_TestData))] + public void VARIANT_ToObject_I4ArrayINTSingleDimensionNonZeroLowerBounds_ReturnsExpected(int[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionINT_TestData))] + public void VARIANT_ToObject_I4ArrayINTMultiDimension_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionINT_TestData))] + public void VARIANT_ToObject_I4ArrayINTMultiDimensionNonZeroLowerBound_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I4, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.INT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(int).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUINT_TestData))] + public void VARIANT_ToObject_ARRAYUINTSingleDimension_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUINT_TestData))] + public void VARIANT_ToObject_ARRAYUINTSingleDimensionNonZeroLowerBounds_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionUINT_TestData() + { + yield return new object[] { new uint[0,0] }; + yield return new object[] { new uint[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUINT_TestData))] + public void VARIANT_ToObject_ARRAYUINTMultiDimension_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUINT_TestData))] + public void VARIANT_ToObject_ARRAYUINTMultiDimensionNonZeroLowerBound_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UINT, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUINT_TestData))] + public void VARIANT_ToObject_UI4ArrayUINTSingleDimension_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUINT_TestData))] + public void VARIANT_ToObject_UI4ArrayUINTSingleDimensionNonZeroLowerBounds_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUINT_TestData))] + public void VARIANT_ToObject_UI4ArrayUINTMultiDimension_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUINT_TestData))] + public void VARIANT_ToObject_UI4ArrayUINTMultiDimensionNonZeroLowerBound_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI4, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UINT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI8_TestData))] + public void VARIANT_ToObject_ARRAYI8SingleDimension_ReturnsExpected(long[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I8, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorI8_TestData))] + public void VARIANT_ToObject_ARRAYI8SingleDimensionNonZeroLowerBounds_ReturnsExpected(long[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I8, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(long).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionI8_TestData() + { + yield return new object[] { new long[0,0] }; + yield return new object[] { new long[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI8_TestData))] + public void VARIANT_ToObject_ARRAYI8MultiDimension_ReturnsExpected(long[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I8, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(long).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionI8_TestData))] + public void VARIANT_ToObject_ARRAYI8MultiDimensionNonZeroLowerBound_ReturnsExpected(long[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.I8, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(long).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI8_TestData))] + public void VARIANT_ToObject_ARRAYUI8SingleDimension_ReturnsExpected(ulong[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI8, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorUI8_TestData))] + public void VARIANT_ToObject_ARRAYUI8SingleDimensionNonZeroLowerBounds_ReturnsExpected(ulong[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI8, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(ulong).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionUI8_TestData() + { + yield return new object[] { new ulong[0,0] }; + yield return new object[] { new ulong[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI8_TestData))] + public void VARIANT_ToObject_ARRAYUI8MultiDimension_ReturnsExpected(ulong[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI8, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(ulong).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionUI8_TestData))] + public void VARIANT_ToObject_ARRAYUI8MultiDimensionNonZeroLowerBound_ReturnsExpected(ulong[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.UI8, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UI8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(ulong).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorR4_TestData))] + public void VARIANT_ToObject_ARRAYR4SingleDimension_ReturnsExpected(float[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorR4_TestData))] + public void VARIANT_ToObject_ARRAYR4SingleDimensionNonZeroLowerBounds_ReturnsExpected(float[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R4, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(float).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionR4_TestData() + { + yield return new object[] { new float[0,0] }; + yield return new object[] { new float[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionR4_TestData))] + public void VARIANT_ToObject_ARRAYR4MultiDimension_ReturnsExpected(float[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R4, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(float).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionR4_TestData))] + public void VARIANT_ToObject_ARRAYR4MultiDimensionNonZeroLowerBound_ReturnsExpected(float[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R4, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(float).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorR8_TestData))] + public void VARIANT_ToObject_ARRAYR8SingleDimension_ReturnsExpected(double[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R8, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorR8_TestData))] + public void VARIANT_ToObject_ARRAYR8SingleDimensionNonZeroLowerBounds_ReturnsExpected(double[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R8, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(double).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionR8_TestData() + { + yield return new object[] { new double[0,0] }; + yield return new object[] { new double[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionR8_TestData))] + public void VARIANT_ToObject_ARRAYR8MultiDimension_ReturnsExpected(double[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R8, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(double).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionR8_TestData))] + public void VARIANT_ToObject_ARRAYR8MultiDimensionNonZeroLowerBound_ReturnsExpected(double[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.R8, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.R8, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(double).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorERROR_TestData))] + public void VARIANT_ToObject_ARRAYERRORSingleDimension_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.ERROR, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.ERROR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorERROR_TestData))] + public void VARIANT_ToObject_ARRAYERRORSingleDimensionNonZeroLowerBounds_ReturnsExpected(uint[] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.ERROR, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.ERROR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable MultiDimensionERROR_TestData() + { + yield return new object[] { new uint[0,0] }; + yield return new object[] { new uint[2,3] { { 1, 2, 3 }, { 4, 5, 6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionERROR_TestData))] + public void VARIANT_ToObject_ARRAYERRORMultiDimension_ReturnsExpected(uint[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.ERROR, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.ERROR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionERROR_TestData))] + public void VARIANT_ToObject_ARRAYERRORMultiDimensionNonZeroLowerBound_ReturnsExpected(int[,] result) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.ERROR, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.ERROR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(uint).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(result, array); + }); + } + + public static IEnumerable ArrayBOOL_TestData() + { + yield return new object[] { Array.Empty(), Array.Empty() }; + yield return new object[] { new VARIANT_BOOL[] { VARIANT_BOOL.TRUE, VARIANT_BOOL.FALSE, VARIANT_BOOL.TRUE }, new bool[] { true, false, true } }; + } + + [StaTheory] + [MemberData(nameof(ArrayBOOL_TestData))] + public void VARIANT_ToObject_ARRAYBOOLSingleDimension_ReturnsExpected(object result, bool[] expected) + { + VARIANT_BOOL[] boolResult = (VARIANT_BOOL[])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BOOL, boolResult); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BOOL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(ArrayBOOL_TestData))] + public void VARIANT_ToObject_ARRAYBOOLSingleDimensionNonZeroLowerBounds_ReturnsExpected(object result, bool[] expected) + { + VARIANT_BOOL[] boolResult = (VARIANT_BOOL[])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BOOL, boolResult, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BOOL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(bool).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + public static IEnumerable MultiDimensionBOOL_TestData() + { + yield return new object[] { new VARIANT_BOOL[0,0], new bool[0,0] }; + yield return new object[] { new VARIANT_BOOL[2, 3] { { VARIANT_BOOL.TRUE, VARIANT_BOOL.FALSE, VARIANT_BOOL.TRUE }, { VARIANT_BOOL.FALSE, VARIANT_BOOL.TRUE, VARIANT_BOOL.FALSE } }, new bool[2,3] { { true, false, true }, { false, true, false } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionBOOL_TestData))] + public void VARIANT_ToObject_ARRAYBOOLMultiDimension_ReturnsExpected(object result, bool[,] expected) + { + VARIANT_BOOL[,] boolResult = (VARIANT_BOOL[,])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BOOL, boolResult); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BOOL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(bool).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionBOOL_TestData))] + public void VARIANT_ToObject_ARRAYBOOLMultiDimensionNonZeroLowerBound_ReturnsExpected(object result, bool[,] expected) + { + VARIANT_BOOL[,] boolResult = (VARIANT_BOOL[,])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BOOL, boolResult, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BOOL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(bool).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + public static IEnumerable ArrayDECIMAL_TestData() + { + yield return new object[] { Array.Empty(), Array.Empty() }; + + VarDecFromR8(1.1, out DECIMAL d1); + VarDecFromR8(2.2, out DECIMAL d2); + VarDecFromR8(3.3, out DECIMAL d3); + yield return new object[] { new DECIMAL[] { d1, d2, d3 }, new decimal[] { 1.1m, 2.2m, 3.3m } }; + } + + [StaTheory] + [MemberData(nameof(ArrayDECIMAL_TestData))] + public void VARIANT_ToObject_ARRAYDECIMALSingleDimension_ReturnsExpected(object result, decimal[] expected) + { + DECIMAL[] decimalResult = (DECIMAL[])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DECIMAL, decimalResult); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DECIMAL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(ArrayDECIMAL_TestData))] + public void VARIANT_ToObject_ARRAYDECIMALSingleDimensionNonZeroLowerBounds_ReturnsExpected(object result, decimal[] expected) + { + DECIMAL[] decimalResult = (DECIMAL[])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DECIMAL, decimalResult, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DECIMAL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(decimal).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + public static IEnumerable MultiDimensionDECIMAL_TestData() + { + yield return new object[] { new DECIMAL[0,0], new decimal[0,0] }; + VarDecFromR8(1.1, out DECIMAL d1); + VarDecFromR8(2.2, out DECIMAL d2); + VarDecFromR8(3.3, out DECIMAL d3); + VarDecFromR8(3.1, out DECIMAL d4); + VarDecFromR8(2.2, out DECIMAL d5); + VarDecFromR8(1.3, out DECIMAL d6); + yield return new object[] { new DECIMAL[2, 3] { { d1, d2, d3 }, { d4, d5, d6 } }, new decimal[2,3] { { 1.1m, 2.2m, 3.3m }, { 3.1m, 2.2m, 1.3m } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionDECIMAL_TestData))] + public void VARIANT_ToObject_ARRAYDECIMALMultiDimension_ReturnsExpected(object result, decimal[,] expected) + { + DECIMAL[,] decimalResult = (DECIMAL[,])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DECIMAL, decimalResult); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DECIMAL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(decimal).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionDECIMAL_TestData))] + public void VARIANT_ToObject_ARRAYDECIMALMultiDimensionNonZeroLowerBound_ReturnsExpected(object result, decimal[,] expected) + { + DECIMAL[,] decimalResult = (DECIMAL[,])result; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DECIMAL, decimalResult, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DECIMAL, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(decimal).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorCY_TestData))] + public void VARIANT_ToObject_ARRAYCYSingleDimension_ReturnsExpected(long[] result, decimal[] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.CY, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.CY, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorCY_TestData))] + public void VARIANT_ToObject_ARRAYCYSingleDimensionNonZeroLowerBounds_ReturnsExpected(long[] result, decimal[] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.CY, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.CY, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(decimal).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + public static IEnumerable MultiDimensionCY_TestData() + { + yield return new object[] { new long[0,0], new decimal[0,0] }; + yield return new object[] { new long[2, 3] { { 11000, 22000, 33000 }, { 31000, 22000, 13000 } }, new decimal[2,3] { { 1.1m, 2.2m, 3.3m }, { 3.1m, 2.2m, 1.3m } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionCY_TestData))] + public void VARIANT_ToObject_ARRAYCYMultiDimension_ReturnsExpected(long[,] result, decimal[,] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.CY, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.CY, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(decimal).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionCY_TestData))] + public void VARIANT_ToObject_ARRAYCYMultiDimensionNonZeroLowerBound_ReturnsExpected(long[,] result, decimal[,] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.CY, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.CY, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(decimal).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorDATE_TestData))] + public void VARIANT_ToObject_ARRAYDATESingleDimension_ReturnsExpected(double[] result, DateTime[] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.DATE, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DATE, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(VectorDATE_TestData))] + public void VARIANT_ToObject_ARRAYDATESingleDimensionNonZeroLowerBounds_ReturnsExpected(double[] result, DateTime[] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.DATE, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DATE, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(DateTime).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(expected.Length, array.GetLength(0)); + Assert.Equal(expected, array); + }); + } + + public static IEnumerable MultiDimensionDATE_TestData() + { + yield return new object[] { new double[0,0], new DateTime[0,0] }; + + var d1 = new DateTime(2020, 05, 13, 13, 3, 12); + var d2 = new DateTime(2020, 05, 13, 13, 3, 11); + var d3 = new DateTime(2020, 3, 13, 13, 3, 12); + var d4 = new DateTime(1892, 1, 2, 3, 4, 5, 6); + var d5 = new DateTime(2010, 2, 3, 4, 5, 6); + var d6 = new DateTime(8000, 10, 11, 12, 13, 14); + yield return new object[] { new double[2, 3] { { d1.ToOADate(), d2.ToOADate(), d3.ToOADate() }, { d4.ToOADate(), d5.ToOADate(), d6.ToOADate() } }, new DateTime[2,3] { { d1, d2, d3 }, { d4, d5, d6 } } }; + } + + [StaTheory] + [MemberData(nameof(MultiDimensionDATE_TestData))] + public void VARIANT_ToObject_ARRAYDATEMultiDimension_ReturnsExpected(double[,] result, DateTime[,] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.DATE, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DATE, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(DateTime).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaTheory] + [MemberData(nameof(MultiDimensionDATE_TestData))] + public void VARIANT_ToObject_ARRAYDATEMultiDimensionNonZeroLowerBound_ReturnsExpected(double[,] result, DateTime[,] expected) + { + SAFEARRAY *psa = CreateSafeArray(VARENUM.DATE, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DATE, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(DateTime).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(expected.GetLength(0), array.GetLength(0)); + Assert.Equal(expected.GetLength(1), array.GetLength(1)); + Assert.Equal(expected, array); + }); + } + + [StaFact] + public void VARIANT_ToObject_ARRAYBSTRSingleDimension_ReturnsExpected() + { + IntPtr ptr1 = Marshal.StringToBSTR("text"); + IntPtr ptr2 = Marshal.StringToBSTR(""); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BSTR, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new string[] { null, "text", "" }, array); + }); + } + finally + { + Marshal.FreeBSTR(ptr1); + Marshal.FreeBSTR(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYBSTRSingleDimensionNonZeroLowerBound_ReturnsExpected() + { + IntPtr ptr1 = Marshal.StringToBSTR("text"); + IntPtr ptr2 = Marshal.StringToBSTR(""); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BSTR, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(string).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new string[] { null, "text", "" }, array); + }); + } + finally + { + Marshal.FreeBSTR(ptr1); + Marshal.FreeBSTR(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYBSTRMultiDimension_ReturnsExpected() + { + IntPtr ptr1 = Marshal.StringToBSTR("text"); + IntPtr ptr2 = Marshal.StringToBSTR(""); + IntPtr ptr3 = Marshal.StringToBSTR("text3"); + IntPtr ptr4 = Marshal.StringToBSTR("text4"); + IntPtr ptr5 = Marshal.StringToBSTR("text5"); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BSTR, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(string).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new string[,] { { null, "text", "" }, { "text3", "text4", "text5" } }, array); + }); + } + finally + { + Marshal.FreeBSTR(ptr1); + Marshal.FreeBSTR(ptr2); + Marshal.FreeBSTR(ptr3); + Marshal.FreeBSTR(ptr4); + Marshal.FreeBSTR(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYBSTRMultiDimensionNonZeroLowerBound_ReturnsExpected() + { + IntPtr ptr1 = Marshal.StringToBSTR("text"); + IntPtr ptr2 = Marshal.StringToBSTR(""); + IntPtr ptr3 = Marshal.StringToBSTR("text3"); + IntPtr ptr4 = Marshal.StringToBSTR("text4"); + IntPtr ptr5 = Marshal.StringToBSTR("text5"); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.BSTR, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.BSTR, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(string).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new string[,] { { null, "text", "" }, { "text3", "text4", "text5" } }, array); + }); + } + finally + { + Marshal.FreeBSTR(ptr1); + Marshal.FreeBSTR(ptr2); + Marshal.FreeBSTR(ptr3); + Marshal.FreeBSTR(ptr4); + Marshal.FreeBSTR(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYUNKNOWNSingleDimension_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.UNKNOWN, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { null, o1, o2 }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYUNKNOWNSingleDimensionNonZeroLowerBound_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.UNKNOWN, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { null, o1, o2 }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYUNKNOWNMultiDimension_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + var o3 = new object(); + var o4 = new object(); + var o5 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + IntPtr ptr3 = Marshal.GetIUnknownForObject(o3); + IntPtr ptr4 = Marshal.GetIUnknownForObject(o4); + IntPtr ptr5 = Marshal.GetIUnknownForObject(o5); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.UNKNOWN, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { null, o1, o2 }, { o3, o4, o5 } }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + Marshal.Release(ptr3); + Marshal.Release(ptr4); + Marshal.Release(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYUNKNOWNMultiDimensionNonZeroLowerBound_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + var o3 = new object(); + var o4 = new object(); + var o5 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + IntPtr ptr3 = Marshal.GetIUnknownForObject(o3); + IntPtr ptr4 = Marshal.GetIUnknownForObject(o4); + IntPtr ptr5 = Marshal.GetIUnknownForObject(o5); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.UNKNOWN, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { null, o1, o2 }, { o3, o4, o5 } }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + Marshal.Release(ptr3); + Marshal.Release(ptr4); + Marshal.Release(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_DISPATCHArrayUNKNOWNSingleDimension_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { null, o1, o2 }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_DISPATCHArrayUNKNOWNSingleDimensionNonZeroLowerBound_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { null, o1, o2 }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_DISPATCHArrayUNKNOWNMultiDimension_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + var o3 = new object(); + var o4 = new object(); + var o5 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + IntPtr ptr3 = Marshal.GetIUnknownForObject(o3); + IntPtr ptr4 = Marshal.GetIUnknownForObject(o4); + IntPtr ptr5 = Marshal.GetIUnknownForObject(o5); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { null, o1, o2 }, { o3, o4, o5 } }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + Marshal.Release(ptr3); + Marshal.Release(ptr4); + Marshal.Release(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_DISPATCHArrayUNKNOWNMultiDimensionNonZeroLowerBound_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + var o3 = new object(); + var o4 = new object(); + var o5 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + IntPtr ptr3 = Marshal.GetIUnknownForObject(o3); + IntPtr ptr4 = Marshal.GetIUnknownForObject(o4); + IntPtr ptr5 = Marshal.GetIUnknownForObject(o5); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.UNKNOWN, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { null, o1, o2 }, { o3, o4, o5 } }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + Marshal.Release(ptr3); + Marshal.Release(ptr4); + Marshal.Release(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYDISPATCHSingleDimension_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DISPATCH, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { null, o1, o2 }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYDISPATCHSingleDimensionNonZeroLowerBound_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + try + { + var result = new IntPtr[] { IntPtr.Zero, ptr1, ptr2 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DISPATCH, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { null, o1, o2 }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYDISPATCHMultiDimension_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + var o3 = new object(); + var o4 = new object(); + var o5 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + IntPtr ptr3 = Marshal.GetIUnknownForObject(o3); + IntPtr ptr4 = Marshal.GetIUnknownForObject(o4); + IntPtr ptr5 = Marshal.GetIUnknownForObject(o5); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DISPATCH, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { null, o1, o2 }, { o3, o4, o5 } }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + Marshal.Release(ptr3); + Marshal.Release(ptr4); + Marshal.Release(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYDISPATCHMultiDimensionNonZeroLowerBound_ReturnsExpected() + { + var o1 = new object(); + var o2 = new object(); + var o3 = new object(); + var o4 = new object(); + var o5 = new object(); + IntPtr ptr1 = Marshal.GetIUnknownForObject(o1); + IntPtr ptr2 = Marshal.GetIUnknownForObject(o2); + IntPtr ptr3 = Marshal.GetIUnknownForObject(o3); + IntPtr ptr4 = Marshal.GetIUnknownForObject(o4); + IntPtr ptr5 = Marshal.GetIUnknownForObject(o5); + try + { + var result = new IntPtr[2,3] { { IntPtr.Zero, ptr1, ptr2 }, { ptr3, ptr4, ptr5 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.DISPATCH, result, 1, 2); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.DISPATCH, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(2, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { null, o1, o2 }, { o3, o4, o5 } }, array); + }); + } + finally + { + Marshal.Release(ptr1); + Marshal.Release(ptr2); + Marshal.Release(ptr3); + Marshal.Release(ptr4); + Marshal.Release(ptr5); + } + } + + [StaFact] + public void VARIANT_ToObject_ARRAYVARIANTSingleDimension_ReturnsExpected() + { + using var v1 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 1 + } + }; + using var v2 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 2 + } + }; + using var v3 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 3 + } + }; + var result = new VARIANT[] { v1, v2, v3 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.VARIANT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.VARIANT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(array); + Assert.Equal(1, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { 1, 2, 3 }, array); + }); + } + + [StaFact] + public void VARIANT_ToObject_ARRAYVARIANTSingleDimensionNonZeroLowerBound_ReturnsExpected() + { + using var v1 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 1 + } + }; + using var v2 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 2 + } + }; + using var v3 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 3 + } + }; + var result = new VARIANT[] { v1, v2, v3 }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.VARIANT, result, 1); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.VARIANT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(1), array); + Assert.Equal(1, array.Rank); + Assert.Equal(1, array.GetLowerBound(0)); + Assert.Equal(result.Length, array.GetLength(0)); + Assert.Equal(new object[] { 1, 2, 3 }, array); + }); + } + + [StaFact] + public void VARIANT_ToObject_ARRAYVARIANTMultiDimension_ReturnsExpected() + { + using var v1 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 1 + } + }; + using var v2 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 2 + } + }; + using var v3 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 3 + } + }; + using var v4 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 4 + } + }; + using var v5 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 5 + } + }; + using var v6 = new VARIANT + { + vt = VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + llVal = 6 + } + }; + + var result = new VARIANT[2,3] { { v1, v2, v3 }, { v4, v5, v6 } }; + SAFEARRAY *psa = CreateSafeArray(VARENUM.VARIANT, result); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.VARIANT, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObject(variant, value => + { + Array array = (Array)value; + Assert.IsType(typeof(object).MakeArrayType(2), array); + Assert.Equal(2, array.Rank); + Assert.Equal(0, array.GetLowerBound(0)); + Assert.Equal(0, array.GetLowerBound(1)); + Assert.Equal(result.GetLength(0), array.GetLength(0)); + Assert.Equal(result.GetLength(1), array.GetLength(1)); + Assert.Equal(new object[,] { { 1, 2, 3 }, { 4, 5, 6 } }, array); + }); + } + + [StaTheory] + [InlineData((ushort)VARENUM.I1)] + [InlineData((ushort)VARENUM.UI1)] + [InlineData((ushort)VARENUM.I2)] + [InlineData((ushort)VARENUM.UI2)] + [InlineData((ushort)VARENUM.I4)] + [InlineData((ushort)VARENUM.UI4)] + [InlineData((ushort)VARENUM.I8)] + [InlineData((ushort)VARENUM.UI8)] + [InlineData((ushort)VARENUM.BSTR)] + [InlineData((ushort)VARENUM.LPWSTR)] + [InlineData((ushort)VARENUM.LPSTR)] + [InlineData((ushort)VARENUM.UNKNOWN)] + [InlineData((ushort)VARENUM.DISPATCH)] + [InlineData((ushort)VARENUM.EMPTY)] + [InlineData((ushort)VARENUM.NULL)] + [InlineData((ushort)VARENUM.CF)] + [InlineData((ushort)VARENUM.VOID)] + [InlineData((ushort)VARENUM.PTR)] + [InlineData((ushort)VARENUM.SAFEARRAY)] + [InlineData((ushort)VARENUM.CARRAY)] + [InlineData((ushort)VARENUM.RECORD)] + [InlineData((ushort)VARENUM.BLOB)] + [InlineData((ushort)VARENUM.STREAM)] + [InlineData((ushort)VARENUM.STORAGE)] + [InlineData((ushort)VARENUM.STREAMED_OBJECT)] + [InlineData((ushort)VARENUM.STORED_OBJECT)] + [InlineData((ushort)VARENUM.BLOB_OBJECT)] + public void VARIANT_ToObject_ARRAYNoData_ReturnsExpected(ushort vt) + { + SAFEARRAY* psa = CreateSafeArray(VARENUM.I1, Array.Empty()); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | (VARENUM)vt + }; + AssertToObjectEqual(null, variant); + } + + [StaTheory] + [InlineData(128)] + [InlineData(129)] + [InlineData((ushort)VARENUM.BSTR_BLOB)] + public void VARIANT_ToObject_ARRAYInvalidTypeNoData_ThrowsInvalidOleVariantTypeException(ushort vt) + { + SAFEARRAY* psa = CreateSafeArray(VARENUM.I1, Array.Empty()); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | (VARENUM)vt + }; + AssertToObjectThrows(variant); + } + + [StaFact] + public void VARIANT_ToObject_ARRAYVECTOR_ThrowsInvalidOleVariantTypeException() + { + SAFEARRAY* psa = CreateSafeArray(VARENUM.I1, Array.Empty()); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.VECTOR | VARENUM.I4 + }; + Assert.Throws(null, () => variant.ToObject()); + } + + [StaFact] + public void VARIANT_ToObject_ARRAYTypeEMPTY_ThrowsInvalidOleVariantTypeException() + { + SAFEARRAY* psa = CreateSafeArray(VARENUM.I1, Array.Empty()); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.EMPTY, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [InlineData((ushort)VARENUM.I1, (ushort)VARENUM.UI1)] + [InlineData((ushort)VARENUM.UI1, (ushort)VARENUM.I1)] + [InlineData((ushort)VARENUM.I2, (ushort)VARENUM.UI2)] + [InlineData((ushort)VARENUM.UI2, (ushort)VARENUM.I2)] + [InlineData((ushort)VARENUM.I4, (ushort)VARENUM.UI4)] + [InlineData((ushort)VARENUM.UI4, (ushort)VARENUM.I4)] + [InlineData((ushort)VARENUM.INT, (ushort)VARENUM.UINT)] + [InlineData((ushort)VARENUM.INT, (ushort)VARENUM.I2)] + [InlineData((ushort)VARENUM.INT, (ushort)VARENUM.I8)] + [InlineData((ushort)VARENUM.UINT, (ushort)VARENUM.INT)] + [InlineData((ushort)VARENUM.UINT, (ushort)VARENUM.UI2)] + [InlineData((ushort)VARENUM.UINT, (ushort)VARENUM.UI8)] + [InlineData((ushort)VARENUM.I8, (ushort)VARENUM.UI8)] + [InlineData((ushort)VARENUM.UI8, (ushort)VARENUM.I8)] + [InlineData((ushort)VARENUM.UNKNOWN, (ushort)VARENUM.DISPATCH)] + [InlineData((ushort)VARENUM.UNKNOWN, (ushort)VARENUM.I4)] + [InlineData((ushort)VARENUM.UNKNOWN, (ushort)VARENUM.UI4)] + [InlineData((ushort)VARENUM.UNKNOWN, (ushort)VARENUM.I8)] + [InlineData((ushort)VARENUM.UNKNOWN, (ushort)VARENUM.UI8)] + [InlineData((ushort)VARENUM.DISPATCH, (ushort)VARENUM.I4)] + [InlineData((ushort)VARENUM.DISPATCH, (ushort)VARENUM.UI4)] + [InlineData((ushort)VARENUM.DISPATCH, (ushort)VARENUM.I8)] + [InlineData((ushort)VARENUM.DISPATCH, (ushort)VARENUM.UI8)] + public void VARIANT_ToObject_ARRAYTypeDifferent_ThrowsSafeArrayTypeMismatchException(ushort arrayVt, ushort vt) + { + SAFEARRAY* psa = CreateSafeArray((VARENUM)arrayVt, Array.Empty()); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | (VARENUM)vt, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [InlineData((ushort)VARENUM.INT_PTR)] + [InlineData((ushort)VARENUM.UINT_PTR)] + public void VARIANT_ToObject_ARRAYTypeInvalid_ThrowsArgumentException(ushort vt) + { + SAFEARRAY* psa = CreateSafeArray((VARENUM)vt, Array.Empty()); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | (VARENUM)vt, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [InlineData(33)] + [InlineData(255)] + [InlineData(256)] + [InlineData(512)] + public void VARIANT_ARRAYBigRank_ThrowsTypeLoadException(int rank) + { + SAFEARRAYBOUND* saBounds = stackalloc SAFEARRAYBOUND[rank]; + for (uint i = 0; i < rank; i++) + { + saBounds[i] = new SAFEARRAYBOUND + { + cElements = 0, + lLbound = 0 + }; + } + + SAFEARRAY *psa = SafeArrayCreate(VARENUM.I4, (uint)rank, saBounds); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.I4, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + + private unsafe static SAFEARRAY* CreateSafeArray(VARENUM vt, T[] result, int lbound = 0) where T : unmanaged + { + var saBound = new SAFEARRAYBOUND + { + cElements = (uint)result.Length, + lLbound = lbound + }; + SAFEARRAY *psa = SafeArrayCreate(vt, 1, &saBound); + Assert.True(psa != null); + + VARENUM arrayVt = VARENUM.EMPTY; + HRESULT hr = SafeArrayGetVartype(psa, &arrayVt); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(vt, arrayVt); + + for (int i = 0; i < result.Length; i++) + { + T value = result[i]; + int index = i + lbound; + // Insert pointers directly. + if (value is IntPtr valuePtr) + { + hr = SafeArrayPutElement(psa, &index, (void*)valuePtr); + } + else + { + hr = SafeArrayPutElement(psa, &index, &value); + } + Assert.Equal(HRESULT.S_OK, hr); + } + + return psa; + } + + private unsafe SAFEARRAY* CreateSafeArray(VARENUM vt, T[,] multiDimArray, int lbound1 = 0, int lbound2 = 0) where T : unmanaged + { + SAFEARRAYBOUND* saBounds = stackalloc SAFEARRAYBOUND[2]; + saBounds[0] = new SAFEARRAYBOUND + { + cElements = (uint)multiDimArray.GetLength(0), + lLbound = lbound1 + }; + saBounds[1] = new SAFEARRAYBOUND + { + cElements = (uint)multiDimArray.GetLength(1), + lLbound = lbound2 + }; + SAFEARRAY *psa = SafeArrayCreate(vt, 2, saBounds); + Assert.True(psa != null); + + VARENUM arrayVt = VARENUM.EMPTY; + HRESULT hr = SafeArrayGetVartype(psa, &arrayVt); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(vt, arrayVt); + + for (int i = 0; i < multiDimArray.GetLength(0); i++) + { + for (int j = 0; j < multiDimArray.GetLength(1); j++) + { + int* indices = stackalloc int[] { i + lbound1, j + lbound2 }; + T value = multiDimArray[i, j]; + // Insert pointers directly. + if (value is IntPtr valuePtr) + { + hr = SafeArrayPutElement(psa, indices, (void*)valuePtr); + } + else + { + hr = SafeArrayPutElement(psa, indices, &value); + } + Assert.Equal(HRESULT.S_OK, hr); + } + } + + return psa; + } + + [StaFact] + public void ToObject_RECORDRecordData_ReturnsExpected() + { + int record = 1; + IntPtr mem = Marshal.AllocCoTaskMem(sizeof(int)); + (*(int*)mem) = record; + var recordInfo = new CustomRecordInfo + { + GetGuidAction = () => (typeof(int).GUID, HRESULT.S_OK) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); + + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = pRecordInfo, + pvRecord = mem.ToPointer() + } + } + }; + // Records actually don't work in .NET Core... +#if false + AssertToObjectEqual(1, variant); +#else + AssertToObjectThrows(variant); +#endif + } + + [StaFact] + public void ToObject_RECORDNullRecordData_ReturnsNull() + { + var recordInfo = new CustomRecordInfo(); + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = pRecordInfo + } + } + }; + AssertToObjectEqual(null, variant); + } + + [StaFact] + public void ToObject_RECORDNullRecordInfo_ThrowsArgumentException() + { + int record = 1; + IntPtr mem = Marshal.AllocCoTaskMem(sizeof(int)); + (*(int*)mem) = record; + + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = IntPtr.Zero, + pvRecord = mem.ToPointer() + } + } + }; + AssertToObjectThrows(variant); + } + + [StaFact] + public void ToObject_RECORDInvalidGetGuidHRData_ThrowsArgumentException() + { + int record = 1; + IntPtr mem = Marshal.AllocCoTaskMem(sizeof(int)); + (*(int*)mem) = record; + + var recordInfo = new CustomRecordInfo + { + GetGuidAction = () => (Guid.Empty, HRESULT.DISP_E_DIVBYZERO) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = pRecordInfo, + pvRecord = mem.ToPointer() + } + } + }; + // Records actually don't work in .NET Core... +#if false + AssertToObjectThrows(variant); +#endif + } + + [StaFact] + public void ToObject_RECORDInvalidGetGuidHRNoData_ReturnsNull() + { + var record = new CustomRecordInfo + { + GetGuidAction = () => (Guid.Empty, HRESULT.DISP_E_DIVBYZERO) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(record); + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = pRecordInfo, + } + } + }; + AssertToObjectEqual(null, variant); + } + + public static IEnumerable RECORD_TestData() + { + yield return new object[] { Guid.Empty }; + yield return new object[] { new Guid("8856f961-340a-11d0-a96b-00c04fd705a2") }; + } + + [StaTheory] + [MemberData(nameof(RECORD_TestData))] + public void ToObject_RECORDInvalidGuidData_ThrowsArgumentException(Guid guid) + { + int record = 1; + IntPtr mem = Marshal.AllocCoTaskMem(sizeof(int)); + (*(int*)mem) = record; + + var recordInfo = new CustomRecordInfo + { + GetGuidAction = () => (guid, HRESULT.S_OK) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = pRecordInfo, + pvRecord = mem.ToPointer() + } + } + }; + AssertToObjectThrows(variant); + } + + [StaTheory] + [MemberData(nameof(RECORD_TestData))] + public void ToObject_RECORDInvalidGuidNoData_ReturnsNull(Guid guid) + { + var record = new CustomRecordInfo + { + GetGuidAction = () => (guid, HRESULT.S_OK) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(record); + try + { + using var variant = new VARIANT + { + vt = VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + recordVal = new VARIANT.VARIANTRecord + { + pRecInfo = pRecordInfo, + } + } + }; + AssertToObjectEqual(null, variant); + } + finally + { + Marshal.Release(pRecordInfo); + } + } + + [StaFact] + public void ToObject_RECORDARRAYValid_ReturnsExpected() + { + var result = new int[] { 1, 2 }; + var recordInfo = new CustomRecordInfo + { + GetGuidAction = () => (typeof(int).GUID, HRESULT.S_OK) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); + try + { + SAFEARRAY *psa = CreateRecordSafeArray(result, pRecordInfo); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + // Records actually don't work in .NET Core... +#if false + AssertToObjectEqual(new int[] { 0, 0 }, variant); +#endif + } + finally + { + Marshal.Release(pRecordInfo); + } + } + + [StaFact] + public void ToObject_RECORDARRAYInvalidFFeatures_ThrowsArgumentException() + { + var result = new int[] { 1, 2 }; + var record = new CustomRecordInfo(); + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(record); + try + { + SAFEARRAY *psa = CreateRecordSafeArray(result, pRecordInfo); + psa->fFeatures &= ~FADF.RECORD; + try + { + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + finally + { + // Make sure disposal works. + psa->fFeatures |= FADF.RECORD; + } + } + finally + { + Marshal.Release(pRecordInfo); + } + } + + [StaFact] + public void ToObject_RECORDARRAYInvalidGetGuidHR_ThrowsArgumentException() + { + var result = new int[] { 1, 2 }; + var record = new CustomRecordInfo + { + GetGuidAction = () => (Guid.Empty, HRESULT.DISP_E_DIVBYZERO) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(record); + try + { + SAFEARRAY *psa = CreateRecordSafeArray(result, pRecordInfo); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + finally + { + Marshal.Release(pRecordInfo); + } + } + + public static IEnumerable RECORDARRAY_InvalidGuid_TestData() + { + yield return new object[] { Guid.Empty }; + yield return new object[] { new Guid("8856f961-340a-11d0-a96b-00c04fd705a2") }; + } + + [StaTheory] + [MemberData(nameof(RECORDARRAY_InvalidGuid_TestData))] + public void ToObject_RECORDARRAY_InvokeInvalidGuid_ThrowsArgumentException(Guid guid) + { + var result = new int[] { 1, 2 }; + var record = new CustomRecordInfo + { + GetGuidAction = () => (guid, HRESULT.S_OK) + }; + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(record); + try + { + SAFEARRAY *psa = CreateRecordSafeArray(result, pRecordInfo); + using var variant = new VARIANT + { + vt = VARENUM.ARRAY | VARENUM.RECORD, + data = new VARIANT.VARIANTUnion + { + parray = psa + } + }; + AssertToObjectThrows(variant); + } + finally + { + Marshal.Release(pRecordInfo); + } + } + + private class CustomRecordInfo : IRecordInfo + { + HRESULT IRecordInfo.RecordInit(void* pvNew) => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordClear(void* pvExisting) => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordCopy(void* pvExisting, void* pvNew) => throw new NotImplementedException(); + + public Func<(Guid, HRESULT)> GetGuidAction { get; set; } + + HRESULT IRecordInfo.GetGuid(Guid* pguid) + { + (Guid guid, HRESULT hr) = GetGuidAction(); + *pguid = guid; + return hr; + } + + HRESULT IRecordInfo.GetName(BSTR* pbstrName) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetSize(uint* pcbSize) + { + *pcbSize = (uint)sizeof(int); + return HRESULT.S_OK; + } + + HRESULT IRecordInfo.GetTypeInfo(out ITypeInfo ppTypeInfo) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetField(void* pvData, out string szFieldName, VARIANT* pvarField) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetFieldNoCopy(void* pvData, out string szFieldName, VARIANT* pvarField, void* ppvDataCArray) => throw new NotImplementedException(); + + HRESULT IRecordInfo.PutField(INVOKEKIND wFlags, void* pvData, out string szFieldName, VARIANT* pvarField) => throw new NotImplementedException(); + + HRESULT IRecordInfo.PutFieldNoCopy(INVOKEKIND wFlags, void* pvData, out string szFieldName, VARIANT* pvarField) => throw new NotImplementedException(); + + HRESULT IRecordInfo.GetFieldNames(uint* pcNames, BSTR* rgBstrNames) => throw new NotImplementedException(); + + BOOL IRecordInfo.IsMatchingType(ref IRecordInfo pRecordInfoInfo) => throw new NotImplementedException(); + + void* IRecordInfo.RecordCreate() => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordCreateCopy(void* pvSource, void** ppvDest) => throw new NotImplementedException(); + + HRESULT IRecordInfo.RecordDestroy(void* pvRecord) => throw new NotImplementedException(); + } + + private static SAFEARRAY* CreateRecordSafeArray(T[] result, IntPtr recordInfo, int lbound = 0) + { + var saBound = new SAFEARRAYBOUND + { + cElements = (uint)result.Length, + lLbound = lbound + }; + SAFEARRAY *psa = SafeArrayCreateEx(VARENUM.RECORD, 1, &saBound, recordInfo); + Assert.True(psa != null); + + VARENUM arrayVt = VARENUM.EMPTY; + HRESULT hr = SafeArrayGetVartype(psa, &arrayVt); + Assert.Equal(HRESULT.S_OK, hr); + Assert.Equal(VARENUM.RECORD, arrayVt); + + return psa; + } + + private static void AssertToObjectThrows(VARIANT variant) where T : Exception + { + VARIANT copy = variant; + IntPtr pv = (IntPtr)(©); + Assert.Throws(() => Marshal.GetObjectForNativeVariant(pv)); + + Assert.Throws(() => variant.ToObject()); + } + + private static void AssertToObjectEqual(object expected, VARIANT variant) + => AssertToObject(variant, actual => Assert.Equal(expected, actual)); + + private static void AssertToObjectEqualExtension(object expected, VARIANT variant) where T : Exception + { + // Not supported type. + VARIANT copy = variant; + IntPtr pv = (IntPtr)(©); + Assert.Throws(() => Marshal.GetObjectForNativeVariant(pv)); + + Assert.Equal(expected, variant.ToObject()); + } + + private static void AssertToObject(VARIANT variant, Action action) + { + IntPtr pv = (IntPtr)(&variant); + action(Marshal.GetObjectForNativeVariant(pv)); + + action(variant.ToObject()); + } + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromCLSID(Guid* clsid, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromFileTime(FILETIME* pftIn, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitVariantFromFileTime(FILETIME* pftIn, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromBuffer(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromInt16Vector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromUInt16Vector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromBooleanVector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromInt32Vector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromUInt32Vector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromInt64Vector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromUInt64Vector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromDoubleVector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Propsys, ExactSpelling = true)] + private unsafe static extern HRESULT InitPropVariantFromFileTimeVector(void* pv, uint cb, VARIANT* ppropvar); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static unsafe extern SAFEARRAY* SafeArrayCreate(VARENUM vt, uint cDims, SAFEARRAYBOUND* rgsabound); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static unsafe extern SAFEARRAY* SafeArrayCreateEx(VARENUM vt, uint cDims, SAFEARRAYBOUND* rgsabound, IntPtr pvExtra); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static unsafe extern HRESULT SafeArrayDestroy(SAFEARRAY* psa); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private unsafe static extern HRESULT SafeArrayPutElement(SAFEARRAY* psa, int* rgIndices, void* pv); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static extern HRESULT VarDecFromI8(long i64In, out DECIMAL pdecOut); + + [DllImport(Libraries.Oleaut32, ExactSpelling = true)] + private static extern HRESULT VarDecFromR8(double dblIn, out DECIMAL pdecOut); + } +} diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx index c5fbf9d390..264cc6224f 100644 --- a/src/System.Windows.Forms/src/Resources/SR.resx +++ b/src/System.Windows.Forms/src/Resources/SR.resx @@ -436,12 +436,6 @@ You cannot alter the document or add to it once you have printed it. - - Double cannot be converted to a date. - - - Integer cannot be converted to a float. - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf index 2323c16c4e..d6edeefd3d 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf @@ -687,16 +687,6 @@ Po vytištění nelze dokument upravovat ani do něj již nelze přidávat položky. - - Double cannot be converted to a date. - Nelze převést typ Double na typ Date. - - - - Integer cannot be converted to a float. - Nelze převést typ Integer na typ Float. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Vytvoření druhé smyčky zpráv v jednom vlákně je neplatná operace. Použijte místo toho funkci Application.RunDialog nebo Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf index 27d14d225f..9c4a11dffc 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf @@ -687,16 +687,6 @@ Das Dokument kann nach dem Drucken nicht mehr geändert oder ergänzt werden. - - Double cannot be converted to a date. - "Double" kann nicht in "Date" konvertiert werden. - - - - Integer cannot be converted to a float. - "Integer" kann nicht in "Float" konvertiert werden. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Das Starten einer zweiten Meldungsschleife auf einem einzelnen Thread ist ein ungültiger Vorgang. Verwenden Sie Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf index c96d5300ff..19408345a9 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf @@ -687,16 +687,6 @@ No se puede modificar el documento ni agregarle información una vez impreso. - - Double cannot be converted to a date. - No se puede convertir double en date. - - - - Integer cannot be converted to a float. - No se puede convertir integer en float. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. No se permite empezar un bucle de segundo mensaje en un subproceso sencillo. Utilice en su lugar Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf index 353dd8209a..3b0959e560 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf @@ -687,16 +687,6 @@ Vous ne pouvez modifier ni ajouter le document une fois que vous l'avez imprimé. - - Double cannot be converted to a date. - Impossible de convertir une valeur double en date. - - - - Integer cannot be converted to a float. - Impossible de convertir une valeur integer en float. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Le démarrage d'une deuxième boucle de messages sur un seul thread n'est pas une opération valide. Utilisez Form.ShowDialog à la place. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf index 94abb68677..5ed0ab1c3c 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf @@ -687,16 +687,6 @@ Impossibile eseguire modifiche o aggiunte al documento dopo la stampa. - - Double cannot be converted to a date. - Impossibile convertire un tipo double in un tipo date. - - - - Integer cannot be converted to a float. - Impossibile convertire un numero intero in un valore float. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Non è possibile avviare un secondo ciclo di messaggi su un thread singolo. Usare Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf index 8a852e0916..ceeebe3ad8 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf @@ -687,16 +687,6 @@ ドキュメントが印刷された後にドキュメントの変更やドキュメントへの追加はできません。 - - Double cannot be converted to a date. - double を date に変換できません。 - - - - Integer cannot be converted to a float. - integer を float に変換できません。 - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. 単一スレッド上で 2 回目のメッセージ ループを開始することは有効な操作ではありません。Form.ShowDialog を使用してください。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf index 6a485ee49f..737399fbc2 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf @@ -687,16 +687,6 @@ 문서를 인쇄한 후에는 문서를 변경하거나 문서에 추가할 수 없습니다. - - Double cannot be converted to a date. - double 형식을 date 형식으로 변환할 수 없습니다. - - - - Integer cannot be converted to a float. - integer 형식을 float 형식으로 변환할 수 없습니다. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. 단일 스레드에서 두 번째 메시지 루프를 시작할 수 없습니다. 대신 Form.ShowDialog를 사용하십시오. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf index 1309a58a8a..44ff823db8 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf @@ -687,16 +687,6 @@ Po wydrukowaniu dokumentu nie można już go zmieniać ani dodawać do niego. - - Double cannot be converted to a date. - Nie można przekonwertować typu double na typ date. - - - - Integer cannot be converted to a float. - Nie można przekonwertować typu integer na typ float. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Rozpoczynanie drugiej pętli komunikatów w pojedynczym wątku nie jest prawidłową operacją. Użyj zamiast tego elementu Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf index b852f1faec..be138ee7cf 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf @@ -687,16 +687,6 @@ Não é possível alterar o documento ou adicioná-lo após imprimi-lo. - - Double cannot be converted to a date. - Não é possível converter duplo em data. - - - - Integer cannot be converted to a float. - Não é possível converter inteiro em flutuante. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Iniciar um segundo loop de mensagens em um único thread não é uma operação válida. Use Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf index 63e2a5d163..e0277ee23c 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf @@ -687,16 +687,6 @@ После печати внесение изменений или добавление данных в документ невозможно. - - Double cannot be converted to a date. - Тип double нельзя привести к типу date. - - - - Integer cannot be converted to a float. - Тип integer нельзя привести к типу float. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Запуск второго цикла сообщения в единичном потоке является недопустимой операцией. Вместо этого используйте Form.ShowDialog. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf index 65f7c6978f..236031342c 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf @@ -687,16 +687,6 @@ Belgeyi yazdırdıktan sonra belgeyi değiştiremez ya ek yapamazsınız. - - Double cannot be converted to a date. - Double tarihe dönüştürülemez. - - - - Integer cannot be converted to a float. - Tam sayı kayan noktalıya dönüştürülemez. - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. Tek bir iş parçacığı üzerinde ikinci ileti döngüsünü başlatmak geçerli bir işlem değildir. Yerine Form.ShowDialog kullanın. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf index fd00e84c9b..46a5651e09 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf @@ -687,16 +687,6 @@ 打印文档后将无法更改文档或向其中添加内容。 - - Double cannot be converted to a date. - 无法将 double 转换为 date。 - - - - Integer cannot be converted to a float. - 无法将 integer 转换为 float。 - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. 在单个线程上开始另一个消息循环是无效操作。请改用 Form.ShowDialog。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf index 3945170085..ae86df9592 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf @@ -687,16 +687,6 @@ 一旦列印文件之後,您就不能變更或增加文件內容。 - - Double cannot be converted to a date. - Double 無法轉換為 Date。 - - - - Integer cannot be converted to a float. - Integer 無法轉換為 Float。 - - Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead. 在單一執行緒上啟動第二個訊息迴圈不是有效的作業。請使用 Form.ShowDialog 代替。 diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs index 6e680235a2..4e3f11d501 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2IManagedPerPropertyBrowsingHandler.cs @@ -265,8 +265,8 @@ namespace System.Windows.Forms.ComponentModel.Com2Interop { try { - using var variant = ptr[i]; - objects[i] = Marshal.GetObjectForNativeVariant((IntPtr)(&variant)); + using Oleaut32.VARIANT variant = ptr[i]; + objects[i] = variant.ToObject(); } catch (Exception ex) {