• Главная
  • Контакты
Подписаться:
  • Twitter
  • Facebook
  • RSS
  • VK
  • PushAll
logo

logo

  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • За год
    • Положительные
    • Отрицательные
  • Сортировка
    • По дате (возр)
    • По дате (убыв)
    • По рейтингу (возр)
    • По рейтингу (убыв)
    • По комментам (возр)
    • По комментам (убыв)
    • По просмотрам (возр)
    • По просмотрам (убыв)
Главная
  • Все
    • Положительные
    • Отрицательные
  • За сегодня
    • Положительные
    • Отрицательные
  • За вчера
    • Положительные
    • Отрицательные
  • За 3 дня
    • Положительные
    • Отрицательные
  • За неделю
    • Положительные
    • Отрицательные
  • За месяц
    • Положительные
    • Отрицательные
  • Главная
  • «Мечта лентяя» или скриптовый движок на самом себе

«Мечта лентяя» или скриптовый движок на самом себе +11

24.04.2017 07:16
EasyLy 9 3800 Источник
C#*, .NET*
У разработчиков прикладного ПО очень часто возникает потребность встроить в свой продукт некий скриптовый язык, который бы решал часть задач, не описанных детально на момент проектирования системы. Действительно удобно: и возможность расширения функциональности есть, и трудоёмкость создания такого решения, на первый взгляд, невелика.

Эту давнюю мечту можно было бы назвать «мечтой лентяя», если бы имеющиеся общедоступные встраиваемые скриптовые средства были бы просты. Готовые средства существовали давно, например на платформе Windows, ещё в прошлом веке можно было использовать интерфейсы VBScript и Jscript через COM-интерфейс IActiveScriptSite. В настоящее время существует большое количество и других решений, например на базе Lua, но все они имеют одну неприятную особенность, сильно ограничивающую желание их применять.

Скрипты прекрасно работают и сами по себе, на них можно выполнять и логику, и арифметику, но пользы от них ровным счётом никакой, если сложно или нет возможности:

• добавлять функции и объекты для доступа к объектам разрабатываемой системы,
• проводить синтаксический контроль исходного скрипта и генерировать сообщения о синтаксических ошибках,
• выполнять скрипт в пошаговом режиме, подобно отладчику, с нотификацией точки исполнения и статусом.

И ещё, хотелось бы, чтобы делалось всё это просто и интуитивно понятно и не приходилось бы проводить бессонные ночи за чтением многочисленной документации по новому API. Увы, это удаётся далеко не всегда и весьма нечасто.

Прикладное ПО сейчас очень часто пишется на C#, и хотелось бы иметь что-то знакомое, но гибкое, и позволяющее писать скрипты. Такое решение есть, и оно заслуживает пристального внимания. Это пространство имён System.CodeDom.Compiler с его классом CSharpCodeProvider. Всё это появилось ещё в .NET 4.0, но по какой-то причине в большинстве публикаций по C# не затрагивался вопрос написания скриптов на C#, используя сам же язык C# в качестве базового. А это очень и очень удобно для написания и дальнейшего сопровождения продукта.

В этом случае самый главный и интересный метод — CompileAssemblyFromSource(), который выполняет компиляцию, генерирует сообщения об ошибках, и мы уже запросто можем написать «Hello world!»

using System;
using System.IO;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // готовим текст скрипта
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("namespace ConsoleApplication1");
            sb.AppendLine("{");
            sb.AppendLine("   public class MyScripter");
            sb.AppendLine("   {");
            sb.AppendLine("       public void Hello()");
            sb.AppendLine("       {");
            sb.AppendLine("             Console.WriteLine(\"Hello world!\");");
            sb.AppendLine("       }");
            sb.AppendLine("   }");
            sb.AppendLine("}");

            // компилируем
            CSharpCodeProvider codeProvider = new CSharpCodeProvider();
            CompilerResults compileResults = codeProvider.CompileAssemblyFromSource(
                       new CompilerParameters(), new string[] { sb.ToString() });

            // выводим ошибки, если они есть
            foreach (CompilerError err in compileResults.Errors)
                Console.WriteLine("Error({0:1}): {2} {3}", err.Line, err.Column, 
                                  err.ErrorNumber, err.ErrorText);
            if (compileResults.Errors.HasErrors) return;

            // загружаем получившуюся dll в память
            byte[] dllBytes = File.ReadAllBytes(compileResults.PathToAssembly);
            Assembly asmDll = Assembly.Load(dllBytes, null);
            Type objType = asmDll.GetType("ConsoleApplication1.MyScripter");

            // создаём объект класса из скрипта
            object oClassInst = Activator.CreateInstance(objType);

            // получаем точка входа и выполняем её
            MethodInfo entry = objType.GetMethod("Hello", new Type[] {});
            entry.Invoke(oClassInst, null);
        }
    }
}

