news 2026/10/17 8:51:46

VS2010 MFC 嵌入 CEF3 90.6.0:JS 与 C++ 双向调用实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VS2010 MFC 嵌入 CEF3 90.6.0:JS 与 C++ 双向调用实战

简介:这份资源面向需要在VS2010环境下做桌面端Web集成的C++开发者,核心是用CEF3 90.6.0版本把Chromium浏览器内核嵌入MFC窗口,并打通JavaScript与C++的双向调用。包内共629个文件,以269个h头文件、168个pak资源包、28个dll动态库、14个lib静态库和9个cpp源码为主,另有obj、pdb、tlog等编译中间产物与bin、dat等运行时数据,压缩包约457.22MB,属于可直接编译运行的完整工程目录。资源围绕CefRenderWidgetHost子类封装、CEF与MFC消息循环同步、CefMessageRouterBrowserSide消息路由注册以及自定义scheme处理工厂等关键环节展开,能帮助读者理解浏览器控件在MFC对话框中的创建、尺寸同步与JS回调返回结果的完整链路。目前已有1556人学习下载,适合具备一定MFC与C++基础、希望快速跑通CEF3嵌入与前后端交互的中高级开发者参考。

1. 把 Chromium 塞进 MFC 窗口:一个老项目的真实落地场景

前阵子接手一个维护了快十年的工业控制上位机,界面全是 MFC 那套老控件,但客户突然要求内嵌一个能跑现代前端页面的浏览器控件,还要支持 JS 调用本地 C++ 函数。团队第一反应是 WebBrowser 控件,结果一跑就翻车——内核太老,Vue 3 的页面直接白屏。后来翻到一个基于 VS2010 + CEF3 90.6.0 版本的嵌入方案,把 Chromium 内核塞进 MFC 窗口,同时打通了 JS 与 C++ 的双向调用。这套东西对还在维护 MFC 老项目、又不得不兼容现代 Web 的开发者来说,基本是绕不开的路子。它解决的核心问题就一个:让老框架能跑新页面,还能让页面里的 JS 直接喊话 C++。

2. 环境搭建与工程结构:从零把 CEF3 编译进 VS2010

2.1 为什么是 CEF3 90.6.0 配 VS2010

CEF3 的版本选择在这个场景里不是随便挑的。90.x 是最后一个对 VS2010 工具链相对友好的分支,再往后的版本对 C++11 特性依赖加重,VS2010 的编译器会直接报一堆语法错误。90.6.0 这个补丁版本在稳定性上也比早期 90.x 好,社区里反馈的崩溃问题少很多。

VS2010 本身只支持到 C++03 加部分 C++11 特性,所以编译 CEF3 的时候必须用官方提供的二进制分发包,而不是自己从 Chromium 源码编。自己编的话,光是 depot_tools 那套环境就能耗掉一整天,而且 VS2010 根本编不过最新的 Chromium 源码。常见做法是直接下载 cef_binary_90.6.0 的 Windows 包,里面已经带好了 libcef.lib、libcef.dll 和头文件。

工程结构上,我一般会这样组织:

MyCefMfcApp/ ├── MyCefMfcApp.sln ├── MyCefMfcApp/ │ ├── MyCefMfcApp.h/cpp # MFC 主程序 │ ├── MainFrm.h/cpp # 主框架窗口 │ ├── CefViewWnd.h/cpp # CEF 浏览器窗口封装 │ ├── SimpleHandler.h/cpp # CEF 事件处理 │ └── JsBridge.h/cpp # JS 与 C++ 通信桥 ├── cef/ │ ├── include/ # CEF 头文件 │ ├── lib/ # libcef.lib │ └── bin/ # libcef.dll + 资源文件 └── web/ └── index.html # 前端页面

这个结构的关键是把 CEF 相关的东西全部隔离在 cef 目录下,业务代码只依赖 CefViewWnd 和 JsBridge 两个封装层。后面升级 CEF 版本的时候,只需要替换 cef 目录,业务代码基本不用动。

