news 2026/9/30 4:01:19

WinForm窗口置顶沉底实战:绕过TopMost的Windows API方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WinForm窗口置顶沉底实战:绕过TopMost的Windows API方案

简介:本资源是一份面向C# WinForm开发者的实用技术文档,聚焦窗口置顶与置底显示的核心实现方案,适用于桌面应用开发、系统工具定制及UI交互增强等场景。内容深入解析Windows API调用机制,涵盖FindWindow/SetParent实现桌面层嵌入(兼容XP/Win2003)、GetDesktopHandle/EmbedDesktop多层桌面枚举、以及SetWindowLong修改WS_EX_TOPMOST/WS_EX_TOOLWINDOW扩展样式的底层控制方法,并明确指出各方案在Vista及以上系统的兼容性差异与适配建议。资源为单文件PDF文档(42KB),结构清晰,含完整代码示例、关键常量定义、枚举类型说明及分步逻辑注释,便于开发者直接复用或调试验证。目前已有1960人学习下载,适合具备基础WinForm开发经验、需解决窗口层级控制问题的中高级程序员快速掌握跨系统置顶/置底技术要点。

1. WinForm 窗口“钉死”桌面:不是靠 TopMost 就能搞定的玄学问题

你有没有试过在 WinForm 里设TopMost = true,结果窗口在全屏游戏、远程桌面、甚至 Alt+Tab 切换时突然消失?或者更诡异的是——你想让日志窗体沉底不抢焦点,却一设TopMost = false就被任务栏盖住、被其他程序彻底吞没?这不是 Bug,是 Windows 桌面窗口层级(Z-order)和前台激活策略共同作用下的黑匣子行为。TopMost只控制“是否在所有非 TopMost 窗口之上”,它既不保证“永远最前”,也不定义“最底层”的语义。真正决定一个 WinForm 窗口能否稳定驻留在桌面视觉层(Desktop Window Station)的顶层或底层,需要绕过 .NET 封装,直调 Windows API 的SetWindowPos、SetForegroundWindow和ShowWindow组合拳,并精准处理WS_EX_NOACTIVATE、WS_EX_TOOLWINDOW等扩展样式。本文面向正在开发监控面板、工业上位机弹窗、桌面运维助手、温湿度告警浮窗等场景的 C# 工程师——这些窗口必须“物理级钉牢”,不能因用户切屏、启动新程序、甚至锁屏后唤醒而失位。我们不讲理论堆砌,只拆解一套经产线设备、24 小时无人值守系统实测验证的落地方案。


2. 用 SetWindowPos 实现真·永久置顶:绕过 TopMost 的三大限制

.NET 的Form.TopMost是个简化封装,背后调用的是SetWindowPos,但它做了三处关键妥协:

  • 自动重置TopMost状态(如窗口失去焦点时被系统降级);
  • 无法与WS_EX_NOACTIVATE配合(导致置顶窗抢焦点,打断用户操作);
  • 不支持SWP_NOOWNERZORDER等精细 Z-order 控制标志。

要突破这些,必须手写 P/Invoke 调用原生 API,并构建可复用的DesktopAnchor类。

2.1 声明核心 Windows API 并封装安全调用

using System; using System.Runtime.InteropServices; using System.Windows.Forms; public static class DesktopAnchor { // 关键 API 声明 [DllImport("user32.dll", SetLastError = true)] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); // 常量定义(必须用 IntPtr.Zero 代替 null,否则 SWP_NOACTIVATE 失效) private const int HWND_TOPMOST = -1; private const int HWND_NOTOPMOST = -2; private const int HWND_TOP = 0; private const int HWND_BOTTOM = 1; private const uint SWP_NOSIZE = 0x0001; private const uint SWP_NOMOVE = 0x0002; private const uint SWP_NOACTIVATE = 0x0010; private const uint SWP_SHOWWINDOW = 0x0040; private const uint SWP_NOOWNERZORDER = 0x0200; // 关键!避免被 Owner 窗口拖拽影响层级 // 显示命令 private const int SW_SHOW = 5; private const int SW_HIDE = 0; /// <summary> /// 将窗口强制置于桌面最顶层(不抢焦点) /// </summary> public static void PinToTop(Form form) { if (form.IsHandleCreated == false) return; // 先确保窗口可见且未最小化 if (form.WindowState == FormWindowState.Minimized) form.WindowState = FormWindowState.Normal; ShowWindow(form.Handle, SW_SHOW); // 核心:HWND_TOPMOST + SWP_NOACTIVATE + SWP_NOOWNERZORDER // 这三者缺一不可:前者置顶,后者防抢焦,中间者防被父窗拖拽降级 SetWindowPos(form.Handle, (IntPtr)HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); } /// <summary> /// 将窗口置于桌面最底层(但保持可见,不被任务栏遮挡) /// </summary> public static void PinToBottom(Form form) { if (form.IsHandleCreated == false) return; // 必须先取消 TopMost,否则底层无效 form.TopMost = false; // 关键:用 HWND_BOTTOM + SWP_NOACTIVATE + SWP_NOOWNERZORDER // 注意:不能用 HWND_NOTOPMOST,它只是“非置顶”,不等于“最底层” SetWindowPos(form.Handle, (IntPtr)HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); } }

参数说明:

  • hWndInsertAfter:(IntPtr)HWND_TOPMOST表示插入到所有非 TopMost 窗口之前;(IntPtr)HWND_BOTTOM表示插入到所有同级窗口之后(即最底层)。
  • SWP_NOACTIVATE:这是防止置顶窗偷走输入焦点的“后悔药”,没有它,用户正在打字时弹出告警窗会直接中断输入流。
  • SWP_NOOWNERZORDER:WinForm 默认父子窗体共享 Z-order,若主窗体被拖到后台,子窗体会跟着沉底。此标志切断该继承关系,让目标窗体独立锚定。
  • SWP_SHOWWINDOW:确保窗口在调整位置后立即显示,避免闪白。

2.2 在 Form 中集成锚定逻辑:生命周期感知的自动管理

单纯调用PinToTop()一次远远不够。窗口可能被用户拖动、缩放、最小化、甚至被系统策略(如平板模式、多桌面)重置。必须在关键事件中持续“加固”。

public partial class AlertForm : Form { private bool _isPinnedTop = false; private Timer _anchorTimer; public AlertForm() { InitializeComponent(); InitializeAnchorTimer(); } private void InitializeAnchorTimer() { _anchorTimer = new Timer { Interval = 300 }; // 每 300ms 检查一次 _anchorTimer.Tick += (s, e) => EnsurePinnedState(); _anchorTimer.Start(); } private void EnsurePinnedState() { // 仅当窗口可见且未最小化时才锚定 if (this.Visible && this.WindowState != FormWindowState.Minimized) { if (_isPinnedTop) DesktopAnchor.PinToTop(this); else DesktopAnchor.PinToBottom(this); } } protected override void OnLoad(EventArgs e) { base.OnLoad(e); // 首次加载时立即锚定 _isPinnedTop = true; DesktopAnchor.PinToTop(this); } protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); if (this.Visible) { // 可见时恢复锚定状态 EnsurePinnedState(); } } protected override void OnActivated(EventArgs e) { base.OnActivated(e); // 被激活时,若本意是底层,则主动放弃焦点 if (!_isPinnedTop && this.ContainsFocus) { this.Activate(); // 先激活确保句柄有效 NativeMethods.SetForegroundWindow(IntPtr.Zero); // 强制交还焦点给前一个窗口 } } // 提供外部切换锚定状态的方法 public void TogglePinTop() { _isPinnedTop = !_isPinnedTop; if (_isPinnedTop) { DesktopAnchor.PinToTop(this); } else { DesktopAnchor.PinToBottom(this); } } } // 辅助类:提供 SetForegroundWindow 的安全调用 internal static class NativeMethods { [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); }

为什么用 Timer 而不是事件监听?
LocationChanged、SizeChanged等事件在窗口被系统强制重排(如多显示器拔插、DPI 缩放变更)时不会触发。而SetWindowPos的副作用(如窗口被系统重置 Z-order)也无对应事件。300ms 定时轮询是工业级方案中成本最低、覆盖最全的兜底手段——实测在 i5-8250U 上 CPU 占用 < 0.1%,远低于 UI 渲染开销。


3. 沉底不是 Hide:让窗口“存在但不可交互”的底层逻辑

很多开发者误以为Visible = false或Hide()就是“沉底”,其实这是销毁视觉存在感,而非锚定层级。真正的“最底层”需满足三个条件:

  1. 视觉存在:不被任务栏、开始菜单、其他窗口完全遮挡;
  2. 交互隔离:不响应鼠标点击、键盘输入,不抢焦点;
  3. 层级稳固:不随父窗移动、不被系统策略降级。

这需要组合WS_EX_NOACTIVATE、WS_EX_TOOLWINDOW和ShowWindow状态控制。

3.1 用 CreateParams 注入扩展样式:从创建之初就锁定底层基因

CreateParams是 WinForm 窗体创建时传递给 Windows 的原始参数结构,是设置WS_EX_*样式的唯一可靠时机。

public partial class BackgroundMonitorForm : Form { private const int WS_EX_NOACTIVATE = 0x08000000; private const int WS_EX_TOOLWINDOW = 0x00000080; private const int WS_EX_LAYERED = 0x00080000; // 支持透明度,可选 protected override CreateParams CreateParams { get { var cp = base.CreateParams; // 关键:注入 NOACTIVATE + TOOLWINDOW // TOOLWINDOW 让窗口不显示在任务栏,且默认 Z-order 更低 cp.ExStyle |= WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW; return cp; } } protected override void OnLoad(EventArgs e) { base.OnLoad(e); // 创建后立即沉底 DesktopAnchor.PinToBottom(this); // 可选:设置半透明(需 WS_EX_LAYERED) // this.Opacity = 0.85; } }

WS_EX_TOOLWINDOW的真实作用:
它不仅隐藏任务栏按钮,更重要的是——Windows 将其归类为“工具窗口”(Tool Window),这类窗口的默认 Z-order 基准值比普通窗体低。配合HWND_BOTTOM使用,能确保它真的压在所有普通应用之下,包括资源管理器、浏览器等。实测中,未加此样式时,PinToBottom()在 Chrome 全屏下仍会被部分覆盖;加上后,稳居桌面画布最底层。

3.2 拦截鼠标消息:彻底杜绝“误触”风险

即使设置了WS_EX_NOACTIVATE,某些驱动(如触摸屏驱动、绘图板驱动)仍可能将点击事件路由到你的窗体。需在WndProc中拦截WM_MOUSEFIRST到WM_MOUSELAST范围消息。

private const int WM_MOUSEFIRST = 0x0200; private const int WM_MOUSELAST = 0x0209; private const int WM_NCHITTEST = 0x0084; protected override void WndProc(ref Message m) { // 拦截所有鼠标消息,防止穿透点击 if (m.Msg >= WM_MOUSEFIRST && m.Msg <= WM_MOUSELAST) { // 直接丢弃,不调用 base return; } // 特殊处理:让鼠标穿透(可选,需配合 WS_EX_TRANSPARENT) if (m.Msg == WM_NCHITTEST) { var result = base.WndProc(ref m); // 返回 HTTRANSPARENT 表示“此区域透明,点击透传给下层窗口” if (_isTransparentMode) { m.Result = (IntPtr)(-1); // HTTRANSPARENT } return; } base.WndProc(ref m); }

HTTRANSPARENT 的陷阱:
它要求窗体同时设置WS_EX_TRANSPARENT样式,但该样式会导致Paint事件失效(重绘异常)。因此生产环境更推荐纯拦截 +WS_EX_NOACTIVATE组合,而非依赖穿透。实测拦截方案在 1080p/4K 屏幕下均无性能损耗。


4. 避坑:WinForm 置顶/沉底的 5 个血泪经验

现象、原因、解决,一条都不能少——这是我在 7 个工业上位机项目里踩出来的坑。

4.1 现象:窗口在远程桌面(RDP)会话中置顶失效,Alt+Tab 后消失

原因:RDP 会话有自己的桌面隔离机制,HWND_TOPMOST在会话内有效,但跨会话(如本地桌面 vs RDP 桌面)时,系统不维护跨会话的 Z-order 一致性。.NET的TopMost在 RDP 下被强制降级。
解决:检测是否在 RDP 会话中,改用SetWindowPos+HWND_TOP(非TOPMOST)并配合SWP_NOOWNERZORDER。添加运行时检测:

private static bool IsRemoteSession() { return Environment.GetEnvironmentVariable("SESSIONNAME")?.StartsWith("RDP") == true; } // 在 PinToTop 中调用 if (IsRemoteSession()) { SetWindowPos(hWnd, (IntPtr)HWND_TOP, ...); // 用 HWND_TOP 替代 HWND_TOPMOST } else { SetWindowPos(hWnd, (IntPtr)HWND_TOPMOST, ...); }

4.2 现象:多显示器环境下,窗口在副屏置顶后,主屏全屏应用(如 PowerPoint)仍能盖住它

原因:HWND_TOPMOST是全局概念,但 Windows 对全屏独占模式(如 DirectX 游戏、PowerPoint 演示)有更高优先级的“覆盖层”(Overlay Layer),普通 TopMost 窗口无法穿透。
解决:放弃“绝对置顶”,改用WS_EX_LAYERED+UpdateLayeredWindow实现半透明覆盖层。但这会增加复杂度,更务实的方案是:检测到 PowerPoint/Edge 全屏时,临时降级为HWND_TOP,退出全屏后恢复TOPMOST。监听SystemEvents.SessionSwitch和窗口枚举即可。

4.3 现象:调用PinToBottom()后,窗口被任务栏完全遮挡,无法看到

原因:HWND_BOTTOM插入位置在所有窗口之后,但任务栏是 Shell 窗口,其 Z-order 由系统管理,普通SetWindowPos无法将其压到任务栏之下。
解决:不追求“压任务栏之下”,而是“紧贴桌面画布底部”。用SetWindowPos将窗口尺寸设为屏幕分辨率,Y 坐标设为Screen.PrimaryScreen.Bounds.Height - 30(留出任务栏高度),再设WS_EX_TOOLWINDOW防止被任务栏按钮遮挡。本质是视觉沉底,而非 Z-order 沉底。

4.4 现象:WS_EX_NOACTIVATE生效,但窗口仍偶尔获得焦点(尤其在快速连续弹窗时)

原因:SetForegroundWindow调用存在竞争条件。当多个线程同时调用PinToTop(),其中一个成功设为前台,另一个立刻调用SetForegroundWindow(IntPtr.Zero)可能失败。
解决:引入原子锁 + 焦点状态缓存。在OnActivated中记录最后激活时间戳,EnsurePinnedState()中检查是否在 100ms 内被意外激活,若是则强制SetForegroundWindow(IntPtr.Zero)并 Sleep(10) 后重试。

4.5 现象:WinForm 窗体继承自Form,但使用了UserControl作为主内容,PinToTop()后 UserControl 内部控件(如 TextBox)无法获得焦点

原因:WS_EX_NOACTIVATE作用于整个窗体句柄,会阻止所有子控件接收输入。这不是 Bug,是设计使然。
解决:不要对含交互控件的窗体使用WS_EX_NOACTIVATE。改为仅对纯展示型窗体(如告警灯、状态条)启用沉底;对需输入的窗体,用TopMost = true+Form.Activated事件手动管理焦点归属,这才是正解。


5. 进阶技巧:让置顶窗“智能呼吸”——根据用户行为动态升降级

硬钉死一个层级,在真实桌面环境中反而显得僵硬。比如:

  • 用户正在编辑文档,告警窗应保持置顶;
  • 用户切换到全屏视频,告警窗应自动降级为HWND_TOP,避免干扰;
  • 用户 Alt+Tab 回到主程序,告警窗应立即恢复TOPMOST。

这需要监听系统级窗口活动,而非仅靠本进程事件。

5.1 用 WinEvent Hook 监听全局前台窗口变化

SetWinEventHook可捕获EVENT_SYSTEM_FOREGROUND事件,精度远超GetForegroundWindow轮询。

public class ForegroundWatcher : IDisposable { private const uint EVENT_SYSTEM_FOREGROUND = 3; private const uint WINEVENT_OUTOFCONTEXT = 0x0000; private IntPtr _hook; public event Action<string> ForegroundChanged; public ForegroundWatcher() { _hook = SetWinEventHook( EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT); } private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { if (hwnd == IntPtr.Zero) return; var className = GetClassName(hwnd); var windowText = GetWindowText(hwnd); // 过滤掉系统内部窗口(如 TaskManager、Shell_TrayWnd) if (className == "Shell_TrayWnd" || className == "WorkerW") return; ForegroundChanged?.Invoke($"{className}: {windowText}"); } [DllImport("user32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); [DllImport("user32.dll")] private static extern bool UnhookWinEvent(IntPtr hWinEventHook); [DllImport("user32.dll")] private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll")] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); public void Dispose() { if (_hook != IntPtr.Zero) { UnhookWinEvent(_hook); _hook = IntPtr.Zero; } } }

5.2 构建智能升降级策略引擎

public class SmartAnchorManager { private readonly ForegroundWatcher _watcher; private readonly Form _targetForm; private readonly HashSet<string> _fullScreenClasses = new() { "Shell_TrayWnd", "ApplicationFrameHost" }; private string _lastForegroundClass = ""; public SmartAnchorManager(Form targetForm) { _targetForm = targetForm; _watcher = new ForegroundWatcher(); _watcher.ForegroundChanged += OnForegroundChanged; } private void OnForegroundChanged(string info) { var parts = info.Split(new[] { ": " }, StringSplitOptions.None); if (parts.Length < 1) return; var className = parts[0].Trim(); var title = parts.Length > 1 ? parts[1].Trim() : ""; // 检测全屏应用特征 bool isFullScreen = IsFullScreenApp(className, title); if (isFullScreen && _lastForegroundClass != className) { // 进入全屏:降级为 TOP(非 TOPMOST),避免遮挡 DesktopAnchor.PinToTop(_targetForm); // 注意:这里用 TOP,非 TOPMOST _targetForm.TopMost = false; // 清除 .NET 层标记 } else if (!isFullScreen && _lastForegroundClass == className) { // 退出全屏:恢复 TOPMOST DesktopAnchor.PinToTop(_targetForm); } _lastForegroundClass = className; } private bool IsFullScreenApp(string className, string title) { // 常见全屏应用类名 if (_fullScreenClasses.Contains(className)) return true; // 标题含关键词 if (title.Contains("PowerPoint", StringComparison.OrdinalIgnoreCase) || title.Contains("全屏", StringComparison.OrdinalIgnoreCase) || title.Contains("Presentation", StringComparison.OrdinalIgnoreCase)) return true; // 检查窗口矩形是否匹配屏幕 if (GetWindowRect(_targetForm.Handle, out RECT rect)) { var screen = Screen.FromHandle(_targetForm.Handle); return rect.Right - rect.Left == screen.Bounds.Width && rect.Bottom - rect.Top == screen.Bounds.Height; } return false; } [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } }

为什么不用GetForegroundWindow轮询?
GetForegroundWindow()返回的是“当前拥有输入焦点的窗口句柄”,但在多线程、RDP、UAC 提权等场景下,它返回NULL或错误句柄的概率高达 30%。而WinEventHook是系统内核级通知,只要事件发生就必达,实测稳定性 99.99%。代价是需Unhook清理,但这是值得的。

5.3 最终落地:一个可直接复制的完整窗体模板

public partial class SmartAlertForm : Form { private readonly SmartAnchorManager _anchorManager; private readonly Timer _resizeTimer; public SmartAlertForm() { InitializeComponent(); // 初始化智能锚定 _anchorManager = new SmartAnchorManager(this); // 防抖窗口大小变更 _resizeTimer = new Timer { Interval = 150 }; _resizeTimer.Tick += (s, e) => { _resizeTimer.Stop(); this.PerformLayout(); // 触发布局重算 }; this.Resize += (s, e) => { if (!_resizeTimer.Enabled) _resizeTimer.Start(); }; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); // 首次加载:置顶 DesktopAnchor.PinToTop(this); this.TopMost = true; } protected override void OnHandleDestroyed(EventArgs e) { _anchorManager?.Dispose(); _resizeTimer?.Dispose(); base.OnHandleDestroyed(e); } }

我把这套方案用在了某电厂 DCS 上位机的报警看板上,7×24 小时运行,从未出现层级错乱。最深的教训是:别信TopMost的文档,信SetWindowPos的返回值;别依赖事件,信定时轮询的确定性;别追求“绝对最底层”,信“视觉最底层”的工程妥协。希望帮到你。

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

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

天镜漏洞扫描系统落地指南:部署、配置与验证的避坑要点

简介&#xff1a;白皮书《天镜脆弱性扫描与管理系统》完整呈现了启明星辰这款漏洞扫描产品的定位与能力&#xff0c;适用于网络安全运维、等保测评、漏洞管理等场景的技术人员。内容涵盖产品简介、功能特点、技术优势、典型应用和主要功能&#xff0c;重点介绍了多引擎分布式部…

作者头像 李华
网站建设 2026/9/30 3:59:55

AI工程从零构建:裸露核心接口的底层实践

1. 这不是调包&#xff0c;是亲手搭起AI工程的地基“AI Engineering from Scratch”——看到这个标题&#xff0c;我第一反应不是兴奋&#xff0c;而是下意识摸了摸键盘边角那层被磨亮的漆。过去三年&#xff0c;我带过17个从零起步的工程师团队做AI落地项目&#xff0c;其中12…

作者头像 李华
网站建设 2026/9/30 3:58:27

SpringBoot+Vue+MySQL党员教育管理系统设计与实现

这个项目跑起来的第一感觉就是&#xff1a;它不是一个“玩具系统”&#xff0c;而是把“管理端 用户端 学习考试闭环 数据统计”全部串起来了。SpringBootVueMySQL这套组合在毕业设计里非常常见&#xff0c;但真正把“管理平台”做成“能用、能演示、能写论文”的完整项目&a…

作者头像 李华
网站建设 2026/9/30 3:58:02

TensorFlow安装避坑与图像分类实战:2024框架选型指南

TensorFlow&#xff08;以下简称TF&#xff09;这个框框架我断断续续用了六七年&#xff0c;从1.x时代的session、graph、placeholder一路折腾到2.x的Eager Execution和Keras一体化&#xff0c;期间踩过的坑比看到过的教程还多。今天不打算写那种照本宣科的官方文档翻译&#x…

作者头像 李华
网站建设 2026/9/30 3:58:00

ensp实战:防火墙子接口终结VLAN,协同DHCP与安全策略配置详解

做网络这块的兄弟应该都清楚&#xff0c;ensp 里防火墙和交换机的组合是出镜率最高的实战模型。今天我把防御课第七章的综合练习完整拆一遍——防火墙加 DHCP 加 VLAN 的协同配置实验&#xff0c;用的就是 ensp 里的华为 USG6000V 和 S5700。这个实验表面上是一个综合练习&…

作者头像 李华
网站建设 2026/9/30 3:57:55

鸿蒙ArkUI仿腾讯视频轮播图:Swiper组件定制与性能优化

1. 项目背景与整体设计思路1.1 先搞清楚腾讯视频首页轮播图到底好在哪做鸿蒙应用开发的人应该都有体会&#xff1a;首页轮播图是大多数内容类App的门面&#xff0c;用户打开App第一眼看到的就是它。腾讯视频首页顶部那张大图轮播&#xff0c;看着好像只是一个普通的Banner切换&…

作者头像 李华