Запускаем на исполнение:

image

Итак, в простейшем виде скрипт на C# успешно работает. Простым изменением текста скрипта мы можем влиять на его работу. Собственно, осталось лишь передать в скрипт в качестве примера какой-либо объект из основной программы.

В качестве такого объекта вполне подойдёт объект типа string:

using System;
using System.IO;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string sMyStr = "Before Script.";

            // готовим текст скрипта
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("namespace ConsoleApplication1");
            sb.AppendLine("{");
            sb.AppendLine("   public class MyScripter");
            sb.AppendLine("   {");
            sb.AppendLine("       public void Hello(ref string s)");
            sb.AppendLine("       {");
            sb.AppendLine("             Console.WriteLine(\"Hello world!\");");
            sb.AppendLine("             s=\"After Script.\";");
            sb.AppendLine("       }");
            sb.AppendLine("   }");
            sb.AppendLine("}");

            // компилируем
            CSharpCodeProvider codeProvider = new CSharpCodeProvider();
            CompilerResults compileResults = codeProvider.CompileAssemblyFromSource(
                     new CompilerParameters(), new string[] { sb.ToString() });

            // выводим ошибки, если они есть
            foreach (CompilerError err in compileResults.Errors)
                Console.WriteLine("Error({0:1}): {2} {3}", err.Line, err.Column, 
                                   err.ErrorNumber, err.ErrorText);
            if (compileResults.Errors.HasErrors) return;

            // загружаем получившуюся dll в память
            byte[] dllBytes = File.ReadAllBytes(compileResults.PathToAssembly);
            Assembly asmDll = Assembly.Load(dllBytes, null);
            Type objType = asmDll.GetType("ConsoleApplication1.MyScripter");

            // создаём объект класса из скрипта
            object oClassInst = Activator.CreateInstance(objType);

            // получаем точка входа и готовим параметры
            MethodInfo entry = objType.GetMethod("Hello", 
                          new Type[] { typeof(string).MakeByRefType() });
            Object[] param = new Object[] { sMyStr };

            Console.WriteLine(param[0]);        // до выполнения скрипта
            
            entry.Invoke(oClassInst, param);    // вызов метода

            Console.WriteLine(param[0]);        // после выполнения скрипта
        }
    }
}

Запускаем на исполнение:

image

Видно, что теперь мы можем передавать и возвращать значения из кода скрипта. Если в качестве параметра передать не ссылку на строку, а какой-то внутренний объект информационной системы, то мы вполне можем воздействовать на систему из скрипта.

У данного механизма есть режим исполнения в режиме отладчика, для этого нужно подключать .pdb файл, есть и много других интересных возможностей.

Недостатком подхода можно считать только то, что при компиляции создаётcя dll во временном каталоге ОС.

Путь разрешения этого недостатка ведёт нас в сторону использования пространства System.Reflection.Emit, но это достаточно объёмный материал, подходящий для отдельной статьи. Это сложно, т. к. в данном случае компилятор и генерацию придется писать самостоятельно. Но зато какие возможности по придумыванию своего собственного синтаксиса! Да и назвать новый язык программирования можно в честь себя или любимой кошки.
Удачи!

Аркадий Пчелинцев, архитектор проектов
Поделиться с друзьями
-->

