From 2ef482fe432e12a3054f0777d635c9566b213fc4 Mon Sep 17 00:00:00 2001 From: mavToday Date: Tue, 26 Nov 2024 17:30:16 +0100 Subject: [PATCH] Proposal --- src/dscom.client/AssemblyResolver.cs | 19 +-- src/dscom.client/Program.cs | 169 +++++++++++++++++++------- src/dscom/RegisterAssemblySettings.cs | 46 +++++++ src/dscom/RegistrationServices.cs | 12 ++ 4 files changed, 190 insertions(+), 56 deletions(-) create mode 100644 src/dscom/RegisterAssemblySettings.cs diff --git a/src/dscom.client/AssemblyResolver.cs b/src/dscom.client/AssemblyResolver.cs index 5255c9a8..3c0870d5 100644 --- a/src/dscom.client/AssemblyResolver.cs +++ b/src/dscom.client/AssemblyResolver.cs @@ -22,25 +22,20 @@ namespace dSPACE.Runtime.InteropServices; /// internal sealed class AssemblyResolver : AssemblyLoadContext, IDisposable { + private readonly string[] _paths; + private bool _disposedValue; - internal AssemblyResolver(TypeLibConverterOptions options) : base("dscom", isCollectible: options.ShouldEmbed()) + internal AssemblyResolver(string[] paths, bool isCollectible) + : base("dscom", isCollectible) { - Options = options; + _paths = paths; Resolving += Context_Resolving; } private Assembly? Context_Resolving(AssemblyLoadContext context, AssemblyName name) { - var dir = Path.GetDirectoryName(Options.Assembly); - - var asmPaths = Options.ASMPath; - if (Directory.Exists(dir)) - { - asmPaths = asmPaths.Prepend(dir).ToArray(); - } - - foreach (var path in asmPaths) + foreach (var path in _paths) { var dllToLoad = Path.Combine(path, $"{name.Name}.dll"); if (File.Exists(dllToLoad)) @@ -63,8 +58,6 @@ public Assembly LoadAssembly(string path) return LoadFromAssemblyPath(path); } - public TypeLibConverterOptions Options { get; } - private void Dispose(bool disposing) { if (!_disposedValue) diff --git a/src/dscom.client/Program.cs b/src/dscom.client/Program.cs index aec34a29..626419ce 100644 --- a/src/dscom.client/Program.cs +++ b/src/dscom.client/Program.cs @@ -15,6 +15,7 @@ using System.CommandLine; using System.CommandLine.NamingConventionBinder; using System.Diagnostics; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -27,58 +28,68 @@ public static class ConsoleApp public static int Main(string[] args) { var tlbexportCommand = new Command("tlbexport", "Export the assembly to the specified type library") { - new Argument("Assembly", "File name of assembly to parse"), - new Option(new[] {"--out", "/out"}, description: "File name of type library to be produced"), - new Option(new[] {"--tlbreference", "/tlbreference"}, description: "Type library used to resolve references", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] {"--tlbrefpath", "/tlbrefpath"}, description: "Path used to resolve referenced type libraries", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] {"--asmpath", "/asmpath"}, description: "Look for assembly references here", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] {"--silent", "/silent"}, description: "Suppresses all output except for errors"), - new Option(new[] {"--silence", "/silence"}, description: "Suppresses output for the given warning (Can not be used with /silent)", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] {"--verbose", "/verbose"}, description: "Detailed log output"), - new Option(new[] {"--names", "/names"}, description: "A file in which each line specifies the capitalization of a name in the type library.", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] { "--overridename", "/overridename"}, description: "Overwrites the library name"), - new Option(new[] {"--overridetlbid", "/overridetlbid"}, description: "Overwrites the library id"), - new Option(new[] {"--createmissingdependenttlbs", "/createmissingdependenttlbs"}, description: "Generate missing type libraries for referenced assemblies. (default true)"), - new Option(new[] { "--embed", "/embed"}, () => TypeLibConverterOptions.NotSpecifiedViaCommandLineArgumentsDefault, description: "Embeds type library into the assembly. (default: false)") { Arity = ArgumentArity.ZeroOrOne }, - new Option(new[] {"--index", "/index"}, () => 1, description: "If the switch --embed is specified, the index indicates the resource ID to be used for the embedded type library. Must be a number between 1 and 65535. Ignored if --embed not present. (default 1)") - }; + new Argument("Assembly", "File name of assembly to parse"), + new Option(new[] {"--out", "/out"}, description: "File name of type library to be produced"), + new Option(new[] {"--tlbreference", "/tlbreference"}, description: "Type library used to resolve references", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--tlbrefpath", "/tlbrefpath"}, description: "Path used to resolve referenced type libraries", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--asmpath", "/asmpath"}, description: "Look for assembly references here", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--silent", "/silent"}, description: "Suppresses all output except for errors"), + new Option(new[] {"--silence", "/silence"}, description: "Suppresses output for the given warning (Can not be used with /silent)", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--verbose", "/verbose"}, description: "Detailed log output"), + new Option(new[] {"--names", "/names"}, description: "A file in which each line specifies the capitalization of a name in the type library.", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] { "--overridename", "/overridename"}, description: "Overwrites the library name"), + new Option(new[] {"--overridetlbid", "/overridetlbid"}, description: "Overwrites the library id"), + new Option(new[] {"--createmissingdependenttlbs", "/createmissingdependenttlbs"}, description: "Generate missing type libraries for referenced assemblies. (default true)"), + new Option(new[] { "--embed", "/embed"}, () => TypeLibConverterOptions.NotSpecifiedViaCommandLineArgumentsDefault, description: "Embeds type library into the assembly. (default: false)") { Arity = ArgumentArity.ZeroOrOne }, + new Option(new[] {"--index", "/index"}, () => 1, description: "If the switch --embed is specified, the index indicates the resource ID to be used for the embedded type library. Must be a number between 1 and 65535. Ignored if --embed not present. (default 1)") + }; var tlbdumpCommand = new Command("tlbdump", "Dump a type library") - { - new Argument("TypeLibrary", "File name of type library"), - new Option(new[] {"--out", "/out"}, description: "File name of the output"), - new Option(new[] {"--tlbreference", "/tlbreference"}, description: "Type library used to resolve references", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] {"--tlbrefpath", "/tlbrefpath"}, description: "Path used to resolve referenced type libraries", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - new Option(new[] {"--filterregex", "/filterregex"}, description: "Regex to filter the output", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, - }; + { + new Argument("TypeLibrary", "File name of type library"), + new Option(new[] {"--out", "/out"}, description: "File name of the output"), + new Option(new[] {"--tlbreference", "/tlbreference"}, description: "Type library used to resolve references", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--tlbrefpath", "/tlbrefpath"}, description: "Path used to resolve referenced type libraries", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--filterregex", "/filterregex"}, description: "Regex to filter the output", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + }; var tlbregisterCommand = new Command("tlbregister", "Register a type library") - { - new Argument("TypeLibrary", "File name of type library"), - new Option(new[] {"--foruser", "/foruser"}, description: "Registered for use only by the calling user identity."), - }; + { + new Argument("TypeLibrary", "File name of type library"), + new Option(new[] {"--foruser", "/foruser"}, description: "Registered for use only by the calling user identity."), + }; var tlbunregisterCommand = new Command("tlbunregister", "Unregister a type library") - { - new Argument("TypeLibrary", "File name of type library"), - new Option(new[] {"--foruser", "/foruser"}, description: "Registered for use only by the calling user identity."), - }; + { + new Argument("TypeLibrary", "File name of type library"), + new Option(new[] {"--foruser", "/foruser"}, description: "Registered for use only by the calling user identity."), + }; var tlbembedCommand = new Command("tlbembed", "Embeds a source type library into a target file") - { - new Argument("SourceTypeLibrary","File name of type library"), - new Argument("TargetAssembly", "File name of target assembly to receive the type library as a resource"), - new Option(new[] {"--index", "/index"}, () => 1, description:"Index to use for resource ID for the type library. If omitted, defaults to 1. Must be a positive integer from 1 to 65535.") - }; + { + new Argument("SourceTypeLibrary","File name of type library"), + new Argument("TargetAssembly", "File name of target assembly to receive the type library as a resource"), + new Option(new[] {"--index", "/index"}, () => 1, description:"Index to use for resource ID for the type library. If omitted, defaults to 1. Must be a positive integer from 1 to 65535.") + }; + + var registerCommand = new Command("asmregister", "register assembly") + { + new Argument("TargetAssembly", "File name of target assembly to receive the type library as a resource"), + new Option(new[] {"--asmpath", "/asmpath"}, description: "Look for assembly references here", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] { "--tblrefpath", "/tblrefpath"}, description: "Look for type library references here", getDefaultValue: () => Array.Empty()) { Arity = ArgumentArity.ZeroOrMore}, + new Option(new[] {"--TLB", "/TLB"}, description: "Will create and register a typelibrary for the given assembly"), + new Option(new[] {"--codebase", "/codebase"}, description: "Will register the assembly with codebase"), + }; var rootCommand = new RootCommand - { - tlbexportCommand, - tlbdumpCommand, - tlbregisterCommand, - tlbunregisterCommand, - tlbembedCommand - }; + { + tlbexportCommand, + tlbdumpCommand, + tlbregisterCommand, + tlbunregisterCommand, + tlbembedCommand, + registerCommand + }; rootCommand.Description = $"dSPACE COM tools ({(Environment.Is64BitProcess ? "64Bit" : "32Bit")})"; @@ -87,6 +98,7 @@ public static int Main(string[] args) ConfigureTLBRegisterHandler(tlbregisterCommand); ConfigureTLBUnRegisterHandler(tlbunregisterCommand); ConfigureTLBEmbedHandler(tlbembedCommand); + ConfigureAsmRegisterHandler(registerCommand); return rootCommand.Invoke(args); } @@ -153,6 +165,61 @@ private static void ConfigureTLBEmbedHandler(Command tlbembedCommand) tlbembedCommand.Handler = CommandHandler.Create((settings) => TypeLibEmbedder.EmbedTypeLib(settings)); } + /// + /// Will register an assembly like regasm.exe + /// + /// + /// + private static void ConfigureAsmRegisterHandler(Command registerCommand) + { + registerCommand.Handler = CommandHandler.Create( + (options) => + { + try + { + var paths = options.ASMPath.Append(Path.GetDirectoryName(options.TargetAssembly)).ToArray(); + + using var assemblyResolver = new AssemblyResolver(paths!, false); + + if (!File.Exists(options.TargetAssembly)) + { + throw new FileNotFoundException($"File {options.TargetAssembly} not found."); + } + + var assembly = assemblyResolver.LoadAssembly(options.TargetAssembly); + + var lypeLibConvertOptions = new TypeLibConverterOptions() + { + ASMPath = paths!, + + TLBRefpath = options.TLBRefpath, + + Assembly = options.TargetAssembly, + + Out = Path.ChangeExtension(options.TargetAssembly, ".tlb") + }; + + if (options.TLB) + { + // Export TLB + ExportTypeLibraryImpl(assembly, lypeLibConvertOptions); + + // Register TLB + RegisterTypeLib(lypeLibConvertOptions.Out); + } + + var registrationService = new RegistrationServices(); + registrationService.RegisterAssembly(assembly, options.Codebase); + + return 0; + } + catch (Exception e) + { + return HandleException(e, "Failed to register assembly."); + } + }); + } + private static void ConfigureTLBExportHandler(Command tlbexportCommand) { tlbexportCommand.Handler = CommandHandler.Create((options) => @@ -191,7 +258,15 @@ private static void ConfigureTLBExportHandler(Command tlbexportCommand) [MethodImpl(MethodImplOptions.NoInlining)] private static void ExportTypeLibraryImpl(TypeLibConverterOptions options, out WeakReference weakRef) { - using var assemblyResolver = new AssemblyResolver(options); + var dir = Path.GetDirectoryName(options.Assembly); + + var asmPaths = options.ASMPath; + if (Directory.Exists(dir)) + { + asmPaths = asmPaths.Prepend(dir).ToArray(); + } + + using var assemblyResolver = new AssemblyResolver(asmPaths, options.ShouldEmbed()); weakRef = new WeakReference(assemblyResolver, trackResurrection: true); if (!File.Exists(options.Assembly)) { @@ -199,6 +274,14 @@ private static void ExportTypeLibraryImpl(TypeLibConverterOptions options, out W } var assembly = assemblyResolver.LoadAssembly(options.Assembly); + + ExportTypeLibraryImpl(assembly, options); + } + + private static void ExportTypeLibraryImpl( + Assembly assembly, + TypeLibConverterOptions options) + { var typeLibConverter = new TypeLibConverter(); var nameResolver = options.Names.Length > 0 ? NameResolver.Create(options.Names) : NameResolver.Create(assembly); var typeLib = typeLibConverter.ConvertAssemblyToTypeLib(assembly, options, new TypeLibExporterNotifySink(options, nameResolver)); diff --git a/src/dscom/RegisterAssemblySettings.cs b/src/dscom/RegisterAssemblySettings.cs new file mode 100644 index 00000000..0c52160d --- /dev/null +++ b/src/dscom/RegisterAssemblySettings.cs @@ -0,0 +1,46 @@ +// 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. + +namespace dSPACE.Runtime.InteropServices; + +/// +/// Represents the settings used by . +/// +public class RegisterAssemblySettings +{ + /// + /// Gets or sets target assembly path. + /// + public string TargetAssembly { get; set; } = string.Empty; + + /// + /// Gets or sets a path to a directory with assemblies. + /// + public string[] ASMPath { get; set; } = Array.Empty(); + + /// + /// Gets or sets a path to a directory with type libraries. + /// + public string[] TLBRefpath { get; set; } = Array.Empty(); + + /// + /// Get or sets the flag for creating a type library + /// + public bool TLB { get; set; } + + /// + /// Gets or sets the flag for registration with codebase + /// + public bool Codebase { get; set; } +} diff --git a/src/dscom/RegistrationServices.cs b/src/dscom/RegistrationServices.cs index 5f951d6a..d406df95 100644 --- a/src/dscom/RegistrationServices.cs +++ b/src/dscom/RegistrationServices.cs @@ -283,6 +283,18 @@ private static IReadOnlyCollection GetComRegistratableTypes(Assembly assem { static bool TypeMustBeRegistered(Type type) { + // Only public types are com visible + if (!type.IsPublic && !type.IsNestedPublic && !type.IsByRef) + { + return false; + } + + // Type could be defined as not com visible + if (!type.IsComVisible()) + { + return false; + } + if (type.IsGenericType) { return false;