2.2 VS2010 工程配置的几处硬性设置

VS2010 的工程属性里,有几项必须改,否则编译能过但运行必崩。

第一,C/C++ → 代码生成 → 运行时库,必须选多线程调试 DLL (/MDd)或多线程 DLL (/MD)。CEF3 的二进制包是用 /MD 编的,如果你的工程用 /MT,链接阶段就会报重复符号或者运行时堆冲突。

第二,链接器 → 输入 → 附加依赖项,加上libcef.lib和libcef_dll_wrapper.lib。后者是 CEF 提供的 C++ 封装层,必须和你的工程一起编译。官方包里带的是源码,需要把libcef_dll_wrapper这个静态库工程加进解决方案一起编。

第三,C/C++ → 预处理器 → 预处理器定义,加上WIN32、_WINDOWS、NDEBUG(Release)、_DEBUG(Debug),以及CEF_USE_SANDBOX如果不用沙箱就别加。

第四,把cef/bin目录下的所有文件(libcef.dll、chrome_elf.dll、icudtl.dat、locales 文件夹、resources 文件夹)复制到你的输出目录。少一个文件,程序启动就静默退出,这是最常见的翻车点。

提示:VS2010 默认的字符集是 Unicode,CEF3 也要求 Unicode,所以工程属性里字符集必须选「使用 Unicode 字符集」,否则 CEF 初始化会失败。

2.3 初始化 CEF:一个进程只做一次的事

CEF 的初始化必须在 MFC 的InitInstance里做,而且整个进程只能做一次。代码大概长这样:

// MyCefMfcApp.cpp #include "include/cef_app.h" #include "include/cef_browser.h" #include "include/cef_command_line.h" BOOL CMyCefMfcApp::InitInstance() { // 1. 获取当前模块路径,CEF 需要知道 dll 和资源在哪 CString strExePath; GetModuleFileName(NULL, strExePath.GetBuffer(MAX_PATH), MAX_PATH); strExePath.ReleaseBuffer(); int nPos = strExePath.ReverseFind(_T('\\')); CString strDir = strExePath.Left(nPos); // 2. 设置 CEF 的资源和子进程路径 CefMainArgs main_args(GetModuleHandle(NULL)); CefRefPtr<CefApp> app = new CSimpleApp(); // 自定义 App 类 // 3. 子进程处理:如果是渲染进程或 GPU 进程,走这里 int exit_code = CefExecuteProcess(main_args, app.get(), NULL); if (exit_code >= 0) { return exit_code; // 子进程直接退出 } // 4. 主进程初始化设置 CefSettings settings; settings.no_sandbox = true; // VS2010 环境下沙箱容易出问题,先关掉 settings.multi_threaded_message_loop = false; // 用 MFC 的消息循环 CefString(&settings.locales_dir_path) = strDir + _T("\\locales"); CefString(&settings.resources_dir_path) = strDir + _T("\\resources"); CefString(&settings.cache_path) = strDir + _T("\\cache"); // 5. 初始化 CEF if (!CefInitialize(main_args, settings, app.get(), NULL)) { AfxMessageBox(_T("CEF 初始化失败")); return FALSE; } // 6. 正常走 MFC 的窗口创建流程 CMainFrame* pFrame = new CMainFrame(); m_pMainWnd = pFrame; pFrame->Create(NULL, _T("CEF MFC Demo")); pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }

这段代码里有几个参数需要解释。no_sandbox = true是因为 VS2010 编译出来的程序在沙箱模式下经常因为权限问题起不来,开发阶段先关掉,上线前再根据实际情况决定是否开启。multi_threaded_message_loop = false表示 CEF 不自己起消息循环,而是把消息处理交给 MFC 的PreTranslateMessage,这样 MFC 的窗口消息和 CEF 的消息不会打架。

CefExecuteProcess那一步很关键,它负责处理 CEF 的子进程。如果你漏了这一步,浏览器窗口能创建出来,但页面渲染会异常,因为渲染进程没起来。