Комментарии (9)


  1. ColdPhoenix
    24.04.2017 10:21
    #10188124
    +4

    простите, но вы, как минимум, на год опоздали.
    Сейчас гораздо лучше брать Roslyn и интегрировать его.


    1. EasyLy
      24.04.2017 16:28
      #10188852

      Roslyn – хорошая штука, но требует .Net 4.5.2. Ниже он не соберётся. Да и великоват он
      для простых задач.


  1. molnij
    24.04.2017 10:44
    #10188158
    +8

    Ох сколько вам открытий чудных…
    Во-первых, если вы будете исполнять скрипты чуть больше чем hello world, вдруг выяснится отличие интерпретируемых от компилируемых языков — фаза компиляции ни разу не бесплатная. И если у интерпретируемых она естественным образом встроена в воркфлоу, то у шарпа вполне можно огрести заметные тормоза на ней.
    Потом обнаружится, что «гадить» в общий домен временными сборками скрипта — идея так себе, особенно если вспомнить, что удалить однажды загруженную сборку из домена невозможно (это слегка коррелирует с наличием временных файлов .dll, но проблема совсем в другом). А если делать по-науке и плодить домены, то придется изрядно позаниматься любовью с передачей нетривиальных параметров между доменами.
    А статей на эту тему достаточно. Даже на хабре поиск по CSharpCodeProvider выдает вполне адекватную подборку.


    1. Sinatr
      25.04.2017 10:46
      #10189830

      Хочется добавить, что решение проблемы (необходимость скриптовать некие сценарии) с помощью CodeDOM не совсем верно в общем случае (а случай в статье как раз общий, ни слова о конкретном сценарии).

      Полезность примера выводящего «Hello world» в данном случае тоже близка с 0.

      Единственно что обьяснили — это как скормить исходник и получить список ошибок, а это, извините, на статью не тянет, тема не раскрыта вообще от слова «полностью».

      Я в свое время (эмм… десять лет назад) использовал CodeDOM для пре-компиляции формул (вызывался метод, тело которого пользователь писал на C#, методу передавался список входных/выходных параметров, которые можно было использовать в вычислениях и логике и возвращать результат), с появление Roslyn это конечно смешно (хотя уверен новичкам сгодилось), и тут эта статья…


  1. ApeCoder
    24.04.2017 10:46
    #10188164

    https://blogs.msdn.microsoft.com/cdndevs/2015/12/01/adding-c-scripting-to-your-development-arsenal-part-1/


  1. kuda78
    24.04.2017 11:20
    #10188234
    +1

    добавлять функции и объекты для доступа к объектам разрабатываемой системы

    Необходимо реализовать у объекта интерфейс IDispatch. В C# с использованием рефлексии реализация доступа к методам и свойствам будет элементарен.

    проводить синтаксический контроль исходного скрипта и генерировать сообщения о синтаксических ошибках

    А чем этот момент не устроил в ActiveScriptSite? В ошибке будет указан и номер строки и что за ошибка.

    выполнять скрипт в пошаговом режиме, подобно отладчику, с нотификацией точки исполнения и статусом.

    С этим вопросом тоже все очень просто.
    https://docs.microsoft.com/en-us/scripting/winscript/active-script-debugging-overview


  1. ifle
    24.04.2017 12:25
    #10188330

    Вроде бы ClearScript самое оно
    https://clearscript.codeplex.com/


  1. mxms
    24.04.2017 12:45
    #10188372

    В этом отношении идеальный вариант это web-сервер на Forth, конфигурируемый Forth и со скриптингом на Forth.


  1. hensew
    24.04.2017 14:28
    #10188584

    Тоже бодался с этой темой, на Хабре в подходящее время вышла статья по NiL.JS
    Javascript удобнее для скриптов н никаких проблем с выгрузкой старого кода.

МЕТКИ

  • Хабы
  • Теги

.NET

C#

c#

СЕРВИСЫ
  • logo

    CloudLogs.ru - Облачное логирование

    • Храните логи вашего сервиса или приложения в облаке. Удобно просматривайте и анализируйте их.
Все публикации автора
  • Понимающий робот: голосовой интерфейс на ESP32 и OpenAI +6

    • 19.05.2025 06:30

    Ода структурам на примере работы с блоком питания Fnirsi DPS 150 +6

    • 24.01.2025 08:35

    Рассматриваем циклы процессора в контроллере CH32x035 +34

    • 14.01.2025 08:06

    Детали протокола управления блоком питания Fnirsi DPS-150 +18

    • 10.01.2025 08:54

    Применение нейросетей в DIY +20

    • 25.12.2024 11:54

    Стабилизируем положение потенциометра, считанное с АЦП +39

    • 14.11.2024 09:01

    Анализируем протокол управления блоком питания Fnirsi DPS 150 +28

    • 29.10.2024 08:39

    Будущее наступило, восстание машин уже идёт. Просто ползучим методом +25

    • 11.10.2024 08:46

    В погоне за скоростью. Оптимизация нейросетевых вычислений на процессоре К1967ВН044 компании «Миландр»

    • 26.06.2024 09:03

    Почему может не работать отладка на плате XIAO BLE

    • 19.06.2024 08:28

Подписка


ЛУЧШЕЕ

  • Сегодня
  • Вчера
  • Позавчера
08:01

Как установить OpenJarvis: разворачиваем локального ИИ-агента +23

07:45

Взламываем механизм тревоги в коже +20

07:02

Куда уходит память? Семплирующее профилирование колонок с Алисой в проде +19

12:16

Безопасность микроядра: где заканчивается трюизм и начинается инженерия +17

07:01

Работа двигателей внутреннего сгорания на озоне? +17

10:13

Странные машины: как хакеры собирают процессор из данных +16

09:01

GitLab Runners: полное руководство от shell до Kubernetes +16

10:41

Как научить ИИ понимать графики: разбираем новый набор данных ChartNet от MIT +14

09:19

Коробка с нейросетями: готовая ИИ-инфраструктура, которая просто работает +14

08:28

Есть ли жизнь после дисков, или когда игры перестали быть «вещью» +14

07:03

Что внутри #[derive(Serialize)]: TokenStream, syn, quote и почему этот serde так долго компилируется +14

06:37

Проектируем с нуля калькулятор на FPGA. Часть 7: микрокод для самодельного CPU +13

10:05

Как я превратил облачное видеонаблюдение в полноценные камеры для умного дома +12

07:19

В поисках Deus ex Machina. Почему ИИ пытаются внедрить все, но это не всегда получается +12

06:07

Учу 1С за две недели или что не так со студенческой практикой и ИТ-образованием +12

12:32

Как Immich помогает в работе с корпоративным фотоархивом +11

10:09

Даже HTTPS выдает атакующего. Артефакты для поиска хакера на каждом этапе атаки +11

09:17

14 лет на вершине: как Nokia потеряла рынок телефонов +11

07:25

Лучшие токены — те, которые вы не потратили: анатомия экономного LLM-бота +11

07:00

От телекома до пассажирских перевозок: два ПАК для мониторинга и удаленного управления +11

11:59

«Вы сливаете наши номера!» Как мы искали крота внутри компании, а нашли мобильных операторов +388

12:13

Всё о компьютерах в фильме «Парк юрского периода» +62

07:00

Как мы тысячу «яблок» для песочниц в дата-центры устанавливали +49

17:21

Легко ли сделать эмулятор ZX Spectrum для SEGA Mega Drive? +43

08:31

Кто работает, когда ничего не работает: от алерта до постмортема +42

08:44

Как развернуть модель для генерации видео LTX-2 на сервере +40

07:03

Zynq 7000. HDMI для платы Zynq Mini и Linux +31

09:01

Обучаем ИИ видеть то, чего он никогда не видел +29

13:01

Docker Fundamentals: хранение данных в Docker +27

15:55

Скоростная разработка электроники +23

15:14

Сравниваем LLM, 12 тестов для среднего класса: три Sonnet против GigaChat 2 MAX и YandexGPT Pro 5.1 +23

06:03

Ты больше не управляешь людьми: почему это происходит и что с этим делать +22

07:02

Как один баг в sendmail создал эру спама и привёл к SPF, DKIM и DMARC +19

09:49

«Но я пытался»: как Европа строит суверенное облако [и что уже пошло не так] +18

08:05

Подключаю свой перчик к умному дому, или как я датчик влажности почвы для Home Assistant делал +17

05:15

От гипотезы до производства: как устроены проекты НИОКР +17

20:05

Сможете ли вы найти пять ошибок в Python‑коде, который вызывает LLM +16

16:35

Серверная стойка 5 и 10 дюймов под homelab. (Lab Rax) +16

08:00

От расшифровки звонков до собственных приложений: как ИИ взрослеет внутри CRM +16

07:51

Моды, которые превращают ванильный Fallout 4 в современную игру. Часть 1 +16

11:27

Простая конструкция, но высокая вычислительная мощность: в чем особенность разработки базовых станций +70

07:01

Фотография при флуоресценции в мягком ультрафиолетовом свете +65

11:01

Перебор IP-адресов на хостинге — и как с этим бороться +63

11:37

Загадка падающей цепочки: почему конец цепи ускоряется сильнее g? +59

13:57

Открытые LLM в продакшене: 8 выводов о llama.cpp, Gemma и Qwen +38

15:02

Вы не знаете CSS. Мои вопросы о CSS с ответами. Версия 2026 года +36

12:19

OpenIDE Pro: публичный релиз для всех! Java, Kotlin, Go, TypeScript, Python, PHP и DB-клиент в одном дистрибутиве +33

08:00

Мониторинг модулей ядра и защита от Dirty-уязвимостей с помощью Wazuh +33

07:17

Лазерные спеклы — что это такое и как их применить с пользой? +27

09:11

Attack Replay: новый инструмент для воспроизведения и модификации трафика при тестировании СЗИ +26

08:40

Alice AI ART 2.0: путь к unified‑модели, которая одинаково хорошо умеет генерировать и редактировать картинки +26

07:15

Digital Points — программный осциллограф, так и не ставший стартапом +26

16:13

Две недели в Школе 21: кузница IT-рабов или выживальщик для программистов? +23

08:05

Что я узнал за год, мониторя 6 500 сайтов: 108 312 падений в цифрах +23

18:06

Как я добавил LLM в чат друзей, и приказал ей отыгрывать терминатора (и открыл портал в мультивселенную безумия) +22

14:37

Сериализация one-nio: от истоков к поддержке JDK 25 +22

11:42

Немецкий «король патентов» Артур Фишер. Как человек без высшего образования стал самым плодовитым изобретателем ФРГ +21

15:22

АЭРОДИСК сократил руководителя техподдержки. А он отсудил больше 7 миллионов и вернулся на работу +20

16:01

Как из организатора DevOps-сообщества я стал директором ассоциации, которая развивает Cloud-native-технологии +19

22:05

«Web 3.0» оказался скамом и «Web N+1» окажется им же. В чём реальная проблема сети Интернет? +18

ОБСУЖДАЕМОЕ

  • Может ли айтишник обеспечить себе уверенную пенсию: обзор стратегий +12

    • 321   18000

    «Вы сливаете наши номера!» Как мы искали крота внутри компании, а нашли мобильных операторов +392

    • 158   30000

    А что, если AI-код вообще не нужно ревьюить? -5

    • 62   9100

    Открытые LLM в продакшене: 8 выводов о llama.cpp, Gemma и Qwen +38

    • 50   14000

    Перебор IP-адресов на хостинге — и как с этим бороться +63

    • 48   18000

    Подключаю свой перчик к умному дому, или как я датчик влажности почвы для Home Assistant делал +17

    • 47   9700

    Жизнь в цивилизации и вне ее +9

    • 45   6800

    Серверная стойка 5 и 10 дюймов под homelab. (Lab Rax) +16

    • 41   14000

    Путь повышения КПД атомной электростанции втрое. Почему формула Карно не всегда верна. Путь к альтернативной энергетике +6

    • 36   5500

    OpenIDE Pro: публичный релиз для всех! Java, Kotlin, Go, TypeScript, Python, PHP и DB-клиент в одном дистрибутиве +33

    • 34   15000

    Манифест программиста, использующего AI-кодинг-агента +15

    • 33   17000

    Архитектура независимого умного дома: паттерны аппаратного проектирования и Embedded-разработка на ESP32: Сенсорбокс +9

    • 32   12000

    Две недели в Школе 21: кузница IT-рабов или выживальщик для программистов? +23

    • 30   14000

    АЭРОДИСК сократил руководителя техподдержки. А он отсудил больше 7 миллионов и вернулся на работу +20

    • 30   12000

    Как и зачем появились длинные чеки у самой крупной в США аптечной сети CVS +15

    • 30   8800
  • Главная
  • Контакты
© 2026. Все публикации принадлежат авторам.