1
+ using System ;
2
+ using System . Globalization ;
3
+ using System . Runtime . InteropServices ;
4
+
5
+ namespace Rubberduck . VBEditor . Variants
6
+ {
7
+ public enum VARENUM
8
+ {
9
+ VT_EMPTY = 0x0000 ,
10
+ VT_NULL = 0x0001 ,
11
+ VT_I2 = 0x0002 ,
12
+ VT_I4 = 0x0003 ,
13
+ VT_R4 = 0x0004 ,
14
+ VT_R8 = 0x0005 ,
15
+ VT_CY = 0x0006 ,
16
+ VT_DATE = 0x0007 ,
17
+ VT_BSTR = 0x0008 ,
18
+ VT_DISPATCH = 0x0009 ,
19
+ VT_ERROR = 0x000A ,
20
+ VT_BOOL = 0x000B ,
21
+ VT_VARIANT = 0x000C ,
22
+ VT_UNKNOWN = 0x000D ,
23
+ VT_DECIMAL = 0x000E ,
24
+ VT_I1 = 0x0010 ,
25
+ VT_UI1 = 0x0011 ,
26
+ VT_UI2 = 0x0012 ,
27
+ VT_UI4 = 0x0013 ,
28
+ VT_I8 = 0x0014 ,
29
+ VT_UI8 = 0x0015 ,
30
+ VT_INT = 0x0016 ,
31
+ VT_UINT = 0x0017 ,
32
+ VT_VOID = 0x0018 ,
33
+ VT_HRESULT = 0x0019 ,
34
+ VT_PTR = 0x001A ,
35
+ VT_SAFEARRAY = 0x001B ,
36
+ VT_CARRAY = 0x001C ,
37
+ VT_USERDEFINED = 0x001D ,
38
+ VT_LPSTR = 0x001E ,
39
+ VT_LPWSTR = 0x001F ,
40
+ VT_RECORD = 0x0024 ,
41
+ VT_INT_PTR = 0x0025 ,
42
+ VT_UINT_PTR = 0x0026 ,
43
+ VT_ARRAY = 0x2000 ,
44
+ VT_BYREF = 0x4000
45
+ }
46
+
47
+ [ Flags ]
48
+ public enum VariantConversionFlags : ushort
49
+ {
50
+ NO_FLAGS = 0x00 ,
51
+ VARIANT_NOVALUEPROP = 0x01 , //Prevents the function from attempting to coerce an object to a fundamental type by getting the Value property. Applications should set this flag only if necessary, because it makes their behavior inconsistent with other applications.
52
+ VARIANT_ALPHABOOL = 0x02 , //Converts a VT_BOOL value to a string containing either "True" or "False".
53
+ VARIANT_NOUSEROVERRIDE = 0x04 , //For conversions to or from VT_BSTR, passes LOCALE_NOUSEROVERRIDE to the core coercion routines.
54
+ VARIANT_LOCALBOOL = 0x08 //For conversions from VT_BOOL to VT_BSTR and back, uses the language specified by the locale in use on the local computer.
55
+ }
56
+
57
+ /// <summary>
58
+ /// Handles variant conversions, enabling us to have same implicit conversion behaviors within
59
+ /// .NET as we can observe it from VBA/VB6.
60
+ /// </summary>
61
+ /// <remarks>
62
+ /// The <see cref="VariantChangeType"/> function is the same one used internally by VBA/VB6.
63
+ /// However, we have to wrap the metadata, which the class helps with.
64
+ ///
65
+ /// See the link for details on how marshaling are handled with <see cref="object"/>
66
+ /// https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-objects
67
+ /// </remarks>
68
+ public static class VariantConverter
69
+ {
70
+ private const string dllName = "oleaut32.dll" ;
71
+
72
+ // HRESULT VariantChangeType(
73
+ // VARIANTARG *pvargDest,
74
+ // const VARIANTARG *pvarSrc,
75
+ // USHORT wFlags,
76
+ // VARTYPE vt
77
+ // );
78
+ [ DllImport ( dllName , EntryPoint = "VariantChangeType" , CharSet = CharSet . Auto , SetLastError = true , PreserveSig = true ) ]
79
+ private static extern int VariantChangeType ( ref object pvargDest , ref object pvarSrc , VariantConversionFlags wFlags , VARENUM vt ) ;
80
+
81
+ // HRESULT VariantChangeTypeEx(
82
+ // VARIANTARG *pvargDest,
83
+ // const VARIANTARG *pvarSrc,
84
+ // LCID lcid,
85
+ // USHORT wFlags,
86
+ // VARTYPE vt
87
+ // );
88
+ [ DllImport ( dllName , EntryPoint = "VariantChangeTypeEx" , CharSet = CharSet . Auto , SetLastError = true , PreserveSig = true ) ]
89
+ private static extern int VariantChangeTypeEx ( ref object pvargDest , ref object pvarSrc , int lcid , VariantConversionFlags wFlags , VARENUM vt ) ;
90
+
91
+ public static object ChangeType ( object value , VARENUM vt )
92
+ {
93
+ return ChangeType ( value , vt , null ) ;
94
+ }
95
+
96
+ private static bool HRESULT_FAILED ( int hr ) => hr < 0 ;
97
+ public static object ChangeType ( object value , VARENUM vt , CultureInfo cultureInfo )
98
+ {
99
+ object result = null ;
100
+ var hr = cultureInfo == null
101
+ ? VariantChangeType ( ref result , ref value , VariantConversionFlags . NO_FLAGS , vt )
102
+ : VariantChangeTypeEx ( ref result , ref value , cultureInfo . LCID , VariantConversionFlags . NO_FLAGS , vt ) ;
103
+ if ( HRESULT_FAILED ( hr ) )
104
+ {
105
+ throw Marshal . GetExceptionForHR ( hr ) ;
106
+ }
107
+
108
+ return result ;
109
+ }
110
+
111
+ public static object ChangeType ( object value , Type targetType )
112
+ {
113
+ return ChangeType ( value , GetVarEnum ( targetType ) ) ;
114
+ }
115
+
116
+ public static object ChangeType ( object value , Type targetType , CultureInfo culture )
117
+ {
118
+ return ChangeType ( value , GetVarEnum ( targetType ) , culture ) ;
119
+ }
120
+
121
+ public static VARENUM GetVarEnum ( Type target )
122
+ {
123
+ switch ( target )
124
+ {
125
+ case null :
126
+ return VARENUM . VT_EMPTY ;
127
+ case Type dbNull when dbNull == typeof ( DBNull ) :
128
+ return VARENUM . VT_NULL ;
129
+ case Type err when err == typeof ( ErrorWrapper ) :
130
+ return VARENUM . VT_ERROR ;
131
+ case Type disp when disp == typeof ( DispatchWrapper ) :
132
+ return VARENUM . VT_DISPATCH ;
133
+ case Type unk when unk == typeof ( UnknownWrapper ) :
134
+ return VARENUM . VT_UNKNOWN ;
135
+ case Type cy when cy == typeof ( CurrencyWrapper ) :
136
+ return VARENUM . VT_CY ;
137
+ case Type b when b == typeof ( bool ) :
138
+ return VARENUM . VT_BOOL ;
139
+ case Type s when s == typeof ( sbyte ) :
140
+ return VARENUM . VT_I1 ;
141
+ case Type b when b == typeof ( byte ) :
142
+ return VARENUM . VT_UI1 ;
143
+ case Type i16 when i16 == typeof ( short ) :
144
+ return VARENUM . VT_I2 ;
145
+ case Type ui16 when ui16 == typeof ( ushort ) :
146
+ return VARENUM . VT_UI2 ;
147
+ case Type i32 when i32 == typeof ( int ) :
148
+ return VARENUM . VT_I4 ;
149
+ case Type ui32 when ui32 == typeof ( uint ) :
150
+ return VARENUM . VT_UI4 ;
151
+ case Type i64 when i64 == typeof ( long ) :
152
+ return VARENUM . VT_I8 ;
153
+ case Type ui64 when ui64 == typeof ( ulong ) :
154
+ return VARENUM . VT_UI8 ;
155
+ case Type sng when sng == typeof ( float ) :
156
+ return VARENUM . VT_R4 ;
157
+ case Type dbl when dbl == typeof ( double ) :
158
+ return VARENUM . VT_R8 ;
159
+ case Type dec when dec == typeof ( decimal ) :
160
+ return VARENUM . VT_DECIMAL ;
161
+ case Type dt when dt == typeof ( DateTime ) :
162
+ return VARENUM . VT_DATE ;
163
+ case Type s when s == typeof ( string ) :
164
+ return VARENUM . VT_BSTR ;
165
+ //case Type a when a == typeof(Array):
166
+ // return VARENUM.VT_ARRAY;
167
+ case Type obj when obj == typeof ( object ) :
168
+ case Type var when var == typeof ( VariantWrapper ) :
169
+ return VARENUM . VT_VARIANT ;
170
+ default :
171
+ throw new NotSupportedException ( "Unrecognized system type that cannot be mapped to a VARENUM out of the box." ) ;
172
+ }
173
+ }
174
+
175
+ public static VARENUM GetVarEnum ( TypeCode typeCode )
176
+ {
177
+ switch ( typeCode )
178
+ {
179
+ case TypeCode . Empty :
180
+ return VARENUM . VT_EMPTY ;
181
+ case TypeCode . Object :
182
+ return VARENUM . VT_UNKNOWN ;
183
+ case TypeCode . DBNull :
184
+ return VARENUM . VT_NULL ;
185
+ case TypeCode . Boolean :
186
+ return VARENUM . VT_BOOL ;
187
+ case TypeCode . Char :
188
+ return VARENUM . VT_UI2 ;
189
+ case TypeCode . SByte :
190
+ return VARENUM . VT_I1 ;
191
+ case TypeCode . Byte :
192
+ return VARENUM . VT_UI1 ;
193
+ case TypeCode . Int16 :
194
+ return VARENUM . VT_I2 ;
195
+ case TypeCode . UInt16 :
196
+ return VARENUM . VT_UI2 ;
197
+ case TypeCode . Int32 :
198
+ return VARENUM . VT_I4 ;
199
+ case TypeCode . UInt32 :
200
+ return VARENUM . VT_UI4 ;
201
+ case TypeCode . Int64 :
202
+ return VARENUM . VT_I8 ;
203
+ case TypeCode . UInt64 :
204
+ return VARENUM . VT_UI8 ;
205
+ case TypeCode . Single :
206
+ return VARENUM . VT_R4 ;
207
+ case TypeCode . Double :
208
+ return VARENUM . VT_R8 ;
209
+ case TypeCode . Decimal :
210
+ return VARENUM . VT_DECIMAL ;
211
+ case TypeCode . DateTime :
212
+ return VARENUM . VT_DATE ;
213
+ case TypeCode . String :
214
+ return VARENUM . VT_BSTR ;
215
+ default :
216
+ throw new ArgumentOutOfRangeException ( nameof ( typeCode ) , typeCode , null ) ;
217
+ }
218
+ }
219
+ }
220
+ }
0 commit comments