diff --git a/dscom.sln b/dscom.sln index eb0ed44..8a321b3 100644 --- a/dscom.sln +++ b/dscom.sln @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dscom.build", "src\dscom.bu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "assembly4", "src\dscom.demo\assembly4\assembly4.csproj", "{375866D7-1313-408D-AE5A-A77E48711EDB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "assembly5", "src\dscom.demo\assembly5\assembly5.csproj", "{ACECABE4-AD32-4618-889F-E210D9564C8F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -95,6 +97,14 @@ Global {375866D7-1313-408D-AE5A-A77E48711EDB}.Release|Any CPU.Build.0 = Release|Any CPU {375866D7-1313-408D-AE5A-A77E48711EDB}.Release|x86.ActiveCfg = Release|x86 {375866D7-1313-408D-AE5A-A77E48711EDB}.Release|x86.Build.0 = Release|x86 + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Debug|x86.ActiveCfg = Debug|x86 + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Debug|x86.Build.0 = Debug|x86 + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Release|Any CPU.Build.0 = Release|Any CPU + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Release|x86.ActiveCfg = Release|x86 + {ACECABE4-AD32-4618-889F-E210D9564C8F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -109,6 +119,7 @@ Global {5B402A1B-18B1-4D88-804A-BC0E58EF3730} = {0A2E33B4-9DF7-4199-BC39-B0FC9C99FA97} {F8F68E57-CFFE-4EA5-9C1A-2CD9223B5D85} = {0A2E33B4-9DF7-4199-BC39-B0FC9C99FA97} {375866D7-1313-408D-AE5A-A77E48711EDB} = {CAAB6257-7EC0-484E-9593-B60CEE8F47D1} + {ACECABE4-AD32-4618-889F-E210D9564C8F} = {CAAB6257-7EC0-484E-9593-B60CEE8F47D1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3F23D9EA-A9D9-4CE5-94B5-050A9B68CA1C} diff --git a/src/dscom.demo/assembly5/ExplodeyBaseClass.cs b/src/dscom.demo/assembly5/ExplodeyBaseClass.cs new file mode 100644 index 0000000..1b7e75b --- /dev/null +++ b/src/dscom.demo/assembly5/ExplodeyBaseClass.cs @@ -0,0 +1,7 @@ +using System.Windows.Controls; + +namespace dSPACE.Runtime.InteropServices.DemoAssembly5; + +public class ExplodeyBaseClass : UserControl +{ +} diff --git a/src/dscom.demo/assembly5/ExplodeyMember.cs b/src/dscom.demo/assembly5/ExplodeyMember.cs new file mode 100644 index 0000000..2d7905f --- /dev/null +++ b/src/dscom.demo/assembly5/ExplodeyMember.cs @@ -0,0 +1,9 @@ +using System.Windows.Controls; + +namespace dSPACE.Runtime.InteropServices.DemoAssembly5; + +public class ExplodeyMember +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Example")] + public UserControl Control => null!; +} diff --git a/src/dscom.demo/assembly5/ExplodeyParameter.cs b/src/dscom.demo/assembly5/ExplodeyParameter.cs new file mode 100644 index 0000000..6deeca5 --- /dev/null +++ b/src/dscom.demo/assembly5/ExplodeyParameter.cs @@ -0,0 +1,13 @@ +using System.Windows.Controls; + +namespace dSPACE.Runtime.InteropServices.DemoAssembly5; + +public class ExplodeyParameter +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "example")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "example")] + public void Example(UserControl thing) + { + // nop + } +} diff --git a/src/dscom.demo/assembly5/IExportableType.cs b/src/dscom.demo/assembly5/IExportableType.cs new file mode 100644 index 0000000..cf4ffac --- /dev/null +++ b/src/dscom.demo/assembly5/IExportableType.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace dSPACE.Runtime.InteropServices.DemoAssembly5; + +[ComVisible(true), Guid("E27C3BDB-ACEF-44F9-8568-481D44681C04"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public interface IExportableType +{ + void DoIt(); +} diff --git a/src/dscom.demo/assembly5/Properties/AssemblyInfo.cs b/src/dscom.demo/assembly5/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ce565d0 --- /dev/null +++ b/src/dscom.demo/assembly5/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] diff --git a/src/dscom.demo/assembly5/assembly5.csproj b/src/dscom.demo/assembly5/assembly5.csproj new file mode 100644 index 0000000..34325f8 --- /dev/null +++ b/src/dscom.demo/assembly5/assembly5.csproj @@ -0,0 +1,25 @@ + + + + net6.0-windows;net48 + AnyCPU + 10.0 + enable + enable + dSPACE.Runtime.InteropServices.DemoAssembly5 + dSPACE.Runtime.InteropServices.DemoAssembly5 + true + Recommended + true + AnyCPU;x86 + true + + + true + + + + + + + \ No newline at end of file diff --git a/src/dscom/AssemblyExtensions.cs b/src/dscom/AssemblyExtensions.cs index 225f918..dbb9b4b 100644 --- a/src/dscom/AssemblyExtensions.cs +++ b/src/dscom/AssemblyExtensions.cs @@ -51,4 +51,24 @@ public static TypeLibIdentifier GetLibIdentifier(this Assembly assembly) { return assembly.GetLibIdentifier(Guid.Empty); } + + internal static IEnumerable GetLoadableTypes(this Assembly assembly) + { + return GetLoadableTypesAndLog(assembly, null); + } + + internal static IEnumerable GetLoadableTypesAndLog(this Assembly assembly, WriterContext? context) + { + try + { + return assembly.GetTypes(); + } + // https://stackoverflow.com/questions/7889228/how-to-prevent-reflectiontypeloadexception-when-calling-assembly-gettypes + catch (ReflectionTypeLoadException e) + { + context?.LogWarning($"Type library exporter encountered an error while processing '{assembly.GetName().Name}'. Error: {e.LoaderExceptions.First()!.Message}"); + + return e.Types.Where(t => t is not null)!; + } + } } diff --git a/src/dscom/MethodBaseExtensions.cs b/src/dscom/MethodBaseExtensions.cs new file mode 100644 index 0000000..fb456c1 --- /dev/null +++ b/src/dscom/MethodBaseExtensions.cs @@ -0,0 +1,43 @@ +// Copyright 2022 dSPACE GmbH, Mark Lechtermann, Matthias Nissen and Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; +using System.Reflection; + +namespace dSPACE.Runtime.InteropServices; + +/// +/// Extension methods for . +/// +[Browsable(false)] +internal static class MethodBaseExtensions +{ + /// + /// Return the parameters defined in a method, or an empty array if the + /// parameter types are unloadable + /// + /// The method to enumerate + /// The loadable parameters + internal static IEnumerable GetLoadableParameters(this MethodInfo method) + { + try + { + return method.GetParameters(); + } + catch + { + return Array.Empty(); + } + } +} diff --git a/src/dscom/TypeExtensions.cs b/src/dscom/TypeExtensions.cs new file mode 100644 index 0000000..0ace7ba --- /dev/null +++ b/src/dscom/TypeExtensions.cs @@ -0,0 +1,61 @@ +// Copyright 2022 dSPACE GmbH, Mark Lechtermann, Matthias Nissen and Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; +using System.Reflection; + +namespace dSPACE.Runtime.InteropServices; + +/// +/// Extension methods for . +/// +[Browsable(false)] +internal static class TypeExtensions +{ + /// + /// Return the members defined in a type, or an empty array if the + /// members cannot be enumerated. + /// + /// The type to enumerate + /// The members which were able to be enumerated + internal static IEnumerable GetLoadableMembers(this Type type) + { + try + { + return type.GetMembers(); + } + catch + { + return Array.Empty(); + } + } + + /// + /// Return the methods defined in a type, or an empty array if the + /// methods cannot be enumerated. + /// + /// The type to enumerate + /// The Methods which were able to be enumerated + internal static IEnumerable GetLoadableMethods(this Type type) + { + try + { + return type.GetMethods(); + } + catch + { + return Array.Empty(); + } + } +} diff --git a/src/dscom/names/ComAliasNameResolver.cs b/src/dscom/names/ComAliasNameResolver.cs index f033389..d3c379f 100644 --- a/src/dscom/names/ComAliasNameResolver.cs +++ b/src/dscom/names/ComAliasNameResolver.cs @@ -24,7 +24,7 @@ internal sealed class ComAliasNameResolver : INameResolver public ComAliasNameResolver(Assembly assembly) { - var comVisibleTypes = assembly.GetTypes().Where(t => t.IsPublic && t.GetCustomAttribute() != null); + var comVisibleTypes = assembly.GetLoadableTypes().Where(t => t.IsPublic && t.GetCustomAttribute() != null); var types = comVisibleTypes .Where(t => t.GetCustomAttribute() != null) .ToDictionary(t => t as object, t => t.GetCustomAttribute()?.Alias ?? string.Empty) @@ -35,7 +35,7 @@ public ComAliasNameResolver(Assembly assembly) } var members = comVisibleTypes - .SelectMany(t => t.GetMembers().Where(m => m.GetCustomAttribute() != null)) + .SelectMany(t => t.GetLoadableMembers().Where(m => m.GetCustomAttribute() != null)) .ToDictionary(m => m as object, m => m.GetCustomAttribute()?.Alias ?? string.Empty) ; foreach (var kv in members) @@ -44,8 +44,8 @@ public ComAliasNameResolver(Assembly assembly) } var parameters = comVisibleTypes - .SelectMany(t => t.GetMethods()) - .SelectMany(m => m.GetParameters().Where(p => p.GetCustomAttribute() != null)) + .SelectMany(t => t.GetLoadableMethods()) + .SelectMany(m => m.GetLoadableParameters().Where(p => p.GetCustomAttribute() != null)) .ToDictionary(p => p as object, p => p.GetCustomAttribute()?.Alias ?? string.Empty) ; foreach (var kv in parameters) diff --git a/src/dscom/writer/LibraryWriter.cs b/src/dscom/writer/LibraryWriter.cs index 8eb098c..c097d33 100644 --- a/src/dscom/writer/LibraryWriter.cs +++ b/src/dscom/writer/LibraryWriter.cs @@ -91,21 +91,7 @@ private void CollectAllTypes() var comVisibleAttributeAssembly = Assembly.GetCustomAttribute(); var typesAreVisibleForComByDefault = comVisibleAttributeAssembly == null || comVisibleAttributeAssembly.Value; - Type?[] types; - try - { - types = Assembly.GetTypes().ToArray(); - } - catch (ReflectionTypeLoadException e) - { - Context.LogWarning($"Type library exporter encountered an error while processing '{Assembly.GetName().Name}'. Error: {e.LoaderExceptions.First()!.Message}"); - - if (!e.Types.Any(t => t != null)) - { - throw e.LoaderExceptions.First()!; - } - types = e.Types; - } + var types = Assembly.GetLoadableTypesAndLog(Context); List classInterfaceWriters = new();