news 2026/8/12 13:58:47

基于DuiLib与VC++的桌面应用开发实战:从架构设计到性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于DuiLib与VC++的桌面应用开发实战:从架构设计到性能优化

1. 项目概述:为什么选择DuiLib与VC++打造桌面应用

在桌面应用开发领域,尤其是Windows平台,我们常常面临一个选择:是使用成熟的商业UI框架,还是投入精力自研一套界面库?对于追求极致性能、深度定制和原生体验的项目来说,后者往往是更优解。今天要聊的这个项目——“自定义仿360桌面开发实战”,就是一个典型的案例。它没有选择Qt、WPF或Electron,而是回归了经典的VC++(Visual C++)搭配DuiLib这套组合拳。这听起来可能有点“复古”,但对于需要高度模仿特定软件(如360安全卫士)的界面风格、交互逻辑,并且对安装包体积、内存占用和启动速度有苛刻要求的场景,这套方案的优势就凸显出来了。

简单来说,这个项目就是用VC++作为后端逻辑和性能的基石,用DuiLib作为前端界面的“画笔”,从头开始绘制一个功能、外观都高度仿真的360桌面助手。DuiLib是一个基于DirectUI思想的轻量级C++开源界面库,它的核心思想是将界面元素(按钮、窗口、列表等)全部自绘,而不是依赖系统的标准控件。这意味着开发者对界面的每一个像素都有绝对的控制权,可以实现任意复杂的视觉效果,比如圆角、阴影、渐变和动画,这正是模仿360那种绚丽风格所必需的。而VC++,作为微软官方的“亲儿子”,提供了最强大、最底层的Windows API访问能力和编译优化,确保应用运行起来既快又稳。

这个项目适合谁呢?首先,是那些对Windows原生开发有浓厚兴趣,不满足于使用“黑盒”框架的C++开发者。其次,是需要在特定领域(如安全软件、系统工具)开发具有强烈品牌风格UI的团队。最后,也是对于那些希望深入理解桌面软件UI渲染机制、消息循环和资源管理的学习者。通过这个实战,你不仅能学会如何搭建一个DuiLib项目,更能掌握一套从零构建复杂桌面应用的完整方法论。接下来,我们就深入拆解这个项目的核心思路与实现细节。

2. 核心架构与工具选型解析

2.1 为什么是DuiLib+VC++,而非其他方案?

在项目启动前,技术选型是首要决策。市面上常见的桌面方案有Qt、WinForms/WPF、Electron等。Qt功能强大、跨平台,但商业授权费用不菲,且其风格与360的差异较大,定制成本高。WinForms/WPF基于.NET,需要庞大的运行时,且难以实现DuiLib那种纯自绘的像素级控制。Electron基于Web技术,开发效率高,但内存占用和性能是硬伤,对于一个系统桌面辅助工具来说,这是不可接受的。

DuiLib+VC++的组合,完美契合了我们的需求:

  1. 极致轻量与高性能:编译后是纯粹的原生二进制,无额外运行时依赖。DuiLib本身代码精炼,通过GDI/GDI+进行自绘,渲染效率极高。
  2. 无限定制能力:DirectUI思想让界面与逻辑彻底分离。所有控件都是“画”出来的,你可以轻松实现360桌面那种动态背景、透明毛玻璃效果、非规则形状窗口等。
  3. 与Windows深度集成:VC++可以无缝调用所有Windows API,方便我们实现诸如监控文件系统变化、获取系统信息、注入Shell等底层功能,这是仿360桌面管理功能的核心。
  4. 开源与免费:DuiLib遵循BSD协议,可以放心用于商业项目,这解决了法律风险。

注意:选择DuiLib意味着你需要接受一定的学习曲线和手动处理更多细节(如消息转发、控件布局计算),但换来的控制力和性能提升是值得的。

2.2 开发环境搭建与项目初始化

工欲善其事,必先利其器。我们的开发环境基于Visual Studio(建议VS2015或以上版本),因为它对VC++和Windows SDK的支持最完善。

