|
5 | 5 | namespace Rubberduck.VBEditor.ComManagement.TypeLibsAbstract
|
6 | 6 | {
|
7 | 7 | [ComImport(), Guid("00020400-0000-0000-C000-000000000046")]
|
8 |
| - [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] |
| 8 | + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
9 | 9 | public interface IDispatch
|
10 | 10 | {
|
| 11 | + [PreserveSig] int GetTypeInfoCount([Out] out uint pctinfo); |
| 12 | + [PreserveSig] int GetTypeInfo([In] uint iTInfo, [In] uint lcid, [Out] out ComTypes.ITypeInfo pTypeInfo); |
| 13 | + [PreserveSig] int GetIDsOfNames([In] ref Guid riid, [In] string[] rgszNames, [In] uint cNames, [In] uint lcid, [Out] out int[] rgDispId); |
| 14 | + |
| 15 | + [PreserveSig] |
| 16 | + int Invoke([In] int dispIdMember, |
| 17 | + [In] ref Guid riid, |
| 18 | + [In] uint lcid, |
| 19 | + [In] uint dwFlags, |
| 20 | + [In, Out] ref ComTypes.DISPPARAMS pDispParams, |
| 21 | + [Out] out Object pVarResult, |
| 22 | + [In, Out] ref ComTypes.EXCEPINFO pExcepInfo, |
| 23 | + [Out] out uint pArgErr); |
| 24 | + } |
| 25 | + |
| 26 | + public static class ComHelper |
| 27 | + { |
| 28 | + public static bool HRESULT_FAILED(int hr) => hr < 0; |
| 29 | + } |
| 30 | + |
| 31 | + public static class IDispatchHelper |
| 32 | + { |
| 33 | + static Guid GUID_NULL = new Guid(); |
| 34 | + |
| 35 | + public enum InvokeKind : int |
| 36 | + { |
| 37 | + DISPATCH_METHOD = 1, |
| 38 | + DISPATCH_PROPERTYGET = 2, |
| 39 | + DISPATCH_PROPERTYPUT = 4, |
| 40 | + DISPATCH_PROPERTYPUTREF = 8, |
| 41 | + } |
| 42 | + |
| 43 | + [StructLayout(LayoutKind.Sequential)] |
| 44 | + public struct VARIANT |
| 45 | + { |
| 46 | + short vt; |
| 47 | + short reserved1; |
| 48 | + short reserved2; |
| 49 | + short reserved3; |
| 50 | + IntPtr data1; |
| 51 | + IntPtr data2; |
| 52 | + } |
| 53 | + |
| 54 | + // Convert input args into a contigious array of real COM VARIANTs for the DISPPARAMS struct |
| 55 | + private static ComTypes.DISPPARAMS PrepareDispatchArgs(object[] args) |
| 56 | + { |
| 57 | + var pDispParams = new ComTypes.DISPPARAMS(); |
| 58 | + |
| 59 | + if ((args != null) && (args.Length != 0)) |
| 60 | + { |
| 61 | + var variantStructSize = Marshal.SizeOf(typeof(VARIANT)); |
| 62 | + pDispParams.cArgs = args.Length; |
| 63 | + |
| 64 | + var argsVariantLength = variantStructSize * pDispParams.cArgs; |
| 65 | + var variantArgsArray = Marshal.AllocHGlobal(argsVariantLength); |
| 66 | + |
| 67 | + // In IDispatch::Invoke, arguments are passed in reverse order |
| 68 | + IntPtr variantArgsArrayOffset = variantArgsArray + argsVariantLength; |
| 69 | + foreach (var arg in args) |
| 70 | + { |
| 71 | + variantArgsArrayOffset -= variantStructSize; |
| 72 | + Marshal.GetNativeVariantForObject(arg, variantArgsArrayOffset); |
| 73 | + } |
| 74 | + pDispParams.rgvarg = variantArgsArray; |
| 75 | + } |
| 76 | + return pDispParams; |
| 77 | + } |
| 78 | + |
| 79 | + [DllImport("oleaut32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] |
| 80 | + static extern Int32 VariantClear(IntPtr pvarg); |
| 81 | + |
| 82 | + // frees all unmanaged memory assoicated with the DISPPARAMS |
| 83 | + private static void UnprepareDispatchArgs(ComTypes.DISPPARAMS pDispParams) |
| 84 | + { |
| 85 | + if (pDispParams.rgvarg != IntPtr.Zero) |
| 86 | + { |
| 87 | + // free the array of COM VARIANTs |
| 88 | + var variantStructSize = Marshal.SizeOf(typeof(VARIANT)); |
| 89 | + var variantArgsArrayOffset = pDispParams.rgvarg; |
| 90 | + int argIndex = 0; |
| 91 | + while (argIndex < pDispParams.cArgs) |
| 92 | + { |
| 93 | + VariantClear(variantArgsArrayOffset); |
| 94 | + variantArgsArrayOffset += variantStructSize; |
| 95 | + argIndex++; |
| 96 | + } |
| 97 | + Marshal.FreeHGlobal(pDispParams.rgvarg); |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + // TODO support DISPATCH_PROPERTYPUTREF (property-set) which requires special handling |
| 102 | + public static object Invoke(IDispatch obj, int memberId, InvokeKind invokeKind, object[] args = null) |
| 103 | + { |
| 104 | + var pDispParams = PrepareDispatchArgs(args); |
| 105 | + var pExcepInfo = new ComTypes.EXCEPINFO(); |
| 106 | + |
| 107 | + int hr = obj.Invoke(memberId, ref GUID_NULL, 0, (uint)invokeKind, |
| 108 | + ref pDispParams, out object pVarResult, ref pExcepInfo, out uint pErrArg); |
| 109 | + |
| 110 | + UnprepareDispatchArgs(pDispParams); |
| 111 | + |
| 112 | + if (ComHelper.HRESULT_FAILED(hr)) |
| 113 | + { |
| 114 | + if ((hr == (int)KnownComHResults.DISP_E_EXCEPTION) && (ComHelper.HRESULT_FAILED(pExcepInfo.scode))) |
| 115 | + { |
| 116 | + throw Marshal.GetExceptionForHR(pExcepInfo.scode); |
| 117 | + } |
| 118 | + throw Marshal.GetExceptionForHR(hr); |
| 119 | + } |
| 120 | + |
| 121 | + return pVarResult; |
| 122 | + } |
11 | 123 | }
|
12 | 124 |
|
13 | 125 | // A compatible version of ITypeInfo, where COM objects are outputted as IntPtrs instead of objects
|
@@ -215,5 +327,6 @@ public enum KnownComHResults : int
|
215 | 327 | {
|
216 | 328 | E_VBA_COMPILEERROR = unchecked((int)0x800A9C64),
|
217 | 329 | E_NOTIMPL = unchecked((int)0x80004001),
|
| 330 | + DISP_E_EXCEPTION = unchecked((int)0x80020009), |
218 | 331 | }
|
219 | 332 | }
|
0 commit comments