2.4 退出时的清理顺序

CEF 的关闭必须在 MFC 窗口销毁之后、程序退出之前做:

int CMyCefMfcApp::ExitInstance() { CefShutdown(); // 必须在所有 CEF 对象释放后调用 return CWinApp::ExitInstance(); }

顺序反了的话,程序退出时会卡死或者报访问违例。血泪经验是:CefShutdown之前确保所有CefBrowser对象已经释放,否则它会等渲染进程退出,而渲染进程又在等主进程的消息,互相等就死锁了。

3. 核心模块拆解:浏览器窗口封装与消息路由

3.1 CefViewWnd:把浏览器变成 MFC 的子窗口

CEF 的浏览器窗口本质上是一个独立的原生窗口,要把它嵌到 MFC 的对话框或者 View 里,需要拿到它的父窗口句柄。核心做法是在 MFC 窗口的OnCreate里创建浏览器,然后把浏览器窗口的父句柄设成当前 MFC 窗口。

// CefViewWnd.cpp BOOL CCefViewWnd::CreateBrowser(const CRect& rect, CWnd* pParent) { // 保存父窗口 m_pParent = pParent; // 浏览器窗口信息 CefWindowInfo window_info; window_info.SetAsChild(pParent->GetSafeHwnd(), CefRect(rect.left, rect.top, rect.Width(), rect.Height())); // 浏览器设置 CefBrowserSettings browser_settings; browser_settings.javascript_access_clipboard = STATE_ENABLED; browser_settings.webgl = STATE_ENABLED; // 创建浏览器实例 CefRefPtr<CSimpleHandler> handler = new CSimpleHandler(this); CefBrowserHost::CreateBrowser(window_info, handler.get(), _T("file:///web/index.html"), browser_settings, NULL, NULL); return TRUE; }

SetAsChild这个调用是把 CEF 窗口变成子窗口的关键。第一个参数是父窗口句柄,第二个参数是初始位置和大小。注意这里的坐标是相对于父窗口客户区的,不是屏幕坐标。

CSimpleHandler是继承自CefClient的事件处理器,它负责接收浏览器生命周期事件、JS 消息、加载状态变化等。一个 Handler 可以挂多个浏览器实例,但通常一个浏览器配一个 Handler 更清晰。

3.2 消息路由:MFC 消息循环与 CEF 消息的融合

因为设置了multi_threaded_message_loop = false,CEF 的消息需要由 MFC 的消息循环来驱动。做法是在 MFC 窗口的PreTranslateMessage里把消息转给 CEF:

// MainFrm.cpp BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { // 把消息交给 CEF 处理 if (m_pCefView && m_pCefView->GetBrowserHost()) { if (m_pCefView->GetBrowserHost()->GetWindowHandle() == pMsg->hwnd) { // 如果是 CEF 窗口的消息,让 CEF 自己处理 return false; } } return CFrameWnd::PreTranslateMessage(pMsg); }

更完整的做法是重写CefViewWnd的WindowProc,在里面调用CefBrowserHost::GetWindowHandle()判断消息归属。如果消息的 hwnd 是 CEF 的窗口句柄,就交给 CEF 的默认处理流程,否则走 MFC 的默认处理。

这里有个容易踩的坑:CEF 的窗口句柄在浏览器创建完成之前是无效的,所以PreTranslateMessage里必须判空。另外,CEF 的窗口句柄在页面跳转时可能会变,所以不能缓存,每次都要重新获取。

3.3 窗口大小自适应

MFC 窗口缩放时,CEF 的窗口不会自动跟着变,需要手动调整:

void CCefViewWnd::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); if (m_pBrowser && m_pBrowser->GetHost()) { // 获取 CEF 窗口句柄 CefWindowHandle hwnd = m_pBrowser->GetHost()->GetWindowHandle(); if (hwnd) { // 用 Win32 API 调整 CEF 窗口大小 ::SetWindowPos(hwnd, NULL, 0, 0, cx, cy, SWP_NOZORDER | SWP_NOACTIVATE); } } }

SetWindowPos的最后一个参数用SWP_NOZORDER | SWP_NOACTIVATE,避免调整大小时抢焦点或者改变 Z 序。如果窗口里有多个子控件,CEF 窗口的 Z 序需要特别注意,它默认会盖住其他控件,需要用SetWindowPos的SWP_NOZORDER配合BringWindowToTop来调整。

3.4 加载状态与错误处理

页面加载失败时,CEF 会回调OnLoadError,这时候需要给用户一个提示,而不是白屏:

// SimpleHandler.cpp void CSimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) { if (errorCode == ERR_ABORTED) { return; // 用户主动取消的加载,不处理 } // 构造一个简单的错误页面 std::stringstream ss; ss << "<html><body bgcolor=\"white\">" << "<h2>页面加载失败</h2>" << "<p>错误码: " << errorCode << "</p>" << "<p>地址: " << failedUrl.ToString() << "</p>" << "</body></html>"; frame->LoadString(ss.str(), failedUrl); }

ERR_ABORTED是用户点击链接后快速取消的情况,这种不需要显示错误页。其他错误码比如ERR_CONNECTION_REFUSED、ERR_FILE_NOT_FOUND才需要处理。LoadString是 CEF 提供的直接加载 HTML 字符串的方法,比写临时文件再加载要干净。

4. JS 与 C++ 双向调用:打通前端和后端的任督二脉

4.1 C++ 调用 JS:ExecuteJavaScript

C++ 侧调用 JS 最简单的方式是ExecuteJavaScript:

// 在 C++ 中调用页面里的 JS 函数 void CCefViewWnd::CallJsFunction(const CString& strFuncName, const CString& strParam) { if (!m_pBrowser || !m_pBrowser->GetMainFrame()) { return; } // 拼接 JS 调用语句 CString strScript; strScript.Format(_T("%s('%s');"), strFuncName, strParam); // 执行 JS m_pBrowser->GetMainFrame()->ExecuteJavaScript( CefString(strScript), m_pBrowser->GetMainFrame()->GetURL(), 0); }

ExecuteJavaScript的第二个参数是脚本来源 URL,一般传当前页面 URL 就行,第三个参数是起始行号,传 0。这个方法没有返回值,如果需要 JS 返回结果给 C++,得用CefFrame::ExecuteJavaScript的回调版本,或者走 JS 主动调用 C++ 的通道。

注意:ExecuteJavaScript必须在 UI 线程调用,如果在工作线程里调,需要先CefPostTask切回 UI 线程。

4.2 JS 调用 C++:注册原生函数

JS 调 C++ 是这套方案的核心价值。CEF3 提供了CefV8Handler和CefRegisterExtension两种方式,我一般用前者,因为它更灵活。

首先定义一个 V8 Handler:

// JsBridge.h class CJsBridge : public CefV8Handler { public: CJsBridge(CWnd* pWnd) : m_pWnd(pWnd) {} virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) override; private: CWnd* m_pWnd; IMPLEMENT_REFCOUNTING(CJsBridge); };

然后在Execute里根据函数名分发:

// JsBridge.cpp bool CJsBridge::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) { if (name == "callNative") { // 检查参数 if (arguments.size() < 1 || !arguments[0]->IsString()) { exception = "参数错误:需要传入字符串"; return true; } CString strCmd = arguments[0]->GetStringValue().c_str(); // 把命令发给 MFC 窗口处理 if (m_pWnd && ::IsWindow(m_pWnd->GetSafeHwnd())) { m_pWnd->PostMessage(WM_NATIVE_CALL, 0, (LPARAM)new CString(strCmd)); } // 返回一个值给 JS retval = CefV8Value::CreateString("received"); return true; } return false; }

Execute的返回值表示是否处理了这个函数调用。返回true表示已处理,CEF 不会再找其他 Handler。retval是返回给 JS 的值,exception是抛出的异常信息。

接下来在页面加载完成后注册这个函数:

// SimpleHandler.cpp void CSimpleHandler::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) { // 获取全局对象 CefRefPtr<CefV8Value> object = context->GetGlobal(); // 创建原生函数 CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("callNative", m_pJsBridge); // 挂到 window 对象上 object->SetValue("callNative", func, V8_PROPERTY_ATTRIBUTE_NONE); }

OnContextCreated在每个 frame 的 JS 上下文创建时调用,这时候把callNative挂到 window 上,页面里的 JS 就能直接调window.callNative('命令')了。

4.3 完整调用链路:从 JS 点击到 C++ 执行

把上面的串起来,一个完整的调用链路是这样的:

  1. 页面里 JS 执行window.callNative('openFile')
  2. CEF 的 V8 引擎捕获到调用,进入CJsBridge::Execute
  3. Execute里把命令通过PostMessage发给 MFC 窗口
  4. MFC 窗口的OnNativeCall处理消息,执行对应操作
  5. 如果需要返回结果,C++ 再调ExecuteJavaScript把结果传回页面

MFC 侧的消息处理:

// MainFrm.cpp LRESULT CMainFrame::OnNativeCall(WPARAM wParam, LPARAM lParam) { CString* pStr = (CString*)lParam; if (pStr) { if (*pStr == _T("openFile")) { // 执行打开文件操作 CFileDialog dlg(TRUE); if (dlg.DoModal() == IDOK) { // 把结果传回 JS CString strResult; strResult.Format(_T("onFileSelected('%s')"), dlg.GetPathName()); m_pCefView->ExecuteJavaScript(strResult); } } delete pStr; } return 0; }

这里用PostMessage而不是SendMessage,是因为Execute是在 CEF 的渲染线程里执行的,直接SendMessage到 UI 线程可能造成死锁。PostMessage是异步的,把命令丢到 UI 线程的消息队列里就返回,不会阻塞渲染线程。

4.4 异步回调的封装

上面的模式有个问题:每次调用都要手动管理new CString和delete,容易内存泄漏。更好的做法是封装一个异步回调管理器:

class CAsyncCallbackMgr { public: // 注册一个回调,返回回调 ID int RegisterCallback(std::function<void(const CString&)> callback) { int id = m_nNextId++; m_mapCallbacks[id] = callback; return id; } // 触发回调 void TriggerCallback(int id, const CString& result) { auto it = m_mapCallbacks.find(id); if (it != m_mapCallbacks.end()) { it->second(result); m_mapCallbacks.erase(it); } } private: int m_nNextId = 1; std::map<int, std::function<void(const CString&)>> m_mapCallbacks; };

JS 侧调用时传入一个回调 ID,C++ 处理完后通过ExecuteJavaScript调用window.onNativeResult(id, result),JS 侧再根据 ID 找到对应的回调函数执行。这样就把异步调用的生命周期管理起来了。

5. 避坑与常见问题:那些让我加班到凌晨的坑

5.1 程序启动就闪退,没有任何报错

现象:编译通过,双击 exe 后窗口一闪就没了,调试模式下也看不到异常。

原因:CEF 的资源文件没复制全。最常见的是漏了icudtl.dat或者locales文件夹。CEF 初始化时会去加载这些文件,找不到就直接exit,连错误对话框都不弹。

解决:把cef/bin目录下的所有文件复制到 exe 同级目录。特别注意locales文件夹里至少要有zh-CN.pak和en-US.pak,resources文件夹里要有cef.pak和cef_100_percent.pak。用 VS2010 的生成后事件自动复制:

xcopy /Y /E /I "$(SolutionDir)cef\bin\*.*" "$(OutDir)"

5.2 页面能加载但 JS 调用 C++ 没反应

现象:页面正常显示,但window.callNative是 undefined,控制台报错。

原因:OnContextCreated没有触发,或者注册的时机不对。如果页面里有 iframe,每个 iframe 都会触发一次OnContextCreated,但只有主 frame 的 window 对象才是页面里直接访问的那个。

解决:在OnContextCreated里判断frame->IsMain(),只给主 frame 注册。另外,如果页面是通过LoadString加载的,OnContextCreated的触发时机可能比预期晚,需要在OnLoadEnd里再检查一次。

5.3 关闭窗口时程序卡死

现象:点关闭按钮后窗口消失,但进程还在任务管理器里,CPU 占用为 0。

原因:CefShutdown被调用了,但还有CefBrowser对象没释放。CEF 在CefShutdown里会等所有浏览器实例销毁,而浏览器实例又在等渲染进程退出,渲染进程在等主进程的消息循环,但主进程的消息循环已经停了,死锁。

解决:在ExitInstance里先手动关闭所有浏览器:

int CMyCefMfcApp::ExitInstance() { if (m_pCefView && m_pCefView->GetBrowser()) { m_pCefView->GetBrowser()->GetHost()->CloseBrowser(true); } CefShutdown(); return CWinApp::ExitInstance(); }

CloseBrowser(true)的true表示强制关闭,不等页面确认。

5.4 VS2010 编译 libcef_dll_wrapper 报错

现象:把libcef_dll_wrapper工程加进解决方案后,编译报一堆error C2039、error C2065。

原因:libcef_dll_wrapper的工程配置和你的主工程不一致,最常见的是字符集和运行时库不匹配。

解决:把libcef_dll_wrapper工程的字符集改成 Unicode,运行时库改成 /MD 或 /MDd,和主工程保持一致。另外,CEF 的头文件路径要加到附加包含目录里,通常是$(SolutionDir)cef\include。

5.5 页面里的中文显示乱码

现象:HTML 文件里写的中文在浏览器里显示成方块或者问号。

原因:HTML 文件保存的编码和页面里声明的编码不一致。VS2010 默认保存为 GBK,但页面里声明的是 UTF-8。

解决:把 HTML 文件用 UTF-8 无 BOM 格式保存,并在<head>里加<meta charset="utf-8">。如果 HTML 文件是动态生成的,确保生成时用的是 UTF-8 编码。CEF 内部默认按 UTF-8 处理字符串,所以 C++ 侧传给 JS 的中文也要先转成 UTF-8:

CStringA strUtf8 = CW2A(strChinese, CP_UTF8);

6. 二次开发改造:从能跑到好用还差哪几步

这套方案跑通之后,真正花时间的是二次开发。我一般会从三个方向改:通信协议、调试工具、打包部署。

通信协议方面,原生的callNative只支持字符串参数,实际项目里经常需要传 JSON。我的做法是在 JS 侧统一用JSON.stringify把对象转成字符串,C++ 侧收到后用CefParseJSON解析:

CefRefPtr<CefValue> value = CefParseJSON(strJson, JSON_PARSER_RFC); if (value && value->GetType() == VTYPE_DICTIONARY) { CefRefPtr<CefDictionaryValue> dict = value->GetDictionary(); CString strAction = dict->GetString("action").c_str(); // 根据 action 分发 }

这样 JS 侧调用就变成了window.callNative(JSON.stringify({action: 'openFile', path: '/tmp/a.txt'})),扩展性比纯字符串好很多。

调试工具方面,CEF 支持远程调试,在CefSettings里设置settings.remote_debugging_port = 9222,然后用 Chrome 打开http://localhost:9222就能看到页面里的 console 和 DOM。这个在排查 JS 报错时非常有用,比在 MFC 里打日志快得多。不过要注意,远程调试端口不要在生产环境开,有安全风险。

打包部署方面,CEF 的运行时文件加起来有 100 多 MB,直接扔给客户不太现实。我一般用 Inno Setup 或者 NSIS 做安装包,把libcef.dll、chrome_elf.dll、icudtl.dat、locales、resources这些文件打包进去。安装时注意locales文件夹只需要保留用到的语言包,比如只留zh-CN.pak和en-US.pak,能省 20 多 MB。

还有一个容易忽略的点是缓存目录。CEF 默认会在 exe 同级目录下创建cache文件夹,如果程序装在Program Files下,普通用户没有写权限,会导致页面加载失败。我的做法是在CefSettings里把cache_path指到%LOCALAPPDATA%下:

CString strCachePath; strCachePath.Format(_T("%s\\MyApp\\cache"), (LPCTSTR)SHGetKnownFolderPath(FOLDERID_LocalAppData)); CefString(&settings.cache_path) = strCachePath;

从那以后我每次给 MFC 项目嵌 CEF,都会先把缓存路径、资源路径、子进程路径这三项在CefSettings里显式指定,不再依赖默认值。默认值在开发机上跑没问题,一到客户环境就各种找不到文件。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 8:51:43

Windows 远程调试 Ubuntu 上的 Python 模型:VSCode Remote-SSH 实战指南

1. 为什么我要折腾这套远程调试环境手里只有一台 Windows 笔记本&#xff0c;代码却要跑在远程的 Ubuntu 机器上&#xff0c;这种场景我相信做开发的朋友都不陌生。可能是公司配的开发机是 Ubuntu&#xff0c;可能是实验室的服务器装的是 Ubuntu 22.04 LTS&#xff0c;也可能是…

作者头像 李华
网站建设 2026/10/17 8:51:39

Windows 远程调试 Ubuntu:VSCode Remote-SSH 配置与避坑指南

1. 为什么我要折腾这套远程调试环境手里只有一台 Windows 笔记本&#xff0c;代码却要跑在远程的 Ubuntu 机器上&#xff0c;这个场景我相信很多人都遇到过。可能是实验室的服务器、公司内网的开发机、云上租的 GPU 实例&#xff0c;也可能是家里那台常年开机的迷你主机。不管哪…

作者头像 李华
网站建设 2026/10/17 8:51:39

LLM-VeriPPA:面向数字前端的仿真报错与PPA报告语义解析引擎

1. 这不是又一个“LLMEDA”噱头&#xff1a;它真正在解决数字前端工程师每天撕掉的三张草稿纸你有没有过这样的经历&#xff1a;凌晨两点&#xff0c;仿真器突然弹出一行红色报错——ERROR: [VRFC 10-2063] Module top_level not found&#xff0c;你盯着这行字看了十分钟&…

作者头像 李华
网站建设 2026/10/17 8:51:34

ReAct实战:从思维链到智能体,边想边做的大模型应用范式

1. 别急着写代码&#xff0c;先搞清楚 ReAct 到底解决了什么这几年调大模型&#xff0c;大家应该都遇到过同一个尴尬&#xff1a;让模型处理稍微复杂一点的任务&#xff0c;它要么一本正经地编答案&#xff0c;要么拿着工具乱打一通。后来我把 ReAct 这套“边想边做”的思路落地…

作者头像 李华
网站建设 2026/10/17 8:51:20

Qt C++集成OpenCV实现低延迟RTSP视频流处理

简介&#xff1a;本资源面向Qt C开发者与计算机视觉初学者&#xff0c;解决在跨平台桌面应用中集成RTSP实时视频流并实现QML端低延迟显示的核心问题&#xff0c;适用于安防监控、智能终端UI开发等实际场景。压缩包共57个文件&#xff0c;约632KB&#xff0c;包含4个核心C源文件…

作者头像 李华
网站建设 2026/10/17 8:51:13

云安全靶场-Kubernetes Goat

Kubernetes Goat Sensitive keys in codebases 这个是git泄露&#xff0c;其中git里有密钥泄露 访问1230端口扫目录git泄露 git结构 .git/ ├─ config # 仓库配置&#xff08;你刚才看的&#xff0c;里面有 bare 参数&#xff09; ├─ logs/HEAD # 操作日志&#…

作者头像 李华