基于 WinForm + NLua + ScintillaNET 开发的 Lua 代码编辑器 / 运行器,核心实现了Lua 脚本编辑、逐行高亮执行、后台线程运行、强制终止死循环四大核心功能,
·

基于 WinForm + NLua + ScintillaNET 开发的 Lua 代码编辑器 / 运行器,核心实现了Lua 脚本编辑、逐行高亮执行、后台线程运行、强制终止死循环四大核心功能,解决了 Lua 执行阻塞界面、死循环无法停止的关键问题。
1. 核心技术栈
- 界面:WinForm 窗体
- 代码编辑:ScintillaNET(代码高亮、行标记)
- Lua 引擎:NLua(C# 与 Lua 互操作、执行 Lua 代码)
- 线程控制:后台线程执行 Lua,避免卡死 UI
2. 核心功能
- Lua 代码编辑 Scintilla 控件提供代码编辑区域,内置测试脚本演示功能。
- 逐行执行高亮 通过 Lua 内置
debug.sethook钩子监听代码执行行号,调用 C# 方法在编辑器中标记:- 黄色:当前正在执行的行
- 浅绿色:已执行完成的行
- 后台安全执行 Lua 代码在独立后台线程中运行,不会阻塞 WinForm 主界面。
- 强制终止死循环(核心亮点) 点击停止按钮直接调用
Thread.Abort()强制中止执行线程,无论 Lua 死循环还是 C# 死循环都能立刻停止,彻底解决卡死问题。 - C# ↔ Lua 互操作 注册 C# 方法供 Lua 调用:
cs_print:Lua 打印日志cs_infinite_loop:C# 死循环函数(测试用)mark_line:Lua 通知 C# 标记执行行
3. 关键机制
- 执行状态控制:用
_isExecuting标记运行状态,防止重复执行 - 线程安全:跨线程更新 UI 使用
Invoke方法 - 资源释放:Lua 执行完毕 / 中止后自动释放
Lua实例资源 - 异常处理:捕获线程中止、代码报错等异常,弹窗提示错误信息
4. 核心流程
- 编辑 Lua 代码 → 点击运行
- 启动后台线程,初始化 Lua 环境、注册函数、安装调试钩子
- Lua 逐行执行,实时高亮当前行
- 执行完成 / 报错 / 点击停止 → 终止线程、清理标记、释放资源
总结
这是一个轻量化、可直接运行的 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);
}
}
}
更多推荐

所有评论(0)