Chrome 学习小记5——demo:(动态壁纸基础)

Chrome 学习小记5------demo:(动态壁纸基础)

​ 欸!我们有一个很自然的想法,如果我们将创建的窗口,挂载到咱们的Windows里的桌面,这不就是Windows的动态壁纸桌面吗?非常好。我们就可以进一步的对我们的程序做一个改造。

​ 为了挂载到桌面,我们的第一步就是找出来WorkerW,也就是我们这篇博客的主角。

啥是WorkerW呢

简单来说,WorkerW 就像是 桌面上的透明舞台 ,给程序提供一个 "桌面级别的画布",而不会破坏桌面图标和现有窗口层次。我们的处理核心就是在这里。实际上是一个承载层。

WorkerW 的典型用途
用途 说明
动态壁纸 / 桌面动画 将自定义窗口嵌入 WorkerW,使动画或应用看起来就在桌面背景上。
屏保/Widget 显示 像一些天气插件、桌面时钟,可以通过 WorkerW 显示在桌面图标之上。
桌面交互隔离 桌面图标仍然可用,而 WorkerW 上的窗口不会挡住图标,形成视觉分层。
系统主题或特效 Windows 自带的一些动画效果(如 Vista/7 的动态背景)也是通过 WorkerW 实现。
这些窗口的Z关系(从上到下)
  1. 普通应用窗口
  2. WorkerW(可嵌入自定义界面)
  3. SHELLDLL_DefView(桌面图标)
  4. Progman(桌面管理顶层窗口)

重点:WorkerW 一般位于桌面图标前面或者后面,取决于消息触发顺序。通过它嵌入的窗口不会挡住桌面图标,但看起来就像是桌面的一部分。

把他创建出来,给我们后续的工作奠定基础。

Step 1. 找到桌面顶层窗口 Progman
cpp 复制代码
HWND progman = FindWindow(L"Progman", nullptr);
if (!progman) return nullptr;

​ Progman是咱们桌面体系的最顶层的Window,我们现在要找出来他,委托他派生一个WorkerW出来。

向 Progman 发送 0x052C 消息

​ 下面的三行话实际上第一行话就能正确的工作(对于Windows 10+),但是为了兼容不同版本的Windows,我们都写上,实际上,这里就是我们让 Explorer 创建 WorkerW 窗口。

cpp 复制代码
SendMessageTimeout(progman, 0x052C, 0, 0, SMTO_NORMAL, 1000, nullptr);
SendMessageTimeout(progman, 0x052C, 0xD, 0, SMTO_NORMAL, 1000, nullptr);
SendMessageTimeout(progman, 0x052C, 0xD, 1, SMTO_NORMAL, 1000, nullptr);

​ 这种SendMessageTimeout实际上是一种异步的操作,防止咱们的电脑死住。当然,笔者测试过,需要等待一定的事件,要不然可能会创建没有成功,函数就结束了。虽然最严肃的办法是采用一个带有超时机制的TimeOut循环。

复制代码
Sleep(100); // Delay to check the WorkerW

找出来WorkerW 窗口

cpp 复制代码
HWND desktopWnd = GetDesktopWindow();
HWND found = nullptr;
for (HWND w = nullptr; (w = FindWindowEx(desktopWnd, w, L"WorkerW", nullptr)) != nullptr;) {
    HWND defView = FindWindowEx(w, nullptr, L"SHELLDLL_DefView", nullptr);
    if (defView == nullptr) {
        found = w;
        break;
    }
}

​ 现在我们终于可以准备查找我们的Windows了,我们先去枚举所有 WorkerW 窗口。如果 WorkerW 不包含 SHELLDLL_DefView,说明它是空白 WorkerW,可安全用作嵌入自定义窗口。找到了这样安全的WorkerW后,咱们就返回。


fallback: 备选方案

cpp 复制代码
if (!found) {
    HWND defInProgman = FindWindowEx(progman, nullptr, L"SHELLDLL_DefView", nullptr);
    if (defInProgman) {
        return progman;
    }
}
  • 如果没有找到空 WorkerW,就退而求其次:用 Progman 本身作为父窗口。
  • 这种情况下,嵌入窗口可能会覆盖桌面图标。
cpp 复制代码
#include "Windows.h"
HWND GetWorkerW() {
  // 找 Progman
  HWND progman = FindWindow(L"Progman", nullptr);
  if (!progman) {
    return nullptr;
  }

  SendMessageTimeout(progman, 0x052C, 0, 0, SMTO_NORMAL, 1000, nullptr);
  SendMessageTimeout(progman, 0x052C, 0xD, 0, SMTO_NORMAL, 1000, nullptr);
  SendMessageTimeout(progman, 0x052C, 0xD, 1, SMTO_NORMAL, 1000, nullptr);

  // 等一小段时间让系统有机会创建 WorkerW
  Sleep(100); // 可以适当调节

  // HWND workerw = nullptr;

  HWND desktopWnd = GetDesktopWindow();

  // 枚举 WorkerW 窗口
  HWND found = nullptr;
  for (HWND w = nullptr;
       (w = FindWindowEx(desktopWnd, w, L"WorkerW", nullptr)) != nullptr;) {
    // 检查这个 WorkerW 是否 *不含* SHELLDLL_DefView 子窗口
    HWND defView = FindWindowEx(w, nullptr, L"SHELLDLL_DefView", nullptr);
    if (defView == nullptr) {
      // 这个可能是我们要的那个
      found = w;
      break;
    }
  }

  // 如果没找到,尝试在 Progman 下的子窗口里查
  if (!found) {
    HWND defInProgman = FindWindowEx(progman, nullptr, L"SHELLDLL_DefView", nullptr);
    if (defInProgman) {
      // Progman 下有 DefView,就用 progman 作为备选父窗口
      return progman;
    }
  }

  return found;
}

设置我们的具体样式

​ 下面我们就来装饰一下得到的WorkerW。就用咱们创建的Widget的原生Window。

cpp 复制代码
HWND hwnd = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
HWND workerw = GetWorkerW();

​ 下一步就是设置咱们的WorkerW为我们的父窗口。

复制代码
if (workerw && hwnd) {
    SetParent(hwnd, workerw);

扩展样式(Extended Style)

cpp 复制代码
LONG exStyles = GetWindowLong(hwnd, GWL_EXSTYLE);
exStyles |= WS_EX_NOACTIVATE | WS_EX_TRANSPARENT;
SetWindowLong(hwnd, GWL_EXSTYLE, exStyles);
  • WS_EX_NOACTIVATE:窗口不会激活,不抢焦点。
  • WS_EX_TRANSPARENT:鼠标点击穿透,不阻挡桌面交互。
  • 组合效果:你的窗口可以显示,但不影响用户正常点击桌面图标或其他窗口。

普通样式(Style)

cpp 复制代码
LONG styles = GetWindowLong(hwnd, GWL_STYLE);
styles &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME | WS_POPUPWINDOW);
styles |= WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, styles);
  • 去掉标题栏、边框、可调整大小等装饰。
  • 设置成 WS_POPUP 类型窗口,方便做全屏桌面显示。

设置大小和显示位置

cpp 复制代码
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, screen_w, screen_h,
             SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
  • HWND_BOTTOM:放在底部 z-order,确保桌面图标在上层可用。
  • SWP_NOACTIVATE:不激活窗口。
  • SWP_FRAMECHANGED:让窗口样式生效。
  • SWP_SHOWWINDOW:显示窗口。
  • screen_w, screen_h:全屏覆盖桌面大小。

现在我们的任务完成了!

(我的截图工具把咱们的)任务栏给隐藏了,正常而言是直接像是替换了我们的桌面一样。

demo代码合计

cpp 复制代码
#include <windows.h>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include "ui/aura/env.h"
#include "base/at_exit.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
#include "base/lazy_instance.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/test/test_discardable_memory_allocator.h"
#include "build/build_config.h"
#include "base/test/test_timeouts.h"
#include "mojo/core/embedder/embedder.h"
#include "ui/accessibility/platform/ax_platform_for_test.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ime/init/input_method_initializer.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"
#include "ui/base/win/scoped_ole_initializer.h"
#include "ui/compositor/test/test_context_factories.h"
#include "ui/display/screen.h"
#include "ui/gfx/font_util.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/views/background.h"
#include "ui/views/buildflags.h"
#include "ui/views/controls/label.h"
#include "ui/views/examples/example_base.h"
#include "ui/views/examples/examples_window.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/test/desktop_test_views_delegate.h"
#include "ui/views/view.h"
#include "ui/views/widget/any_widget_observer.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/examples/examples_color_mixer.h"
#if defined(USE_AURA)
#include "ui/wm/core/wm_state.h"
#endif

#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/views/widget/desktop_aura/desktop_screen.h"
#endif

// ------------------------
// 自定义 View
// ------------------------
class SimpleView : public views::View {
 public:
  SimpleView() {
    SetLayoutManager(std::make_unique<views::FillLayout>());
    auto* label = new views::Label(
      u"Hello, simple Views Widget!");
    auto fonts = label->GetDefaultFontList().GetFonts();
    if(!fonts.empty()) {
      gfx::Font default_font = fonts[0];
      gfx::Font large_font(default_font.GetFontName(), 20);
      label->SetFontList(gfx::FontList(large_font));
    }
    AddChildView(label);
    SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
  }
  ~SimpleView() override = default;
};

class SimpleDelegate : public views::WidgetDelegate {
 public:
  SimpleDelegate() = default;
  ~SimpleDelegate() override = default;

  views::View* GetContentsView() override {
    if (!contents_) {
      contents_ = new SimpleView();
    }
    return contents_;
  }

  std::u16string GetWindowTitle() const override {
    return u"Simple Demo";
  }

  void assignedClosure(base::OnceClosure on_close) {
    on_close_ = std::move(on_close);
  }

  void WindowClosing() override {
    // Call the run_loop's Quit method to end the message loop.
    std::cerr << "Window is Closing!" << std::endl;
    if (on_close_) {
      std::move(on_close_).Run();
    }
  }

 private:
  raw_ptr<views::View> contents_ = nullptr;
  base::OnceClosure on_close_;
};

base::LazyInstance<base::TestDiscardableMemoryAllocator>::DestructorAtExit
    g_discardable_memory_allocator = LAZY_INSTANCE_INITIALIZER;

bool g_initialized_once = false;

#include "Windows.h"
HWND GetWorkerW() {
  // 找 Progman
  HWND progman = FindWindow(L"Progman", nullptr);
  if (!progman) {
    return nullptr;
  }

  SendMessageTimeout(progman, 0x052C, 0, 0, SMTO_NORMAL, 1000, nullptr);
  SendMessageTimeout(progman, 0x052C, 0xD, 0, SMTO_NORMAL, 1000, nullptr);
  SendMessageTimeout(progman, 0x052C, 0xD, 1, SMTO_NORMAL, 1000, nullptr);

  // 等一小段时间让系统有机会创建 WorkerW
  Sleep(100); // 可以适当调节

  // HWND workerw = nullptr;

  HWND desktopWnd = GetDesktopWindow();

  // 枚举 WorkerW 窗口
  HWND found = nullptr;
  for (HWND w = nullptr;
       (w = FindWindowEx(desktopWnd, w, L"WorkerW", nullptr)) != nullptr;) {
    // 检查这个 WorkerW 是否 *不含* SHELLDLL_DefView 子窗口
    HWND defView = FindWindowEx(w, nullptr, L"SHELLDLL_DefView", nullptr);
    if (defView == nullptr) {
      // 这个可能是我们要的那个
      found = w;
      break;
    }
  }

  // 如果没找到,尝试在 Progman 下的子窗口里查
  if (!found) {
    HWND defInProgman = FindWindowEx(progman, nullptr, L"SHELLDLL_DefView", nullptr);
    if (defInProgman) {
      // Progman 下有 DefView,就用 progman 作为备选父窗口
      return progman;
    }
  }

  return found;
}


