在最近的自我学习过程中,一个练习的小项目需要知道桌面什么时候会发生切换(P.S.不是 Win10,11 的虚拟桌面)。登陆系统时候的桌面和进入系统后的桌面不是一个桌面。现在,我们需要在发生桌面切换的时候响应(做一些动作),那么怎么实现呢?
最开始时候我考虑到可以循环取活动桌面的信息,然后判断是不是用户桌面。这样的话会比较low。然后,在观察系统事件时候,我不经意间发现了一个细节,桌面切换这件事,系统也在监听!Windows 操作系统中有很多用到进程间通信的地方,不同组件之间是通过相互协作来完成操作的。微软使用了一个命名事件对象来告诉其他进程什么时候会发生桌面切换。
什么是事件对象?
Windows 事件对象你可以把它简单地理解为一个全局的内核对象,在多线程同步处理中常常会用到。也可以用于一些进程间的同步操作。
怎么使用?
操作事件对象很简单,Windows 提供了一系列 API 供我们使用,如 CreateEvent、OpenEvent、SetEvent、ResetEvent 等。
进程可以拥有事件对象的句柄,一个线程可以设置事件的激活状态,另一个线程(进程)可以通过 WaitForSingleObject 或者 WaitForMultipleObjectEx 等待一个或多个事件的到达。这样,当一个线程 开始/完成 处理某件事情时候,可以告知其他线程(进程)事件已经(或者即将)发生。
WinSta0_DesktopSwitch
这个事件的状态告知我们何时发生了桌面切换,外部进程可以以同步方式(SYNCHRONIZE)获取该事件的访问句柄(仅通过 OpenEvent 就可以做到)。
hDesktopSwitchEvent = OpenEvent(SYNCHRONIZE, FALSE, TEXT("WinSta0_DesktopSwitch"));
if (!hDesktopSwitchEvent) {
LogToFile(L"Failed to open WinSta0_DesktopSwitch event.");
return false;
}
然后,就可以等待事件的到达了,当桌面由默认用户桌面(Winsta0\Default)切换到登陆桌面(Winsta0\Winlogon)时,或者由登陆桌面切换回用户桌面,这个事件都会被触发,并在触发后自动重置。

(dwWaitCount 最好用 unsigned long long)
这样,我们就可以订阅该系统事件来获知何时发生了桌面切换。那么怎么判断当前具体切换到的桌面呢?很简单,可以用下面的示例:
// 获取当前激活桌面名称
std::wstring GetCurrentActiveDesktopName() {
HDESK hDesktop = OpenInputDesktop(NULL, FALSE, GENERIC_READ);
if (hDesktop == nullptr) {
LogToFile(L"Failed to get the current input desktop.");
return L"";
}
DWORD dwSize = 255;
WCHAR szDesktopName[256] = { 0 };
if (GetUserObjectInformationW(hDesktop, UOI_NAME, szDesktopName, dwSize, &dwSize)) {
return szDesktopName;
}
return L"";
}
OpenInputDesktop 用于获取活动输入桌面的句柄(HDESK),然后对于用户对象,我们都可以使用 GetUserObjectInformation 函数来获取其包含的信息。从中就可以获取到桌面名称啦~是不是很简单呢?
补充说明
这样的事件其实还有很多,例如资源管理器的初始化事件:ShellDesktopSwitchEvent。这个事件在操作系统启动后,第一次初始化 explorer.exe 时被 winlogon.exe 创建和设置。并在后面运行过程中一直保持激活状态(此为 手动重置 类型的事件)。如果我们的程序需要等待桌面初始化完成(启动的较晚阶段)再进行处理,例如常见的系统美化、动态壁纸等程序,就需要这个事件来控制进程的初始化,这会比循环捕获桌面窗口或者进程的状态好得多。如果你需要重启资源管理器,并且需要知道啥时候启动完成,你可以在重启前重置事件状态(ResetEvent),那么会在后面初始化 explorer.exe 完成时再次设置状态,这可以帮助我们了解何时处理(控制流程)。
好,分享就到这里结束了,欢迎各位交流~
文章出处链接:Windows 中神秘的事件对象 - 哔哩哔哩。
作者:涟幽 Alex