当前位置: 首页 > news >正文

实现桌面动态壁纸(二)

目录

前言

一、关于 WorkerW 工作区窗口

二、关于窗口关系

2.1 窗口以及窗口隶属关系

2.2 桌面管理层窗口组分简析

2.3 厘清两个概念的区别

2.4 关于设置父窗口

三、编写代码以供在 Vista 上实现

3.1 方法二:子类化并自绘窗口背景

四、初步分析桌面管理层窗口创建的原理

4.1 桌面管理层窗口的创建流程

4.2 从管理层窗口回调看 0x052C 消息

总结


文章出处来源:[​https://blog.csdn.net/qq_59075481/article/details/133801491​]。

前言

这是实现 D2WT (Dynamic Desktop Wallpaper Tools) 系列的第二节,在本节中,我们进一步讨论 WorkerW 窗口的功能,介绍桌面窗口创建的流程,同时讨论为什么在 Vista 上无法嵌入窗口。

【提示】本文涉及的关于窗口的处理部分基于我曾经发的《桌面自定义 WorkerW 窗口》一文。里面的思路有类似的地方,但比那边讲的大概更加透彻。 

需要查看第一节的可以点击这里:实现桌面动态壁纸(一)

相关系列文章:

序号文章标题(链接)AID
1实现桌面动态壁纸(一)125361650
2实现桌面动态壁纸(二)[本文]

133801491

3实现桌面动态壁纸(三)[未来发布]---
4实现桌面动态壁纸——认识 WebView2 控件138637909

一、关于 WorkerW 工作区窗口

WorkerWWindows 操作系统中的一个窗口站 (Window Station) 和桌面 (Desktop) 的组合。它是用于用户界面的一个基础组件,用于管理和控制用户界面。WorkerW 从操作系统内核中获取资源,包括 CPU 资源和内存资源,并将其分配给用户进程,以便它们能够在屏幕上显示图形和交互元素。WorkerW 通过窗口管理器将窗口和界面元素显示在屏幕上,同时允许用户与它们进行交互。(以上这段来源于网络)

WorkerW/A 属于工作区窗口,它基本上通过调用 Shell API 函数中的 SHCreateWorkerWindowW/A 创建。其中 W 代表 WideChar (UNICODE) 版本的窗口,而 SHCreateWorkerWindowA 是该函数的 ASCII 版本。任何需要侦听窗口消息的应用程序都会调用此 API 来创建工作区窗口。 SHCreateWorkerWindowW 是为文档化的导出函数,通过分析 explorer.exe 发现该函数是从 api-ms-win-shlwapi-winrt-storage-l1-1-1.dll 中导入的,但是看到这个名称可能会很陌生。

explorer.exe 的导入表上,SHCreateWorkerWindowW 函数是通过解析名为 api-ms-win-shlwapi-winrt-storage-l1-1-1API 集而重定向到 shlwapi.dll,所以,最终是需要分析 shlwapi.dll 里面的函数。

API 集:微软推出的用高度命名的链接库名称分类 API 的最小唯一核心库,将 API 调用通过内置加载器转发到真实的 Dll 上,截止 Win11 已经更新到 V10 版本)

根据 ReactOS 的开发者文档可以知道 SHCreateWorkerWindow 的定义和内部实现。

HWND WINAPI SHCreateWorkerWindow(
WNDPROC        wndProc,
HWND                hWndParent,
DWORD             dwExStyle,
DWORD             dwStyle,
HMENU              hMenu,
LONG_PTR        wnd_extra
)

SHCreateWorkerWindowA/W 其实就是 CreateWindowExA/W 的封装:

HWND WINAPI SHCreateWorkerWindowA(
WNDPROC      wndProc,
HWND 	     hWndParent,
DWORD 	     dwExStyle,
DWORD 	     dwStyle,
HMENU 	     hMenu,
LONG_PTR     wnd_extra
)
{static const char szClass[] = "WorkerA";WNDCLASSA wc;HWND hWnd;TRACE("(%p, %p, 0x%08x, 0x%08x, %p, 0x%08lx)\n",wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);/* Create Window class */wc.style         = 0;wc.lpfnWndProc   = DefWindowProcA;wc.cbClsExtra    = 0;wc.cbWndExtra    = sizeof(LONG_PTR);wc.hInstance     = shlwapi_hInstance;wc.hIcon         = NULL;wc.hCursor       = LoadCursorA(NULL, (LPSTR)IDC_ARROW);wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);wc.lpszMenuName  = NULL;wc.lpszClassName = szClass;SHRegisterClassA(&wc);hWnd = CreateWindowExA(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0,hWndParent, hMenu, shlwapi_hInstance, 0);if (hWnd){SetWindowLongPtrA(hWnd, 0, wnd_extra);if (wndProc) SetWindowLongPtrA(hWnd, GWLP_WNDPROC, (LONG_PTR)wndProc);}return hWnd;
}HWND WINAPI SHCreateWorkerWindowW(
WNDPROC      wndProc,
HWND 	     hWndParent,
DWORD 	     dwExStyle,
DWORD 	     dwStyle,
HMENU 	     hMenu,
LONG_PTR     wnd_extra
)
{static const WCHAR szClass[] = { 'W', 'o', 'r', 'k', 'e', 'r', 'W', 0 };WNDCLASSW wc;HWND hWnd;TRACE("(%p, %p, 0x%08x, 0x%08x, %p, 0x%08lx)\n",wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);/* If our OS is natively ANSI, use the ANSI version */if (GetVersion() & 0x80000000)  /* not NT */{TRACE("fallback to ANSI, ver 0x%08x\n", GetVersion());return SHCreateWorkerWindowA(wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);}/* Create Window class */wc.style         = 0;wc.lpfnWndProc   = DefWindowProcW;wc.cbClsExtra    = 0;wc.cbWndExtra    = sizeof(LONG_PTR);wc.hInstance     = shlwapi_hInstance;wc.hIcon         = NULL;wc.hCursor       = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);wc.lpszMenuName  = NULL;wc.lpszClassName = szClass;SHRegisterClassW(&wc);hWnd = CreateWindowExW(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0,hWndParent, hMenu, shlwapi_hInstance, 0);if (hWnd){SetWindowLongPtrW(hWnd, 0, wnd_extra);if (wndProc) SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)wndProc);}return hWnd;
}

DWM 机制完善之前的操作系统上,切换桌面壁纸或者系统主题的时候,窗口的绘制会出现卡顿、频闪现象。在切换主题的时候,微软通过 LockWindowUpdate 函数,阻止其他窗口的绘制,并显示一个“请稍后”窗口,来避免用户看到卡顿的桌面管理层窗口。但是,这给用户的体验并不是特别好,因为需要“等待”。随后,在 DWM 组件的支持下,切换壁纸前,首先将 DefView 窗口分离出来,然后利用 WorkerW 窗口去绘制 DefView 的背景,在内存中首先生成双缓冲,将新壁纸和旧壁纸的图案之间合成交叉溶解的图像动画,从而实现窗口背景的平滑处理。

下图展示了在切换主题的交叉阶段,桌面管理层窗口的变化(新旧壁纸的交叉溶解效果):

我们意识到,SHCreateWorkerWindow 只能创建类名是 WorkerW 的窗口,关键部分并不在于这个函数,想要知道系统是如何实现透明层次的,还需要研究其窗口过程以及后续的处理,我想这需要对桌面窗口有一个深入一点的理解。

二、关于窗口关系

2.1 窗口以及窗口隶属关系

(TODO:之后补充)

2.2 桌面管理层窗口组分简析

我们知道,桌面管理层窗口在未产生 WorkerW 分层时,窗口的层次应该如下所示:

我么可以通过简单的手法理解这些窗口的作用:

(1) SysHeader32 窗口

SysHeader32 窗口是一个不可见窗口,这个窗口主要负责在 ListView 上绘制每个图标的文本。

验证方法:通过 SendMessageW(hSysHead, WM_CLOSE, 0, 0) 即可关闭该窗口,按 F5 刷新桌面,可以观察到图标的文本已经消失,但是图标依然可以正常点击:

并且右键菜单依然是有效的:

(2) SysListView32 窗口

SysListView32 窗口主要负责控制图标列表的显示和操作,关闭或者隐藏后,图标列表将不可见。

验证方法:隐藏窗口 ShowWindow(hListView, SW_HIDE) 可以发现图标立即消失。

