miércoles, 27 de abril de 2011

Creando un compilador de batchs

Hola, pues esta entrada la hago pensando en mi pasado, jejejeje, pues bien en mis comienzos me gustaba crear archivos batch, los cuales me salian interesantes, de todo tipo, pues bien, en esas épocas daba todo por un compilador de batchs a exe, encontre uno que era de paga, se llamaba Quick Batch File Compiler de la empresa abyssmedia, y bueno no conocía Taringa y pasaba horas buscando un serial o un crack(recuerden eso es malo XD). Bueno, pasaron los años, aprendí .NET, y hace poco publique una entrada la cual nos muestra el uso de los servicios de compilación de .NET (VISITAR), y aplicando eso, mas un poco de creatividad me salió una dll la cual tiene lo necesario para compilar .bat a .exe, lo que hubiera dado por algo asi.

Pues bien, lo que hace esta aplicación es generar un código CSharp el cual crea un .bat temporal con el código que le asignemos y que luego lo inicializa, luego que termine su ejecución lo elimina, pues bien averiguando, es la misma forma en como trabaja Quick Batch File Compiler, bueno, el código que se genera es el siguiente:


using System;
using System.IO;

class Tmp {
public static void Main(string[] args) {
string tmpPath = Environment.GetEnvironmentVariable("temp");
// Crea un nombre aleatorio
tmpPath += Path.GetRandomFileName() + ".bat";

// Creamos un batch temporal
Stream arch = File.Open(tmpPath, FileMode.OpenOrCreate);

using (StreamWriter writer = new StreamWriter(arch)) {
writer.Write(@"[BatSource]");
}
arch.Close();

// Iniciar el batch
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = tmpPath;
// Para que no habra otra ventana
p.StartInfo.UseShellExecute = false;
p.Start();

// La aplicacion no continuara mientras la ejecucion
// del bat haya terminado
p.WaitForExit(); 

// Verificar si existe la ruta del archivo batch
if (File.Exists(tmpPath))
File.Delete(tmpPath);
}
}



Bueno el anterior lo guardo en mi archivo de recursos y luego lo llamo en la apliación, y este será el código que tendremos para compilar un exe, simple. Si habrán notado, existe una línea la cual tenemos el siguiente código:




writer.Write(@"[BatSource]");

Pues bien, cuando llame a todo el código reemplazare la cadena [BatSource] por el código fuente del archivo bat el cual quereamos compilar, se que es una manera muy tosca de hacer las cosas pero espero mejorarla para futuras versiones.

Bueno, esa es la introducción para el trabajo que realizaremos, para hacer una dll funcionable y reutilizable cree las siguientes clases:

  • CSharpUtil
  • CSharpCompiler (Contiene la enumeración CompilationResult)
  • BatCompiler

La clase CSharpUtil simplemente me devuelve el código CSharp con o sin el código fuente del archivo bat, y este lo utilizo en la clase BatCompiler la cual en su método CreateExeFromBatSource, creamos una instancia de la clase CSharpCompiler la cual esta en base al post en el que muestro como usar los servicios de compilación de .NET, y luego compila el archivo batch a un ejecutable, practicamente el código que realiza todo este trabajo es el siguiente:


public void CreateExeFromBatSource(string batSource) {
 CSharpCompiler csCompiler = new CSharpCompiler(this.exePath, true);
 string csSource = CSharpUtil.GetCompleteCSharpCode(batSource);

 // Creamos un archivo ejecutable
 CompilationResult result = csCompiler.CompileFromSourceCode(csSource);

 if (result == CompilationResult.Complete) {
  // La compilacion fue todo un exito
  this.result = result;
 }
 else {
  Console.WriteLine(csCompiler.Errores);
 }
}

Y bueno la clase CSharpCompiler es básicamente la misma de mi anterior entrada solo que con un poco de abstracción y sin comentarios de documentación, jejejeje.


public class CSharpCompiler {
  private string exePath;
  private int errorCounter;
  private string errores;
  private bool generateExecutable;

  public string ExePath {
  get { return exePath; }
  set { exePath = value; }
  }
  public int ErrorCounter {
  get { return errorCounter; }
  }
  public string Errores {
  get { return errores; }
  }
  public bool GenerateExecutable {
  get { return generateExecutable; }
  set { generateExecutable = value; }
  }
  
  public CSharpCompiler(string exePath, bool generateExecutable) {
  this.exePath = exePath;
  this.generateExecutable = generateExecutable;
  }
  
  public CompilationResult CompileFromSourceCode(string sourceCode) {
  StringBuilder errores = null;
  CSharpCodeProvider provider = new CSharpCodeProvider();
  
  CompilerParameters parameters = new CompilerParameters();
  parameters.GenerateExecutable = generateExecutable;
  parameters.ReferencedAssemblies.Add("System.dll");
  parameters.OutputAssembly = this.exePath;

  CompilerResults results = provider.CompileAssemblyFromSource(parameters, sourceCode);

  this.errorCounter = results.Errors.Count;

  if (results.Errors.Count > 0) {
  errores = new StringBuilder();
  foreach (CompilerError error in results.Errors) {
     errores.AppendLine(string.Format("Error en linea: {0}", error.Line));
     errores.AppendLine("\t" + error.ErrorText);
  }
  this.errores = errores.ToString();
  }
  if (results.Errors.Count > 0)
  return CompilationResult.Errors;
  else
  return CompilationResult.Complete;
  }
}

Y finalmente hice un proyecto de consola para hacer las pruebas a esta dll, y funciona bien hasta donde lo probe, pues bien, al ser una dll reutilizable esta puede ser utilizada en todo tipo de aplicaciones, consola, escritorio, web, otras dll, etc. Pues bien espero les sea útil dejo abajo el link con el código fuente de la aplicación.



Pues bien, como podrán haber visto, hace unos años hacer algo así hubiera sido algo imposible para mi, pero aprovechando las facilidades que las plataformas de desarrollo de hoy nos brindan, este tipo de cosas se nos hacen más fáciles mas un poco de creatividad obviamente. Espero que les haya gustado, si quieren aportar con ideas o código para que este pequeño proyecto siga creciendo en funcionalidad es bienvenido, solo dejen sus comentarios, bueno esta entrada va para mis amigos de elHacker.net no me había dado el buen nivel que tiene esta comunidad, sigan adelante!.


No hay comentarios:

Publicar un comentario