第一步:获取并编译DuiLib库DuiLib的源码可以从GitHub等开源仓库获取。通常,我们需要先编译出静态库(.lib)或动态库(.dll)。

  1. 打开DuiLib的解决方案文件(.sln)。
  2. 将编译配置设置为“Release”和“Win32”(即使目标平台是x64,DuiLib也常以Win32编译以保证兼容性)。
  3. 编译DuiLib项目。成功后,在输出目录会找到DuiLib.libDuiLib.dll(如果选择动态库),以及至关重要的UIlib.h等头文件。

第二步:创建VC++空项目并配置

  1. 在VS中新建一个“Win32项目”,选择“空项目”。
  2. 配置项目属性:
    • C/C++ -> 常规 -> 附加包含目录:添加DuiLib头文件所在路径。
    • 链接器 -> 常规 -> 附加库目录:添加DuiLib库文件(.lib)所在路径。
    • 链接器 -> 输入 -> 附加依赖项:添加DuiLib.lib
    • 如果使用动态库,需要将编译好的DuiLib.dll复制到你的项目输出目录(如DebugRelease文件夹)。

第三步:组织项目资源结构仿360桌面的UI资源(图片、XML布局文件、字体)会非常多。一个清晰的目录结构至关重要。我通常这样组织:

YourProject/ ├── src/ // 源代码 ├── include/ // 头文件 ├── lib/ // 第三方库,如DuiLib.lib ├── bin/ // 输出目录,存放exe和dll └── resources/ // 资源文件 ├── skins/ // 皮肤文件夹,以功能模块命名 │ ├── MainWnd/ // 主窗口皮肤 │ ├── TaskMgr/ // 任务管理皮肤 │ └── ... ├── images/ // 图片资源(PNG格式,支持透明通道) ├── xml/ // 全局或公共布局文件 └── config/ // 配置文件

这种结构便于团队协作和资源管理,也与DuiLib通过XML路径加载资源的机制相匹配。

3. DuiLib核心机制与界面搭建实战

3.1 理解DuiLib的窗口、消息与渲染循环

DuiLib应用的核心是一个继承自WindowImplBase的窗口类。这个基类封装了Windows窗口创建、消息循环和DuiLib渲染引擎的集成。

窗口创建与消息泵: 你的主窗口类(如CMainFrame)重写WindowImplBase的虚函数。在InitWindow函数中初始化界面,在Notify函数中处理控件通知消息(如按钮点击)。DuiLib的消息机制是双重的:既处理标准的Windows消息(如WM_PAINT,WM_SIZE),也处理自己定义的内部通知消息(如DUI_MSGTYPE_CLICK)。理解这一点对调试至关重要。

XML布局与皮肤机制: 这是DuiLib的灵魂。界面布局完全由XML文件定义,实现了UI与逻辑的分离。一个简单的按钮定义如下:

<Button name="btn_close" width="24" height="24" normalimage="file='close.png' dest='0,0,24,24'"/>

在代码中,我们通过CControlUI* pCloseBtn = static_cast<CControlUI*>(m_PaintManager.FindControl(_T("btn_close")));来获取这个按钮的指针,并为其绑定事件。

资源加载与那个经典的“skin.xml”问题: 网络热词中提到了“duilib加载资源文件失败skin.xml”,这绝对是DuiLib新手遇到的第一个“拦路虎”。失败原因通常有以下几个:

  1. 路径错误:这是最常见的原因。DuiLib默认使用相对路径,但其基准路径可能是程序运行目录,也可能是资源压缩包内的虚拟路径。务必在程序启动初期,通过CPaintManagerUI::SetResourcePathCPaintManagerUI::SetResourceZip明确设置资源路径。
  2. XML格式错误:标签未闭合、属性值引号不匹配等。建议使用XML验证工具先检查。
  3. 编码问题:XML文件保存的编码(如UTF-8 with BOM)与代码中读取的预期编码不一致。通常建议XML保存为UTF-8无BOM格式,并在代码中做相应处理。
  4. 资源未成功导入:如果使用资源压缩包(.zip),需要确保压缩包本身被正确加载,且skin.xml在压缩包内的路径正确。

实操心得:我习惯在InitInstance函数中,使用绝对路径来设置资源路径进行调试,例如CPaintManagerUI::SetResourcePath(L"D:\\Project\\resources\\skins\\MainWnd\\");。等一切正常后,再改为相对路径或从配置文件中读取。同时,在FindControl失败时,立即检查对应的XML控件name属性是否拼写正确,这是第二个高频错误点。

