none
Перекомпиляция самого себя c# RRS feed

  • Вопрос

  • Есть программа, скажем самая простая - консольная, которая выводит одно число, жестко прописано в коде...
    Цель: программа должна перекомпилировать сама себя бесконечное кол-во раз и изменила это число(на случайное новое)...
    Проблема:
    Чтобы скомпилировать новую программу - нужен код.... внутри этого кода должен быть код содержания этой программы, а в нутри него он же и так бесконечный цикл...:(
    Вот как бы решить эту дилемму...

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.CSharp;
    using System.CodeDom.Compiler;
    using System.IO;
    using System.Reflection;
    using System.Threading;
    using System.Diagnostics;
    
    namespace Перекомпиляция
    {
      class Program
      {
        static CompilerResults results;
    
        static void Main(string[] args)
        {
          if (File.Exists("Updata.exe"))
            File.Delete("Updata.exe");
          if (File.Exists("Перекомпиляция_.exe"))
            File.Delete("Перекомпиляция_.exe");
    
          string key="10";
          Console.WriteLine("Создано с ключом {0}", key);
    
          Random rnd = new Random();
          int sKey = rnd.Next(0, 100);
          Compilir(sKey.ToString());
          if (results.Errors.Count == 0)
          {
            Console.WriteLine("Новое приложение создано без ошибок...");
            Console.WriteLine("Ключ {0}", sKey.ToString());
            CompilirUpdata();
            Console.WriteLine("Перезапуск через 4 секунды");
            Thread.Sleep(TimeSpan.FromSeconds(4));
            Process process = new Process();
            process.StartInfo.FileName = "Updata.exe";
            process.Start();
            //kill сам себя...
            foreach (Process p in Process.GetProcesses())
            {
              if (p.ProcessName.Contains("Перекомпиляция"))
                p.Kill();
            }
          }
          else
          {
            Console.ReadKey();
          }
        }
    
        static void CompilirUpdata()
        {
          string source =
          @"
    using System;
    using System.Threading;
    using System.Diagnostics;
    using System.IO;
    namespace Updata
    {
      class Program
      {
        static void Main(string[] args)
        {
          Console.WriteLine(""Запуск Updata"");
          Console.WriteLine(""Перезапуск через 4 секунды"");
    
          if (File.Exists(""Перекомпиляция.exe""))
          {
             if (File.Exists(""Перекомпиляция2.exe""))
              File.Delete(""Перекомпиляция2.exe"");
             File.Move(""Перекомпиляция.exe"", ""Перекомпиляция2.exe"");
          }
          if (File.Exists(""Перекомпиляция_.exe""))
          {
             if (File.Exists(""Перекомпиляция.exe""))
              File.Delete(""Перекомпиляция.exe"");
             File.Move(""Перекомпиляция_.exe"", ""Перекомпиляция.exe"");
          }
          Thread.Sleep(TimeSpan.FromSeconds(4));    
          Process process = new Process();
          process.StartInfo.FileName = ""Перекомпиляция.exe"";
          process.Start();
        }
      }
    }
          "
          ;
    
          // Настройки компиляции 
          Dictionary<string, string> providerOptions = new Dictionary<string, string> 
            { 
              {"CompilerVersion", "v3.5"} 
            };
          CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
    
          String path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Updata.exe");
    
          CompilerParameters compilerParams = new CompilerParameters { OutputAssembly = path, GenerateExecutable = true };
          compilerParams.ReferencedAssemblies.Add("System.dll");
    
          // Компиляция 
          results = provider.CompileAssemblyFromSource(compilerParams, source);
    
          // Выводим информацию об ошибках 
          Console.WriteLine("Number of Errors: {0}", results.Errors.Count);
          foreach (CompilerError err in results.Errors)
          {
            Console.WriteLine("ERROR {0}", err.ErrorText);
          }
        }
        static void Compilir(string key)
        {
          string source =
            @"
    using System;
    using System.Threading;
    using System.Diagnostics;
    using System.IO;
    namespace Updata
    {
      class Program
      {
        static void Main(string[] args)
        {"+
          "string key=\"" + key + "\";" +
          @"Console.WriteLine(""Создано с ключом {0}"", key);
          Console.WriteLine(""Перезапущенно""); 
    // вот тут можно написать весь код...
    // только в нем нужно будет опять в этом же месте повторить //его и до бесконечности... 
          Console.ReadKey();
        }
      }
    } 
    ";
    
          // Настройки компиляции 
          Dictionary<string, string> providerOptions = new Dictionary<string, string> 
          { 
            {"CompilerVersion", "v3.5"} 
          };
          CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
    
          String path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Перекомпиляция_.exe");
    
          CompilerParameters compilerParams = new CompilerParameters { OutputAssembly = path, GenerateExecutable = true };
          compilerParams.ReferencedAssemblies.Add("System.dll");
    
          // Компиляция 
          results = provider.CompileAssemblyFromSource(compilerParams, source);
    
          // Выводим информацию об ошибках 
          Console.WriteLine("Number of Errors: {0}", results.Errors.Count);
          foreach (CompilerError err in results.Errors)
          {
            Console.WriteLine("ERROR {0}", err.ErrorText);
            Console.WriteLine("Line error {0}", err.Line);
          }
        }
      }
    }
    
    

     

    20 января 2011 г. 11:52

Ответы

  • Пример:

    using System;
    using System.Reflection;
    using System.CodeDom.Compiler;
    using Microsoft.CSharp;
    
    namespace ConsoleApplication1
    {
      public class Program
      {
        public const string CODE_TEMPLATE = @"
          using System;
          using System.Reflection;
          using System.CodeDom.Compiler;
          using Microsoft.CSharp;
    
          public class Class{0}
          {{
            private static Assembly Compile(string source)
            {{
              CompilerParameters compilerParameters = new CompilerParameters() {{ GenerateInMemory = true }};
              compilerParameters.ReferencedAssemblies.AddRange(new[] {{ ""System.dll"", ""Microsoft.CSharp.dll"" }});
              return new CSharpCodeProvider().CompileAssemblyFromSource(compilerParameters, source).CompiledAssembly;
            }}
    
            public static void Generate()
            {{
              int classNumber = {0};
              Console.WriteLine(""Hello from class {{0}}."", classNumber);
              Console.ReadKey();
              string codeTemplate = Assembly.GetEntryAssembly().GetType(""ConsoleApplication1.Program"").GetField(""CODE_TEMPLATE"").GetValue(null).ToString();
              string code = String.Format(codeTemplate, classNumber + 1);
              Assembly assembly = Compile(code);
              MethodInfo methodInfo = assembly.GetType(String.Concat(""Class"", (classNumber + 1).ToString())).GetMethod(""Generate"");
              methodInfo.Invoke(null, null);
            }}
          }}
        ";
    
        static void Main(string[] args)
        {
          Generate();
        }
    
        private static Assembly Compile(string source)
        {
          CompilerParameters compilerParameters = new CompilerParameters() { GenerateInMemory = true };
          compilerParameters.ReferencedAssemblies.AddRange(new[] { "System.dll", "Microsoft.CSharp.dll" });
          return new CSharpCodeProvider().CompileAssemblyFromSource(compilerParameters, source).CompiledAssembly;
        }
    
        private static void Generate()
        {
          Console.WriteLine("Hello from main program.");
          Console.ReadKey();
          string code = String.Format(CODE_TEMPLATE, 0);
          Assembly assembly = Compile(code);
          MethodInfo methodInfo = assembly.GetType("Class0").GetMethod("Generate");
          methodInfo.Invoke(null, null);
        }
      }
    }
    
    

    • Помечено в качестве ответа dampirik 25 января 2011 г. 12:28
    21 января 2011 г. 21:21
  • Судя по скриншоту ошибки можно предположить, что Вы изменили пространство имен класса Program. Пожалуйста, замените все встречающиеся строки "ConsoleApplication1" в тексте программы на "Перекомпиляция_2".
    • Помечено в качестве ответа dampirik 25 января 2011 г. 12:28
    24 января 2011 г. 16:45

Все ответы

  • Если процесс будет запрограммирован как бесконечный, то в результате будет либо StackOverflowException или OutOfMemoryException :)

    Сама себя она перекомпилировать не может, может только запустить вновь скомпилированный из исходного текста код. Если скомпилированный код будет запускаться в том же самом домене (это возможно только при условии отсутствия совпадающих идентификаторов классов), можно сделать шаблон текста программы статическим (рекомендую отдельный строковый ресурс сборки). Доступ к нему будет у всех экземпляров классов (через отражение), следовательно его не нужно будет встраивать в каждый генерируемый экземпляр.

    20 января 2011 г. 13:33
  •  можно сделать шаблон текста программы статическим (рекомендую отдельный строковый ресурс сборки). Доступ к нему будет у всех экземпляров классов (через отражение), следовательно его не нужно будет встраивать в каждый генерируемый экземпляр.

    Вот я примерно так и хотел... только что-то я запутался в конец...:(

    Не продемонстрируйте на примере каком-нить...

    20 января 2011 г. 15:40
  • Пример:

    using System;
    using System.Reflection;
    using System.CodeDom.Compiler;
    using Microsoft.CSharp;
    
    namespace ConsoleApplication1
    {
      public class Program
      {
        public const string CODE_TEMPLATE = @"
          using System;
          using System.Reflection;
          using System.CodeDom.Compiler;
          using Microsoft.CSharp;
    
          public class Class{0}
          {{
            private static Assembly Compile(string source)
            {{
              CompilerParameters compilerParameters = new CompilerParameters() {{ GenerateInMemory = true }};
              compilerParameters.ReferencedAssemblies.AddRange(new[] {{ ""System.dll"", ""Microsoft.CSharp.dll"" }});
              return new CSharpCodeProvider().CompileAssemblyFromSource(compilerParameters, source).CompiledAssembly;
            }}
    
            public static void Generate()
            {{
              int classNumber = {0};
              Console.WriteLine(""Hello from class {{0}}."", classNumber);
              Console.ReadKey();
              string codeTemplate = Assembly.GetEntryAssembly().GetType(""ConsoleApplication1.Program"").GetField(""CODE_TEMPLATE"").GetValue(null).ToString();
              string code = String.Format(codeTemplate, classNumber + 1);
              Assembly assembly = Compile(code);
              MethodInfo methodInfo = assembly.GetType(String.Concat(""Class"", (classNumber + 1).ToString())).GetMethod(""Generate"");
              methodInfo.Invoke(null, null);
            }}
          }}
        ";
    
        static void Main(string[] args)
        {
          Generate();
        }
    
        private static Assembly Compile(string source)
        {
          CompilerParameters compilerParameters = new CompilerParameters() { GenerateInMemory = true };
          compilerParameters.ReferencedAssemblies.AddRange(new[] { "System.dll", "Microsoft.CSharp.dll" });
          return new CSharpCodeProvider().CompileAssemblyFromSource(compilerParameters, source).CompiledAssembly;
        }
    
        private static void Generate()
        {
          Console.WriteLine("Hello from main program.");
          Console.ReadKey();
          string code = String.Format(CODE_TEMPLATE, 0);
          Assembly assembly = Compile(code);
          MethodInfo methodInfo = assembly.GetType("Class0").GetMethod("Generate");
          methodInfo.Invoke(null, null);
        }
      }
    }
    
    

    • Помечено в качестве ответа dampirik 25 января 2011 г. 12:28
    21 января 2011 г. 21:21
  • Ругается на

    methodInfo.Invoke(null
    , null
    ); 

    Exception has been thrown by the target of an invocation.

    Попробую разобраться...

    23 января 2011 г. 13:40
  • Если Вы ничего не меняли, этот код не должен выбрасывать исключений.

    Попробуйте заключить содержимое метода Generate в шаблоне в блок try-catch с выводом сообщения исключения на консоль и напишите его в этой теме.

    24 января 2011 г. 6:01
  • Под какую версию .net framework вы собирали проект (под 4-ю версию должно все компилироваться и работать без ошибок)? Изменяли ли что-нибудь, если изменяли, то приведите пожалуйтса код. И как уже посоветовал Алексей, оберните метод в try-catch и напишите информацию об исключении.


    Для связи [mail]
    24 января 2011 г. 11:33
  • Как не странно код не изменял... .net framework 4

    http://otherplanet.ru/123.jpg - вот исключение...

    24 января 2011 г. 14:12
  • Судя по скриншоту ошибки можно предположить, что Вы изменили пространство имен класса Program. Пожалуйста, замените все встречающиеся строки "ConsoleApplication1" в тексте программы на "Перекомпиляция_2".
    • Помечено в качестве ответа dampirik 25 января 2011 г. 12:28
    24 января 2011 г. 16:45
  • Судя по скриншоту ошибки можно предположить, что Вы изменили пространство имен класса Program. Пожалуйста, замените все встречающиеся строки "ConsoleApplication1" в тексте программы на "Перекомпиляция_2".

    Действительно...есть такое...

    Большое Вам спасибо...

    25 января 2011 г. 12:28