前阵子接手一个设备数据采集的Windows桌面项目Qt 5.12写的工控机上常年跑着三百多个VBScript脚本。这些脚本承载了客户好几年积累的业务规则温度超限判定、湿度变化率计算、设备启停逻辑甚至还有些订单金额计算。客户的态度很明确——规则不重写新系统还得继续沿用。当时的表情确实比较复杂C和VBScript虽然名字里都有个C但一个是现代编译型语言一个是老牌解释型脚本在Qt界面程序里塞进一个VBScript引擎听起来多少有点“魔改”的味道。不过真正做完之后发现整个技术路线并没有想象中那么邪门。Windows系统其实一直把VBScript以COM组件的形式暴露给开发者而Qt的ActiveQt模块天然就是给COM调用设计的所以这件事的答案很清晰用Qt的QAxObject去驱动Windows脚本引擎把VBScript当成一个“进程内脚本服务”来用。这篇文章把我实际摸索出的方案、代码骨架以及踩过的坑完整记录下来。如果你也遇到类似的历史包袱想在新写的C/Qt程序里继续调用VBScript可以参考这里的做法。1. 什么场景下Qt程序会需要执行VBScript1.1 存量脚本是不动产老系统的业务规则这类需求大多出自制造业、工控、医疗设备等领域的遗留系统。业务规则沉淀在VBScript里已经十几年有些规则用自然语言都很难描述清楚但脚本本身却一直在线上稳定运行。全部用C重写意味着需求梳理、代码编写、测试验证、归档发布整个流程走一遍对于还在正常运转的生产系统来说这个成本往往不值得。我遇到的典型情况分三种规则型脚本字段校验、阈值判断、范围计算这类脚本简单但数量极大。流程型脚本按顺序调用外部程序、读写Excel、操作文件逻辑复杂但常年不变。热更新需求业务参数经常调整主程序发版周期太长通过改脚本就能立刻生效。第三种情况的吸引力最大。运维或工艺工程师只需要编辑一个文本文件主程序每次启动或按配置重载一下脚本内容新规则就生效了完全不碰C代码。这也是“为什么不用C直接重写”之外更现实的原因——让不懂C编译链的人去维护业务规则脚本几乎是成本最低的解决方案。1.2 两种接入思路进程隔离 vs 进程内嵌在C/Qt里调用VBScript宏观上只有两条路。第一条是进程隔离。用QProcess调起系统自带的cscript.exe去执行一个.vbs脚本文件脚本把结果打印到标准输出C程序读取输出拿到结果。优点是实现简单、几乎不存在兼容性问题缺点是每次调用都要启动一个新解释器进程性能开销大而且只能传递字符串参数复杂数据结构来回序列化非常痛苦不适合高频场景。第二条是进程内嵌。把VBScript解释器以COM组件形式加载到Qt进程内通过IActiveScript或MSScriptControl接口在同一个进程空间里注入脚本、调用函数、取返回值。性能好可以传数字、字符串、数组等结构化参数数据交互能力完全不在一个量级。本文重点讲第二种。它才是“在C中使用VBScript”的正解第一种只适合低频简单任务我在最后一章会当作保底方案再提一下。2. 环境准备从.pro文件到COM初始化2.1 ActiveQt模块与平台限定Qt对COM的封装主要放在ActiveQt模块里其中QAxObject是操作COM组件的核心类。要在工程里启用它先改.pro文件QT core gui win32 { QT axcontainer }如果项目只在Windows上运行直接写QT axcontainer也行。但建议养成跨平台意识因为其他平台上没有这个模块不加win32条件裸写的话Linux或macOS上qmake会直接报“Unknown module”错误。源文件里也要做平台保护所有涉及QAxObject的头文件和代码都用宏包起来#ifdef Q_OS_WIN #include QAxObject #include windows.h #endif我见过有人不写这个宏结果团队里某个人在macOS上拉代码一编译直接报一堆COM头文件找不到。这种小细节在后期维护时特别救命。2.2 COM初始化小代码里的大坑Windows上使用COM对象之前当前线程需要先初始化COM库。QAxObject内部虽然会在某些情况下自动处理但为了行为可控最好在程序启动时显式做一次初始化。我一般在main()函数最开始就调用#ifdef Q_OS_WIN CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); #endif然后在main()末尾或程序退出前调用CoUninitialize()。这里有个值得注意的点COM的线程模型和Qt的事件循环是两套机制。QAxObject创建的COM对象通常具有线程亲和性哪个线程创建的对象就只能在这个线程里调用。如果你在主线程创建了ScriptControl对象然后丢到工作线程里去调用轻则返回空值重则直接崩溃。我后来在后台执行脚本时是把整个引擎的创建、脚本注入、函数调用都封装成一个对象整体放到同一个线程里处理从根源上避免跨线程。2.3 确认目标机器有可用的脚本引擎VBScript在Windows上不是一个独立应用而是以COM组件形式存在于系统中的。开发前最好先确认目标环境里有没有下面两个组件ProgID组件说明依赖MSScriptControl.ScriptControl微软脚本控制组件封装程度高适合QAxObject直接调用MSSCRIPT.OCX不一定预装VBScript.ScriptVBScript引擎本身的COM对象底层基于IActiveScript接口vbscript.dll老版本Windows自带检查方式很简单在命令行里执行reg query HKCR\MSScriptControl.ScriptControl reg query HKCR\VBScript.Script如果不做检查代码里setControl(MSScriptControl.ScriptControl)之后会发现isNull()返回true这个坑我后面详细说。3. 方案选型ScriptControl与IActiveScript谁更合适3.1 MSScriptControl.ScriptControl封装最完整但依赖组件MSScriptControl.ScriptControl是微软提供的一个ActiveX控件它把底层脚本引擎做了一层封装给外部暴露了Language、AddCode、Run、Eval、Error等属性和方法全部走IDispatch自动化调用。在Qt里用QAxObject操作它体验非常接近调用普通QObjectQAxObject script; script.setControl(MSScriptControl.ScriptControl); script.setProperty(Language, VBScript); script.dynamicCall(AddCode(QString), code);这套方案最大的优势是省事你不需要了解IActiveScript的接口细节也不需要自己实现ScriptSite回调。业务逻辑可以快速跑通适合时间紧、脚本量大的情况。缺点也明显MSSCRIPT.OCX并不是所有Windows系统都预装。虽然开发机上装了Visual Studio或Office时经常会带上它但目标工控机上有没有就得打问号。而且这个组件主要以32位进程内服务器形式提供64位主程序调用它时经常出问题。3.2 VBScript引擎原生COM接口不依赖组件但代码量大走vbscript.dll的IActiveScript接口是更加底层的方案。VBScript引擎实现了标准的Windows脚本接口IActiveScript和IActiveScriptParseC代码可以直接创建引擎、注入脚本、获取脚本级的IDispatch调用函数。优点是不依赖任何第三方OCX只要系统里有VBScript引擎就能用而且32位和64位系统都有对应版本的vbscript.dll位数兼容性更好。缺点是把底层的事情全部暴露给你了要实现IActiveScriptSite回调接口、要自己管理引擎状态、要处理事件和错误回调。一套流程走下来代码量是ScriptControl方案的几倍。3.3 方案对比与建议对比维度MSScriptControl.ScriptControlIActiveScript直接接入开发效率高QAxObject直接调用低需实现COM回调接口系统依赖MSSCRIPT.OCX可能未安装vbscript.dll老系统自带32/64位兼容主要是32位64位程序调用受限有32位和64位两套实现功能完整性封装完整Run/Eval/Error都有更底层可定制性强长期可维护性组件可能无法在新系统复用依赖系统脚本组件同样会被淘汰我的建议是短期应急用MSScriptControl快速实现长期维护用IActiveScript或干脆规划迁移。如果你只是临时在内部工具里调一段VBScript没必要跟底层COM硬磕ScriptControl性价比最高。但如果要交付给客户、部署到大量不确定环境的机器上最好用IActiveScript方案至少不依赖那个不一定存在的OCX。4. 用QAxObject嵌入VBScript核心代码实战这一章以MSScriptControl.ScriptControl为主线因为它最能体现Qt的优势代码也最容易落地。4.1 创建ScriptControl实例并设置语言构造函数里做一次初始化bool VbScriptEngine::init() { m_script new QAxObject(this); m_script-setControl(MSScriptControl.ScriptControl); if (m_script-isNull()) { qWarning() 创建 MSScriptControl.ScriptControl 失败请检查组件注册; return false; } m_script-setProperty(Language, VBScript); m_script-setProperty(AllowUI, false); m_script-setProperty(UseSafeSubset, false); return true; }三个属性的含义有必要解释一下Language指定脚本引擎的语言类型这里必须是VBScript。实测大小写不严格但建议保持标准写法。AllowUI是否允许脚本弹UI。设为false可以防止脚本里的MsgBox在无人值守场景下把程序挂住。调试时可以临时设为true。UseSafeSubset是否只允许“安全”的内建函数。设为false可以让FileSystemObject、WScript.Shell等对象正常工作但对安全环境来说存在风险需根据实际场景决定。4.2 注入脚本代码AddCode的细节ScriptControl有个特点代码不是在你调用的时候才去逐条解析的而是通过AddCode一次性把整个脚本块注入到引擎里。注入后脚本中的全局函数和变量就常驻在引擎中后续可以多次调用。QString code R( Function CalcDiscount(amount) If amount 10000 Then CalcDiscount amount * 0.9 Else CalcDiscount amount * 0.95 End If End Function ); m_script-dynamicCall(AddCode(QString), code);注意两点第一一次AddCode注入多个Function完全没问题引擎会全部预编译。如果脚本里有语法错误这一句就会触发错误需要在调用后立即检查Error对象。第二AddCode对脚本中不能有Sub Main之类的启动入口没有强制要求。它只负责把代码加载进引擎并不会自动执行顶层代码。如果你在脚本顶层写了非Function/Sub声明之外的执行语句注入时就会被执行。这在某些场景下可以用来做初始化但容易产生副作用建议保持脚本只定义函数。4.3 调用VBScript函数并接收返回值调用方式很简单直接对应脚本中的函数名QVariant args(20000); QVariant result m_script-dynamicCall(Run(QString, QVariant), CalcDiscount, args); double discount result.toDouble(); qDebug() 折后金额: discount;如果函数有多个参数推荐用QVariantList打包QVariantList args; args 12000 VIP; QVariant result m_script-dynamicCall(Run(QString, QVariantList), CalcDiscountEx, args);这里有个非常容易踩的雷MSScriptControl的Run方法在设计上只接受两个形参——函数名和参数数组。如果你写成dynamicCall(Run(QString, QVariant, QVariant), Func, arg1, arg2)第二个参数以外的数据不会按脚本函数的参数传递结果就是脚本函数里第二个参数变成Empty。正确做法是把多个参数塞进一个QVariantList。4.4 脚本报错时如何拿到错误号与行号VBScript脚本在运行时如果出错比如调用了未定义的函数、除数为零等Run调用不会抛出C异常而是返回一个空的QVariant。这时候必须主动去ScriptControl的Error对象里取错误信息。QVariant result m_script-dynamicCall(Run(QString, QVariant), CalcDiscount, QVariant(0)); if (!result.isValid()) { QAxObject *err m_script-querySubObject(Error); if (err) { int num err-property(Number).toInt(); QString desc err-property(Description).toString(); int line err-property(Line).toInt(); qWarning() VBScript错误 num 描述: desc 行号: line; delete err; } }querySubObject返回的是动态分配的子对象用完后记得delete否则会有轻微内存泄漏。这点容易被忽略因为QAxObject体系的子对象不像QObject那样自动挂在父对象下。5. 参数传递QVariant与VARIANT之间的那些坑5.1 类型映射表与注意事项C/Qt和VBScript之间所有数据都通过COM的VARIANT交换。QAxObject帮你做了QVariant到VARIANT的自动转换但搞清楚映射关系能帮你提前避开很多不直观的问题。QVariant类型VARIANT类型VBScript侧看到int/longVT_I4Long/IntegerdoubleVT_R8DoubleboolVT_BOOLBooleanQStringVT_BSTRStringQDateTimeVT_DATEDateQVariantListVT_ARRAY | VT_VARIANTArrayQVariant()空值VT_EMPTYEmptyQObject*VT_DISPATCHObject引用特别提醒几个容易被坑到的地方空QVariant的语义传QVariant()时VBScript侧收到的是Empty如果脚本把这个值直接参与数字运算它会被当成0参与字符串拼接则变成空串。所以该传什么类型就传什么类型不要偷懒默认空值兜底。QByteArray不要直接传QAxObject虽然可以转换字节数组但VBScript对二进制数据的处理能力很弱。稳妥做法是把QByteArray先Base64编码成QString再传在脚本里再做decode。这样看着多了一步实际能省掉大量摸不着头脑的乱码问题。自定义结构体完全不可用C结构体、指针这些类型无法直接跨COM边界。如果必须传复杂数据我的做法是序列化成JSON字符串在VBScript里用简单的字符串函数解析或者传一个QVariantList数组。5.2 多参数为什么推荐QVariantList前面提了一句这里展开说原理。ScriptControl的Run方法在COM层暴露的签名本质上就是“脚本函数名 参数数组”。在VBScript里写ScriptControl.Run Func, a, b, cVBScript宿主在调用COM层时会把a, b, c打包成一个SAFEARRAY再传给Run的第二个形参。在Qt里QVariantList会被QAxBase自动转换成SAFEARRAY(VARIANT)正好符合ScriptControl的预期。所以多参数场景下用QVariantList是唯一不容易出错的路径。QVariantList params; params 25 60 L1; QVariant ret script.dynamicCall(Run(QString, QVariantList), CheckEnv, params);实测Qt 5.12、5.15和6.x系列对这套转换都支持得不错主要版本间行为基本一致。5.3 ByRef参数回传的处理限制VBScript的Function和Sub默认支持参数按引用ByRef传递脚本内部对参数的修改理论上能传回给调用方。但在COM自动化调用中这个特性基本指望不上。原因在于IDispatch::Invoke的默认参数传递方式是ByVal也就是说C传给脚本的是参数的副本脚本里改了也不会同步回C。即便控件内部做了特殊处理QAxObject这一层也不会自动帮你把VARIANT指针传回去。实际工程里遇到需要脚本修改调用方变量的场景我的建议是能设计成返回值就设计成返回值最简单直接。需要返回多个值时让脚本返回一个数组C里解析QVariantList。如果业务逻辑实在需要修改外部对象状态可以在C里暴露一个COM可访问的“全局业务对象”把需要修改的状态变成该对象的属性或方法脚本通过SetProperty/CallMethod去改。但这要求你对COM对象暴露机制有足够了解没必要为普通项目上这么重的手段。6. 完整示例字段校验规则的VBScript实现6.1 场景设计与VBScript脚本内容假设我们做一个环境监测上位机界面输入温度、湿度、压力三个值点击“校验”后由VBScript规则判断是否符合工位要求。规则由工艺工程师维护C程序不参与具体判定逻辑。脚本文件checkrule.vbs内容如下 环境参数校验规则 Function CheckTemperature(v) CheckTemperature (v 15 And v 45) End Function Function CheckHumidity(v) CheckHumidity (v 30 And v 70) End Function Function CheckPressure(v) If v 0 Then CheckPressure False ElseIf v 1.6 Then CheckPressure False Else CheckPressure True End If End Function Function CheckAll(t, h, p) CheckAll CheckTemperature(t) And CheckHumidity(h) And CheckPressure(p) End Function这里的规则本身不复杂但实际生产中规则可能会膨胀到几百行甚至引用外部配置文件。关键是C侧完全不需要关心规则内部怎么写的只按函数名调用就行。6.2 Qt主程序的C调用流程我封装了一个轻量的VbScriptEngine类class VbScriptEngine : public QObject { Q_OBJECT public: explicit VbScriptEngine(QObject *parent nullptr); bool init(const QString scriptCode); QVariant call(const QString func, const QVariantList args {}); private: QAxObject *m_script nullptr; };实现bool VbScriptEngine::init(const QString scriptCode) { m_script new QAxObject(this); m_script-setControl(MSScriptControl.ScriptControl); if (m_script-isNull()) { qWarning() ScriptControl组件不可用; return false; } m_script-setProperty(Language, VBScript); m_script-setProperty(AllowUI, false); m_script-dynamicCall(AddCode(QString), scriptCode); return true; } QVariant VbScriptEngine::call(const QString func, const QVariantList args) { QVariant ret; if (args.isEmpty()) { ret m_script-dynamicCall(Run(QString), func); } else { ret m_script-dynamicCall(Run(QString, QVariantList), func, args); } if (!ret.isValid()) { QAxObject *err m_script-querySubObject(Error); if (err) { int num err-property(Number).toInt(); QString desc err-property(Description).toString(); qWarning() VBScript调用失败: func num desc; delete err; } } return ret; }界面按钮的槽函数void MainWindow::on_checkButton_clicked() { double temp ui-tempEdit-text().toDouble(); double humi ui-humiEdit-text().toDouble(); double pres ui-presEdit-text().toDouble(); QVariantList args; args temp humi pres; QVariant result m_engine-call(CheckAll, args); if (result.isValid()) { bool ok result.toBool(); ui-resultLabel-setText(ok ? 合格 : 不合格); } else { ui-resultLabel-setText(脚本执行异常); } }整个调用链路非常干净C只负责界面和数据规则判定完全交给VBScript。工艺工程师修改脚本文件后程序通过重新加载脚本内容再调一次AddCode或重启引擎新规则立即生效不用重新编译主程序。6.3 把脚本引擎升级成可重载的单例服务实际项目中脚本不太可能只在程序启动时加载一次更常见的需求是运行时热重载。为此我在原来类的基础上加了一个reload操作bool VbScriptEngine::reload(const QString scriptCode) { // 旧引擎直接释放重新创建 delete m_script; m_script nullptr; return init(scriptCode); }还可以加一个文件监听用QFileSystemWatcher监控脚本文件变化发现修改后自动reload。这在需求频繁调整的项目里非常受用操作员在界面上改完规则保存程序立刻就能用新规则校验下一组数据完全不需要停机。7. 踩坑实录从实例化失败到参数丢失的完整排查7.1 实例化失败找不到ProgID现象setControl(MSScriptControl.ScriptControl)之后isNull()返回true程序继续往下走就会在不该崩溃的地方崩掉。排查链路先在命令行执行reg query HKCR\MSScriptControl.ScriptControl看组件是否注册。如果提示“错误: 找不到指定的注册表项或值”说明MSSCRIPT.OCX没注册。检查系统里有没有C:\Windows\SysWOW64\msscript.ocx32位组件通常在这里或C:\Windows\System32\msscript.ocx64位。如果文件存在但未注册可以管理员身份运行regsvr32注册。但要注意——这类OCX要从可信来源获取不要为了省事去路边站点下载同名文件务必关注数字签名和数据来源。如果目标机器上确实没有这个组件又不想引入额外安装包那就只能切换到IActiveScript路线用系统自带的vbscript.dll。这也是我在第3章强调选型时要先想清楚的原因。7.2 32位与64位程序交叉调用失效现象在64位Qt程序里创建ScriptControl成功但调用Run时要么返回空QVariant要么程序直接崩溃。根因MSScriptControl.OCX在历史上是以32位进程内组件为主流分发形态的。64位进程加载32位ActiveX控件COM的注册表视图又不一致结果就会变得非常不可靠。解决最省事把Qt程序编译目标改成x8632位。工控机性能通常不是瓶颈32位程序在Windows上跑得挺稳。更正统用IActiveScript接口。系统自带的vbscript.dll有完整的64位实现通过CLSID_VBScript创建引擎在64位进程里没有位数障碍。折中方案把VBScript调用拆到一个独立的32位辅助进程中比如一个命令行小工具64位Qt程序通过QProcess和它交互。这样既保住64位主程序也能用上ScriptControl但交互成本较高。7.3 多参数只有第一个生效现象调用脚本函数时传了三个参数脚本里后面的两个参数全部变成Empty或者报“缺少参数”。根因前面第5章已经分析过MSScriptControl.Run的自动化接口原生接受“函数名 参数数组”。直接用dynamicCall(Run(QString, QVariant, QVariant, QVariant), ...)时第二个、第三个QVariant会被当成Run方法的其他可选参数处理而不是脚本函数的参数。解决统一使用QVariantList打包参数。这是我在实际项目里遇到后仔细看了COM接口签名才定位到的问题当时一度怀疑是QAxObject的bug后来发现是接口形态不同导致的。7.4 QAxObject返回空QVariant的检查路径dynamicCall返回一个无效QVariant时不一定就是脚本函数返回了Empty还可能是调用本身就失败了。我一般按以下顺序排查先执行一个最简单的脚本看引擎是否活着比如dynamicCall(Eval(QString), 11)如果返回2说明AddCode和Run链路正常。检查脚本函数名是否正确。VBScript函数名不区分大小写但是多一个空格、少一个字符都不行。检查函数是否在脚本顶层定义。如果把Function写在另一个Sub内部这个函数对ScriptControl来说是不可见的。最后再看Error对象。很多时候错误信息已经告诉了你具体行号不需要瞎猜。这套链路排查完绝大多数“返回空QVariant”的问题都能定位到根因。8. 老脚本的未来迁移路径与过渡策略8.1 VBScript的现状微软在2023年已经宣布弃用VBScript在新版Windows和Edge等产品上开始逐步移除相关支持Windows 11 24H2及更新版本默认不再包含VBScript可选功能。这意味着“在新系统里创建VBScript引擎”这个行为并不是100%可依赖的未来只会越来越难。但现实中的存量系统不会因为官方弃用就立刻消失很多工控客户的生产环境依然跑着Win10甚至更老的系统VBScript在这些机器上还能正常工作。所以我的态度是可以继续用但要知道这是有寿命的。凡是新写的项目尽量别再引入VBScript凡是老项目里的VBScript建议留一条可迁移的后路。8.2 用脚本引擎抽象层隔离替换成本如果短期内无法把几百个VBScript脚本一次性迁走至少可以在C侧做一层抽象把“调用脚本函数”这个动作和具体引擎解耦。我设计的接口非常简单class IScriptEngine { public: virtual ~IScriptEngine() default; virtual bool init(const QString code) 0; virtual QVariant call(const QString func, const QVariantList args) 0; };然后分别实现VbScriptEngine和JsScriptEngine底层用Qt的QJSEngine或其他JS引擎。上层业务代码只依赖IScriptEngine指针后续把VBScript替换成JavaScript或Python时只需要换一个工厂函数所有调用点一行都不用改。实测这个抽象的价值非常大。我迁移过一个模块底层从VBScript切到JavaScript除了脚本文件本身需要翻译C侧几乎没有改动回归测试也只覆盖了业务语义层面。8.3 cscript.exe进程调用最后一道保底方案如果嵌入式调用实在搞不定比如目标机器精简得连VBScript DLL都没有但业务又必须用VBScript那还有最后一道保底方案——用QProcess调cscript.exe执行独立的.vbs文件QProcess process; process.start(cscript.exe, QStringList() //Nologo scriptPath); if (!process.waitForFinished(5000)) { process.kill(); return; } QString output QString::fromLocal8Bit(process.readAllStandardOutput());这本质上不是“嵌入”引擎而是进程隔离式调用。具体优缺点前面说过适合低频任务。如果遇到高频校验需求比如每秒校验10次这个方案的性能就完全不够看了。说实话在我的实际项目里最终并没有长期依赖VBScript。通过IScriptEngine这层抽象我把三百多个旧脚本逐步迁到了JavaScript引擎上迁移过程没有伤筋动骨上层业务调用点几乎没有改动。如果你只是临时接一个老系统MSScriptControl是最省力的起点但如果这套逻辑要维护很多年我建议从第一天就把引擎隔离出来给自己留条后路。毕竟脚本再老业务逻辑是真的能平稳替代才是硬道理。
企业数字化 ERP 产品动态
相关推荐
飞机目标检测实战:7930张VOC+YOLO格式数据集使用与训练指南 简介:一套面向飞机目标检测的数据集,适合目标检测算法研究者和计算机视觉初学者直接用于训练与验证。数据采用Pascal VOC与YOLO两种主流标注格式,标注类别只有airplane,非常适合开展单类别目标检测实验、模型精度对比以及参数调优… · 2026/9/23 14:31:23
库卡KRC4机器人KPS600电源模块故障处理与元件级维修指南 简介:库卡KRC4机器人KPS600电源模块故障处理手册是一份面向工业机器人维护工程师、自动化现场调试人员的实用技术资料,内容源自库卡官方TQ Basic V3.110培训教材,重点解决KPS600电源模块的故障识别与排除问题。资源为1个PDF文件,约… · 2026/9/23 14:31:23
Flutter在OpenHarmony上的适老化体重管理应用开发 1. 项目背景与核心需求在智慧养老应用开发中,体重管理功能看似简单,实则蕴含着对老年用户特殊需求的深度考量。作为健康监测的基础指标,体重变化直接反映老年人的营养状况和潜在健康风险。我们团队在开发过程中发现,市面上大多数健… · 2026/9/23 14:31:23
秋日怀人/东海陈光剑 秋日怀人
[东海]陈光剑
秋风木叶下,
人间别离久。
昨夜梦见之,
眉宇韫清秋。
白日徒相望,
明月上西楼。 · 2026/9/23 15:12:52
基于卷积神经网络的人脸识别门禁系统:从原理到部署的完整指南 简介:这份文档围绕卷积神经网络的人脸识别门禁系统设计展开,面向计算机视觉、嵌入式系统方向的学生与工程师,可作为课程设计、毕业设计或课题立项的参考文献。内容系统梳理了卷积神经网络的基础结构与特征提取原理,完整覆盖人脸检… · 2026/9/23 15:12:52
余额宝今天怎么没有收益?3个后端逻辑坑与完整示例解析 余额宝今天怎么没有收益?3个后端逻辑坑与完整示例解析 刚上线的新功能,后台日志里全是红色的 StackTrace,堆栈信息长得像乱码,看着就头疼。明明代码逻辑在本地跑得好好的,一部署到生产环境,收益计算就卡死,甚至直接返回空值。这种“环境差… · 2026/9/23 15:12:52
Qt+FFmpeg+RTSP播放器实战:从取流解码到画面渲染 简介:面向Qt与FFmpeg开发者,一份完整的RTSP视频流拉取与播放工程包。资源专注于解决在Qt环境中利用FFmpeg库拉取RTSP视频流、完成解码并显示到界面这一核心需求,适合具备C与Qt基础、正在学习流媒体开发或希望快速落地监控类项目的技术人员。压… · 2026/9/23 15:12:46
红外遥控硬件设计全链路解析:从NEC编码到抗干扰实战 简介:本资源是北京理工大学《电路与电子线路》课程设计的完整实验报告,面向电子信息类本科生及嵌入式硬件初学者,聚焦红外遥控系统底层实现,解决八路红外发射/接收器从原理设计到参数调试的全流程实践问题。文档以Word(… · 2026/9/23 15:12:46
Relay DevTools 调试指南:从安装到深度理解 Relay 网络与 Store 面板 Relay DevTools 调试指南:从安装到深度理解 Relay 网络与 Store 面板 【免费下载链接】relay Relay is a JavaScript framework for building data-driven React applications. 项目地址: https://gitcode.com/gh_mirrors/relay29/relay
导读
本文以 websit… · 2026/9/23 15:12:46
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29