/// <summary> /// Constructor. /// </summary> /// <param name="console">Console to use to log events. /// Cannot be <c>null</c>.</param> /// <param name="prms"><see cref="Params"/> that determine how /// type libraries are exported from assemblies. Cannot be /// <c>null</c>. </param> /// <exception cref="ArgumentNullException"><paramref name="console"/> /// or <paramref name="prms"/> is <c>null</c>.</exception> public TypeLibHelper(CLConsole console, Params prms) { // Validate input. if (console == null) { throw new ArgumentNullException("console"); } if (prms == null) { throw new ArgumentNullException("prms"); } // Save reference to console. this.console = console; // Determine exporter flags. if (prms.useRegisteredOnly) { this.flags = TypeLibExporterFlags.OnlyReferenceRegistered; } else { this.flags = TypeLibExporterFlags.None; } }
/// <summary> /// Displays a header describing the program and displaying copyright /// information. /// </summary> /// <param name="console"><see cref="CLConsole"/> to use to output. /// Cannot be <c>null</c>.</param> private static void ShowCopyright(CLConsole console) { Debug.Assert(console != null); console.WriteLine("CL's .NET Assembly Registration Tool, version {0} (.NET {1})", GetThisAssemblyVersionAsString(), GetThisAssemblyRutimeVersion()); console.WriteLine(GetThisAssemblyCopyrightInfo()); console.WriteLine(); }
/// <summary> /// Displays usage information to the console. /// </summary> /// <param name="console"><see cref="CLConsole"/> to use to output. /// Cannot be <c>null</c>.</param> private static void ShowUsage(CLConsole console) { Debug.Assert(console != null); // The parser lib doesn't display a sample syntax, so display a little header to include one. console.WriteLine("Syntax: {0} <AssemblyName> [Options]", Path.GetFileName(Assembly.GetExecutingAssembly().Location)); // Now display the usage info returned by the parser. console.WriteLine("Options:"); console.WriteLine(CommandLineArguments.Parser.ArgumentsUsage(typeof(Params))); }
/// <summary> /// Runs the ClpArgAsm registration/unregistration tasks according to the /// given program arguments. /// </summary> /// <param name="args">Program arguments.</param> /// <returns>Program exit code. 0 if everything went well.</returns> public static int Run(string[] args) { // Assume everything will work. int exitCode = 0; // First parse pre-sandbox arguments to have access to parameters // needed before anything else. PreSandboxParams preSandboxPrms = new PreSandboxParams(); CommandLineArguments.Parser.ParseArguments(args, preSandboxPrms, msg => { }); // Create console to use in this method to output according to params. CLConsole console = new CLConsole(preSandboxPrms); // Show copyright information except if user told us not to. if (!preSandboxPrms.nologo) { ShowCopyright(console); } try { // Now parse all parameters and check if user wants help. If parameter issues // are encountered, they will be displayed here. Params prms = new Params(); bool wantsHelp = CommandLineArguments.Parser.ParseHelp(args); bool validParams = CommandLineArguments.Parser.ParseArguments(args, prms); if (!wantsHelp && validParams) { // Create a new AppDomain to perform registration/unregistration tasks, // so that if something goes horribly wrong in the assembly we'll be loading, // we won't be affected by it. Set the ApplicationBase to the current directory // from where we've been invoked, since it might contain dependencies. AppDomain domain = AppDomain.CreateDomain("CLRegAsm.Core", null, new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }); // Create an instance of this class to execute in the new AppDomain. Core sandboxedCore = (Core)domain.CreateInstanceFromAndUnwrap( Assembly.GetExecutingAssembly().CodeBase, typeof(Core).FullName); if (sandboxedCore == null) { throw new CoreException(String.Format("Type \"{0}\" not found when trying to " + "create it in sandboxed AppDomain", typeof(Core).FullName)); } // Execute in sandboxed AppDomain. exitCode = sandboxedCore.RunSandboxed(args, prms); } else { // Display usage information. if (!validParams) { console.WriteLine(); } ShowUsage(console); } } catch (Exception e) { // An error occured. Print it to stderr, then return // a non-zero error code to indicate the error. console.PrintException(e); exitCode = 1; } return(exitCode); }
/// <summary> /// Runs the CLRegAsm registration/unregistration tasks in a sandboxed /// <see cref="AppDomain"/>. Called from <see cref="Core.Run(string[])"/>. /// </summary> /// <param name="sandboxedArgs">Raw program arguments. Use only to redirect to /// other executables; otherwise, use <paramref name="sandboxPrms"/></param> /// <param name="sandboxedPrms">Sandboxed <see cref="Params"/> object /// determining run parameters.</param> /// <returns>Exit code to return from the CLRegAsm tool.</returns> /// <exception cref="ArgumentNullException">Either <paramref name="sandboxedArgs"/> /// or <paramref name="sandboxedPrms"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">Could not locate the assembly /// specified in <paramref name="sandboxPrms"/>. /// /// OR /// /// Registry file specified in <paramref name="sandboxPrms"/> /// overwrites the assembly file. /// /// OR /// /// Type library file specified in <paramref name="sandboxPrms"/> /// but assembly file has an embedded type library. /// /// OR /// /// Type library file specified in <paramref name="sandboxPrms"/> /// overwrites the assembly file.</exception> /// <exception cref="CoreException">The assembly specified in /// <paramref name="sandboxPrms"/> has an invalid format. /// (InnerException will then be a <see cref="BadImageFormatException"/>) /// /// OR /// /// The assembly specified in <paramref name="sandboxPrms"/> /// could not be found. (InnerException will then be a /// <see cref="FileNotFoundException"/>) /// /// OR /// /// Access was denied to the registry keys that need to be updated. /// (InnerException will then be a <see cref="UnauthorizedAccessException"/>) /// /// OR /// /// An exception was thrown from a user function in the assembly /// specified in <paramref name="sandboxPrms"/>. (InnerException will then /// be a <see cref="TargetInvocationException"/>) /// /// OR /// /// An exception was thrown in a static constructor in the assembly /// specified in <paramref name="sandboxPrms"/>. (InnerException will then /// be a <see cref="ReflectionTypeLoadException"/>)</exception> private int RunSandboxed(string[] sandboxedArgs, Params sandboxedPrms) { // This will be changed if an error occurs. int exitCode = 0; // Make sure params are valid and save them in instance variable. if (sandboxedArgs == null) { throw new ArgumentNullException("sandboxedArgs"); } if (sandboxedPrms == null) { throw new ArgumentNullException("sandboxedPrms"); } sandboxedPrms.Validate(); prms = sandboxedPrms; // Create our console object to use for output. console = new CLConsole(prms); // Validate assembly path. string fullAssemblyPath = Path.GetFullPath(prms.assemblyName); if (File.Exists(fullAssemblyPath)) { // Found the assembly easily, take a note of its absolute path. prms.assemblyName = fullAssemblyPath; } else { // Assembly not found - perhaps user specified a filename only. // Let's try to locate this in the current folder or on the PATH. StringBuilder buffer = new StringBuilder(MAX_PATH); if (SearchPath(null, prms.assemblyName, null, buffer.Capacity + 1, buffer, null) != 0) { // Found the assembly by search, update params. prms.assemblyName = buffer.ToString(); } else { // We really can't find the assembly; bail out. throw new ArgumentException(String.Format("Could not locate assembly \"{0}\"", prms.assemblyName), "assemblyName"); } } // Also convert it to its full path. prms.assemblyName = new FileInfo(prms.assemblyName).FullName; // Compute path of regfile. if (prms.registryFile != null) { if (prms.registryFile.Length == 0) { // Use assembly name, replacing extension with .reg. prms.registryFile = Path.ChangeExtension(prms.assemblyName, ".reg"); Debug.Assert(Directory.Exists(Path.GetDirectoryName(prms.registryFile))); } else { // Get full path to specified regfile. prms.registryFile = new FileInfo(prms.registryFile).FullName; // Make sure regfile path is different from assembly path. if (String.Compare(prms.registryFile, prms.assemblyName, true) == 0) { throw new ArgumentException(String.Format("Registry file \"{0}\" " + "overwrites assembly file", prms.registryFile)); } // Create directory that will contain regfile if it doesn't exist. Directory.CreateDirectory(Path.GetDirectoryName(prms.registryFile)); } } // Compute path of TLB according to user-provided value and whether // the assembly has an embedded TLB. if (prms.typeLibrary != null) { if (prms.typeLibrary.Length == 0) { // TLB name not specified. If assembly has an embedded TLB, // user assembly name as TLB name. Otherwise, compute a TLB // name using the assembly name, ending with .tlb. if (AssemblyHasEmbeddedTypeLibrary(prms.assemblyName)) { prms.typeLibrary = prms.assemblyName; } else { prms.typeLibrary = Path.ChangeExtension(prms.assemblyName, ".tlb"); } Debug.Assert(Directory.Exists(Path.GetDirectoryName(prms.typeLibrary))); } else { if (AssemblyHasEmbeddedTypeLibrary(prms.assemblyName)) { // Specifying a TLB name is invalid if assembly has an embedded TLB. throw new ArgumentException("Assembly \"{0}\" has an embedded type library; " + "cannot specify a new type library name."); } // Get full path to TLB file. If user did not specify a full path, // assume file will sit in the same directory as the assembly. if (Path.GetDirectoryName(prms.typeLibrary).Length != 0) { prms.typeLibrary = new FileInfo(prms.typeLibrary).FullName; } else { prms.typeLibrary = Path.Combine(Path.GetDirectoryName(prms.assemblyName), prms.typeLibrary); } // Make sure TLB path is different from assembly path. if (String.Compare(prms.typeLibrary, prms.assemblyName, true) == 0) { throw new ArgumentException(String.Format("Type library \"{0}\" " + "overwrites assembly file", prms.typeLibrary)); } // Create TLB directory if it doesn't exist. Directory.CreateDirectory(Path.GetDirectoryName(prms.typeLibrary)); } } // Set up context, which will take care of per-user registration/unregistration. using (Context context = new Context(prms.perUser && prms.registryFile == null)) { try { // Load the assembly we're going to be working with. // If we load it to generate a regfile, load it for reflection only. Debug.Assert(assembly == null); try { if (prms.registryFile == null) { assembly = Assembly.LoadFrom(prms.assemblyName); } else { assembly = Assembly.ReflectionOnlyLoadFrom(prms.assemblyName); } } catch (BadImageFormatException bife) { throw new CoreException(String.Format("Invalid assembly: \"{0}\"", prms.assemblyName), bife); } catch (FileNotFoundException fnfe) { throw new CoreException(String.Format("Could not locate assembly \"{0}\"", prms.assemblyName), fnfe); } // If user asked for the codebase option, check if the assembly has a strong name. // If it doesn't, it's not fatal but we output a warning. if (prms.setCodeBase && assembly.GetName().GetPublicKey().Length == 0) { console.WriteLine("Warning: the /codebase option was meant to be used with strong-named assemblies only."); } // Create registration services helper object. We need it for just about everything. Debug.Assert(regServices == null); regServices = new RegistrationServices(); // Here we switch depending on what we need to perform. if (prms.registryFile == null) { // Check if we register or unregister. if (!prms.unregister) { // Need to register assembly. try { AssemblyRegistrationFlags regFlags; if (prms.setCodeBase) { regFlags = AssemblyRegistrationFlags.SetCodeBase; } else { regFlags = AssemblyRegistrationFlags.None; } bool registered = regServices.RegisterAssembly(assembly, regFlags); if (registered) { console.WriteLine("Assembly \"{0}\" registered successfully.", prms.assemblyName); } else { console.WriteLine("Assembly \"{0}\" contains no registrable types.", prms.assemblyName); } } catch (UnauthorizedAccessException uae) { // User doesn't have access to the needed registry keys. throw new CoreException("Access denied while trying to update registry " + "during assembly registration", uae); } // Also export and register type library if needed. if (prms.typeLibrary != null) { ExportAndRegisterTypeLibrary(); } } else { // Need to unregister assembly. setCodeBase doesn't matter here. try { bool unregistered = regServices.UnregisterAssembly(assembly); if (unregistered) { console.WriteLine("Assembly \"{0}\" unregistered successfully.", prms.assemblyName); } else { console.WriteLine("Assembly \"{0}\" contains no unregistrable types.", prms.assemblyName); } } catch (UnauthorizedAccessException uae) { // User doesn't have access to the needed registry keys. throw new CoreException("Access denied while trying to update registry " + "during assembly unregistration", uae); } // Also unregister type library if needed. if (prms.typeLibrary != null) { UnregisterTypeLibrary(); } } } else { // User asked us to generate regfile. // Hook our delegate to the ReflectionOnlyAssemblyResolve of the // sandboxed AppDomain. It will be called to load references of our assembly. AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ResolveReflectionOnlyAssembly; try { // Generate the regfile. Will use instance members to get information. exitCode = GenerateRegistryFile(); } finally { // Unhook from the ReflectionOnlyAssemblyResolve now that we no longer need it. AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ResolveReflectionOnlyAssembly; } } } catch (TargetInvocationException tie) { // Error in a user-defined function during assembly registration/unregistration // (for example, in a ComRegisterFunction). throw new CoreException(String.Format("Error in user-defined function from assembly \"{0}\"", assembly.GetName().Name), tie); } catch (ReflectionTypeLoadException rtle) { // Error while loading a class in the assembly (possibly in a static constructor). StringBuilder builder = new StringBuilder(); builder.AppendFormat("Error while loading types in assembly \"{0}\": ", assembly.GetName().Name); foreach (Exception loaderEx in rtle.LoaderExceptions) { builder.AppendLine(); builder.AppendFormat(" {0}", loaderEx.Message); } throw new CoreException(builder.ToString(), rtle); } } return(exitCode); }