在传统观念中,JavaScript 被认为是一种仅限于浏览器中运行的脚本语言。然而,随着技术的发展,JavaScript 的应用范围已经远远超出了网页。现在,即使在桌面应用程序的开发中,JavaScript 也扮演着越来越重要的角色。本文将介绍一个框架,它允许开发者使用 JavaScript 来构建传统的桌面程序,而无需了解 C 或 C++ 等语言。
通常,要在桌面计算机上运行自己编写的程序,需要学习如 C、C#、Pascal 等编程语言。这些语言可以将代码编译成可执行文件。然而,JavaScript由于其运行在浏览器上的特性,并不能直接用于创建桌面应用程序。传统上,使用 C/C++ 来完成这项工作,尽管它们有明显的缺点,比如需要安装庞大的开发工具(如 Visual Studio),编写代码、编译、链接等一系列步骤才能得到可执行程序。一旦程序被分发给客户端,程序的逻辑通常是固定的,这意味着很难改变其行为。
使用如 JavaScript、Lua、Python 等脚本语言是解决上述问题的常见方法。但在大多数情况下,脚本语言通常作为扩展其能力的辅助工具。本文将介绍一个框架,它允许完全使用 JavaScript 来完成桌面程序的开发。
众所周知,QuickJS 是一个高性能的JavaScript运行时环境。SOUI 是一个高性能的直接 UI 库,两者都是用 C++ 构建的。如果能够将 SOUI 的接口导出到 JavaScript,将得到一个新的框架,开发者可以在 XML 中设计 UI,并使用 JavaScript 编写逻辑。这项工作现在由模块 soui4js 完成。
在解释代码如何工作之前,让先看一下项目的文件结构。项目包括核心模块 soui4js 和依赖模块 qjsbind。其他文件夹如 extctrl、jsvodplayer、sliveplayer 是播放器演示的服务器。depends 包含 4 个子文件夹,包括 quickjs、soui4、sdl2 和 vodplayer。quickjs 和 soui4 是两个核心模块,sdl2 和 vodplayer 是播放器演示的服务器。
在运行演示之前,首先运行 copy_bin.bat。它将把依赖的 DLL 复制到 debug 和 release 文件夹中。然后打开 soui4js.sln 并构建所有内容。确保所有模块都构建成功。现在,将在 debug 或 release 文件夹中找到 xliveplayer.exe,具体取决于选择的配置。
运行 xliveplayer.exe,应该看到类似于以下快照的 UI:
可能会问:“提供的代码都是用 C++ 编写的,JavaScript在哪里?”别急,请给更多的耐心。
在 debug/release 文件夹中,可能会看到很多文件。实际上,它们大多数是直播播放器演示的服务器。构建桌面程序所需的模块是 quickjs、soui4 DLL 和 soui4js.dll。xliveplayer.exe 模块是运行 JavaScript 程序的宿主程序。xliveplayer.exe 使用的代码是固定的,在大多数情况下不应该被更改。
需要编写的所有工作是编写一个 main.js 并提供一个 main 函数。运行 xliveplayer.exe 将自动加载 main.js 并运行 main 函数。由于 xliveplayer.exe 实现了相对固定的逻辑,为了避免重建类似的 js 主机,可以简单地将其名称更改为想要的任何名称。
演示中包含的 main.js 相对复杂。为了简单起见,这里使用了另一个项目来解释。
下载源代码,zip 文件包含一个 main.js 和一个 uires.zip。将其解压缩到一个文件夹中(例如,d:\md5calc),并使用命令 xliveplayer.exe d:\md5calc 来加载 md5calc。如果运行成功,可以将文件拖到 UI 上,将看到以下 UI。
这个程序相当简单:它加载 uires.zip 作为资源。它创建了一个包含 listview 控件的主窗口并显示该窗口。它进入一个消息循环并等待用户输入。
以下是 main.js 的代码示例:
import * as soui4 from "soui4";
import * as os from "os";
import * as std from "std";
var g_workDir = "";
class MainDialog extends soui4.JsHostWnd {
constructor() {
super("layout:dlg_main");
this.onEvt = this.onEvent;
}
// ...
};
function main(inst, workDir, args) {
soui4.log(workDir);
g_workDir = workDir;
let theApp = soui4.GetApp();
let souiFac = soui4.CreateSouiFactory();
// 展示如何从 zip 文件加载资源
let resProvider = soui4.CreateZipResProvider(theApp, workDir + "\\uires.zip", "souizip");
if (resProvider == 0) {
soui4.log("load res from uires.zip failed");
return -1;
}
let resMgr = theApp.GetResProviderMgr();
resMgr.AddResProvider(resProvider, "uidef:xml_init");
resProvider.Release();
let hwnd = soui4.GetActiveWindow();
let hostWnd = new MainDialog();
hostWnd.Create(hwnd, 0, 0, 0, 0);
hostWnd.SendMessage(0x110, 0, 0);
// 发送初始化对话框消息
hostWnd.ShowWindow(1);
// 1==SW_SHOWNORMAL
souiFac.Release();
let ret = theApp.Run(hostWnd.GetHwnd());
hostWnd = null;
soui4.log("js quit");
return ret;
}
globalThis.main = main;
import * as soui4 from "soui4";
最重要的是,使用代码 import soui4 APIs,可以使用它来显示 UI 并实现逻辑和一切。
目前调试程序相对困难。必须查看日志以确保代码是否按预期运行。有一个演示如何调试 quickjs 的仓库在 https://github.com/koush/vscode-quickjs-debug,如果时间允许,希望能够将其整合到这项工作中。