3.2 仿360桌面主窗口的布局与自绘控件实现

360桌面的主窗口通常是一个停靠在屏幕一侧的垂直长条,包含天气、日历、加速球、常用软件列表等模块。我们用DuiLib来实现它。

窗口样式设置: 为了模仿那种无边框、可拖动、带阴影的效果,我们需要在创建窗口时指定特殊的样式。

// 在重写的 Create 函数中,或者窗口类构造函数中设置 m_WndInfo.dwStyle = UI_WNDSTYLE_FRAME; // 使用DuiLib自定义的框架样式 m_WndInfo.dwExStyle = WS_EX_LAYERED; // 支持分层窗口,用于实现透明和阴影

然后,在XML的Window标签中,设置sizemininfomaxinfo,以及shadow属性来添加阴影。

复杂布局与自定义容器: DuiLib提供了VerticalLayoutUIHorizontalLayoutUITabLayoutUITileLayoutUI等容器控件。仿360桌面的垂直列表,本质上就是一个VerticalLayoutUI,里面依次排列着各个功能模块的ContainerUI。 每个模块容器(如天气模块)内部,又可以嵌套水平布局和垂直布局来排列图标、文字和按钮。关键在于合理设置inset(内边距)、childmargin(子控件间距)和float(浮动)属性。

自定义控件的开发: 360桌面上那个经典的“加速球”是一个很好的自定义控件例子。DuiLib的自定义控件需要继承自CControlUI或它的子类。

  1. 重写DoPaint函数:在这里使用GDI/GDI+进行绘制。例如,加速球就是一个圆形的渐变填充,加上一个百分比文字。
  2. 处理消息:重写DoEvent函数,处理鼠标移入、移出、点击等事件,以实现悬浮高亮、点击收缩动画等交互。
  3. 暴露属性:重写GetAttributeSetAttribute,使得可以在XML中配置这个控件的属性,如ballcolortextcolor等。
  4. 在XML中使用:需要在Window标签中通过<Include source="你的控件头文件" />引入,然后就可以像使用内置控件一样使用<Custom name="SpeedBall" ... />

通过组合使用内置布局控件和开发自定义控件,我们就能像搭积木一样,构建出与360桌面高度相似的复杂界面。

4. 核心功能模块的VC++实现

4.1 系统信息监控与任务管理模块

仿360桌面的一个重要功能是实时显示CPU、内存、网络使用率,并提供一键加速(清理内存)功能。这需要VC++调用系统API。

获取CPU和内存使用率

  • CPU使用率:使用PDH(性能数据帮助器)API是更专业和稳定的方法。首先通过PdhOpenQuery创建一个查询,然后通过PdhAddCounter添加“\Processor(_Total)\% Processor Time”计数器,定期(如每秒)调用PdhCollectQueryDataPdhGetFormattedCounterValue来获取百分比值。
  • 内存使用率:使用GlobalMemoryStatusEx函数。填充MEMORYSTATUSEX结构体后调用,可以获取物理内存总量、已用、可用等信息,轻松计算出使用率。

“一键加速”功能实现: 所谓的加速,主要是清理进程的工作集(Working Set),让系统将不常用的内存数据交换到磁盘上的页面文件,从而腾出物理内存。这可以通过一个简单的循环调用SetProcessWorkingSetSize来实现,但需要注意:

  1. 需要以管理员权限运行程序,否则对某些系统进程的操作会失败。
  2. 这不是真正的“释放内存”,只是让内存使用看起来变少了,系统需要时这些数据又会被加载回来。因此,在UI上需要谨慎描述这个功能。
  3. 更“高级”的加速可能还包括结束一些不必要的后台进程,这需要枚举进程(CreateToolhelp32Snapshot)、判断其属性,然后调用TerminateProcess务必谨慎,结束系统关键进程会导致不稳定。

进程列表的展示: 使用DuiLib的ListUIListExUI控件来展示进程列表。后台通过CreateToolhelp32Snapshot等API定期获取进程列表,填充到一个数据结构中,然后通知UI线程更新列表控件的内容。这里涉及到跨线程更新UI,需要使用DuiLib的消息机制或Windows的PostMessage来安全地传递数据。

4.2 文件清理与插件化管理架构

