Click here to Skip to main content
15,893,381 members
Articles / Programming Languages / XML

How to Automate Exporting .NET Function to Unmanaged Programs

Rate me:
Please Sign up or sign in to vote.
4.90/5 (44 votes)
21 Nov 2006MIT3 min read 460.6K   4.5K   94  
Post-build tool which can automate exporting .NET function to unmanaged programs
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace ExportDll
{
    enum ParserState
    {
        Normal,
        ClassDeclaration,
        Class,
        DeleteExportDependency,
        MethodDeclaration,
        MethodProperties,
        Method,
        DeleteExportAttribute,
    }
    class Program
    {
        static bool FindWithAttribute(MemberInfo mi, object obj)
        {
            object[] attrs = mi.GetCustomAttributes(typeof(ExportDllAttribute.ExportDllAttribute), false);
            if (attrs.Length > 0)
                return true;
            return false;
        }

        static int Main(string[] args)
        {
            try
            {
                if (args.Length < 1)
                {
                    Console.WriteLine("Parameter error!");
                    return 1;
                }
                bool debug = false;
                
                //Console.WriteLine("Args have {0} elements:", args.Length.ToString());
                //foreach(string arg in args)
                //    Console.WriteLine("\t'{0}'", arg);
                if (args.Length > 1)
                {
                    if (args[1] == "/Debug")
                        debug = true;
                }
                Console.WriteLine("Debug: {0}", debug);
                string filepath = args[0];
                string path = System.IO.Path.GetDirectoryName(filepath);
                if (path == string.Empty)
                {
                    Console.WriteLine("Full path needed!");
                    return 1;
                }
                string ext = System.IO.Path.GetExtension(filepath);
                if (ext != ".dll")
                {
                    Console.WriteLine("Target should be dll!");
                    return 1;
                }
                int exportscount = 0;
                Dictionary<string, Dictionary<string, KeyValuePair<string, string>>> dic = new Dictionary<string, Dictionary<string, KeyValuePair<string, string>>>();
                byte[] rawassembly;
                using (System.IO.FileStream fs = System.IO.File.OpenRead(filepath))
                {
                    System.IO.BinaryReader br = new System.IO.BinaryReader(fs);
                    rawassembly = br.ReadBytes((int)fs.Length);
                    br.Close();
                }
                Assembly assembly = Assembly.Load(rawassembly);
                Type[] types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    MemberInfo[] mis = type.FindMembers(MemberTypes.All, BindingFlags.Public | BindingFlags.Static, new MemberFilter(FindWithAttribute), null);
                    {
                        foreach (MemberInfo mi in mis)
                        {
                            object[] attrs = mi.GetCustomAttributes(typeof(ExportDllAttribute.ExportDllAttribute), false);
                            if (attrs.Length > 0)
                            {
                                ExportDllAttribute.ExportDllAttribute attr = attrs[0] as ExportDllAttribute.ExportDllAttribute;
                                if(!dic.ContainsKey(type.FullName))
                                    dic[type.FullName] = new Dictionary<string, KeyValuePair<string, string>>();
                                dic[type.FullName][mi.Name] = new KeyValuePair<string, string>(attr.ExportName, attr.CallingConvention);
                                exportscount++;
                            }
                        }
                    }
                }
                if (exportscount > 0)
                {
                    int exportpos = 1;
                    string filename = System.IO.Path.GetFileNameWithoutExtension(filepath);
                    System.IO.Directory.SetCurrentDirectory(path);
                    Process proc = new Process();
                    string arguments = string.Format("/nobar{1}/out:{0}.il {0}.dll", filename, debug ? " /linenum " : " ");
                    Console.WriteLine("Deassebly file with arguments '{0}'", arguments);
                    System.Diagnostics.ProcessStartInfo info = new ProcessStartInfo(Properties.Settings.Default.ildasmpath, arguments);
                    info.UseShellExecute = false;
                    info.CreateNoWindow = false;
                    info.RedirectStandardOutput = true;
                    proc.StartInfo = info;
                    proc.Start();
                    proc.WaitForExit();
                    Console.WriteLine(proc.StandardOutput.ReadToEnd());
                    if (proc.ExitCode != 0)
                        return proc.ExitCode;
                    List<string> wholeilfile = new List<string>();
                    System.IO.StreamReader sr = System.IO.File.OpenText(System.IO.Path.Combine(path, filename + ".il"));
                    string methoddeclaration = "";
                    string methodname = "";
                    string classdeclaration = "";
                    string methodbefore = "";
                    string methodafter = "";
                    int methodpos = 0;
                    Stack<string> classnames = new Stack<string>();
                    List<string> externassembly = new List<string>();
                    ParserState state = ParserState.Normal;
                    while (!sr.EndOfStream)
                    {
                        string line = sr.ReadLine();
                        string trimedline = line.Trim();
                        bool addilne = true;
                        switch (state)
                        {
                            case ParserState.Normal:
                                if (trimedline.StartsWith(".corflags"))
                                {
                                    wholeilfile.Add(".corflags 0x00000002");
                                    wholeilfile.Add(string.Format(".vtfixup [{0}] int32 fromunmanaged at VT_01", exportscount));
                                    wholeilfile.Add(string.Format(".data VT_01 = int32[{0}]", exportscount));
                                    Console.WriteLine("Adding vtfixup.");
                                    addilne = false;
                                }
                                else if (trimedline.StartsWith(".class"))
                                {
                                    state = ParserState.ClassDeclaration;
                                    addilne = false;
                                    classdeclaration = trimedline;
                                }
                                else if (trimedline.StartsWith(".assembly extern ExportDllAttribute"))
                                {
                                    addilne = false;
                                    state = ParserState.DeleteExportDependency;
                                    Console.WriteLine("Deleting ExportDllAttribute dependency.");
                                }
                                break;
                            case ParserState.DeleteExportDependency:
                                if (trimedline.StartsWith("}"))
                                {
                                    state = ParserState.Normal;
                                }
                                addilne = false;
                                break;
                            case ParserState.ClassDeclaration:
                                if (trimedline.StartsWith("{"))
                                {
                                    state = ParserState.Class;
                                    string classname = "";
                                    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@".+\s+([^\s]+) extends \[.*");
                                    System.Text.RegularExpressions.Match m = reg.Match(classdeclaration);
                                    if (m.Groups.Count > 1)
                                        classname = m.Groups[1].Value;
                                    classname = classname.Replace("'", "");
                                    if (classnames.Count > 0)
                                        classname = classnames.Peek() + "+" + classname;
                                    classnames.Push(classname);
                                    Console.WriteLine("Found class: " + classname);
                                    wholeilfile.Add(classdeclaration);
                                }
                                else
                                {
                                    classdeclaration += " " + trimedline;
                                    addilne = false;
                                }
                                break;
                            case ParserState.Class:
                                if (trimedline.StartsWith(".class"))
                                {
                                    state = ParserState.ClassDeclaration;
                                    addilne = false;
                                    classdeclaration = trimedline;
                                }
                                else if (trimedline.StartsWith(".method"))
                                {
                                    if (dic.ContainsKey(classnames.Peek()))
                                    {
                                        methoddeclaration = trimedline;
                                        addilne = false;
                                        state = ParserState.MethodDeclaration;
                                    }
                                }
                                else if (trimedline.StartsWith("} // end of class"))
                                {
                                    classnames.Pop();
                                    if (classnames.Count > 0)
                                        state = ParserState.Class;
                                    else
                                        state = ParserState.Normal;
                                }
                                break;
                            case ParserState.MethodDeclaration:
                                if (trimedline.StartsWith("{"))
                                {
                                    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"(?<before>[^\(]+\s+)(?<method>[^\(]+)(?<after>\(.*)");
                                    System.Text.RegularExpressions.Match m = reg.Match(methoddeclaration);
                                    if (m.Groups.Count > 3)
                                    {
                                        methodbefore = m.Groups["before"].Value;
                                        methodafter = m.Groups["after"].Value;
                                        methodname = m.Groups["method"].Value;
                                    }
                                    Console.WriteLine("Found method: " + methodname);
                                    if (dic[classnames.Peek()].ContainsKey(methodname))
                                    {
                                        methodpos = wholeilfile.Count;
                                        state = ParserState.MethodProperties;
                                    }
                                    else
                                    {
                                        wholeilfile.Add(methoddeclaration);
                                        state = ParserState.Method;
                                        methodpos = 0;
                                    }
                                }
                                else
                                {
                                    methoddeclaration += " " + trimedline;
                                    addilne = false;
                                }
                                break;
                            case ParserState.Method:
                                if (trimedline.StartsWith("} // end of method"))
                                {
                                    state = ParserState.Class;
                                }
                                break;
                            case ParserState.MethodProperties:
                                if (trimedline.StartsWith(".custom instance void [ExportDllAttribute]"))
                                {
                                    addilne = false;
                                    state = ParserState.DeleteExportAttribute;
                                }
                                else if (trimedline.StartsWith("// Code"))
                                {
                                    state = ParserState.Method;
                                    if (methodpos != 0)
                                        wholeilfile.Insert(methodpos, methoddeclaration);
                                }
                                break;
                            case ParserState.DeleteExportAttribute:
                                if (trimedline.StartsWith(".custum") || trimedline.StartsWith("// Code"))
                                {
                                    KeyValuePair<string, string> attr = dic[classnames.Peek()][methodname];
                                    methoddeclaration = methodbefore + "modopt([mscorlib]" + attr.Value + ") " + methodname + methodafter;
                                    Console.WriteLine("\tChanging calling convention: " + attr.Value);
                                    if (methodpos != 0)
                                        wholeilfile.Insert(methodpos, methoddeclaration);
                                    wholeilfile.Add(".vtentry 1 : " + exportpos.ToString());
                                    wholeilfile.Add(string.Format(".export [{0}] as {1}", exportpos, dic[classnames.Peek()][methodname].Key));
                                    Console.WriteLine("\tAdding .vtentry:{0} .export:{1}", exportpos, dic[classnames.Peek()][methodname].Key);
                                    exportpos++;
                                    state = ParserState.Method;
                                }
                                else
                                    addilne = false;
                                break;
                        }
                        if (addilne)
                            wholeilfile.Add(line);
                    }
                    sr.Close();
                    System.IO.StreamWriter sw = System.IO.File.CreateText(System.IO.Path.Combine(path, filename + ".il"));
                    foreach (string line in wholeilfile)
                    {
                        sw.WriteLine(line);
                    }
                    sw.Close();
                    string res = filename + ".res";
                    if (System.IO.File.Exists(filename + ".res"))
                        res = " /resource=" + res;
                    else
                        res = "";
                    proc = new Process();
                    arguments = string.Format("/nologo /quiet /out:{0}.dll {0}.il /DLL{1} {2}", filename, res, debug ? "/debug" : "/optimize");
                    Console.WriteLine("Compiling file with arguments '{0}'", arguments);
                    info = new ProcessStartInfo(Properties.Settings.Default.ilasmpath, arguments);
                    info.UseShellExecute = false;
                    info.CreateNoWindow = false;
                    info.RedirectStandardOutput = true;
                    proc.StartInfo = info;
                    proc.Start();
                    proc.WaitForExit();
                    Console.WriteLine(proc.StandardOutput.ReadToEnd());
                    if (proc.ExitCode != 0)
                        return proc.ExitCode;
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return -1;
            }
            return 0;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Systems / Hardware Administrator
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions