Sciter之c++与前端交互(五)
入门介绍了前端调用c能力,本章将介绍c与html(前端)相互调用、数据传递等。
前言
Sciter 是一个高质量但小众的嵌入式 UI 引擎,适合追求性能、体积和原生集成的桌面应用开发者。
我觉得 Sciter 比较有意思,它很小众,是闭源的,商用需要许可。它是Andrew Fedoniouk开发维护,Andrew获得了物理学和应用数学硕士学位以及艺术文凭。他的职业生涯始于俄罗斯航空航天工业的研究员。这种跨领域背景使他既具备深厚的技术功底,又懂得用户界面设计的艺术。
Sciter官网:https://sciter.com/ 2025-11-15 sciter-js-sdk最新版v6.0.2.28
本次入门开发环境:window 10 + Clion 2024.3 + Sciter-js v6.0.2.28(2025-11-15最新版) + Bundled MinGW 11.0
1.前端调用后端c++:将c++对象暴露给前端调用
基于入门(一):https://lingkang.top/archives/sciter-ru-men-zhi-hello-yi
下面将演示常见的两种方式:
- 第一种方式是常见的直接调用
c++暴露的方法 - 第二中方式暴露更灵活的
sciter::value相当于javascript中var,可以是number、string、json、arr、function等
c++
#include <cstdint> // C++ 标准头文件(首选)
#include <windows.h>
#include "sciter-x-window.hpp"
#include "aux-cvt.h"
#include "sciter-x.h"
class myWindow : public sciter::window {
public:
myWindow() : window(SW_MAIN | SW_ENABLE_DEBUG) {}
// 将c++的调用接口暴露给前端,前端调用时:Window.this.myWindow.nativeCallHello()
SOM_PASSPORT_BEGIN(myWindow)
SOM_FUNCS(
SOM_FUNC(nativeCallHello)
)
SOM_PASSPORT_END
// 暴露名称,使用 xcall 调用 数据类型是 js的 sciter::value
BEGIN_FUNCTION_MAP
// 0表示没有参数
FUNCTION_0("byXcallReturnFun", byXcallReturnFun)
// _1 是指有一个入参,入参相当于前端js的var对象,可能是number、string、arr、json、function等
FUNCTION_1("byXcall", byXcall)
END_FUNCTION_MAP
// 暴露给前端js调用的方法
sciter::string nativeCallHello() {
MessageBoxW(NULL, L"hello,我是后端c++方法", L"确认", MB_OK);
return WSTR("ok");
}
// sciter::value 可能是number、string、arr、json、function等
sciter::value byXcall(sciter::value obj) {
sciter::string data = obj.to_string();
std::cout << "length=" << data.size() << std::endl;
if (!data.empty()) {
// 打印宽字符串
std::wcout << L"前端入参 param = " << data << std::endl;
return L"您的入参是: " + data;
}
// 例如返回一个json
sciter::value json;
json.set_item("code", 0);
// 字符串含有中文时使用宽字符
json.set_item("msg", L"error 您输入的值为空!");
return json;
}
sciter::value byXcallReturnFun() {
// 定义一个方法给前端调用
std::function<std::wstring(const std::wstring)> myFun = [=](const std::wstring param) -> std::wstring {
return L"听我说:" + param;
};
return sciter::value(myFun);
}
};
int uimain(std::function<int()> run) {
// 创建ui窗口实例
sciter::om::hasset<myWindow> window = new myWindow();
// 加载前端UI的html文件
window->load(
WSTR("file://C:\\Users\\Administrator\\Desktop\\project\\sciter\\app\\demo_20251121\\ui\\hello-ui.html"));
window->expand();
return run();
}
/**
* 图形用户界面(GUI)应用程序的标准入口函数,相当于控制台程序中的 main 函数。
* 2025-11-15 by lingkang
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 启用试调
SciterSetOption(NULL, SCITER_SET_SCRIPT_RUNTIME_FEATURES,
ALLOW_FILE_IO |
ALLOW_SOCKET_IO |
ALLOW_EVAL |
ALLOW_SYSINFO);
// 初始化sciter
sciter::application::start();
// 加载我们的窗口
uimain([]() -> int { return sciter::application::run(); });
// 结束时 关闭
sciter::application::shutdown();
return 0;
}
static std::vector<sciter::string> _argv;
// 设置 sciter 的初始化应用
namespace sciter {
namespace application {
HINSTANCE hinstance() {
return nullptr; // not used
}
const std::vector<sciter::string> &argv() {
// 获取命令行参数
return _argv;
}
}
}
前端html
<!DOCTYPE html>
<html
window-width=600px
window-height=450px
window-resizable=true
>
<head>
<meta charset="UTF-8">
<title>lingkang的sciter前端与c++相互调用演示</title>
<style>
.flex-row {
display: flex;
flex-direction: row;
gap: 10px;
}
</style>
</head>
<body style="display: flex;flex-direction: column;gap: 20px">
<button type="button" id="btn">点击调用c++能力(Window.this.myWindow.nativeCallHello())</button>
<div class="flex-row">
<input type="text" placeholder="请输入入参" id="xcallValue"></input>
<button type="button" id="btn-xcall">基于xcall调用</button>
</div>
<button type="button" id="btn-xcall-fun">基于xcall调用返回方法</button>
<script>
// 给按钮绑定点击事件
document.getElementById('btn').onclick = function () {
// 一定要用 Window.this.c++定义的类名.要调用的方法名称
const result = Window.this.myWindow.nativeCallHello()
console.log('result=' + result)
}
// 基于 xcall 调用
document.getElementById('btn-xcall').onclick = async function () {
var val = document.getElementById('xcallValue').value
var result = Window.this.xcall('byXcall', val)
if (typeof result === 'object') {
// 返回对象时
console.log(JSON.stringify(result))
} else {
console.log(result)
}
}
// 基于xcall调用返回方法
document.getElementById('btn-xcall-fun').onclick = function () {
var fun = Window.this.xcall('byXcallReturnFun')
var res = fun('lingkang,你好')// 调用方法
console.log(res)
}
</script>
</body>
</html>
效果

后端调用前端:
下面将演示:
c++操作domc++执行js到前端javascript环境
c++
#include <cstdint> // C++ 标准头文件(首选)
#include <windows.h>
#include <thread>
#include "sciter-x-window.hpp"
#include "aux-cvt.h"
#include "sciter-x.h"
#include "sciter-x-dom.h"
#include "sciter-x-threads.h"
class myWindow : public sciter::window {
public:
myWindow() : window(SW_MAIN | SW_ENABLE_DEBUG) {}
// 将c++的调用接口暴露给前端,前端调用时:Window.this.myWindow.方法名()
SOM_PASSPORT_BEGIN(myWindow)
SOM_FUNCS(
SOM_FUNC(cppGetHtml),
SOM_FUNC(theadUpdateDom),
SOM_FUNC(callJavaScriptFun),
SOM_FUNC(executeJs)
)
SOM_PASSPORT_END
// dom 操作
sciter::astring cppGetHtml() {
sciter::dom::element document = root();
sciter::astring htmlContent = document.get_html();
std::cout << "获取前端的html代码" << htmlContent.c_str() << std::endl;
return htmlContent;
}
void theadUpdateDom() {
// 启动后台线程
sciter::thread([](myWindow *win) {
for (int i = 0; i < 5; ++i) {
std::cout << "Thread running: " << i << std::endl;
sciter::dom::element document = win->root();
HELEMENT id = document.get_element_by_id("id-h2");
const BYTE *html;
std::string html_content = "c++线程执行次数:" + std::to_string(i);
html = reinterpret_cast<const BYTE *>(html_content.c_str());
// 操作dom设置内容,主线程才能更新GUI,此方法会在主线程中执行
SciterSetElementHtml(id, html, html_content.length() + 1, 0);
// 模拟间隔,休眠2秒
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
}
}, this);
}
// 调用html中定义的js方法
void callJavaScriptFun() {
// 入参,只有一个
VALUE argvs[1];
LPCWSTR chars = L"入参字符串:lingkang";
ValueStringDataSet(&argvs[0], chars, wcslen(chars), 0);
VALUE result;
// 调用前端js定义的 my_js_fun 方法
SCDOM_RESULT ok = SciterCallScriptingFunction(root(), "my_js_fun", argvs, 1, &result);
if (ok == SCDOM_OK) {
// 获取字符串数据
std::cout << "调用js的结果: " << VALUE_to_string(result) << std::endl;
} else {
// 处理错误
std::cout << "调用js出错啦 " << std::endl;
}
// 清理
ValueClear(&result);
ValueClear(&argvs[0]);
}
/**
* 转为字符串
*/
std::string VALUE_to_string(VALUE val) {
LPCWSTR str_data;
UINT str_length;
ValueStringData(&val, &str_data, &str_length);
std::string utf8_str;
if (str_data && str_length > 0) {
// 宽字符转换为UTF-8
int utf8_length = WideCharToMultiByte(CP_UTF8, 0, str_data, str_length, nullptr, 0, NULL, NULL);
utf8_str.resize(utf8_length);
WideCharToMultiByte(CP_UTF8, 0, str_data, str_length, &utf8_str[0], utf8_length, NULL, NULL);
}
return utf8_str;
}
void executeJs(sciter::string js) {
LPCWSTR myJs = js.c_str();// L"console.log('我是c++的js')";
VALUE result;
// 执行js
SciterEvalElementScript(root(), myJs, wcslen(myJs), &result);
std::cout << "执行js的结果: " << VALUE_to_string(result) << std::endl;
ValueClear(&result);
}
};
int uimain(std::function<int()> run) {
// 创建ui窗口实例
sciter::om::hasset<myWindow> window = new myWindow();
// 加载前端UI的html文件
window->load(
WSTR("file://C:\\Users\\Administrator\\Desktop\\project\\sciter\\app\\demo_20251121\\ui\\hello-ui_cpp.html"));
window->expand();
return run();
}
/**
* 图形用户界面(GUI)应用程序的标准入口函数,相当于控制台程序中的 main 函数。
* 2025-11-15 by lingkang
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 启用试调
SciterSetOption(NULL, SCITER_SET_SCRIPT_RUNTIME_FEATURES,
ALLOW_FILE_IO |
ALLOW_SOCKET_IO |
ALLOW_EVAL |
ALLOW_SYSINFO);
// 初始化sciter
sciter::application::start();
// 加载我们的窗口
uimain([]() -> int { return sciter::application::run(); });
// 结束时 关闭
sciter::application::shutdown();
return 0;
}
static std::vector<sciter::string> _argv;
// 设置 sciter 的初始化应用
namespace sciter {
namespace application {
HINSTANCE hinstance() {
return nullptr; // not used
}
const std::vector<sciter::string> &argv() {
// 获取命令行参数
return _argv;
}
}
}
html
<!DOCTYPE html>
<html
window-width=600px
window-height=450px
window-resizable=true
>
<head>
<meta charset="UTF-8">
<title>lingkang的sciter前端与c++相互调用演示</title>
<style>
.flex-row {
display: flex;
flex-direction: row;
gap: 10px;
}
</style>
</head>
<body style="display: flex;flex-direction: column;gap: 20px">
<h1>c++调用前端</h1>
<h2 id="id-h2"></h2>
<button id="btn-getHtml">c++获取html代码</button>
<button id="btn-cpp-thread">c++线程操作dom</button>
<button id="btn-cpp-callJsFun">c++调用js的方法</button>
<div>
<textarea id="exJs">
console.log('我是c++的js');
// 删除当前页面 ~
document.body.remove();
function aa(){
return '结果666';
}
// call 返回一个值给c++,放到最后执行的方法有返回值就行
aa();
</textarea>
<button id="btn-exJx">c++执行js脚本</button>
</div>
<script>
// 给按钮绑定点击事件
document.getElementById('btn-getHtml').onclick = function () {
const html = Window.this.myWindow.cppGetHtml()
console.log(html)
}
document.getElementById('btn-cpp-thread').onclick = function () {
// 启动线程更新 <h2 id="id-h2"></h2>
Window.this.myWindow.theadUpdateDom()
}
document.getElementById('btn-cpp-callJsFun').onclick = function () {
Window.this.myWindow.callJavaScriptFun()
}
// 定义一个js方法给c++调用
function my_js_fun(param) {
console.log('js方法被调用' + param);
return '你的入参是:' + param
}
document.getElementById('btn-exJx').onclick = function () {
const js = document.getElementById('exJs').value
console.log('c++要执行的js:\n'+js)
Window.this.myWindow.executeJs(js)
}
</script>
</body>
</html>
结果截图

点击 c++调用js的方法

篇幅有限,下一篇将介绍 c++ 与前端的状态、事件监听与交互