文件清理: 模仿360的“电脑清理”功能,需要扫描系统中的垃圾文件,如临时文件、缓存、日志等。这涉及到:

  1. 目录枚举:使用FindFirstFileFindNextFileAPI递归扫描特定目录(如%TEMP%、浏览器缓存路径)。
  2. 文件筛选规则:根据文件扩展名(如.tmp,.log,.cache)、最后修改时间(如超过7天)、目录名等规则来判断是否为垃圾文件。
  3. 安全删除:删除文件使用DeleteFileAPI,删除目录使用RemoveDirectory极其重要:在删除前,必须向用户展示扫描结果,并由用户确认。误删系统文件可能导致严重后果。对于需要更高权限才能删除的文件,应考虑提权操作。

插件化架构设计: 一个成熟的桌面助手应该支持功能扩展。我们可以设计一个简单的插件系统:

  1. 插件接口:定义一个抽象的插件接口类IPlugin,包含GetName(),Initialize(),Execute(),Uninitialize()等纯虚函数。
  2. 动态加载:主程序在启动时,扫描特定目录(如plugins\)下的所有DLL文件。使用LoadLibrary加载DLL,然后使用GetProcAddress获取DLL中导出的CreatePluginInstance函数地址,调用该函数创建插件对象。
  3. 通信机制:主程序与插件之间可以通过接口直接调用,也可以定义一套简单的消息/事件机制。主程序可以将自身的主窗口句柄、资源管理器接口等传递给插件。
  4. UI集成:插件可以返回自己的UI描述(一段XML字符串),主程序将其动态加载并嵌入到主界面的某个容器(如TabLayoutUI)中。这样,每个插件就成为了主程序的一个功能选项卡。

这种架构使得“天气插件”、“新闻插件”、“启动项管理插件”等可以独立开发、测试和部署,大大提升了项目的可维护性和可扩展性。

5. 打包部署与性能优化实战

5.1 解决“Flutter打包怎么带VC++库”的启示:处理运行时依赖

网络热词中提到了一个看似不相关的问题:“flutter打包怎么带vc++库”。这恰恰点出了所有Windows C++程序部署的一个核心痛点——VC++运行时库(VC++ Redistributable)的依赖。我们的DuiLib+VC++应用同样面临此问题。

我们的程序在编译时,如果使用了动态链接运行时库(/MD或/MDd),那么目标机器上就必须安装对应版本的VC++运行库。否则会出现“找不到MSVCP140.dll”等错误。

解决方案有以下几种

  1. 静态链接运行时库(/MT或/MTd):这是最干净的方案。在项目属性 -> C/C++ -> 代码生成 -> 运行时库中,选择“多线程(/MT)”(Release)或“多线程调试(/MTd)”(Debug)。这样,运行时库的代码会被直接打包进你的.exe文件,无需额外依赖。代价是:可执行文件体积会显著增大。
  2. 打包并安装运行库:将对应的vcredist_x86.exevcredist_x64.exe打包进你的安装程序,在安装时静默运行它(如/install /quiet /norestart)。这是许多商业软件的做法。
  3. 本地部署DLL:将所需的msvcp140.dll,vcruntime140.dll等文件复制到你的.exe同级目录下。这适用于简单的绿色软件。

实操心得:对于像仿360桌面这样希望做成绿色、小巧工具的项目,我强烈推荐使用静态链接(/MT)。虽然文件会大几MB,但彻底避免了用户在老旧或纯净系统上无法运行的尴尬,用户体验最好。在发布最终版本前,一定要在虚拟机里安装一个干净的Windows系统进行测试,这是检验依赖是否处理干净的“金标准”。

5.2 内存优化与界面流畅性调优

基于DuiLib的应用,如果不加注意,容易出现内存泄漏和界面卡顿。

内存泄漏排查: DuiLib的控件对象通常由CPaintManagerUI的生命周期管理。最常见的泄漏发生在:

  • 手动new了控件但没有加入DuiLib的管理:如果你通过new创建了一个CButtonUI,但忘了调用Add将其添加到某个容器,或者添加后又在别处delete了它,都会导致问题。确保控件的创建和销毁由XML解析器或CPaintManagerUI统一管理。
  • 自定义控件中分配的资源未释放:在你的自定义控件的DoPaint中,如果创建了GDI对象(如HPEN,HBRUSH),必须在绘制结束后用DeleteObject删除,或者使用RAII对象(如Gdiplus::Pen)管理。 使用Visual Studio的内存诊断工具(如_CrtDumpMemoryLeaks)或第三方工具(如VLD)可以有效定位泄漏点。

