diff --git a/Package/Excel-DNA.Interop/Excel-DNA.Interop.nuspec b/Package/Excel-DNA.Interop/Excel-DNA.Interop.nuspec index bf503542..ec6112e2 100644 --- a/Package/Excel-DNA.Interop/Excel-DNA.Interop.nuspec +++ b/Package/Excel-DNA.Interop/Excel-DNA.Interop.nuspec @@ -2,7 +2,7 @@ Excel-DNA.Interop - 15.0.1 + 16.0.0 Excel-DNA - Primary Interop Assemblies for Excel Excel-DNA Contributors excel-dna @@ -10,15 +10,17 @@ images\ExcelDna-nu.png MIT + README.md false This package is deprecated, and has been replaced by ExcelDna.Interop - https://www.nuget.org/packages/ExcelDna.Interop/. Provides a selection of Excel Primary Interop Assemblies Excel Interop PIA ExcelDna - + + diff --git a/Package/Excel-DNA.Interop/README.md b/Package/Excel-DNA.Interop/README.md new file mode 100644 index 00000000..e31d9c7f --- /dev/null +++ b/Package/Excel-DNA.Interop/README.md @@ -0,0 +1,5 @@ +# Excel-DNA.Interop + +This package is deprecated. Use [ExcelDna.Interop](https://www.nuget.org/packages/ExcelDna.Interop/) instead. + +The dependency on `ExcelDna.Interop` supplies the Microsoft Office primary interop assemblies used by Excel-DNA add-ins. diff --git a/Package/ExcelDna.Interop.Dao/ExcelDna.Interop.Dao.nuspec b/Package/ExcelDna.Interop.Dao/ExcelDna.Interop.Dao.nuspec index 57845669..89f34feb 100644 --- a/Package/ExcelDna.Interop.Dao/ExcelDna.Interop.Dao.nuspec +++ b/Package/ExcelDna.Interop.Dao/ExcelDna.Interop.Dao.nuspec @@ -8,6 +8,7 @@ http://excel-dna.net images\ExcelDna-nu.png MIT + README.md false Provides a local copy of the Microsoft.Office.Interop.Access.Dao.dll primary interop assembly from Microsoft Office. Provides the Primary Interop Assembly for the Access DAO library. @@ -24,5 +25,6 @@ + diff --git a/Package/ExcelDna.Interop.Dao/README.md b/Package/ExcelDna.Interop.Dao/README.md new file mode 100644 index 00000000..f7c33225 --- /dev/null +++ b/Package/ExcelDna.Interop.Dao/README.md @@ -0,0 +1,7 @@ +# ExcelDna.Interop.Dao + +This package provides a local copy of the `Microsoft.Office.Interop.Access.Dao.dll` primary interop assembly for the Microsoft Access DAO library. + +The assembly is available for .NET Framework 4.5.2 and `net6.0-windows7.0` projects. The package configures C# and Visual Basic projects to embed the interop types and not copy the assembly to the output directory. F# projects are excluded from that build behavior. + +For Excel-DNA documentation, see [excel-dna.net](https://excel-dna.net/). diff --git a/Package/ExcelDna.Interop/ExcelDna.Interop.nuspec b/Package/ExcelDna.Interop/ExcelDna.Interop.nuspec index 1c583976..79e4c8c6 100644 --- a/Package/ExcelDna.Interop/ExcelDna.Interop.nuspec +++ b/Package/ExcelDna.Interop/ExcelDna.Interop.nuspec @@ -8,6 +8,7 @@ http://excel-dna.net images\ExcelDna-nu.png MIT + README.md false Provides a local copy of the Primary Interop Assemblies from Microsoft Office: Microsoft.Office.Interop.Excel.dll, Office.dll, Microsoft.Vbe.Interop.dll. Provides a selection of Excel Primary Interop Assemblies @@ -28,5 +29,6 @@ + diff --git a/Package/ExcelDna.Interop/README.md b/Package/ExcelDna.Interop/README.md new file mode 100644 index 00000000..eae0d1bd --- /dev/null +++ b/Package/ExcelDna.Interop/README.md @@ -0,0 +1,11 @@ +# ExcelDna.Interop + +This package provides local copies of these Microsoft Office primary interop assemblies: + +- `Microsoft.Office.Interop.Excel.dll` +- `Microsoft.Vbe.Interop.dll` +- `office.dll` + +The assemblies are available for .NET Framework 4.5.2 and `net6.0-windows7.0` projects. The package configures C# and Visual Basic projects to embed the interop types and not copy the assemblies to the output directory. F# projects are excluded from that build behavior. + +For Excel-DNA documentation, see [excel-dna.net](https://excel-dna.net/). diff --git a/Package/package-interop.cmd b/Package/package-interop.cmd index a41ad1df..3f23fe1f 100644 --- a/Package/package-interop.cmd +++ b/Package/package-interop.cmd @@ -12,13 +12,13 @@ if not exist "%outputPath%" mkdir "%outputPath%" echo on -nuget.exe pack "%basePath%\Excel-DNA.Interop\Excel-DNA.Interop.nuspec" -BasePath "%basePath%\Excel-DNA.Interop" -OutputDirectory "%outputPath%" -Verbosity detailed -NonInteractive -Prop ExcelDnaVersion="%ExcelDnaVersion%" +"%basePath%\nuget.exe" pack "%basePath%\Excel-DNA.Interop\Excel-DNA.Interop.nuspec" -BasePath "%basePath%\Excel-DNA.Interop" -OutputDirectory "%outputPath%" -Verbosity detailed -NonInteractive -Prop ExcelDnaVersion="%ExcelDnaVersion%" @if errorlevel 1 goto end -nuget.exe pack "%basePath%\ExcelDna.Interop\ExcelDna.Interop.nuspec" -BasePath "%basePath%\ExcelDna.Interop" -OutputDirectory "%outputPath%" -Verbosity detailed -NonInteractive +"%basePath%\nuget.exe" pack "%basePath%\ExcelDna.Interop\ExcelDna.Interop.nuspec" -BasePath "%basePath%\ExcelDna.Interop" -OutputDirectory "%outputPath%" -Verbosity detailed -NonInteractive @if errorlevel 1 goto end -nuget.exe pack "%basePath%\ExcelDna.Interop.Dao\ExcelDna.Interop.Dao.nuspec" -BasePath "%basePath%\ExcelDna.Interop.Dao" -OutputDirectory "%outputPath%" -Verbosity detailed -NonInteractive +"%basePath%\nuget.exe" pack "%basePath%\ExcelDna.Interop.Dao\ExcelDna.Interop.Dao.nuspec" -BasePath "%basePath%\ExcelDna.Interop.Dao" -OutputDirectory "%outputPath%" -Verbosity detailed -NonInteractive @if errorlevel 1 goto end :end diff --git a/Package/push-interop.cmd b/Package/push-interop.cmd index 131279a8..8816a62a 100644 --- a/Package/push-interop.cmd +++ b/Package/push-interop.cmd @@ -7,13 +7,13 @@ set outputPath=%basePath%\nupkg if not exist "%outputPath%" mkdir "%outputPath%" -nuget.exe push "%outputPath%\Excel-DNA.Interop.15.0.1.nupkg" -Source https://api.nuget.org/v3/index.json -Verbosity detailed -NonInteractive +nuget.exe push "%outputPath%\Excel-DNA.Interop.16.0.0.nupkg" -Source https://api.nuget.org/v3/index.json -Verbosity detailed -NonInteractive @if errorlevel 1 goto end -nuget.exe push "%outputPath%\ExcelDna.Interop.15.0.1.nupkg" -Source https://api.nuget.org/v3/index.json -Verbosity detailed -NonInteractive +nuget.exe push "%outputPath%\ExcelDna.Interop.16.0.0.nupkg" -Source https://api.nuget.org/v3/index.json -Verbosity detailed -NonInteractive @if errorlevel 1 goto end -nuget.exe push "%outputPath%\ExcelDna.Interop.Dao.15.0.1.nupkg" -Source https://api.nuget.org/v3/index.json -Verbosity detailed -NonInteractive +nuget.exe push "%outputPath%\ExcelDna.Interop.Dao.16.0.0.nupkg" -Source https://api.nuget.org/v3/index.json -Verbosity detailed -NonInteractive @if errorlevel 1 goto end :end diff --git a/Source/ExcelDna.Integration/ComInterop/ComServer.cs b/Source/ExcelDna.Integration/ComInterop/ComServer.cs index 6f0fcfa3..0ab571a4 100644 --- a/Source/ExcelDna.Integration/ComInterop/ComServer.cs +++ b/Source/ExcelDna.Integration/ComInterop/ComServer.cs @@ -95,6 +95,7 @@ internal static HRESULT DllGetClassObject(CLSID clsid, IID iid, out IntPtr ppunk ComWrappers cw = new System.Runtime.InteropServices.Marshalling.StrategyBasedComWrappers(); nint ptr = cw.GetOrCreateComInterfaceForObject(factory, CreateComInterfaceFlags.None); HRESULT hrQI = Marshal.QueryInterface(ptr, in iid, out ppunk); + Marshal.Release(ptr); #else IntPtr punkFactory = Marshal.GetIUnknownForObject(factory); HRESULT hrQI = Marshal.QueryInterface(punkFactory, ref iid, out ppunk); diff --git a/Source/ExcelDna.Integration/ComInterop/DummyComAddIn.cs b/Source/ExcelDna.Integration/ComInterop/DummyComAddIn.cs index 67451754..65e051e4 100644 --- a/Source/ExcelDna.Integration/ComInterop/DummyComAddIn.cs +++ b/Source/ExcelDna.Integration/ComInterop/DummyComAddIn.cs @@ -14,24 +14,29 @@ namespace ExcelDna.Integration.ComInterop [GeneratedComClass] internal partial class DummyComAddIn : Generator.Interfaces.IDTExtensibility2 { + private const int S_OK = 0; + private const int E_NOTIMPL = unchecked((int)0x80004001); + public int GetTypeInfoCount(out uint pctinfo) { - throw new NotImplementedException(); + pctinfo = 0; + return S_OK; } public int GetTypeInfo(uint iTInfo, uint lcid, out nint ppTInfo) { - throw new NotImplementedException(); + ppTInfo = 0; + return E_NOTIMPL; } - public int GetIDsOfNames(Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, [In][Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] rgDispId) + public int GetIDsOfNames(in Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, [In][Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] rgDispId) { - throw new NotImplementedException(); + return E_NOTIMPL; } - public int Invoke(int dispIdMember, Guid riid, uint lcid, INVOKEKIND wFlags, [MarshalUsing(typeof(Generator.Interfaces.DispParamsMarshaller))] in Generator.Interfaces.DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) + public int Invoke(int dispIdMember, in Guid riid, uint lcid, ushort wFlags, [MarshalUsing(typeof(Generator.Interfaces.DispParamsMarshaller))] in Generator.Interfaces.DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) { - throw new NotImplementedException(); + return E_NOTIMPL; } #region IDTExtensibility2 interface diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/ExcelObserverRtdServer.cs b/Source/ExcelDna.Integration/ComInterop/Generator/ExcelObserverRtdServer.cs index c597f961..ee948081 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/ExcelObserverRtdServer.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/ExcelObserverRtdServer.cs @@ -12,6 +12,9 @@ namespace ExcelDna.Integration.ComInterop.Generator [GeneratedComClass] internal partial class ExcelObserverRtdServer : Rtd.ExcelObserverRtdServer, IRtdServer { + private const int S_OK = 0; + private const int E_NOTIMPL = unchecked((int)0x80004001); + private Dispatcher dispatcher; public ExcelObserverRtdServer() @@ -29,22 +32,24 @@ public ExcelObserverRtdServer() // IDispatch: public int GetTypeInfoCount(out uint pctinfo) { - throw new NotImplementedException(); + pctinfo = 0; + return S_OK; } public int GetTypeInfo(uint iTInfo, uint lcid, out nint ppTInfo) { - throw new NotImplementedException(); + ppTInfo = 0; + return E_NOTIMPL; } - public int GetIDsOfNames(Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] int[] rgDispId) + public int GetIDsOfNames(in Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] int[] rgDispId) { dispatcher.GetIDsOfNames(rgszNames, rgDispId); return 0; } - public int Invoke(int dispIdMember, Guid riid, uint lcid, INVOKEKIND wFlags, [MarshalUsing(typeof(DispParamsMarshaller))] in DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) + public int Invoke(int dispIdMember, in Guid riid, uint lcid, ushort wFlags, [MarshalUsing(typeof(DispParamsMarshaller))] in DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) { dispatcher.Invoke(dispIdMember, pDispParams, pVarResult); diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/ExcelRibbon.cs b/Source/ExcelDna.Integration/ComInterop/Generator/ExcelRibbon.cs index 99486d1e..62816b35 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/ExcelRibbon.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/ExcelRibbon.cs @@ -15,6 +15,10 @@ namespace ExcelDna.Integration.ComInterop.Generator [GeneratedComClass] internal partial class ExcelRibbon : ExcelComAddIn, Interfaces.IDTExtensibility2, Interfaces.IRibbonExtensibility { + private const int S_OK = 0; + private const int E_NOTIMPL = unchecked((int)0x80004001); + private const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); + private MethodInfo[] methods; private CustomUI.IExcelRibbon customRibbon; @@ -26,23 +30,29 @@ public ExcelRibbon(ITypeHelper t) public int GetTypeInfoCount(out uint pctinfo) { - throw new NotImplementedException(); + pctinfo = 0; + return S_OK; } public int GetTypeInfo(uint iTInfo, uint lcid, out nint ppTInfo) { - throw new NotImplementedException(); + ppTInfo = 0; + return E_NOTIMPL; } - public int GetIDsOfNames(Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, [In][Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] rgDispId) + public int GetIDsOfNames(in Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, [In][Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] rgDispId) { + bool foundAll = true; for (int i = 0; i < cNames; ++i) + { rgDispId[i] = (rgszNames[i] == "LoadImage") ? methods.Length : Array.FindIndex(methods, m => m?.Name == rgszNames[i]); + foundAll &= rgDispId[i] >= 0; + } - return 0; + return foundAll ? S_OK : DISP_E_UNKNOWNNAME; } - public int Invoke(int dispIdMember, Guid riid, uint lcid, INVOKEKIND wFlags, [MarshalUsing(typeof(Generator.Interfaces.DispParamsMarshaller))] in Generator.Interfaces.DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) + public int Invoke(int dispIdMember, in Guid riid, uint lcid, ushort wFlags, [MarshalUsing(typeof(Generator.Interfaces.DispParamsMarshaller))] in Generator.Interfaces.DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) { try { @@ -139,8 +149,17 @@ public virtual void OnBeginShutdown(ref Generator.Interfaces.SafeArray custom) public int GetCustomUI([MarshalAs(UnmanagedType.BStr)] string RibbonID, [MarshalAs(UnmanagedType.BStr)] out string result) { - result = customRibbon.GetCustomUI(RibbonID); - return 0; + try + { + result = customRibbon.GetCustomUI(RibbonID); + return S_OK; + } + catch (Exception ex) + { + ExcelDna.Logging.Logger.ComAddIn.Error(ex, "Ribbon GetCustomUI failed"); + result = string.Empty; + return E_NOTIMPL; + } } #endregion diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/ArrayMarshaller.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/ArrayMarshaller.cs index 3685f110..603fb15f 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/ArrayMarshaller.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/ArrayMarshaller.cs @@ -1,16 +1,30 @@ #if COM_GENERATED +using System; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ExcelDna.Integration.ComInterop.Generator.Interfaces { internal static class ArrayMarshaller { - public unsafe static nint ArrayToPtr(T[] str) + public static nint ArrayToPtr(T[] values) { - return (nint)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(str)); + if (values == null || values.Length == 0) + return nint.Zero; + + int size = Marshal.SizeOf(); + nint result = Marshal.AllocHGlobal(size * values.Length); + for (int i = 0; i < values.Length; ++i) + Marshal.StructureToPtr(values[i], result + i * size, false); + + return result; + } + + public static void FreePtr(nint ptr) + { + if (ptr != nint.Zero) + Marshal.FreeHGlobal(ptr); } public static T[] PtrToArray<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(nint str, int len) diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispParamsMarshaller.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispParamsMarshaller.cs index e0edf5ef..c1fb0134 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispParamsMarshaller.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispParamsMarshaller.cs @@ -11,11 +11,18 @@ internal static class DispParamsMarshaller { public static unsafe DispParamsNative ConvertToUnmanaged(DispParams managed) { + int* rgdispidNamedArgs = null; + if (managed.rgdispidNamedArgs != 0) + { + rgdispidNamedArgs = (int*)Marshal.AllocHGlobal(sizeof(int)); + *rgdispidNamedArgs = managed.rgdispidNamedArgs; + } + return new DispParamsNative { cArgs = managed.cArgs, cNamedArgs = managed.cNamedArgs, - rgdispidNamedArgs = managed.rgdispidNamedArgs != 0 ? &managed.rgdispidNamedArgs : null, + rgdispidNamedArgs = rgdispidNamedArgs, rgvarg = managed.rgvarg != null ? ArrayMarshaller.ArrayToPtr(managed.rgvarg.Reverse().Select(VariantMarshaller.ConvertToUnmanaged).ToArray()) @@ -39,6 +46,22 @@ public static unsafe DispParams ConvertToManaged(DispParamsNative unmanaged) }; } + public static unsafe void Free(DispParamsNative unmanaged) + { + if (unmanaged.rgvarg != 0) + { + int size = Marshal.SizeOf(); + for (int i = 0; i < unmanaged.cArgs; ++i) + { + VariantMarshaller.Free(Marshal.PtrToStructure(unmanaged.rgvarg + i * size)); + } + ArrayMarshaller.FreePtr(unmanaged.rgvarg); + } + + if (unmanaged.rgdispidNamedArgs != null) + Marshal.FreeHGlobal((nint)unmanaged.rgdispidNamedArgs); + } + public static void UpdateArg(DispParams dp, Variant v, int i) { int ri = dp.cArgs - 1 - i; diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispatchObject.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispatchObject.cs index b179f863..6f67143c 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispatchObject.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/DispatchObject.cs @@ -106,7 +106,7 @@ private int[] GetDispIDs(string propName) var dispIds = new int[names.Length]; var hr = dispatch!.GetIDsOfNames( - emptyGuid, + in emptyGuid, names, (uint)names.Length, LOCALE_USER_DEFAULT, @@ -126,9 +126,9 @@ private int[] GetDispIDs(string propName) var hr = dispatch!.Invoke( dispIds[0], - emptyGuid, + in emptyGuid, LOCALE_USER_DEFAULT, - kind, + (ushort)kind, dispParams, variantResult.Ptr, 0, diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IDispatch.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IDispatch.cs index d176a244..806be2e4 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IDispatch.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IDispatch.cs @@ -19,7 +19,7 @@ internal partial interface IDispatch [PreserveSig] int GetIDsOfNames( - Guid riid, + in Guid riid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] rgszNames, uint cNames, uint lcid, @@ -29,9 +29,9 @@ int GetIDsOfNames( [PreserveSig] int Invoke( int dispIdMember, - Guid riid, + in Guid riid, uint lcid, - INVOKEKIND wFlags, + ushort wFlags, [MarshalUsing(typeof(DispParamsMarshaller))] in DispParams pDispParams, nint pVarResult, nint pExcepInfo, diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IRibbonControl.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IRibbonControl.cs index 9e6eb2d2..42bee2b9 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IRibbonControl.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/IRibbonControl.cs @@ -13,7 +13,7 @@ internal partial interface IRibbonControl : IDispatch int get_Id([MarshalAs(UnmanagedType.BStr)] out string result); [PreserveSig] - int get_Context(nint result); + int get_Context(out nint result); [PreserveSig] int get_Tag([MarshalAs(UnmanagedType.BStr)] out string result); diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/SafeArray.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/SafeArray.cs index 16d4fe73..66d1635e 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/SafeArray.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/SafeArray.cs @@ -23,6 +23,9 @@ internal partial struct SafeArray [System.Runtime.InteropServices.DllImport("oleaut32.dll", ExactSpelling = true)] public static unsafe extern /*SafeArray**/nint SafeArrayCreate(ushort vt, uint cDims, /*SAFEARRAYBOUND**/nint rgsabound); + [System.Runtime.InteropServices.DllImport("oleaut32.dll", ExactSpelling = true)] + public static extern int SafeArrayDestroy(nint psa); + public ushort cDims; public ushort fFeatures; public uint cbElements; diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/UnknownObject.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/UnknownObject.cs index d59dd371..d79e5206 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/UnknownObject.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/UnknownObject.cs @@ -1,6 +1,7 @@ #if COM_GENERATED using System; +using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; namespace ExcelDna.Integration.ComInterop.Generator.Interfaces @@ -17,7 +18,11 @@ public UnknownObject(IntPtr unknown) public unsafe bool HasInterface(ref Guid guid) { StrategyBasedComWrappers.DefaultIUnknownStrategy.QueryInterface(P.ToPointer(), in guid, out void* ppObj); - return ppObj != null; + if (ppObj == null) + return false; + + Marshal.Release((IntPtr)ppObj); + return true; } public unsafe int QueryInterface(ref Guid guid, out IntPtr ppv) diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantMarshaller.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantMarshaller.cs index bea5d735..5139898e 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantMarshaller.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantMarshaller.cs @@ -111,6 +111,7 @@ private static VariantNative VariantArrayToUnmanaged(Array array) Marshal.StructureToPtr(new SAFEARRAYBOUND { cElements = (uint)array.GetLength(1), lLbound = 0 }, pBounds + Marshal.SizeOf(), false); nint psa = SafeArray.SafeArrayCreate((ushort)VariantTypeNative.VT_VARIANT, (uint)array.Rank, pBounds); + Marshal.FreeHGlobal(pBounds); SafeArray sa = Marshal.PtrToStructure(psa); for (int col = 0; col < array.GetLength(1); ++col) @@ -146,6 +147,21 @@ public static void UpdateRefBool(VariantNative unmanaged, bool v) short boolVal = v ? (short)VariantBoolNative.VARIANT_TRUE : (short)VariantBoolNative.VARIANT_FALSE; Marshal.StructureToPtr(boolVal, unmanaged.pboolVal, false); } + + public static void Free(VariantNative unmanaged) + { + switch ((VariantTypeNative)unmanaged.vt) + { + case VariantTypeNative.VT_BSTR: + if (unmanaged.bstrVal != 0) + Marshal.FreeBSTR(unmanaged.bstrVal); + break; + case VT_VARIANT_ARRAY: + if (unmanaged.parray != 0) + SafeArray.SafeArrayDestroy(unmanaged.parray); + break; + } + } } } diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantResultMarshaller.cs b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantResultMarshaller.cs index c86ee340..3dc44360 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantResultMarshaller.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/Interfaces/VariantResultMarshaller.cs @@ -14,6 +14,7 @@ internal class VariantResultMarshaller : IDisposable public VariantResultMarshaller() { Ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(default(VariantNative), Ptr, false); } public Variant GetResult() @@ -26,6 +27,7 @@ protected virtual void Dispose(bool disposing) { if (!disposedValue) { + VariantMarshaller.Free(Marshal.PtrToStructure(Ptr)); Marshal.FreeHGlobal(Ptr); disposedValue = true; } diff --git a/Source/ExcelDna.Integration/ComInterop/Generator/RTDUpdateEvent.cs b/Source/ExcelDna.Integration/ComInterop/Generator/RTDUpdateEvent.cs index 47193892..2ab5deee 100644 --- a/Source/ExcelDna.Integration/ComInterop/Generator/RTDUpdateEvent.cs +++ b/Source/ExcelDna.Integration/ComInterop/Generator/RTDUpdateEvent.cs @@ -10,6 +10,9 @@ namespace ExcelDna.Integration.ComInterop.Generator [GeneratedComClass] internal partial class RTDUpdateEvent : Rtd.IRTDUpdateEvent, IRTDUpdateEvent { + private const int S_OK = 0; + private const int E_NOTIMPL = unchecked((int)0x80004001); + private IRTDUpdateEvent impl; public RTDUpdateEvent(IRTDUpdateEvent impl) @@ -61,22 +64,24 @@ void IRTDUpdateEvent.Disconnect() int IDispatch.GetTypeInfoCount(out uint pctinfo) { - throw new NotImplementedException(); + pctinfo = 0; + return S_OK; } int IDispatch.GetTypeInfo(uint iTInfo, uint lcid, out nint ppTInfo) { - throw new NotImplementedException(); + ppTInfo = 0; + return E_NOTIMPL; } - int IDispatch.GetIDsOfNames(Guid riid, string[] rgszNames, uint cNames, uint lcid, int[] rgDispId) + int IDispatch.GetIDsOfNames(in Guid riid, string[] rgszNames, uint cNames, uint lcid, int[] rgDispId) { - throw new NotImplementedException(); + return E_NOTIMPL; } - int IDispatch.Invoke(int dispIdMember, Guid riid, uint lcid, INVOKEKIND wFlags, in DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) + int IDispatch.Invoke(int dispIdMember, in Guid riid, uint lcid, ushort wFlags, in DispParams pDispParams, nint pVarResult, nint pExcepInfo, nint puArgErr) { - throw new NotImplementedException(); + return E_NOTIMPL; } } } diff --git a/Source/ExcelDna.Integration/ComInterop/Util.cs b/Source/ExcelDna.Integration/ComInterop/Util.cs index 3f79d715..fdc05bc4 100644 --- a/Source/ExcelDna.Integration/ComInterop/Util.cs +++ b/Source/ExcelDna.Integration/ComInterop/Util.cs @@ -30,12 +30,20 @@ public static IType TypeAdapter public static int QueryInterfaceForObject(object o, Guid guid, out IntPtr ppv) { Guid iid = guid; + IntPtr pUnk = GetIUnknownForObject(o); - return Marshal.QueryInterface(GetIUnknownForObject(o), + try + { + return Marshal.QueryInterface(pUnk, #if !COM_GENERATED - ref + ref #endif - iid, out ppv); + iid, out ppv); + } + finally + { + Marshal.Release(pUnk); + } } private static IntPtr GetIUnknownForObject(object o) diff --git a/Source/ExcelDna.Integration/CustomUI/RibbonControl.cs b/Source/ExcelDna.Integration/CustomUI/RibbonControl.cs index 2c6ba367..f5b77224 100644 --- a/Source/ExcelDna.Integration/CustomUI/RibbonControl.cs +++ b/Source/ExcelDna.Integration/CustomUI/RibbonControl.cs @@ -1,4 +1,6 @@ -#if COM_GENERATED +#if COM_GENERATED + +using System.Runtime.InteropServices; namespace ExcelDna.Integration.CustomUI { @@ -24,6 +26,19 @@ public string Tag } } + public object Context + { + get + { + int hr = control.get_Context(out nint result); + Marshal.ThrowExceptionForHR(hr); + if (result == 0) + return null; + + return new ComInterop.Generator.DynamicComObject(new ComInterop.Generator.Interfaces.DispatchObject(result)); + } + } + internal RibbonControl(ComInterop.Generator.Interfaces.IRibbonControl control) { this.control = control; diff --git a/Source/ExcelDna.SourceGenerator.NativeAOT/Generator.cs b/Source/ExcelDna.SourceGenerator.NativeAOT/Generator.cs index 29c2fe50..40c94868 100644 --- a/Source/ExcelDna.SourceGenerator.NativeAOT/Generator.cs +++ b/Source/ExcelDna.SourceGenerator.NativeAOT/Generator.cs @@ -78,7 +78,7 @@ public static short Initialize(void* xlAddInExportInfoAddress, void* hModuleXll, { if (actions.Length > 0) actions += ",\r\n"; - actions += $"typeof({Util.GetFullTypeName(m.ContainingType)}).GetMethod(\"{m.Name}\")!"; + actions += GetMethod(m); } addIns += $"{regHost}.ExcelAddIns.Add(new ExcelDna.Integration.TypeHelper<{Util.GetFullTypeName(i.Type)}>([{actions}]));\r\n"; @@ -221,7 +221,18 @@ public void Initialize(GeneratorInitializationContext context) private static string GetMethod(IMethodSymbol method) { - return $"typeof({Util.GetFullTypeName(method.ContainingType)}).GetMethod(\"{method.Name}\")!"; + return $"typeof({Util.GetFullTypeName(method.ContainingType)}).GetMethod(\"{method.Name}\", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] {{ {GetParameterTypes(method)} }}, null)!"; + } + + private static string GetParameterTypes(IMethodSymbol method) + { + return string.Join(", ", method.Parameters.Select(GetParameterType)); + } + + private static string GetParameterType(IParameterSymbol parameter) + { + string type = $"typeof({Util.GetFullTypeOfTypeName(parameter.Type)})"; + return parameter.RefKind == RefKind.None ? type : $"{type}.MakeByRefType()"; } private static string GetTypeRefs(string methodType, string extendedMethodType, int parameterCount) diff --git a/Source/ExcelDna.SourceGenerator.NativeAOT/Util.cs b/Source/ExcelDna.SourceGenerator.NativeAOT/Util.cs index 96197e63..f41bd362 100644 --- a/Source/ExcelDna.SourceGenerator.NativeAOT/Util.cs +++ b/Source/ExcelDna.SourceGenerator.NativeAOT/Util.cs @@ -11,6 +11,11 @@ public static string GetFullTypeName(ITypeSymbol type) return type.ToDisplayString().Replace("[*,*]", "[,]"); } + public static string GetFullTypeOfTypeName(ITypeSymbol type) + { + return type.ToDisplayString(FullTypeOfTypeNameFormat).Replace("[*,*]", "[,]"); + } + public static string GetFullGenericTypeName(INamedTypeSymbol type) { return type.ToDisplayString(FullGenericNameFormat); @@ -342,5 +347,9 @@ static bool IsString2DArray(ITypeSymbol type) private static SymbolDisplayFormat FullNameFormat = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); private static SymbolDisplayFormat FullGenericNameFormat = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.None); + private static SymbolDisplayFormat FullTypeOfTypeNameFormat = new SymbolDisplayFormat( + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes); } } diff --git a/Source/ExcelDna.TestAOT/ExcelDna.TestAOT.csproj b/Source/ExcelDna.TestAOT/ExcelDna.TestAOT.csproj index 563c64f8..7058ea00 100644 --- a/Source/ExcelDna.TestAOT/ExcelDna.TestAOT.csproj +++ b/Source/ExcelDna.TestAOT/ExcelDna.TestAOT.csproj @@ -8,23 +8,29 @@ true - win-x64 + win-x64 true true - ExcelDna.TestAOT64 enable enable true + + x86 + x64 + 64 + ExcelDna.TestAOT$(ExcelDnaNativeAOTBitnessSuffix) + $(MSBuildProjectDirectory)\..\ExcelDna.Host.NativeAOT\bin\$(Configuration)\$(ExcelDnaNativeAOTPlatform)\ExcelDna.Host.NativeAOT.$(ExcelDnaNativeAOTPlatform).xll + true - + @@ -39,8 +45,12 @@ - - + + + diff --git a/Source/Tests/ExcelDna.AddIn.RuntimeTestsAOT/ExcelDna.AddIn.RuntimeTestsAOT.csproj b/Source/Tests/ExcelDna.AddIn.RuntimeTestsAOT/ExcelDna.AddIn.RuntimeTestsAOT.csproj index 5fb026ef..26368308 100644 --- a/Source/Tests/ExcelDna.AddIn.RuntimeTestsAOT/ExcelDna.AddIn.RuntimeTestsAOT.csproj +++ b/Source/Tests/ExcelDna.AddIn.RuntimeTestsAOT/ExcelDna.AddIn.RuntimeTestsAOT.csproj @@ -8,31 +8,41 @@ true - win-x64 + win-x64 true true - ExcelDna.AddIn.RuntimeTestsAOT64 enable enable true + + x86 + x64 + 64 + ExcelDna.AddIn.RuntimeTestsAOT$(ExcelDnaNativeAOTBitnessSuffix) + $(MSBuildProjectDirectory)\..\..\ExcelDna.Host.NativeAOT\bin\$(Configuration)\$(ExcelDnaNativeAOTPlatform)\ExcelDna.Host.NativeAOT.$(ExcelDnaNativeAOTPlatform).xll + true - + - - + + + diff --git a/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/Generator.cs b/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/Generator.cs index 5abeb2cf..907d8d6b 100644 --- a/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/Generator.cs +++ b/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/Generator.cs @@ -27,7 +27,7 @@ public static string NativeParamsJoinString(string separator, params string[] va } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeParamsJoinString")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeParamsJoinString", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string[]) }, null)!); typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc2)); @@ -68,7 +68,7 @@ public static string NativeOptional(object refRange, double targetValue, int max } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeOptional")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeOptional", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(object), typeof(double), typeof(int), typeof(bool) }, null)!); typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc4)); @@ -110,7 +110,7 @@ public static Task NativeTaskBool() } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeTaskBool")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeTaskBool", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { }, null)!); typeRefs.Add(typeof(Func>)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc0>)); @@ -183,7 +183,7 @@ public static IObservable NativeStringObservable(string s) } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeStringObservable")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeStringObservable", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(string) }, null)!); typeRefs.Add(typeof(Func>)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc1>)); @@ -220,7 +220,7 @@ public static bool NativeAsyncBool() } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeAsyncBool")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeAsyncBool", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { }, null)!); typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc0)); @@ -257,7 +257,7 @@ public static bool NativeAsyncArgs17(int i1, int i2, int i3, int i4, int i5, int } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeAsyncArgs17")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeAsyncArgs17", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) }, null)!); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc17)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc17)); @@ -335,7 +335,7 @@ public static Version ToVersion(string s) } } """, parameterConversions: """ - ExcelDna.Registration.StaticRegistration.ExcelParameterConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("ToVersion")!); + ExcelDna.Registration.StaticRegistration.ExcelParameterConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("ToVersion", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(string) }, null)!); """); } @@ -369,7 +369,7 @@ public static string NativeVersion17(int i1, int i2, int i3, int i4, int i5, int } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeVersion17")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeVersion17", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(System.Version) }, null)!); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc17)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc17)); @@ -411,7 +411,7 @@ public static string NativeVersion17(int i1, int i2, int i3, int i4, int i5, int List methodRefs = new List(); """, parameterConversions: """ - ExcelDna.Registration.StaticRegistration.ExcelParameterConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("ToVersion")!); + ExcelDna.Registration.StaticRegistration.ExcelParameterConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("ToVersion", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(string) }, null)!); """); } @@ -445,7 +445,7 @@ public static string FromTestType1(TestType1 value) } """, returnConversions: """ - ExcelDna.Registration.StaticRegistration.ExcelReturnConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("FromTestType1")!); + ExcelDna.Registration.StaticRegistration.ExcelReturnConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("FromTestType1", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(ExcelDna.AddIn.RuntimeTestsAOT.TestType1) }, null)!); """); } @@ -487,7 +487,7 @@ public static TestType1 NativeReturnTestType1(string s) } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeReturnTestType1")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeReturnTestType1", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(string) }, null)!); typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc1)); @@ -502,7 +502,7 @@ public static TestType1 NativeReturnTestType1(string s) List methodRefs = new List(); """, returnConversions: """ - ExcelDna.Registration.StaticRegistration.ExcelReturnConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("FromTestType1")!); + ExcelDna.Registration.StaticRegistration.ExcelReturnConversions.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Conversions).GetMethod("FromTestType1", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(ExcelDna.AddIn.RuntimeTestsAOT.TestType1) }, null)!); """); } @@ -537,7 +537,7 @@ public static double NativeCalcSum([ExcelHandle] Calc c) } """, functions: """ List typeRefs = new List(); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeCreateCalc")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeCreateCalc", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(double), typeof(double) }, null)!); typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc2)); @@ -551,7 +551,7 @@ public static double NativeCalcSum([ExcelHandle] Calc c) typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); - ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeCalcSum")!); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeCalcSum", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(ExcelDna.AddIn.RuntimeTestsAOT.Calc) }, null)!); typeRefs.Add(typeof(Func)); typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc1)); @@ -587,7 +587,75 @@ public static IFunctionExecutionHandler LoggingHandlerSelector(IExcelFunctionInf } """, executionHandlers: """ - ExcelDna.Registration.StaticRegistration.ExcelFunctionExecutionHandlerSelectors.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.FunctionLoggingHandler).GetMethod("LoggingHandlerSelector")!); + ExcelDna.Registration.StaticRegistration.ExcelFunctionExecutionHandlerSelectors.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.FunctionLoggingHandler).GetMethod("LoggingHandlerSelector", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(ExcelDna.Integration.IExcelFunctionInfo) }, null)!); + """); + } + + [Fact] + public void OverloadedMethodsUseParameterTypes() + { + Verify(""" + using ExcelDna.Integration; + + namespace ExcelDna.AddIn.RuntimeTestsAOT + { + public class Functions + { + public static string NativeOverload(string value) + { + return value; + } + + [ExcelFunction] + public static string NativeOverload(double value) + { + return value.ToString(); + } + } + } + """, functions: """ + List typeRefs = new List(); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeOverload", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(double) }, null)!); + typeRefs.Add(typeof(Func)); + typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); + typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc1)); + typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); + typeRefs.Add(typeof(Func)); + typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); + + List methodRefs = new List(); + """); + } + + [Fact] + public void NullableReferenceParametersUseRuntimeTypesForMethodLookup() + { + Verify(""" + #nullable enable + using ExcelDna.Integration; + + namespace ExcelDna.AddIn.RuntimeTestsAOT + { + public class Functions + { + [ExcelFunction] + public static string? NativeNullable(string? value) + { + return value; + } + } + } + """, functions: """ + List typeRefs = new List(); + ExcelDna.Registration.StaticRegistration.MethodsForRegistration.Add(typeof(ExcelDna.AddIn.RuntimeTestsAOT.Functions).GetMethod("NativeNullable", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, new Type[] { typeof(string) }, null)!); + typeRefs.Add(typeof(Func)); + typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); + typeRefs.Add(typeof(ExcelDna.Integration.ExtendedFunc1)); + typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); + typeRefs.Add(typeof(Func)); + typeRefs.Add(typeof(System.Linq.Expressions.Expression>)); + + List methodRefs = new List(); """); } @@ -669,4 +737,3 @@ public static short Initialize(void* xlAddInExportInfoAddress, void* hModuleXll, } } } - diff --git a/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/RibbonDispatch.cs b/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/RibbonDispatch.cs index 7489003c..92b7975b 100644 --- a/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/RibbonDispatch.cs +++ b/Source/Tests/ExcelDna.SourceGenerator.NativeAOT.Tests/RibbonDispatch.cs @@ -37,7 +37,8 @@ public class TestRibbon : IExcelRibbon private static int GetDispId(ExcelRibbon ribbon, string name) { int[] ids = new int[1]; - ribbon.GetIDsOfNames(Guid.Empty, new[] { name }, 1, 0, ids); + Guid riid = Guid.Empty; + ribbon.GetIDsOfNames(in riid, new[] { name }, 1, 0, ids); return ids[0]; } @@ -53,7 +54,8 @@ private static ExcelRibbon CreateRibbon() try { Marshal.StructureToPtr(default(VariantNative), pVarResult, false); - ribbon.Invoke(dispId, Guid.Empty, 0, INVOKEKIND.INVOKE_FUNC, in dispParams, pVarResult, 0, 0); + Guid riid = Guid.Empty; + ribbon.Invoke(dispId, in riid, 0, (ushort)INVOKEKIND.INVOKE_FUNC, in dispParams, pVarResult, 0, 0); return VariantMarshaller.ConvertToManaged(Marshal.PtrToStructure(pVarResult)).Value; } finally @@ -78,6 +80,20 @@ public void GetIDsOfNames_FindsCallback_AndLoadImage() Assert.True(GetDispId(ribbon, "LoadImage") >= 0); } + [Fact] + public void IDispatch_TypeInfoProbes_ReturnHResults() + { + ExcelRibbon ribbon = CreateRibbon(); + + int countHr = ribbon.GetTypeInfoCount(out uint count); + int typeInfoHr = ribbon.GetTypeInfo(0, 0, out nint typeInfo); + + Assert.Equal(0, countHr); + Assert.Equal(0u, count); + Assert.NotEqual(0, typeInfoHr); + Assert.Equal(0, typeInfo); + } + [Fact] public void Invoke_GetLabel_MarshalsStringReturnValueBackToExcel() { @@ -108,7 +124,8 @@ public void Invoke_OnToggle_BindsPressedBoolArgument() ExcelRibbon ribbon = CreateRibbon(); TestRibbon.LastPressed = false; DispParams dispParams = new DispParams { cArgs = 2, rgvarg = new[] { new Variant(null), new Variant(true) } }; - ribbon.Invoke(GetDispId(ribbon, "OnToggle"), Guid.Empty, 0, INVOKEKIND.INVOKE_FUNC, in dispParams, 0, 0, 0); + Guid riid = Guid.Empty; + ribbon.Invoke(GetDispId(ribbon, "OnToggle"), in riid, 0, (ushort)INVOKEKIND.INVOKE_FUNC, in dispParams, 0, 0, 0); Assert.True(TestRibbon.LastPressed); } @@ -118,7 +135,8 @@ public void Invoke_OnChange_BindsTextStringArgument() ExcelRibbon ribbon = CreateRibbon(); TestRibbon.LastText = null; DispParams dispParams = new DispParams { cArgs = 2, rgvarg = new[] { new Variant(null), new Variant("hello") } }; - ribbon.Invoke(GetDispId(ribbon, "OnChange"), Guid.Empty, 0, INVOKEKIND.INVOKE_FUNC, in dispParams, 0, 0, 0); + Guid riid = Guid.Empty; + ribbon.Invoke(GetDispId(ribbon, "OnChange"), in riid, 0, (ushort)INVOKEKIND.INVOKE_FUNC, in dispParams, 0, 0, 0); Assert.Equal("hello", TestRibbon.LastText); } }