但是,右键菜单依然可用,说明右键菜单不归它管理:

(3) SHELLDLL_DefView 窗口

这个窗口我们需要通过两步验证它的功能。

SHELLDLL_DefView 窗口控制图标列表窗口的背景绘制工作,这可以从 SysListView32 的属性页看出:

SHELLDLL_DefView 还控制右键菜单,使用 ShowWindow(hDefView, SW_HIDE) 后无法打开右键菜单。

验证是否支持背景绘制工作:

第一步:进一步隐藏 Program Manager 窗口,桌面管理层窗口的背景变成白色:

这说明了,Program 窗口的背景是系统设置的壁纸。

第二步:将 DefView 窗口变成弹出式窗口(独立化),并恢复显示。

会发现,无论 Program 窗口是否可见,图标窗口的背景都是黑色的:

于是我们可以判断出,SHELLDLL_DefView 可以通过获取父窗口(会判断是不是 Progman 窗口)的图像缓冲,来绘制子窗口的背景。

(4) Program Manager 窗口

Program Manager 窗口是桌面管理层的主窗口,Program Manager 窗口响应 WM_CLOSE 时(不响应 SC_CLOSE ),会调用 Shell32.dll 中的符号并显示一个询问是否需要关闭计算机的对话框:

Program 还负责显示桌面壁纸,隐藏或者关闭后背景将变为白色:

至此,我们从窗口的可视化角度简单分析了各个桌面管理层窗口的基本作用。

2.3 厘清两个概念的区别

在这个系列的一开始,我们就用“桌面管理层窗口”来称呼包含桌面图标在内的几个窗口的集合:

但是,我们在第一篇中,我们也提到过桌面窗口这个名字,桌面窗口和桌面管理层窗口有什么区别呢?

桌面窗口是其他窗口的祖先,在系统启动时创建,类名为 “#32769” 。这个窗口由 csrss.exe 进程创建,所有父窗口显示为 NULL 的窗口其实是以该窗口作为父窗口。所有窗口都在这个窗口内。所以它是 Z 序最高的窗口。

而桌面管理层窗口,则是 Z 序最低的窗口。桌面管理层窗口是以名为 Program Manager 窗口为主窗口,管理左面文件夹图标列表的显示、操作、桌面壁纸等功能的一系列窗口。

而这本质上不是一类窗口。此外,通过 GetDesktopWindow 函数获取的窗口句柄是桌面窗口句柄,而不是桌面管理层的窗口句柄。(关于他们的详细内容,在接下来的文章中我们会一一介绍)

2.4 关于设置父窗口

Windows Vista 上,SHELL_DefView 不支持背景透明化,我们想到可以利用扩展属性 WS_EX_LAYERED 实现背景透明,但是 MSDN 上明确说明该扩展属性从 Windows 8 开始,才对子窗口有效果。也就是说,在 Vista 上,对子窗口 SHELL_DefView 设置分层属性是无效的。

这时候,我们就需要将 SHELL_DefView 独立出来,将其变成弹出式窗口,就可以设置该属性了。

这里我么可以使用 SetParent 并指定父窗口为 NULL,随后去除窗口的 WS_CHILD 属性,添加 WS_POPUP | WS_EX_TOOLWINDOW 等属性,来实现将窗口独立化。

HWND SetParent(
_In_            HWND hWndChild,
_In_opt_     HWND hWndNewParent
);

[in] hWndChild

类型:HWND

子窗口的句柄。

[in, optional] hWndNewParent

类型:HWND

新父窗口的句柄。 如果此参数为 NULL,桌面窗口将成为新的父窗口。 如果此参数 HWND_MESSAGE,则子窗口将成为 仅消息窗口。

部分资料对这里的参数为 NULL 时,SetParent 的行为认知可能有误解,这里不是指桌面管理层窗口,他不是 Progman 窗口,而是由 csrss.exe 进程创建的类名为 “#32769” 窗口,他是一切桌面顶级窗口的父窗口(不是所有者窗口),称为桌面窗口,然而顶级窗口的父窗口常常被标记为 NULL

#32769” 窗口是一切桌面窗口的祖先窗口,是系统启动的时候创建的第一个窗口。Spy++ 下可以看到第一个窗口就是它:

查看窗口对应的进程信息:

显然,窗口由 CSRSS 创建。