int main(int argc, char* argv[]) {
  base::CommandLine::Init(argc, argv);
  TestTimeouts::Initialize();
  base::AtExitManager at_exit;
  ui::ScopedOleInitializer ole_initializer;

  base::CommandLine* command_line =
    base::CommandLine::ForCurrentProcess();
  ui::AXPlatformForTest ax_platform;

  // Disabling Direct Composition works around the limitation that
  // InProcessContextFactory doesn't work with Direct Composition, causing the
  // window to not render. See http://crbug.com/936249.
  command_line->AppendSwitch(switches::kDisableDirectComposition);

  base::FeatureList::InitInstance(
      command_line->GetSwitchValueASCII(switches::kEnableFeatures),
      command_line->GetSwitchValueASCII(switches::kDisableFeatures));


  if (!g_initialized_once) {
    mojo::core::Init();

    gl::init::InitializeGLOneOff(
        /*gpu_preference=*/gl::GpuPreference::kDefault);

    base::i18n::InitializeICU();

    ui::RegisterPathProvider();

    base::DiscardableMemoryAllocator::SetInstance(
        g_discardable_memory_allocator.Pointer());

    gfx::InitializeFonts();

    g_initialized_once = true;
  }

  base::test::TaskEnvironment task_environment(
      base::test::TaskEnvironment::MainThreadType::UI);
  auto context_factories =
      std::make_unique<ui::TestContextFactories>(false,
                                                 /*output_to_window=*/true);
  ui::ResourceBundle::InitSharedInstanceWithLocale(
      "en-US", nullptr,
      ui::ResourceBundle::LoadResources::DO_NOT_LOAD_COMMON_RESOURCES);

  ui::ColorProviderManager::Get().AppendColorProviderInitializer(
      base::BindRepeating(&views::examples::AddExamplesColorMixers));

  std::unique_ptr<aura::Env> env = aura::Env::CreateInstance();
  aura::Env::GetInstance()->set_context_factory(
      context_factories->GetContextFactory());
  ui::InitializeInputMethodForTesting();
  views::DesktopTestViewsDelegate views_delegate;
  wm::WMState wm_state;

  std::unique_ptr<display::Screen> desktop_screen =
        views::CreateDesktopScreen();
  // ------------------------
  // 创建 Widget
  // ------------------------
  base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);

  auto* widget = new views::Widget();
  views::Widget::InitParams params(
      views::Widget::InitParams::CLIENT_OWNS_WIDGET,
      views::Widget::InitParams::TYPE_WINDOW);
  int screen_w = GetSystemMetrics(SM_CXSCREEN);
  int screen_h = GetSystemMetrics(SM_CYSCREEN);
  params.bounds = gfx::Rect(100, 100, screen_w, screen_h);
  auto* delegate = new SimpleDelegate();
  delegate->assignedClosure(run_loop.QuitClosure());
  params.delegate = delegate;


  widget->Init(std::move(params));

  HWND hwnd = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
  HWND workerw = GetWorkerW();
  if (workerw && hwnd) {
    // 把 widget 挂到 workerw 下
    SetParent(hwnd, workerw);

    // 扩展样式,使其不激活、不抢焦点、透明鼠标点击
    LONG exStyles = GetWindowLong(hwnd, GWL_EXSTYLE);
    exStyles |= WS_EX_NOACTIVATE | WS_EX_TRANSPARENT;
    SetWindowLong(hwnd, GWL_EXSTYLE, exStyles);

    // 去掉边框、标题等装饰
    LONG styles = GetWindowLong(hwnd, GWL_STYLE);
    styles &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME | WS_POPUPWINDOW);
    styles |= WS_POPUP;  // popup 类型

    SetWindowLong(hwnd, GWL_STYLE, styles);

    // 设置大小和显示位置
    SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, screen_w, screen_h,
                 SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
  }

  widget->Show();

  auto disable_timeout =
        std::make_unique<base::test::ScopedDisableRunLoopTimeout>();

  run_loop.Run();

  ui::ResourceBundle::CleanupSharedInstance();
  ui::ShutdownInputMethod();
  env.reset();


  return 0;
}
相关推荐
weixin_377634845 小时前
【大模型-金融】Trading-R1 多阶段课程学习
学习·金融
今天也好累7 小时前
贪心算法之会议安排问题
c++·笔记·学习·算法·贪心算法
无敌的大魔王7 小时前
学习Java遇到的一些问题
学习
北极糊的狐7 小时前
C 盘清理方法总结
windows·ios
love530love9 小时前
EPGF 架构下的 Python 环境变量设置建议——Anaconda 路径精简后暴露 python 及工具到环境变量的配置记录 [三]
开发语言·人工智能·windows·python·架构·conda·epgf 架构
创业之路&下一个五年10 小时前
高系分二十:微服务系统分析与设计
学习·微服务·总结
GoldenaArcher11 小时前
Postman 学习笔记 II:测试、断言与变量管理
笔记·学习·postman
小志开发12 小时前
SQL从入门到起飞:完整数据库操作练习
数据库·sql·学习·oracle·sqlserver·navicat
知识分享小能手12 小时前
React学习教程,从入门到精通,React Router 语法知识点及使用方法详解(28)
前端·javascript·学习·react.js·前端框架·vue·react