界面流畅性优化

  1. 减少不必要的重绘:在Notify或消息处理函数中,如果只是逻辑状态改变,不涉及UI显示,就不要调用Invalidate。对于频繁更新的数据(如CPU使用率数字),可以考虑设置一个定时器,每500ms或1秒更新一次UI,而不是实时更新。
  2. 图片资源优化:DuiLib支持PNG,但大尺寸的PNG图片会占用较多内存和解码时间。对于UI中用到的图片,应使用工具(如TinyPNG)进行无损或优化压缩。将多个小图标合并成一张雪碧图(Sprite Sheet),通过destsource属性来裁剪显示,可以减少图片文件数量和加载开销。
  3. 复杂布局的延迟加载:如果主界面有多个标签页(Tab),不要一次性初始化所有标签页的UI。可以使用DuiLib的TabLayoutUI,并结合Visible属性或动态创建控件的方式,实现“惰性加载”,即只有当用户切换到某个标签时,才创建和初始化该标签的界面内容。
  4. 避免在主线程进行耗时操作:像文件扫描、网络请求这类可能阻塞的操作,一定要放到单独的 worker 线程中去执行,然后通过消息通知UI线程更新结果。否则会导致界面“假死”,用户体验极差。

6. 开发中的常见“坑”与调试技巧实录

即使有了清晰的架构,实际开发中仍会踩到无数的坑。这里记录几个最典型的问题和我的解决思路。

问题一:控件事件不响应或响应错乱

  • 症状:点击按钮没反应,或者点击A按钮却触发了B按钮的逻辑。
  • 排查
    1. 首先检查XML中控件的name属性是否在代码中FindControl时完全一致(大小写敏感?)。
    2. Notify函数中下断点,查看pMsg->pSender指针是否是你期望的控件。打印或查看pSender->GetName()
    3. 检查事件类型pMsg->Type是否正确。按钮点击事件通常是DUI_MSGTYPE_CLICK
    4. 特别注意:如果控件在一个容器里,并且容器也处理了点击事件,事件可能会被容器“吃掉”。需要检查容器的DoEvent或消息处理逻辑。

问题二:界面布局在缩放或调整大小时错位

  • 症状:窗口拖大拖小后,控件位置乱跑,或者留出空白。
  • 排查
    1. 检查XML布局中,是否对关键容器或控件设置了固定的widthheight。尽量使用percent百分比属性,或者float结合margin来实现自适应。
    2. 重写窗口的OnSize消息处理函数,确保在窗口大小改变后,调用m_PaintManager.SetPos(CDuiRect(...))来更新绘制管理器的工作区域。
    3. 检查自定义控件的EstimateSize函数(如果重写了)是否计算正确。

问题三:程序在部分电脑上运行崩溃,但在开发机上正常

  • 症状:发布出去的程序,在某些用户的电脑上启动即崩溃或运行中崩溃。
  • 排查
    1. 首要怀疑运行时库:如前所述,检查是否使用了正确的运行时库链接方式。让用户检查事件查看器中的应用程序错误日志,看是否缺少DLL。
    2. 检查资源加载路径:程序发布后,资源文件的相对路径可能发生变化。使用绝对路径或确保资源文件被正确打包到安装目录。可以在程序启动时,用MessageBox输出当前尝试加载的资源绝对路径,方便远程调试。
    3. 数据兼容性:如果你的程序保存了配置文件或数据,检查读写逻辑。例如,在x86和x64系统下,某些数据类型或文件操作可能不同。确保使用安全的数据序列化方式。
    4. 使用崩溃转储(Dump):在代码中集成一个异常处理函数(如SetUnhandledExceptionFilter),在程序崩溃时自动生成一个.dmp文件。这个文件包含了崩溃时的调用栈和内存状态,拿回开发机用Visual Studio或WinDbg打开,可以精准定位崩溃的代码行。这是解决线上崩溃问题最强大的武器。

