基于 WinForm + NLua + ScintillaNET 开发的 Lua 代码编辑器 / 运行器,核心实现了Lua 脚本编辑、逐行高亮执行、后台线程运行、强制终止死循环四大核心功能,解决了 Lua 执行阻塞界面、死循环无法停止的关键问题。

1. 核心技术栈

  • 界面:WinForm 窗体
  • 代码编辑:ScintillaNET(代码高亮、行标记)
  • Lua 引擎:NLua(C# 与 Lua 互操作、执行 Lua 代码)
  • 线程控制:后台线程执行 Lua,避免卡死 UI

2. 核心功能

  1. Lua 代码编辑 Scintilla 控件提供代码编辑区域,内置测试脚本演示功能。
  2. 逐行执行高亮 通过 Lua 内置 debug.sethook 钩子监听代码执行行号,调用 C# 方法在编辑器中标记:
    • 黄色:当前正在执行的行
    • 浅绿色:已执行完成的行
  3. 后台安全执行 Lua 代码在独立后台线程中运行,不会阻塞 WinForm 主界面。
  4. 强制终止死循环(核心亮点) 点击停止按钮直接调用 Thread.Abort() 强制中止执行线程,无论 Lua 死循环还是 C# 死循环都能立刻停止,彻底解决卡死问题。
  5. C# ↔ Lua 互操作 注册 C# 方法供 Lua 调用:
    • cs_print:Lua 打印日志
    • cs_infinite_loop:C# 死循环函数(测试用)
    • mark_line:Lua 通知 C# 标记执行行

3. 关键机制

  1. 执行状态控制:用 _isExecuting 标记运行状态,防止重复执行
  2. 线程安全:跨线程更新 UI 使用 Invoke 方法
  3. 资源释放:Lua 执行完毕 / 中止后自动释放 Lua 实例资源
  4. 异常处理:捕获线程中止、代码报错等异常,弹窗提示错误信息

4. 核心流程

  1. 编辑 Lua 代码 → 点击运行
  2. 启动后台线程,初始化 Lua 环境、注册函数、安装调试钩子
  3. Lua 逐行执行,实时高亮当前行
  4. 执行完成 / 报错 / 点击停止 → 终止线程、清理标记、释放资源

总结

这是一个轻量化、可直接运行的 Lua 集成开发工具,完美解决了 Lua 执行阻塞界面、死循环无法终止的痛点,同时实现了代码编辑、逐行调试高亮、C# 与 Lua 双向交互,适合作为 Lua 脚本调试、嵌入 C# 应用的基础框架。

using System;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using NLua;
using ScintillaNET;

namespace LuaEditor.lua全部运行
{
    public partial class Form1 : Form
    {
        private Lua _lua;
        private Thread _luaThread;    // 线程
        private volatile bool _isExecuting;

        private const int MARK_EXECUTED = 1;
        private const int MARK_CURRENT = 2;
        private int _lastLine = -1;

        public Form1()
        {
            InitializeComponent();
            InitScintillaStyle();
            LoadTestScript();
        }

        private void InitScintillaStyle()
        {
            _scintilla.Markers[MARK_EXECUTED].SetBackColor(Color.LightGreen);
            _scintilla.Markers[MARK_EXECUTED].Symbol = MarkerSymbol.LeftRect;

            _scintilla.Markers[MARK_CURRENT].SetBackColor(Color.Yellow);
            _scintilla.Markers[MARK_CURRENT].Symbol = MarkerSymbol.RoundRect;
        }

        // ===================== 测试脚本:调用 C# 死循环函数 =====================
        private void LoadTestScript()
        {
            _scintilla.Text = @"-- 测试:调用 C# 死循环函数
local a = 10

if a > 5 then
    cs_print('进入if分支')
end

-- 调用 C# 死循环
cs_infinite_loop()

cs_print('永远到不了这里')
";
        }

        // ===================== 运行(Thread) =====================
        private void 运行ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_isExecuting)
            {
                MessageBox.Show("正在执行中");
                return;
            }

            string code = _scintilla.Text?.Trim();
            if (string.IsNullOrEmpty(code))
            {
                MessageBox.Show("代码不能为空");
                return;
            }

            ClearAllLineMarkers();
            _lastLine = -1;
            _isExecuting = true;

            _luaThread = new Thread(() => RunLua(code))
            {
                IsBackground = true
            };
            _luaThread.Start();
        }

        private void RunLua(string code)
        {
            try
            {
                _lua = new Lua();
                _lua.State.Encoding = Encoding.UTF8;
                _lua.LoadCLRPackage();
                RegisterLuaFunctions(_lua);
                InstallDebugHook(_lua);

                _lua.DoString(code);
            }
            catch (ThreadAbortException)
            {
                // 线程被中止,正常退出
            }
            catch (Exception ex)
            {
                Invoke((Action)(() =>
                {
                    MessageBox.Show("错误:" + ex.Message);
                }));
            }
            finally
            {
                _isExecuting = false;
                _lua?.Dispose();
                _lua = null;

                Invoke((Action)(() =>
                {
                    _scintilla.MarkerDeleteAll(MARK_CURRENT);
                }));
            }
        }

        // ===================== 【强制停止】能中止任何死循环! =====================
        private void btnStop_Click(object sender, EventArgs e)
        {
            if (!_isExecuting || _luaThread == null) return;

            try
            {
                _isExecuting = false;
                _luaThread.Abort();  // 强制中止线程(唯一能杀死死循环)
            }
            catch { }
        }

        // ===================== C# 死循环函数(给Lua调用) =====================
        public void cs_infinite_loop()
        {
            // 死循环!!!
            while (true)
            {
                // 即使这里死循环,点击停止也能退出!
                Thread.Sleep(10);
            }
        }

        // ===================== 行标记 =====================
        public void MarkExecutingLine(int line)
        {
            if (!_isExecuting) return;

            if (_scintilla.InvokeRequired)
            {
                _scintilla.Invoke(new Action<int>(MarkExecutingLine), line);
                return;
            }

            int realLine = line - 1;
            if (realLine < 0 || realLine >= _scintilla.Lines.Count) return;

            if (_lastLine >= 0)
                _scintilla.Lines[_lastLine].MarkerAdd(MARK_EXECUTED);

            _scintilla.MarkerDeleteAll(MARK_CURRENT);
            _scintilla.Lines[realLine].MarkerAdd(MARK_CURRENT);
            _scintilla.Lines[realLine].EnsureVisible();

            _lastLine = realLine;
        }

        // ===================== 工具 =====================
        private void ClearAllLineMarkers()
        {
            _scintilla.MarkerDeleteAll(MARK_EXECUTED);
            _scintilla.MarkerDeleteAll(MARK_CURRENT);
        }

        public void LuaPrint(string s) { Console.WriteLine(s); }

        public void RegisterLuaFunctions(Lua lua)
        {
            RegisterLuaFunction(lua, "cs_print", nameof(LuaPrint), typeof(string));
            RegisterLuaFunction(lua, "cs_infinite_loop", nameof(cs_infinite_loop));
            RegisterLuaFunction(lua, "mark_line", nameof(MarkExecutingLine), typeof(int));
        }

        private void RegisterLuaFunction(Lua lua, string luaName, string methodName, params Type[] types)
        {
            var m = GetType().GetMethod(methodName, types);
            lua.RegisterFunction(luaName, this, m);
        }

        private void InstallDebugHook(Lua lua)
        {
            lua.DoString(@"
                debug.sethook(function()
                    local line = debug.getinfo(2,'l').currentline
                    if line>0 then mark_line(line) end
                end, 'l')
            ");
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            运行ToolStripMenuItem_Click(null, null);
        }
    }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