13
13
===========================================================*/
14
14
15
15
using System ;
16
+ using System . Collections . Generic ;
16
17
using System . Diagnostics ;
17
18
using System . Globalization ;
18
19
using System . Reflection ;
19
20
using System . Runtime . CompilerServices ;
20
21
using System . Runtime . InteropServices ;
22
+ using System . Runtime . InteropServices . Marshalling ;
21
23
22
24
namespace Microsoft . Win32
23
25
{
@@ -26,102 +28,162 @@ internal static unsafe partial class OAVariantLib
26
28
#region Constants
27
29
28
30
// Constants for VariantChangeType from OleAuto.h
29
- public const int NoValueProp = 0x01 ;
30
- public const int AlphaBool = 0x02 ;
31
- public const int NoUserOverride = 0x04 ;
32
- public const int CalendarHijri = 0x08 ;
33
31
public const int LocalBool = 0x10 ;
34
32
35
- internal static readonly Type ? [ ] ClassTypes = {
36
- typeof ( Empty ) ,
37
- typeof ( void ) ,
38
- typeof ( bool ) ,
39
- typeof ( char ) ,
40
- typeof ( sbyte ) ,
41
- typeof ( byte ) ,
42
- typeof ( short ) ,
43
- typeof ( ushort ) ,
44
- typeof ( int ) ,
45
- typeof ( uint ) ,
46
- typeof ( long ) ,
47
- typeof ( ulong ) ,
48
- typeof ( float ) ,
49
- typeof ( double ) ,
50
- typeof ( string ) ,
51
- typeof ( void ) ,
52
- typeof ( DateTime ) ,
53
- typeof ( TimeSpan ) ,
54
- typeof ( object ) ,
55
- typeof ( decimal ) ,
56
- null , // Enums - what do we do here?
57
- typeof ( Missing ) ,
58
- typeof ( DBNull ) ,
33
+ private static readonly Dictionary < Type , VarEnum > ClassTypes = new Dictionary < Type , VarEnum >
34
+ {
35
+ { typeof ( bool ) , VarEnum . VT_BOOL } ,
36
+ { typeof ( char ) , VarEnum . VT_I2 } ,
37
+ { typeof ( sbyte ) , VarEnum . VT_I1 } ,
38
+ { typeof ( byte ) , VarEnum . VT_UI1 } ,
39
+ { typeof ( short ) , VarEnum . VT_I2 } ,
40
+ { typeof ( ushort ) , VarEnum . VT_UI2 } ,
41
+ { typeof ( int ) , VarEnum . VT_I4 } ,
42
+ { typeof ( uint ) , VarEnum . VT_UI4 } ,
43
+ { typeof ( long ) , VarEnum . VT_I8 } ,
44
+ { typeof ( ulong ) , VarEnum . VT_UI8 } ,
45
+ { typeof ( float ) , VarEnum . VT_R4 } ,
46
+ { typeof ( double ) , VarEnum . VT_R8 } ,
47
+ { typeof ( string ) , VarEnum . VT_BSTR } ,
48
+ { typeof ( DateTime ) , VarEnum . VT_DATE } ,
49
+ { typeof ( decimal ) , VarEnum . VT_DECIMAL } ,
59
50
} ;
60
51
61
- // Keep these numbers in sync w/ the above array.
62
- private const int CV_OBJECT = 0x12 ;
63
-
64
52
#endregion
65
53
66
54
67
55
#region Internal Methods
68
56
69
- #pragma warning disable CS8500
70
-
71
57
/**
72
58
* Changes a Variant from one type to another, calling the OLE
73
59
* Automation VariantChangeTypeEx routine. Note the legal types here are
74
60
* restricted to the subset of what can be legally found in a VB
75
61
* Variant and the types that CLR supports explicitly in the
76
62
* CLR Variant class.
77
63
*/
78
- internal static Variant ChangeType ( Variant source , Type targetClass , short options , CultureInfo culture )
64
+ internal static object ? ChangeType ( object source , Type targetClass , short options , CultureInfo culture )
79
65
{
80
66
ArgumentNullException . ThrowIfNull ( targetClass ) ;
81
67
ArgumentNullException . ThrowIfNull ( culture ) ;
82
68
83
- Variant result = default ;
84
- ChangeType (
85
- & result ,
86
- & source ,
87
- culture . LCID ,
88
- targetClass . TypeHandle . Value ,
89
- GetCVTypeFromClass ( targetClass ) ,
90
- options ) ;
91
- return result ;
92
- }
69
+ object ? result = null ;
93
70
94
- [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "OAVariant_ChangeType" ) ]
95
- private static partial void ChangeType ( Variant * result , Variant * source , int lcid , IntPtr typeHandle , int cvType , short flags ) ;
71
+ if ( Variant . IsSystemDrawingColor ( targetClass ) )
72
+ {
73
+ if ( source is int || source is uint )
74
+ {
75
+ uint sourceData = source is int ? ( uint ) ( int ) source : ( uint ) source ;
76
+ // Int32/UInt32 can be converted to System.Drawing.Color
77
+ Variant . ConvertOleColorToSystemColor ( ObjectHandleOnStack . Create ( ref result ) , sourceData , targetClass . TypeHandle . Value ) ;
78
+ Debug . Assert ( result != null ) ;
79
+ return result ;
80
+ }
81
+ }
96
82
97
- #pragma warning restore CS8500
83
+ if ( ! ClassTypes . TryGetValue ( targetClass , out VarEnum vt ) )
84
+ {
85
+ throw new NotSupportedException ( SR . NotSupported_ChangeType ) ;
86
+ }
98
87
99
- #endregion
88
+ ComVariant vOp = ToOAVariant ( source ) ;
89
+ ComVariant ret = default ;
100
90
91
+ int hr = Interop . OleAut32 . VariantChangeTypeEx ( & ret , & vOp , culture . LCID , options , ( ushort ) vt ) ;
101
92
102
- #region Private Helpers
93
+ using ( vOp )
94
+ using ( ret )
95
+ {
96
+ if ( hr < 0 )
97
+ {
98
+ OAFailed ( hr ) ;
99
+ }
103
100
104
- private static int GetCVTypeFromClass ( Type ctype )
105
- {
106
- Debug . Assert ( ctype != null ) ;
107
- Debug . Assert ( ClassTypes [ CV_OBJECT ] == typeof ( object ) , "OAVariantLib::ClassTypes[CV_OBJECT] == Object.class" ) ;
101
+ result = FromOAVariant ( ret ) ;
102
+ if ( targetClass == typeof ( char ) )
103
+ {
104
+ result = ( char ) ( uint ) result ! ;
105
+ }
106
+ }
108
107
109
- // OleAut Binder works better if unrecognized
110
- // types were changed to Object.
111
- int cvtype = CV_OBJECT ;
108
+ return result ;
109
+ }
112
110
113
- for ( int i = 0 ; i < ClassTypes . Length ; i ++ )
111
+ private static void OAFailed ( int hr )
112
+ {
113
+ switch ( hr )
114
114
{
115
- if ( ctype . Equals ( ClassTypes [ i ] ) )
116
- {
117
- cvtype = i ;
118
- break ;
119
- }
115
+ case HResults . COR_E_OUTOFMEMORY :
116
+ throw new OutOfMemoryException ( ) ;
117
+ case HResults . DISP_E_BADVARTYPE :
118
+ throw new NotSupportedException ( SR . NotSupported_OleAutBadVarType ) ;
119
+ case HResults . DISP_E_DIVBYZERO :
120
+ throw new DivideByZeroException ( ) ;
121
+ case HResults . DISP_E_OVERFLOW :
122
+ throw new OverflowException ( ) ;
123
+ case HResults . DISP_E_TYPEMISMATCH :
124
+ throw new InvalidCastException ( SR . InvalidCast_OATypeMismatch ) ;
125
+ case HResults . E_INVALIDARG :
126
+ throw new ArgumentException ( ) ;
127
+ default :
128
+ Debug . Fail ( "Unrecognized HResult - OAVariantLib routine failed in an unexpected way!" ) ;
129
+ throw Marshal . GetExceptionForHR ( hr ) ;
120
130
}
131
+ }
121
132
122
- return cvtype ;
133
+ private static ComVariant ToOAVariant ( object input )
134
+ {
135
+ return input switch
136
+ {
137
+ string str => ComVariant . Create ( str ) ,
138
+ DateTime dateTime => ComVariant . Create ( dateTime ) ,
139
+ bool b => ComVariant . Create ( b ) ,
140
+ decimal d => ComVariant . Create ( d ) ,
141
+ sbyte i1 => ComVariant . Create ( i1 ) ,
142
+ byte u1 => ComVariant . Create ( u1 ) ,
143
+ short i2 => ComVariant . Create ( i2 ) ,
144
+ ushort u2 => ComVariant . Create ( u2 ) ,
145
+ int i4 => ComVariant . Create ( i4 ) ,
146
+ uint u4 => ComVariant . Create ( u4 ) ,
147
+ long i8 => ComVariant . Create ( i8 ) ,
148
+ ulong u8 => ComVariant . Create ( u8 ) ,
149
+ float r4 => ComVariant . Create ( r4 ) ,
150
+ double r8 => ComVariant . Create ( r8 ) ,
151
+ null => default ,
152
+ Missing => throw new NotSupportedException ( SR . NotSupported_ChangeType ) ,
153
+ DBNull => ComVariant . Null ,
154
+ _ => GetComIPFromObjectRef ( input ) // Convert the object to an IDispatch/IUnknown pointer.
155
+ } ;
156
+ }
157
+
158
+ private static ComVariant GetComIPFromObjectRef ( object ? obj )
159
+ {
160
+ IntPtr pUnk = GetIUnknownOrIDispatchForObject ( ObjectHandleOnStack . Create ( ref obj ) , out bool isIDispatch ) ;
161
+ return ComVariant . CreateRaw ( isIDispatch ? VarEnum . VT_DISPATCH : VarEnum . VT_UNKNOWN , pUnk ) ;
123
162
}
124
163
164
+ [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "MarshalNative_GetIUnknownOrIDispatchForObject" ) ]
165
+ private static partial IntPtr GetIUnknownOrIDispatchForObject ( ObjectHandleOnStack o , [ MarshalAs ( UnmanagedType . Bool ) ] out bool isIDispatch ) ;
166
+
167
+ private static object ? FromOAVariant ( ComVariant input ) =>
168
+ input . VarType switch
169
+ {
170
+ VarEnum . VT_BSTR => input . As < string > ( ) ,
171
+ VarEnum . VT_DATE => input . As < DateTime > ( ) ,
172
+ VarEnum . VT_BOOL => input . As < bool > ( ) ,
173
+ VarEnum . VT_DECIMAL => input . As < decimal > ( ) ,
174
+ VarEnum . VT_I1 => input . As < sbyte > ( ) ,
175
+ VarEnum . VT_UI1 => input . As < byte > ( ) ,
176
+ VarEnum . VT_I2 => input . As < short > ( ) ,
177
+ VarEnum . VT_UI2 => input . As < ushort > ( ) ,
178
+ VarEnum . VT_I4 or VarEnum . VT_INT => input . As < int > ( ) ,
179
+ VarEnum . VT_UI4 or VarEnum . VT_UINT => input . As < uint > ( ) ,
180
+ VarEnum . VT_I8 => input . As < long > ( ) ,
181
+ VarEnum . VT_UI8 => input . As < ulong > ( ) ,
182
+ VarEnum . VT_R4 => input . As < float > ( ) ,
183
+ VarEnum . VT_R8 => input . As < double > ( ) ,
184
+ _ => throw new NotSupportedException ( SR . NotSupported_ChangeType ) ,
185
+ } ;
186
+
125
187
#endregion
126
188
}
127
189
}
0 commit comments