问题四:自定义控件绘制异常或闪烁

  • 症状:自己画的控件显示不全、颜色不对,或者刷新时闪烁。
  • 排查
    1. 双缓冲:确保在DoPaint中使用了双缓冲。DuiLib的CPaintManagerUI默认应该处理了,但如果你在自定义控件中直接操作HDC,可能需要自己创建内存DC进行绘制,最后再BitBlt到目标DC。
    2. 绘制区域DoPaint函数会传入一个CDuiRect rcPaint参数,表示需要重绘的区域。为了提高效率,应该只绘制这个区域内的内容,而不是整个控件。但如果你图省事,也可以忽略它,但可能会影响性能。
    3. GDI资源泄漏:这是导致闪烁和最终崩溃的常见原因。反复创建和销毁GDI对象(如画笔、画刷)而没有删除,会耗尽GDI句柄。使用Gdiplus库的C++类(如Gdiplus::Graphics,Gdiplus::Pen)可以自动管理资源,更安全。

开发这样一个项目,就像在组装一台精密的机械钟表,每一个齿轮(控件)都必须严丝合缝,每一根发条(消息循环)都必须张力适中。过程中遇到的每一个问题,都是对Windows GUI编程和C++功底的一次考验。但当最终看到那个与目标软件神似的界面流畅运行,所有功能都按预期工作时,那种成就感是无与伦比的。这套DuiLib+VC++的技术栈,虽然不如一些新潮框架那样“时尚”,但它所赋予的掌控力和性能表现,在特定的领域内依然无可替代。

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

如何快速修复损坏二维码:QRazyBox终极使用指南

如何快速修复损坏二维码&#xff1a;QRazyBox终极使用指南 【免费下载链接】qrazybox QR Code Analysis and Recovery Toolkit 项目地址: https://gitcode.com/gh_mirrors/qr/qrazybox 你是否曾遇到过这样的烦恼&#xff1a;重要的二维码因为污损、打印模糊或部分缺失而…

作者头像 李华
网站建设 2026/8/12 13:54:47

基于计算机视觉的视频作弊检测:从原理到工程实践

这次我们来看一个很有意思的技术话题&#xff1a;如何通过技术手段分析视频&#xff0c;找出其中可能存在的“开挂”或作弊痕迹。这里的“开挂”通常指在游戏、竞技或特定软件操作中&#xff0c;使用外挂程序获得不公平优势的行为。而“声音自己脑补”则提示我们&#xff0c;分…

作者头像 李华
网站建设 2026/8/12 13:52:57

5步掌握开源3D地形生成工具实战指南

5步掌握开源3D地形生成工具实战指南 【免费下载链接】cesium-terrain-builder A C library and associated command line tools designed to create terrain tiles for use in the Cesium JavaScript library 项目地址: https://gitcode.com/gh_mirrors/ces/cesium-terrain-b…

作者头像 李华
网站建设 2026/8/12 13:52:36

Linux系统下RAR压缩格式的完整处理指南:从安装到实战应用

1. 为什么在Linux上还需要Rar&#xff1f; 提到Linux下的压缩解压&#xff0c;很多人第一反应就是 tar 、 gzip 、 bzip2 或者 xz 。确实&#xff0c;这些是Linux世界的“原住民”&#xff0c;开源、免费、集成度高&#xff0c;处理 .tar.gz 、 .tar.bz2 、 .tar.…

作者头像 李华
网站建设 2026/8/12 13:52:03

Zephyr RTOS在STM32F103C8T6上的VSCode开发环境搭建与实战

最近在尝试将 Zephyr RTOS 移植到 STM32F103C8T6 这款经典的“蓝色药丸”最小系统板上时&#xff0c;发现虽然 Zephyr 官方支持强大&#xff0c;但结合 VSCode 进行一站式开发、编译、调试和烧录的完整中文教程却比较零散。很多开发者卡在环境配置、项目构建或烧录环节&#xf…

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

VCF 9.1 DTGW分布式中转网关+VNA虚拟网络设备一站式配置实操指南

William Lam 2026年8月11日官方速报&#xff1a;VCF 9.1全新DTGW分布式中转网关搭配VNA虚拟设备集群&#xff0c;支持在vCenter单一界面完成全套NSX网络配置&#xff0c;无需反复切换NSX Manager&#xff1b;解决9.0版本必须跨界面操作VPC、防火墙、网关的繁琐流程&#xff0c;…

作者头像 李华