static int Main(string[] args) { //define command line args CommandLineParser clp = new CommandLineParser(); clp.AddKnownOption("f", "format", true); clp.AddKnownOption("h", "help"); clp.AddKnownOption("i", "ignore-missing"); clp.AddKnownOption("M", "stop-if-modified"); clp.AddKnownOption("r", "revision"); clp.AddKnownOption("v", "version"); clp.AddKnownOption("D", "debug"); //initialize vars from args debugOutput = clp.IsOptionSet("D"); bool showRevision = clp.IsOptionSet("r"); bool ignoreMissing = clp.IsOptionSet("i"); bool stopIfModified = clp.IsOptionSet("M"); string inputFile = clp.GetArgument(0); string outputFile = clp.GetArgument(1); string customFormat = "{!}{commit}"; if( clp.IsOptionSet("f") ) customFormat = clp.GetOptionValue("f"); //show help/version if( clp.IsOptionSet("h") || clp.IsOptionSet("v") ) { HandleHelp(clp.IsOptionSet("h")); return 0; } //default action if( String.IsNullOrWhiteSpace(inputFile) && String.IsNullOrWhiteSpace(outputFile) && !showRevision ) showRevision = true; if( String.IsNullOrWhiteSpace(inputFile) ) inputFile = Environment.CurrentDirectory + @"\."; if( debugOutput ) Console.Error.WriteLine("Working on path " + Path.GetDirectoryName(inputFile) ); //process Git info and populate revision and revTime ProcessDirectory(Path.GetDirectoryName(inputFile), ignoreMissing); //show git revision if( showRevision ) { if( revision == null ) { if( ignoreMissing ) { revision = "0000000000000000000000000000000000000000"; revTime = DateTimeOffset.Now; } else { Console.Error.WriteLine("Error: Not a Git working directory."); return 1; } } Console.WriteLine( ResolveFormat(customFormat) ); return 0; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Main processing /////////////////////////////////////////////////////////////////////////////////////////////////////////// if( revision == null ) { if( ignoreMissing ) { revision = "0000000000000000000000000000000000000000"; revTime = DateTimeOffset.Now; } else { Console.Error.WriteLine("Error: Not a Git working directory."); return 1; } } if( stopIfModified && isModified ) { Console.Error.WriteLine("Error: Git working directory contains uncommited changes, stop requested by option."); return 1; } if( !File.Exists(inputFile) ) { Console.Error.WriteLine("Error: Input file doesn't exist."); return 1; } if( debugOutput ) Console.Error.WriteLine("Patching " + Path.GetFileName(inputFile) + "..."); string attrStart = null, attrEnd = null; switch( Path.GetExtension(outputFile).ToLower() ) { case ".cs": attrStart = "["; attrEnd = "]"; break; case ".vb": attrStart = "<"; attrEnd = ">"; break; default: Console.Error.WriteLine("Logic error: invalid AssemblyInfo file extension: " + Path.GetExtension(outputFile).ToLower()); return 1; } using( StreamReader sr = new StreamReader(inputFile, Encoding.Default, true) ) { sr.Peek(); using( StreamWriter sw = new StreamWriter(outputFile, false, sr.CurrentEncoding) ) { while( !sr.EndOfStream ) { string line = sr.ReadLine(); Match m; m = Regex.Match( line, @"^(\s*\" + attrStart + @"\s*assembly\s*:\s*AssemblyInformationalVersion\s*\(\s*"")(.*)(""\s*\)\s*\" + attrEnd + @".*)$", RegexOptions.IgnoreCase); if( m.Success ) { string value = m.Groups[2].Value; value = ResolveFormat(value); line = m.Groups[1].Value + value + m.Groups[3].Value; if( debugOutput ) Console.Error.WriteLine("Found AssemblyInformationalVersion attribute"); } sw.WriteLine(line); } } if( debugOutput ) Console.Error.WriteLine("Patched " + Path.GetFileName(inputFile)); } return 0; }
private static int Main(string[] args) { CommandLineParser clp = new CommandLineParser(); clp.AddKnownOption("a", "assembly-info"); clp.AddKnownOption("", "test-b36min"); clp.AddKnownOption("b", "test-bmin"); clp.AddKnownOption("f", "format", true); clp.AddKnownOption("h", "help"); clp.AddKnownOption("i", "ignore-missing"); clp.AddKnownOption("m", "multi-project"); clp.AddKnownOption("r", "revision"); clp.AddKnownOption("s", "restore"); clp.AddKnownOption("v", "version"); clp.AddKnownOption("x", "test-xmin"); clp.AddKnownOption("B", "de-bmin"); clp.AddKnownOption("", "de-b36min"); clp.AddKnownOption("D", "de-dmin"); clp.AddKnownOption("I", "only-infver"); clp.AddKnownOption("M", "stop-if-modified"); clp.AddKnownOption("X", "de-xmin"); clp.AddKnownOption("", "debug"); debugOutput = clp.IsOptionSet("debug"); if (clp.IsOptionSet("h") || clp.IsOptionSet("v")) { HandleHelp(clp.IsOptionSet("h")); return 0; } if (clp.IsOptionSet("X")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } string xmin = clp.GetArgument(1).Trim().ToLowerInvariant(); DateTime time = DehexMinutes(baseYear, xmin); if (time == DateTime.MinValue) { Console.Error.WriteLine("Invalid xmin value."); return 0; } Console.WriteLine(time.ToString("yyyy-MM-dd HH:mm") + " UTC"); Console.WriteLine(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm K")); return 0; } if (clp.IsOptionSet("B")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } string bmin = clp.GetArgument(1).Trim().ToLowerInvariant(); DateTime time = Debase28Minutes(baseYear, bmin); if (time == DateTime.MinValue) { Console.Error.WriteLine("Invalid bmin value."); return 0; } Console.WriteLine(time.ToString("yyyy-MM-dd HH:mm") + " UTC"); Console.WriteLine(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm K")); return 0; } if (clp.IsOptionSet("de-b36min")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } string bmin = clp.GetArgument(1).Trim().ToLowerInvariant(); DateTime time = Debase36Minutes(baseYear, bmin); if (time == DateTime.MinValue) { Console.Error.WriteLine("Invalid b36min value."); return 0; } Console.WriteLine(time.ToString("yyyy-MM-dd HH:mm") + " UTC"); Console.WriteLine(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm K")); return 0; } if (clp.IsOptionSet("D")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } string dmin = clp.GetArgument(1).Trim(); DateTime time = DedecMinutes(baseYear, dmin); if (time == DateTime.MinValue) { Console.Error.WriteLine("Invalid dmin value."); return 0; } Console.WriteLine(time.ToString("yyyy-MM-dd HH:mm") + " UTC"); Console.WriteLine(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm K")); return 0; } if (clp.IsOptionSet("x")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } revTime = DateTime.UtcNow; long ticks1min = TimeSpan.FromMinutes(1).Ticks; revTime = new DateTime(revTime.Ticks / ticks1min * ticks1min, DateTimeKind.Utc); for (int i = 0; i < 10; i++) { Console.WriteLine(revTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm K") + " = " + HexMinutes(baseYear, 1)); revTime = revTime.AddMinutes(1); } return 0; } if (clp.IsOptionSet("b")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } revTime = DateTime.UtcNow; long ticks20min = TimeSpan.FromMinutes(20).Ticks; revTime = new DateTime(revTime.Ticks / ticks20min * ticks20min, DateTimeKind.Utc); for (int i = 0; i < 10; i++) { Console.WriteLine(revTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm K") + " = " + Base28Minutes(baseYear, 1)); revTime = revTime.AddMinutes(20); } return 0; } if (clp.IsOptionSet("test-b36min")) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } revTime = DateTime.UtcNow; long ticks10min = TimeSpan.FromMinutes(10).Ticks; revTime = new DateTime(revTime.Ticks / ticks10min * ticks10min, DateTimeKind.Utc); for (int i = 0; i < 10; i++) { Console.WriteLine(revTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm K") + " = " + Base36Minutes(baseYear, 1)); revTime = revTime.AddMinutes(10); } return 0; } buildTime = DateTimeOffset.Now; bool patchAssemblyInfoFile = clp.IsOptionSet("a"); bool restoreAssemblyInfoFile = clp.IsOptionSet("s"); multiProjectMode = clp.IsOptionSet("m"); bool showRevision = clp.IsOptionSet("r"); bool stopIfModified = clp.IsOptionSet("M"); bool ignoreMissing = clp.IsOptionSet("i"); onlyInformationalVersion = clp.IsOptionSet("I"); string path = clp.GetArgument(0); string customFormat = "{!}{commit}"; if (clp.IsOptionSet("f")) customFormat = clp.GetOptionValue("f"); if (!patchAssemblyInfoFile && !restoreAssemblyInfoFile && !showRevision) showRevision = true; // Default action if (string.IsNullOrEmpty(path)) path = "."; if (debugOutput) Console.Error.WriteLine("Working on path " + path); bool hasProcessed = false; List<string> projectDirs = new List<string>(); if (multiProjectMode) { // Treat the single directory argument as the solution file name if (!path.ToLowerInvariant().EndsWith(".sln")) { Console.Error.WriteLine("Error: Specified file name is invalid. Only *.sln files accepted in\nmulti-project mode."); return 1; } if (!File.Exists(path)) { Console.Error.WriteLine("Error: Specified solution file does not exist."); return 1; } // Scan the solution file for projects and add them to the list string solutionDir = Path.GetDirectoryName(path); using (StreamReader sr = new StreamReader(path)) { while (!sr.EndOfStream) { string line = sr.ReadLine(); Match m = Regex.Match(line, @"^Project\(.+\) = "".+"", ""(.+\.(?:csproj|vbproj))"""); if (m.Success) { string projectPath = Path.Combine(solutionDir, m.Groups[1].Value); string projectDir = Path.GetDirectoryName(projectPath); if (debugOutput) Console.Error.WriteLine("Add project in " + projectDir); projectDirs.Add(projectDir); } } } if (projectDirs.Count == 0) { Console.Error.WriteLine("Error: Specified solution file contains no projects."); return 1; } // From now on, work with the solution directory as default path to get the revision of path = solutionDir; } if (patchAssemblyInfoFile) { if (!hasProcessed) { ProcessDirectory(path, ignoreMissing, true); if (revision == 0) { if (ignoreMissing) { revTime = DateTimeOffset.Now; } else { Console.Error.WriteLine("Error: Not a Subversion working directory."); return 1; } } hasProcessed = true; } if (stopIfModified && isModified) { Console.Error.WriteLine("Error: Subversion working directory contains uncommited changes, stop requested by option."); return 1; } if (multiProjectMode) { bool success = true; foreach (string projectDir in projectDirs) { success &= PatchAssemblyInfoFile(projectDir); } if (!success) { return 1; } } else { if (!PatchAssemblyInfoFile(path)) { return 1; } } } if (restoreAssemblyInfoFile) { if (multiProjectMode) { bool success = true; foreach (string projectDir in projectDirs) { success &= RestoreAssemblyInfoFile(projectDir); } if (!success) { return 1; } } else { if (!RestoreAssemblyInfoFile(path)) { return 1; } } } if (showRevision) { if (!hasProcessed) { ProcessDirectory(path, ignoreMissing, true); if (revision == 0) { if (ignoreMissing) { revTime = DateTimeOffset.Now; } else { Console.Error.WriteLine("Error: Not a Subversion working directory."); return 1; } } hasProcessed = true; } Console.WriteLine(ResolveFormat(customFormat)); } return 0; }
public static int ShowTestHexMinutes(CommandLineParser clp) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } DateTimeOffset revTime = DateTime.UtcNow; long ticks1min = TimeSpan.FromMinutes(1).Ticks; revTime = new DateTime(revTime.Ticks / ticks1min * ticks1min, DateTimeKind.Utc); for (int i = 0; i < 10; i++) { Console.WriteLine(revTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm K") + " = " + HexMinutes(revTime, baseYear, 1)); revTime = revTime.AddMinutes(1); } return 0; }
private static int Main(string[] args) { CommandLineParser clp = new CommandLineParser(); clp.AddKnownOption("h", "help"); clp.AddKnownOption("v", "verbose"); clp.AddKnownOption("o", "out", true); clp.AddKnownOption("c", "class", true); clp.AddKnownOption("d", "debug"); bool debug = clp.IsOptionSet("d"); bool verbose = clp.IsOptionSet("v"); string inFile = clp.GetArgument(0); string outFile = clp.GetOptionValue("o"); string clsName = clp.GetOptionValue("c"); if (string.IsNullOrEmpty(clsName)) clsName = "DllExport"; if (clp.IsOptionSet("h") || string.IsNullOrEmpty(inFile)) { string productVersion = ""; object[] customAttributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false); if (customAttributes != null && customAttributes.Length > 0) { productVersion = ((AssemblyFileVersionAttribute) customAttributes[0]).Version; } Console.WriteLine("NetDllExport"); Console.WriteLine("Exports static methods in a managed DLL as library functions that can be"); Console.WriteLine("called from an unmanaged Windows application."); Console.WriteLine("Version " + productVersion); Console.WriteLine("Copyright by Yves Goergen, http://unclassified.software/apps/netdllexport"); Console.WriteLine(""); Console.WriteLine("No input file specified."); Console.WriteLine(""); Console.WriteLine("Usage:"); Console.WriteLine(" NetDllExport [options] InputFile"); Console.WriteLine(""); Console.WriteLine("Options:"); Console.WriteLine(" -c, --class ClassName"); Console.WriteLine(" Specifies the managed class whose static methods are exported."); Console.WriteLine(" Default: DllExport"); Console.WriteLine(" -d, --debug"); Console.WriteLine(" Rebuilds the assembly in debug mode and creates a PDB file."); Console.WriteLine(" Does not delete intermediate .il and .res files."); Console.WriteLine(" -h, --help"); Console.WriteLine(" Show this help page."); Console.WriteLine(" -o, --out OutputFile"); Console.WriteLine(" Specifies the output DLL file name."); Console.WriteLine(" Default: InputFile"); Console.WriteLine(" -v, --verbose"); Console.WriteLine(" Be verbose."); return 1; } if (string.IsNullOrEmpty(outFile)) { outFile = inFile; } if (!Path.IsPathRooted(inFile)) { inFile = Path.Combine(Environment.CurrentDirectory, inFile); } if (!Path.IsPathRooted(outFile)) { outFile = Path.Combine(Environment.CurrentDirectory, outFile); } string ilFile = Path.Combine( Path.GetDirectoryName(inFile), Path.GetFileNameWithoutExtension(inFile) + ".il"); string resFile = Path.Combine( Path.GetDirectoryName(inFile), Path.GetFileNameWithoutExtension(inFile) + ".res"); if (verbose) { Console.WriteLine("Input file: " + inFile); Console.WriteLine("Output file: " + outFile); Console.WriteLine("IL file: " + ilFile); Console.WriteLine("RES file: " + resFile); Console.WriteLine("Exported class: " + clsName); } // Disassemble to IL and read that file if (verbose) { Console.WriteLine("Disassembling input file..."); } ILHelper.DisassembleFile(inFile, ilFile); string ilCode = File.ReadAllText(ilFile); // TODO: Add more return values (void|string|...) string methodRegex = @"^\s*.method\s+public\s+(hidebysig\s+)?static\s+(void|bool|int32|int64|uint32|uint64|string|class\s+[\w.]+)\s+(marshal\s*\([^)]+\)\s+)?" + @"(\w+)\s*\([^)]*\)" + @"(\s+cil)?(\s+managed)?" + @"\s*\{"; // Make class name case-insensitive string clsNameRegex = ""; foreach (char c in clsName) { clsNameRegex += "[" + char.ToLower(c) + char.ToUpper(c) + "]"; } // Extract the class to export int funcCount = 0; Match m = Regex.Match(ilCode, @"^(.*\n)(\.class\s([^ \t\r\n.]+\s+)*\w+\." + clsNameRegex + @"\s+.*?\{.*?)(\n.class\s.*)?$", RegexOptions.Singleline); if (m.Success) { string clsCode = m.Groups[2].Value; //string testMethodRegex = // @"^\s*.method\s+public\s+(hidebysig\s+)?static\s+(void|int32|string|class\s+[\w.]+)\s+(marshal\s*\([^)]+\)\s+)?" + // @"(EventMsg)\s*\([^)]*\)" + // @"(\s+cil)?(\s+managed)?" + // @"\s*\{"; //if (Regex.IsMatch(clsCode, testMethodRegex, RegexOptions.Multiline | RegexOptions.Singleline)) //{ //} // Modify each .method in clsCode clsCode = Regex.Replace( clsCode, methodRegex, delegate(Match m2) { funcCount++; if (verbose) { Console.WriteLine("Exported function " + funcCount + ": " + m2.Groups[4].Value); } return m2.Value + Environment.NewLine + ".vtentry " + funcCount + " : 1" + Environment.NewLine + ".export [" + funcCount + "] as " + m2.Groups[4].Value + Environment.NewLine; }, RegexOptions.Multiline | RegexOptions.Singleline); ilCode = m.Groups[1].Value + clsCode + m.Groups[4].Value; } else { Console.Error.WriteLine("Exported class \"" + clsName + "\" not found."); return 1; } if (funcCount == 0) { Console.Error.WriteLine("No functions to export."); return 1; } // Insert vtable code string vtCode = ""; for (int i = 1; i <= funcCount; i++) { vtCode += ".vtfixup [1] int32 fromunmanaged at VT_" + i.ToString("00") + Environment.NewLine + ".data VT_" + i.ToString("00") + " = int32(0)" + Environment.NewLine; } // Change to x86 - should better be done by the source assembly already. // corflags bit meanings: http://stackoverflow.com/a/13767541/143684 ilCode = Regex.Replace( ilCode, "^.corflags 0x0000000[123] .*$", ".corflags 0x00000002" + Environment.NewLine + vtCode, RegexOptions.Multiline); // Write new IL file and assemble it if (verbose) { Console.WriteLine("Assembling IL file..."); } File.WriteAllText(ilFile, ilCode); ILHelper.AssembleFile(ilFile, outFile, debug); // Clean up if (!debug) { File.Delete(ilFile); File.Delete(resFile); } // Finished if (verbose) { Console.WriteLine("DLL functions successfully exported."); } return 0; }
public static int ShowDehexMinutes(CommandLineParser clp) { int baseYear; if (!int.TryParse(clp.GetArgument(0), out baseYear)) { Console.Error.WriteLine("Invalid argument: Base year expected"); return 1; } string xmin = clp.GetArgument(1).Trim().ToLowerInvariant(); DateTime time = DehexMinutes(baseYear, xmin); if (time == DateTime.MinValue) { Console.Error.WriteLine("Invalid xmin value."); return 0; } Console.WriteLine(time.ToString("yyyy-MM-dd HH:mm") + " UTC"); Console.WriteLine(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm K")); return 0; }