[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/ksanadf/DotNetToJScript/master/DotNetToJScript/Program.cs [Back]  [Original]

//    This file is part of DotNetToJScript - A tool to generate a 
//    JScript which bootstraps an arbitrary v2.NET Assembly and class.
//    Copyright (C) James Forshaw 2017
//
//    DotNetToJScript is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    DotNetToJScript is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with DotNetToJScript.  If not, see .

using NDesk.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using System.Xml.Schema;

namespace DotNetToJScript
{
    class Program
    {
        enum ScriptLanguage
        {
            JScript,
            VBA,
            VBScript,
        }

        private const string VERSION = "v1.0.4";

        static object BuildLoaderDelegate(byte[] assembly)
        {
            // Create a bound delegate which will load our assembly from a byte array.
            Delegate res = Delegate.CreateDelegate(typeof(XmlValueGetter),
                assembly,
                typeof(Assembly).GetMethod("Load", new Type[] { typeof(byte[]) }));

            // Create a COM invokable delegate to call the loader. Abuses contra-variance
            // to make an array of headers to an array of objects (which we'll just pass
            // null to anyway).
            return new HeaderHandler(res.DynamicInvoke);
        }

        static object BuildLoaderDelegateMscorlib(byte[] assembly)
        {
            Delegate res = Delegate.CreateDelegate(typeof(Converter),
                assembly,
                typeof(Assembly).GetMethod("Load", new Type[] { typeof(byte[]), typeof(byte[]) }));

            HeaderHandler d = new HeaderHandler(Convert.ToString);

            d = (HeaderHandler)Delegate.Combine(d, (Delegate)d.Clone());
            d = (HeaderHandler)Delegate.Combine(d, (Delegate)d.Clone());

            FieldInfo fi = typeof(MulticastDelegate).GetField("_invocationList", BindingFlags.NonPublic | BindingFlags.Instance);

            object[] invoke_list = d.GetInvocationList();
            invoke_list[1] = res;
            fi.SetValue(d, invoke_list);

            d = (HeaderHandler)Delegate.Remove(d, (Delegate)invoke_list[0]);
            d = (HeaderHandler)Delegate.Remove(d, (Delegate)invoke_list[2]);

            return d;
        }

        const string DEFAULT_ENTRY_CLASS_NAME = "TestClass";

        static string CreateScriptlet(string script, string script_name, bool register_script, Guid clsid)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(Properties.Resources.scriptlet_template);
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineOnAttributes = true;
            settings.Encoding = new UTF8Encoding(false);

            XmlElement reg_node = (XmlElement)doc.SelectSingleNode("/package/component/registration");
            XmlNode root_node = register_script ? reg_node : doc.SelectSingleNode("/package/component");
            XmlNode script_node = root_node.AppendChild(doc.CreateElement("script"));
            script_node.Attributes.Append(doc.CreateAttribute("language")).Value = script_name;
            script_node.AppendChild(doc.CreateCDataSection(script));
            if (clsid != Guid.Empty)
            {
                reg_node.SetAttribute("classid", clsid.ToString("B"));
            }
            
            using (MemoryStream stm = new MemoryStream())
            {
                using (XmlWriter writer = XmlWriter.Create(stm, settings))
                {
                    doc.Save(writer);
                }
                return Encoding.UTF8.GetString(stm.ToArray());
            }
        }

        static HashSet GetValidClasses(byte[] assembly)
        {
            Assembly asm = Assembly.Load(assembly);
            return new HashSet(asm.GetTypes().Where(t => t.IsPublic && t.GetConstructor(new Type[0]) != null).Select(t => t.FullName));
        }

        static void WriteColor(string str, ConsoleColor color)
        {
            ConsoleColor old_color = Console.ForegroundColor;
            Console.ForegroundColor = color;
            try
            {
                Console.Error.WriteLine(str);
            }
            finally
            {
                Console.ForegroundColor = old_color;
            }
        }

        static void WriteError(string str)
        {
            WriteColor(str, ConsoleColor.Red);
        }

        static void WriteError(string format, params object[] args)
        {
            WriteError(String.Format(format, args));
        }

        static string GetEnumString(Type enum_type)
        {
            return String.Join(", ", Enum.GetNames(enum_type));
        }

        static void ParseEnum(string name, out T value) where T : struct
        {
            value = (T)Enum.Parse(typeof(T), name, true);
        }
        
        static void Main(string[] args)
        {
            try
            {
                if (Environment.Version.Major != 2)
                {
                    WriteError("This tool should only be run on v2 of the CLR");
                    Environment.Exit(1);
                }

                string output_file = null;
                string entry_class_name = DEFAULT_ENTRY_CLASS_NAME;
                string additional_script = String.Empty;
                bool mscorlib_only = false;
                bool scriptlet_moniker = false;
                bool scriptlet_uninstall = false;
                bool enable_debug = false;
                RuntimeVersion version = RuntimeVersion.None;
                ScriptLanguage language = ScriptLanguage.JScript;
                Guid clsid = Guid.Empty;

                bool show_help = false;

                OptionSet opts = new OptionSet() {
                        { "n", "Build a script which only uses mscorlib.", v => mscorlib_only = v != null },
                        { "m", "Build a scriptlet file in moniker format.", v => scriptlet_moniker = v != null },
                        { "u", "Build a scriptlet file in uninstall format.", v => scriptlet_uninstall = v != null },
                        { "d", "Enable debug output from script", v => enable_debug = v != null },
                        { "l|lang=", String.Format("Specify script language to use ({0})",
                                        GetEnumString(typeof(ScriptLanguage))), v => ParseEnum(v, out language) },
                        { "v|ver=", String.Format("Specify .NET version to use ({0})", 
                                        GetEnumString(typeof(RuntimeVersion))), v => ParseEnum(v, out version) },
                        { "o=", "Specify output file (default is stdout).", v => output_file = v },
                        { "c=", String.Format("Specify entry class name (default {0})", entry_class_name), v => entry_class_name = v },
                        { "s=", "Specify file with additional script. 'o' is created instance.", v => additional_script = File.ReadAllText(v) },
                        { "clsid=", "Specify a CLSID for the scriptlet", v => clsid = new Guid(v) },
                        { "h|help",  "Show this message and exit", v => show_help = v != null },
                };

                string assembly_path = opts.Parse(args).FirstOrDefault();
                if (!File.Exists(assembly_path) || show_help)
                {
                    Console.Error.WriteLine(@"Usage: DotNetToJScript {0} [options] path\to\asm", VERSION);
                    Console.Error.WriteLine("Copyright (C) James Forshaw 2017. Licensed under GPLv3.");
                    Console.Error.WriteLine("Source code at https://github.com/tyranid/DotNetToJScript");
                    Console.Error.WriteLine("Options");
                    opts.WriteOptionDescriptions(Console.Error);
                    Environment.Exit(1);
                }

                IScriptGenerator generator;
                switch (language)
                {
                    case ScriptLanguage.JScript:
                        generator = new JScriptGenerator();
                        break;
                    case ScriptLanguage.VBA:
                        generator = new VBAGenerator();
                        break;
                    case ScriptLanguage.VBScript:
                        generator = new VBScriptGenerator();
                        break;
                    default:
                        throw new ArgumentException("Invalid script language option");
                }

                byte[] assembly = File.ReadAllBytes(assembly_path);
                try
                {
                    HashSet valid_classes = GetValidClasses(assembly);
                    if (!valid_classes.Contains(entry_class_name))
                    {
                        WriteError("Error: Class '{0}' not found is assembly.", entry_class_name);
                        if (valid_classes.Count == 0)
                        {
                            WriteError("Error: Assembly doesn't contain any public, default constructable classes");
                        }
                        else
                        {
                            WriteError("Use one of the follow options to specify a valid classes");
                            foreach (string name in valid_classes)
                            {
                                WriteError("-c {0}", name);
                            }
                        }
                        Environment.Exit(1);
                    }
                }
                catch (Exception)
                {
                    WriteError("Error: loading assembly information.");
                    WriteError("The generated script might not work correctly");
                }

                BinaryFormatter fmt = new BinaryFormatter();
                MemoryStream stm = new MemoryStream();
                fmt.Serialize(stm, mscorlib_only ? BuildLoaderDelegateMscorlib(assembly) : BuildLoaderDelegate(assembly));

                string script = generator.GenerateScript(stm.ToArray(), entry_class_name, additional_script, version, enable_debug);
                if (scriptlet_moniker || scriptlet_uninstall)
                {
                    if (!generator.SupportsScriptlet)
                    {
                        throw new ArgumentException(String.Format("{0} generator does not support Scriptlet output", generator.ScriptName));
                    }
                    script = CreateScriptlet(script, generator.ScriptName, scriptlet_uninstall, clsid);
                }

                if (!String.IsNullOrEmpty(output_file))
                {
                    File.WriteAllText(output_file, script, new UTF8Encoding(false));
                }
                else
                {
                    Console.WriteLine(script);
                }
            }
            catch (Exception ex)
            {
                ReflectionTypeLoadException tex = ex as ReflectionTypeLoadException;
                if (tex != null)
                {
                    WriteError("Couldn't load assembly file");
                    foreach (var e in tex.LoaderExceptions)
                    {
                        WriteError(e.Message);
                    }
                }
                else
                {
                    WriteError(ex.Message);
                }
            }
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page