接下来,我们用一个很简单的例子测试一下就可以理解正在发生的事情:

#include <iostream>
#include <Windows.h>int main()
{HWND h32769Wnd = NULL;HWND hDesktopwnd = NULL;HWND hNewParent = NULL;HWND hNotepad = NULL;HWND hOwner = NULL;SetLastError(0);h32769Wnd = FindWindowW(L"#32769", NULL);printf("FindDesktopWnd:[ 0x%I64X ], find #32769. err_code:[%d]\n",(unsigned long long)h32769Wnd, GetLastError());hNotepad = FindWindowA("Notepad", NULL);if (hNotepad){hNewParent = GetAncestor(hNotepad, GA_PARENT);GetWindow(hNotepad,GW_OWNER);printf("Notepad:[ 0x%I64X ]; GetAncestorParent:[ 0x%I64X ]; GetOwner:[ 0x%I64X ].\n", (unsigned long long)hNotepad, (unsigned long long)hNewParent,(unsigned long long)hOwner);hDesktopwnd = GetDesktopWindow();printf("Desktopwnd:[ 0x%I64X ], use GetDesktopWindow.\n",(unsigned long long)hDesktopwnd);if (hDesktopwnd){printf("SetParent use hDesktopwnd.\n");hNewParent = SetParent(hNotepad, hDesktopwnd);printf("LastParent:[ 0x%I64X ], retn by SetParent.\n",(unsigned long long)hNewParent);}hNewParent = GetAncestor(hNotepad, GA_PARENT);printf("Notepad:[ 0x%I64X ]; GetAncestorParent:[ 0x%I64X ]; GetOwner:[ 0x%I64X ].\n",(unsigned long long)hNotepad,(unsigned long long)hNewParent,(unsigned long long)hOwner);// --------------------------------------printf("\n\nSetParent use (null) ptr.\n");hNewParent = SetParent(hNotepad, NULL);printf("LastParent:[ 0x%I64X ], retn by SetParent.\n",(unsigned long long)hNewParent);hNewParent = GetAncestor(hNotepad, GA_PARENT);printf("Notepad:[ 0x%I64X ]; GetAncestorParent:[ 0x%I64X ]; GetOwner:[ 0x%I64X ].\n",(unsigned long long)hNotepad,(unsigned long long)hNewParent,(unsigned long long)hOwner);}system("pause");return 0;
}

我们首先尝试使用 FindWindow 查找类名,但是以失败告终,我们获得了无效句柄,这可能和FindWindow 的机制有关(没搞清楚原因,只知道他是 NtUserFindWindowEx 的封装。据我推断,它只从第一个顶级窗口开始检索,而且没有找到 GetLastError 并不能取到非零值)。

随后我们调用 SetParent 尝试设置 Notepad 的父窗口,这里我们进行了横向对比,第一次,我们使用 GetDesktopWindow 函数获取桌面窗口句柄,并把它作为第二参数传入 SetParent,通过分析父窗口和返回值,我们得到和 Spy++ 相同的结论(句柄指向 #32769 窗口);

第二次,我们按照 MSDN 上的说明,把第二个参数设置为 NULL,并再次获取信息,发现效果等同于传入 #32769 的有效句柄,这说明 SetParent 确实会在内部将 NULL 参数解释为桌面窗口( #32769 )的句柄。

下图展示了对 Notepad 窗口进行设置父窗口的操作前后,其父窗口的变化:

(关于 SetParent 的注意事项,在我之前的一篇博客中有详细分析,就不展开讨论了)

SetParentNULL 传参其实有两个作用:

(1)设置窗口成为桌面顶级窗口;

(2)将窗口提升 Z 序至前端(替代 SetForegroundWindow ),甚至解决了 SetForegroundWindow 有时候失败的问题。

关于第二个相当于副产品,解决 SetForegroundWindow 失败网上给的代码一般是这样子的:

if(hWnd)
{HWND hForeWnd = GetForegroundWindow();DWORD dwForeID = GetWindowThreadProcessId(hForeWnd,NULL);DWORD dwCurID = GetCurrentThreadId();AttachThreadInput(dwCurID,dwForeID,TRUE);ShowWindow(hWnd,SW_SHOWNORMAL);SetWindowPos(hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);SetWindowPos(hWnd,HWND_NOTOPMOST,0,0,0,0, SWP_NOSIZE|SWP_NOMOVE);SetForegroundWindow(hWnd);AttachThreadInput(dwCurID,dwForeID,FALSE);// hWnd 就是需要置前的窗口句柄
}

而我们只需要判断这个窗口是不是 POPUP 窗口,并 SetParent 传参 NULL 即可。

三、编写代码以供在 Vista 上实现

Vista 上,DWM 被首次引入操作系统,但是它的框架结构和现在的有很大的不同,比如它不能够响应 0x052C (WM_USER + 300)的消息,而创建 WorkerW 窗口。这就是为什么在第一篇章中,我们直言在 Vista 上即使有开启 DWM 也不能够通过窗口嵌入的方式实现动态壁纸。

那么,如果我们固执的想要在早期的系统环境下实现动态壁纸,我们该如何做呢?

我在之前研究过自己实现一个 WorkerW,那篇博客限于一些原因,一些实现细节没能公布。这里我们可以说,即使不使用 WorkerW 依然可以实现动态壁纸。

我们想到将壁纸主窗口设置为 Progman 的子窗口,但是 SetParent 函数有个坏毛病,它会自动“擦屁股”,自动调用 CZOrderManagerService 内部函数将我们的窗口 Z 序放在 SHELLDLL_DefView 的前面,这是一个非常糟糕的。因为我们的窗口将完全遮盖 SHELLDLL_DefView 窗口,这使得我们无法看到图标列表窗口,我们的窗口始终位于上方。怎么办呢?

别急,这里有几种方法解决问题:

3.1 方法二:子类化并自绘窗口背景

(TODO:后期补充)

四、初步分析桌面管理层窗口创建的原理

由于对桌面管理层窗口的逆向分析没有找到实质性的材料,而作者本人又是初学一些反汇编知识,如有分析错误的地方,还望指拨。

4.1 桌面管理层窗口的创建流程

首先,我们需要回顾一下桌面管理层窗口的组成:

桌面浏览器窗口( DesktopBrowser )主要包括 Progman 父窗口,和 DefView 窗口,DefView 窗口的子窗口 SysListView32 用于绘制桌面图标等相关组件。而 Progman 的背景则绘制为桌面壁纸。

打开 IDA Pro 并反汇编 explorer.exe 可以定位到入口函数 wWinMain,可以看到 wWinMain 调用了 CreateDesktopAndTray 函数,这个函数是对 SHCreateDesktop 的封装,用于创建桌面和 CTray 的相关成员。

F5 的信息可以看出函数调用了延迟加载Shell32.dll 中的 SHCreateDesktop 函数。

跟进 Shell32.dll 查看该函数的内部实现:

有三个函数调用是关键性的:

(1)CDesktopBrowser::CDesktopBrowser 初始化 DesktopBrowserCDesktopBrowser 内部类实现了很多函数,包括图标窗口、任务栏控件、虚拟多桌面等等;

(2)RegisterDesktopClass 是对 RegisterClassW 的封装;

(3)SHFusionCreateWindowEx 是对 CreateWindowExW 的封装。

首先看 SHFusionCreateWindowEx 函数,前面谈到初始化 DesktopBrowser 的过程似乎在主窗口创建之前,然而分析上下文却能发现这两个实际上是并行操作。在 SHFusionCreateWindowEx 内首先激活并发上下文,然后尝试创建主窗口,同时初始化 DesktopBrowser 最后结束并发上下文,并返回窗口句柄。

然后,我们看一下 RegisterDesktopClass 函数,这个就比较简单了:

最重要的是 CDesktopBrowser 这个类,里面包含了有关桌面管理层窗口的很多未导出的内部函数。

RegisterDesktopClass 函数中调用的 CDesktopBrowser::s_DesktopWndProc 回调实现对 SysListView32 窗口的创建和处理。

调用树如下图所示:

SysListView32 窗口的创建和处理在 CreateDesktopView 中完成,流程比较复杂,暂不分析。

然后,继续跟踪,找到了 CDefView 类,一个关键的成员函数为 CDefView::CreateViewWindow

他是对 CDefView::CreateViewWindow2 的封装,CDefView::CreateViewWindow2 进行了一些对参数的初始化处理,随后把工作交给了 CDefView::CreateViewWindow3,在 CDefView::CreateViewWindow3 里面最终实现了创建 SHELLDLL_DefView 窗口。

4.2 从管理层窗口回调看 0x052C 消息

【这部分将在之后完善】


总结

自此,我们的桌面管理层窗口的创建已经基本完成,以上分析只是简单梳理一下流程,其中大量调用通过 COM 类接口实现,这里暂不展开分析。


本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/133801491

文章更新于:2023.10.20,2024.07.04。

文章发布于:2024.07.04。

相关文章:

实现桌面动态壁纸(二)

目录 前言 一、关于 WorkerW 工作区窗口 二、关于窗口关系 2.1 窗口以及窗口隶属关系 2.2 桌面管理层窗口组分简析 2.3 厘清两个概念的区别 2.4 关于设置父窗口 三、编写代码以供在 Vista 上实现 3.1 方法二&#xff1a;子类化并自绘窗口背景 四、初步分析桌面管理层…...

JavaEE——计算机工作原理

冯诺依曼体系&#xff08;VonNeumannArchitecture&#xff09; 现代计算机&#xff0c;大多遵守冯诺依曼体系结构 CPU中央处理器&#xff1a;进行算术运算与逻辑判断 存储器&#xff1a;分为外存和内存&#xff0c;用于存储数据&#xff08;使用二进制存储&#xff09; 输入…...

并发、多线程和HTTP连接之间有什么关系?

一、并发的概念 并发是系统同时处理多个任务或事件的能力。在计算中&#xff0c;这意味着系统能够在同一时间段内处理多个任务&#xff0c;而不是严格按照顺序一个接一个地执行它们。并发提高了系统的效率和资源利用率&#xff0c;从而更好地满足用户的需求。在现代应用程序中&…...

展开说说:Android服务之startService源码解析

通过上一篇文章我们掌握了Android四种的基本使用&#xff0c;本篇从源码层面总结一下startService的执行过程。 本文依然按着是什么&#xff1f;有什么&#xff1f;怎么用&#xff1f;啥原理&#xff1f;的步骤来分析。 1、是什么 上一篇总结了“Service是Android系统中的四…...

Java + MySQL 实现存储完整 Json

Java MySQL 实现存储完整 Json 一、应用场景二、数据库配置三、后端代码配置1、maven 依赖2、实体类3、Service 实现类4、xml 文件 四、测试1、新增接口2、查询接口3、数据表内容 一、应用场景 将前端传过来的 Json 完整存储到 MySQL 中&#xff0c;涉及技术栈为 Java、MyBat…...

解决刚申请下来的AWS EC2,无法用finalshell连接的问题

在AWS的命令页面创建一个root用户 切换到root 模式,输入密码 su root 不知道密码的可以使用一下命令来设置root用户的密码&#xff1a; su passwd root 再切换到root用户 su 修改配置文件 输入 vim /etc/ssh/sshd_config进入文件&#xff0c;键入’i’ &#xff0c;进行…...

如何在PD虚拟机中开启系统的嵌套虚拟化功能?pd虚拟机怎么用 Parallels Desktop 19 for Mac

PD虚拟机是一款可以在Mac电脑中运行Windows系统的应用软件。使用 Parallels Desktop for Mac 体验 macOS 和 Windows 的最优性能&#xff0c;解锁强大性能和无缝交互。 在ParallelsDesktop&#xff08;PD虚拟机&#xff09;中如何开启系统的嵌套虚拟化功能&#xff1f;下面我们…...

vue中实现button按钮的重复点击指令

// 注册一个全局自定义指令 v-debounce Vue.directive(debounce, {// 当被绑定的元素插入到 DOM 中时...inserted: function (el, binding) {let timer;el.addEventListener(click, () > {clearTimeout(timer);timer setTimeout(() > {binding.value(); // 调用传给指令…...

智能与伦理:Kimi与学术道德的和谐共舞

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 Kimi&#xff0c;由月之暗面科技有限公司开发的智能助手&#xff0c;擅长中英文对话&#xff0c;能处理多种文档和网页内容。在论文写作中&#xff0c;Kimi可提供资料查询、信息整理、语…...

mac有什么解压软件 mac怎么解压7z软件 苹果电脑好用的压缩软件有哪些

众所周知&#xff0c;macOS集成解压缩软件归档实用工具&#xff0c;可直接解压zip格式的压缩包。但对于其他比较常见的格式&#xff0c;诸如RAR、7z、TAR等&#xff0c;则无能为力&#xff0c;不过&#xff0c;我们可以选择大量第三方解压缩软件&#xff0c;帮助我们更好地完成…...

C语言_练习题

求最小公倍数 思路&#xff1a;假设两个数&#xff0c;5和7&#xff0c;那么最小至少也要7吧&#xff0c;所以先假定最小公倍数是两个数之间较大的&#xff0c;然后看7能不能同时整除5和7&#xff0c;不能就加1继续除 int GetLCM(int _num1, int _num2) {int max _num1>_n…...

Type-C接口快充取电技术的实现

Type-C接口快充取电技术的实现 Type-C接口快充取电技术主要通过USB PD&#xff08;Power Delivery&#xff09;协议实现。这种技术利用了Type-C接口的物理特性和PD协议的智能性&#xff0c;实现了高效、安全、快速的充电过程。具体实现过程如下&#xff1a; 接口连接与检测&a…...

压测工具---Ultron

压测工具&#xff1a;Ultron 类型&#xff1a;接口级和全链路 接口级 对于接口级别的压测我们可以进行 http接口压测、thrift压测、redis压测、kafka压测、DDMQ压测、MySQL压测等&#xff0c;选对对应的业务线、选择好压测执行的时间和轮数就可以执行压测操作了 全链路 对…...

Kubernetes 负载均衡器解决方案 MetalLB实践

Kubernetes 负载均衡解决方案 MetalLB实践 MetalLB 是一个用于在 Kubernetes 集群中提供外部 IP 地址的负载均衡器实现。 准备工作 1. 安装需知 安装 MetalLB 有一些前置条件&#xff1a; Kubernetesv1.13.0 或者更新的版本 集群中的 CNI 要能兼容 MetalLB&#xff0c;最新…...

力扣爆刷第159天之TOP100五连刷61-65(翻转单词、对称二叉树、遍历求和)

力扣爆刷第159天之TOP100五连刷61-65&#xff08;翻转单词、对称二叉树、遍历求和&#xff09; 文章目录 力扣爆刷第159天之TOP100五连刷61-65&#xff08;翻转单词、对称二叉树、遍历求和&#xff09;一、151. 反转字符串中的单词二、129. 求根节点到叶节点数字之和三、104. 二…...

(七)[重制]C++命名空间与标准模板库(STL)

​ 引言 在专栏C教程的第六篇C中的结构体与联合体中&#xff0c;介绍了C中的结构体和联合体&#xff0c;包括它们的定义、初始化、内存布局和对齐&#xff0c;以及作为函数参数和返回值的应用。在专栏C教程的第七篇中&#xff0c;我们将深入了解C中的命名空间&#xff08;nam…...

Elasticsearch:Runtime fields - 运行时字段(一)

目录 使用运行时字段带来的好处 激励 折衷 映射运行时字段 定义运行时字段而不使用脚本 忽略运行时字段上的脚本错误 更新和删除运行时字段 在搜索请求中定义运行时字段 创建使用其他运行时字段的运行时字段 运行时字段&#xff08;runtime fields&#xff09;是在查询…...

03:C语言运算符

C语言运算符 1、常见运算符2、赋值运算符3、判断运算符4、与- - 1、常见运算符 数学运算符号。常见数学运算符号&#xff0c;跟数学中理解相同 加号 - 减号 * 乘号 / 除号&#xff0c;相除以后的商 % 取余符号&#xff0c;相除以后余数是几 ()括号括起来优先级最高&#xff0…...

JAVA每日作业day7.4

ok了家人们今天学习了Date类和simpleDateformat类&#xff0c;话不多说我们一起看看吧 一.Date类 类 java.util.Date 表示特定的瞬间 ( 日期和时间 ) &#xff0c;精确到毫秒。 1.2 Date类的构造方法 public Date(): 用来创建当前系统时间对应的日期对象。 public Date(long …...

WordPress网站违法关键词字过滤插件下载text-filter

插件下载地址&#xff1a;https://www.wpadmin.cn/2025.html 插件介绍 WordPress网站违法关键词字过滤插件text-filter由本站原创开发,支持中英文关键字自动替换成**号&#xff0c;可以通过自定义保存修改按钮增加“预设关键字”&#xff0c;也可以导入定义好的txt文本形式的关…...

ros1仿真导航机器人 navigation

仅为学习记录和一些自己的思考&#xff0c;不具有参考意义。 1navigation导航框架 2导航设置过程 &#xff08;1&#xff09;启动仿真环境 roslaunch why_simulation why_robocup.launch &#xff08;2&#xff09;启动move_base导航、amcl定位 roslaunch why_simulation nav…...

Python制作动态颜色变换:颜色渐变动效

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame颜色变换函数主循环 完整代码 引言 颜色渐变动画是一种视觉上非常吸引人的效果&#xff0c;常用于网页设计和图形应用中。在这篇博客中&#xff0c;我们将使用Python创建一个动态颜色变换的动画效果。通…...

Python 异步编程介绍与代码示例

Python 异步编程介绍与代码示例 一、异步编程概述 异步编程是一种编程范式&#xff0c;它旨在处理那些需要等待I/O操作完成或执行耗时任务的情况。在传统的同步编程中&#xff0c;代码会按照顺序逐行执行&#xff0c;直到遇到一个耗时操作&#xff0c;它会阻塞程序的执行直到…...

堆叠的作用

一、为什么要堆叠 传统的园区网络采用设备和链路冗余来保证高可靠性&#xff0c;但其链路利用率低、网络维护成本高&#xff0c;堆叠技术将多台交换机虚拟成一台交换机&#xff0c;达到简化网络部署和降低网络维护工作量的目的。 二、堆叠优势 1、提高可靠性 堆叠系统多台成…...

ubuntu 如何查看某一个网卡的ip地址

在Ubuntu中&#xff0c;你可以使用多种方法来查看某一个网卡的IP地址。以下是一些常用的方法&#xff1a; 使用ip命令&#xff1a; ip命令是现代Linux系统中用于显示和操作路由、网络设备、策略路由和隧道的工具。要查看所有网络接口的IP地址&#xff0c;你可以使用&#xff1a…...

跨界客户服务:拓展服务边界,创造更多价值

在当今这个日新月异的商业时代&#xff0c;跨界合作已不再是新鲜词汇&#xff0c;它如同一股强劲的东风&#xff0c;吹散了行业间的壁垒&#xff0c;为企业服务创新开辟了前所未有的广阔天地。特别是在客户服务领域&#xff0c;跨界合作正以前所未有的深度和广度&#xff0c;拓…...

linux驱动编程 - kfifo先进先出队列

简介&#xff1a; kfifo是Linux Kernel里面的一个 FIFO&#xff08;先进先出&#xff09;数据结构&#xff0c;它采用环形循环队列的数据结构来实现&#xff0c;提供一个无边界的字节流服务&#xff0c;并且使用并行无锁编程技术&#xff0c;即当它用于只有一个入队线程和一个出…...

JS 四舍五入使用整理

一、Number.toFixed() 把数字转换为字符串,结果的小数点后有指定位数的数字,重点返回的数据类型为字符串 toFixed() 方法将一个浮点数转换为指定小数位数的字符串表示,如果小数位数高于数字,则使用 0 来填充。 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。…...

上万组风电,光伏,用户负荷数据分享

上万组风电&#xff0c;光伏&#xff0c;用户负荷数据分享 可用于风光负荷预测等研究 获取链接&#x1f517; https://pan.baidu.com/s/1izpymx6R3Y8JsFdx42rL0A 提取码&#xff1a;381i 获取链接&#x1f517; https://pan.baidu.com/s/1izpymx6R3Y8JsFdx42rL0A 提取…...

在物联网快速发展的趋势下,Java 怎样优化对低功耗、资源受限的边缘设备的支持,保障物联网应用的稳定运行?

在物联网快速发展的趋势下&#xff0c;Java可以通过以下方式优化对低功耗、资源受限的边缘设备的支持&#xff0c;以保障物联网应用的稳定运行&#xff1a; 精简Java运行环境&#xff1a;针对边缘设备的资源限制&#xff0c;可以使用精简型的Java运行环境&#xff0c;避免不必要…...