日夜模式

日夜模式 公开 已发布

如何安装动作?

适用于
分类(旧)
Windows

小宇不爱吃豆腐 用户708815787418906 wordpure 3 人赞了这个动作

1 个动作单 收藏了此动作。

更多信息
分享时间 2020-09-07 15:04
最后更新 22天12小时前
修订版本 10
用户许可 -未设置-
Quicker版本 1.44.22
动作大小 9.3 KB

分享到

「全局深色浅色模式切换」

简介

更新C#代码实现,调用Windows API刷新任务栏(资源管理器)避免终端弹窗和任务栏不刷新或者资源管理器重启问题

这段 C# 代码定义了一个 `ToggleThemeAction` 类,用于在 Windows 10 或 11 系统上切换浅色和深色主题。它通过操作 Windows 注册表和调用 Windows API 来实现主题的切换和界面刷新。代码可能是一个自动化工具(Quicker,基于 `Quicker.Public` 命名空间和 `IStepContext` 接口)的一部分。以下是对代码的详细解释,分为几个关键部分:


---


### **1. 导入和依赖**

```csharp

using Microsoft.Win32;

using System;

using System.Diagnostics;

using System.Runtime.InteropServices;

using System.Threading;

using Quicker.Public;

```

- **`Microsoft.Win32`**:提供对 Windows 注册表的访问,用于读取和修改主题设置。

- **`System.Diagnostics`**:用于启动和管理进程(如 `explorer.exe` 和 `rundll32.exe`)。

- **`System.Runtime.InteropServices`**:支持与 Windows API 函数交互(如 `SendMessageTimeout` 和 `SHChangeNotify`)。

- **`System.Threading`**:用于通过 `Thread.Sleep` 暂停执行。

- **`Quicker.Public`**:一个自定义命名空间,可能是 Quicker 自动化工具的一部分,提供 `IStepContext` 接口以集成到工作流中。


---


### **2. Windows API 常量和导入**


#### **常量**

```csharp

private const int HWND_BROADCAST = 0xFFFF;

private const int WM_SETTINGCHANGE = 0x001A;

private const int SMTO_ABORTIFHUNG = 0x0002;

```

- **`HWND_BROADCAST`**:一个特殊的窗口句柄(`0xFFFF`),用于向所有顶级窗口广播消息。

- **`WM_SETTINGCHANGE`**:一个 Windows 消息(`0x001A`),用于通知应用程序系统设置已更改。

- **`SMTO_ABORTIFHUNG`**:用于 `SendMessageTimeout` 的标志,如果接收窗口无响应,则中止消息。


#### **导入的函数**

```csharp

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]

private static extern IntPtr SendMessageTimeout(

    IntPtr hWnd, uint Msg, IntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);


[DllImport("shell32.dll")]

private static extern void SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);

```

- **`SendMessageTimeout`**:向窗口发送消息(如 `WM_SETTINGCHANGE`),并在指定超时时间内等待响应,用于通知系统主题更改。

- **`SHChangeNotify`**:通知系统 shell 设置已更改,触发用户界面刷新。


---


### **3. 辅助方法**


#### **IsWindows10Or11**

```csharp

private static bool IsWindows10Or11()

{

    return Environment.OSVersion.Version.Major >= 10;

}

```

- **功能**:检查当前操作系统是否为 Windows 10 或 11。

- **实现**:通过 `Environment.OSVersion.Version.Major` 获取操作系统的主版本号。如果主版本号大于或等于 10,则返回 `true`。


---


#### **GetCurrentTheme**

```csharp

private static string GetCurrentTheme()

{

    try

    {

        using (var key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"))

        {

            if (key != null)

            {

                var value = key.GetValue("AppsUseLightTheme");

                return value != null && (int)value == 1 ? "light" : "dark";

            }

            return null;

        }

    }

    catch

    {

        return null;

    }

}

```

- **功能**:获取当前系统主题(浅色或深色)。

- **实现**:

  - 打开注册表路径 `HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize`。

  - 读取 `AppsUseLightTheme` 键的值:

    - 如果值为 `1`,返回 `"light"`(浅色主题)。

    - 如果值为 `0`,返回 `"dark"`(深色主题)。

  - 如果键不存在或发生错误,返回 `null`。


---


#### **SetTheme**

```csharp

private static bool SetTheme(string theme)

{

    try

    {

        int value = theme == "dark" ? 0 : 1;

        using (var key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"))

        {

            if (key != null)

            {

                key.SetValue("AppsUseLightTheme", value, RegistryValueKind.DWord);

                key.SetValue("SystemUsesLightTheme", value, RegistryValueKind.DWord);

                key.SetValue("EnableTransparency", value, RegistryValueKind.DWord);

            }

        }

        return true;

    }

    catch

    {

        return false;

    }

}

```

- **功能**:设置系统主题为指定值(`"light"` 或 `"dark"`)。

- **实现**:

  - 根据输入的 `theme` 参数,将 `value` 设置为:

    - `"dark"`:`value = 0`。

    - `"light"`:`value = 1`。

  - 在注册表路径 `HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize` 中创建或打开子键。

  - 设置以下三个注册表键的值:

    - `AppsUseLightTheme`:控制应用程序的主题。

    - `SystemUsesLightTheme`:控制系统界面的主题。

    - `EnableTransparency`:控制窗口透明效果。

  - 如果操作成功,返回 `true`;否则返回 `false`。


---


#### **RefreshExplorerMethod1**

```csharp

private static bool RefreshExplorerMethod1()

{

    try

    {

        IntPtr result;

        bool success = SendMessageTimeout(

            (IntPtr)HWND_BROADCAST,

            WM_SETTINGCHANGE,

            IntPtr.Zero,

            "ImmersiveColorSet",

            SMTO_ABORTIFHUNG,

            5000,

            out result) != IntPtr.Zero;

        return success;

    }

    catch

    {

        return false;

    }

}

```

- **功能**:通过 `SendMessageTimeout` 广播主题更改通知,刷新系统界面。

- **实现**:

  - 使用 `SendMessageTimeout` 向所有窗口(`HWND_BROADCAST`)发送 `WM_SETTINGCHANGE` 消息,参数为 `"ImmersiveColorSet"`。

  - 设置 5 秒超时,并使用 `SMTO_ABORTIFHUNG` 标志以避免挂起。

  - 如果消息发送成功,返回 `true`;否则返回 `false`。


---


#### **RefreshExplorerMethod2**

```csharp

private static bool RefreshExplorerMethod2()

{

    try

    {

        var process = new Process

        {

            StartInfo = new ProcessStartInfo

            {

                FileName = "rundll32.exe",

                Arguments = "user32.dll,UpdatePerUserSystemParameters",

                UseShellExecute = false,

                RedirectStandardOutput = true,

                CreateNoWindow = true

            }

        };

        process.Start();

        process.WaitForExit();

        return true;

    }

    catch

    {

        return false;

    }

}

```

- **功能**:通过运行 `rundll32.exe` 刷新用户界面。

- **实现**:

  - 执行命令 `rundll32.exe user32.dll,UpdatePerUserSystemParameters`,通知系统更新用户参数。

  - 使用 `Process` 类启动进程,隐藏窗口并重定向输出。

  - 等待进程完成,如果成功,返回 `true`;否则返回 `false`。


---


#### **RefreshExplorerMethod3**

```csharp

private static bool RefreshExplorerMethod3()

{

    try

    {

        SHChangeNotify(0x8000000, 0, IntPtr.Zero, IntPtr.Zero);

        return true;

    }

    catch

    {

        return false;

    }

}

```

- **功能**:通过 `SHChangeNotify` 通知系统刷新界面。

- **实现**:

  - 调用 `SHChangeNotify` 函数,参数 `0x8000000` 表示通知 shell 更新。

  - 如果调用成功,返回 `true`;否则返回 `false`。


---


#### **RefreshExplorerMethod4**

```csharp

private static bool RefreshExplorerMethod4()

{

    try

    {

        var process = new Process

        {

            StartInfo = new ProcessStartInfo

            {

                FileName = "taskkill",

                Arguments = "/F /IM explorer.exe",

                UseShellExecute = false,

                RedirectStandardOutput = true,

                CreateNoWindow = true

            }

        };

        process.Start();

        process.WaitForExit();

        Thread.Sleep(1000);

        Process.Start("explorer.exe");

        Thread.Sleep(2000);

        return true;

    }

    catch

    {

        try

        {

            Process.Start("explorer.exe");

        }

        catch { }

        return false;

    }

}

```

- **功能**:通过终止并重启 `explorer.exe` 进程强制刷新界面。

- **实现**:

  - 使用 `taskkill /F /IM explorer.exe` 命令强制终止 Windows 资源管理器进程。

  - 等待 1 秒后重新启动 `explorer.exe`。

  - 再等待 2 秒以确保进程启动完成。

  - 如果发生错误,尝试重新启动 `explorer.exe`,并返回 `false`。


---


#### **RefreshExplorer**

```csharp

private static bool RefreshExplorer()

{

    var methods = new (string Name, Func<bool> Method)[]

    {

        ("Windows API", RefreshExplorerMethod1),

        ("rundll32", RefreshExplorerMethod2),

        ("SHChangeNotify", RefreshExplorerMethod3),

        ("Restart Explorer", RefreshExplorerMethod4)

    };


    foreach (var (name, method) in methods)

    {

        if (method())

        {

            return true;

        }

    }

    return false;

}

```

- **功能**:尝试所有刷新方法,直到一个方法成功或全部失败。

- **实现**:

  - 定义一个包含四种刷新方法的数组,每项包括方法名称和对应的函数。

  - 依次调用每个方法,如果某个方法返回 `true`,则停止并返回 `true`。

  - 如果所有方法都失败,返回 `false`。


---


### **4. 主执行方法 (Exec)**

```csharp

public static object Exec(IStepContext context)

{

    try

    {

        if (!IsWindows10Or11())

        {

            context.SetVarValue("Output", "此动作仅支持Windows 10/11系统");

            return false;

        }


        string currentTheme = GetCurrentTheme();

        if (currentTheme == null)

        {

            context.SetVarValue("Output", "无法获取当前主题设置");

            return false;

        }


        string newTheme = currentTheme == "light" ? "dark" : "light";


        if (SetTheme(newTheme))

        {

            bool refreshSuccess = RefreshExplorer();

            string message = refreshSuccess

                ? $"成功切换到{(newTheme == "dark" ? "深色" : "浅色")}模式并刷新界面"

                : "主题切换成功,但界面刷新失败。可能需要手动刷新或重启资源管理器";

            context.SetVarValue("Output", message);

            return true;

        }

        else

        {

            context.SetVarValue("Output", "切换主题失败");

            return false;

        }

    }

    catch (Exception ex)

    {

        context.SetVarValue("Output", $"发生错误: {ex.Message}");

        throw; // 抛出异常以触发“失败后停止”

    }

}

```

- **功能**:Quicker 工作流的主入口方法,用于切换主题并刷新界面。

- **实现**:

  1. **检查操作系统**:调用 `IsWindows10Or11`,如果不是 Windows 10/11,设置错误消息并返回 `false`。

  2. **获取当前主题**:调用 `GetCurrentTheme`,如果失败,设置错误消息并返回 `false`。

  3. **确定新主题**:如果当前是浅色主题(`"light"`),则切换到深色(`"dark"`),反之亦然。

  4. **设置主题**:调用 `SetTheme(newTheme)` 更改注册表中的主题设置。

  5. **刷新界面**:调用 `RefreshExplorer` 尝试刷新系统界面。

  6. **返回结果**:

     - 如果主题切换和刷新都成功,设置成功消息并返回 `true`。

     - 如果主题切换成功但刷新失败,设置部分成功消息并返回 `true`。

     - 如果主题切换失败,设置失败消息并返回 `false`。

  7. **异常处理**:捕获任何异常,设置错误消息并抛出异常以触发 Quicker 的“失败后停止”机制。


---


### **5. 总体功能总结**

- **主要目标**:在 Windows 10/11 系统上实现浅色和深色主题的切换,并确保界面更新以反映新主题。

- **工作流程**:

  1. 检查系统版本是否支持。

  2. 获取当前主题(浅色或深色)。

  3. 切换到相反的主题(通过修改注册表)。

  4. 使用多种方法尝试刷新系统界面(包括 API 调用、命令行和重启资源管理器)。

  5. 通过 `IStepContext` 返回操作结果(成功/失败消息)。

- **健壮性设计**:

  - 使用 try-catch 块处理潜在错误。

  - 提供多种刷新方法,确保至少有一种方法能成功更新界面。

  - 返回详细的错误或成功消息,方便用户调试或确认操作结果。


---


### **6. 使用场景**

- 这段代码是为 Quicker 自动化工具设计的,允许用户通过一个动作快速切换 Windows 的主题。

- 适用于需要频繁切换浅色/深色主题的用户(如开发人员、设计师或夜间工作者)。

- 代码通过多种刷新方法确保主题切换后界面立即更新,避免用户手动重启资源管理器。


---


### **7. 注意事项**

- **权限要求**:修改注册表和终止/启动 `explorer.exe` 需要管理员权限,否则可能失败。

- **刷新方法可靠性**:重启资源管理器(`RefreshExplorerMethod4`)可能导致用户界面短暂中断,应作为最后手段。

- **Quicker 依赖**:代码依赖 `IStepContext` 接口,需在 Quicker 环境中运行。

- **错误处理**:代码捕获异常并返回用户友好的消息,但仍抛出异常以支持 Quicker 的错误处理机制。


如果您有进一步的问题或需要对某部分代码进行更深入的分析,请告诉我!

最近更新

修订版本 更新时间 更新说明
10 22天12小时前 问题分析
原来的C#实现使用的是 SendMessageTimeout 方法,这是一种同步调用,会等待所有窗口处理完消息才会返回。当系统负载较重时,这个过程会变得很慢,导致用户感觉界面刷新有延迟。

解决方案
我添加了一个新的刷新方法 RefreshExplorerMethod1Fast(),它使用 PostMessage API 替代 SendMessageTimeout。这种方法的特点是:

非阻塞调用:PostMessage 将消息放入目标窗口的消息队列后立即返回,不会等待消息被处理
立即执行:无论系统负载如何,调用都会立即完成
系统范围通知:仍然能够有效地通知所有顶层窗口主题已更改
具体修改内容
添加了 PostMessage API 的导入声明:

csharp
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool PostMessage(
IntPtr hWnd,
uint Msg,
IntPtr wParam,
IntPtr lParam);
实现了新的快速刷新方法 RefreshExplorerMethod1Fast():

使用 PostMessage 发送 WM_SETTINGCHANGE 消息
正确处理了字符串参数的内存分配和释放
这是一种非阻塞调用,不会等待其他程序处理完毕
更新了 RefreshExplorer() 方法:

在刷新方法列表中增加了新的快速方法
保持了原有的容错机制,如果快速方法失败还会尝试其他方法
现在,当系统繁忙时,程序会首先尝试原有的可靠但可能较慢的方法,如果耗时过长,则会使用新的快速刷新方式,从而确保在各种情况下都能快速响应用户的操作。

这个修改无需您进行任何额外操作,已经直接应用到了您的代码中。
9 2025-09-30 12:18 更新使用C#代码,避免PS1弹窗问题,问题,速度不如设置切换快,暂时没有找到设置内的切换方法
8 2025-09-30 11:05 最近接触了Windows API,修改脚本使用Windows API刷新资源管理器

最近讨论

功能建议 · 78
十二猫主 22天6小时前 七七7777 22天3小时前
动作库优化 · 119
开源AI 2025-07-18 07:00 七七7777 2025-09-30 12:54
使用问题 · 199
wenbocn 2025-02-09 21:48 七七7777 2025-09-30 12:55
随便聊聊 · 503
开源AI 2024-04-26 15:18 七七7777 2025-09-30 12:55
使用问题 · 385
LIJAINGJAY 2024-04-09 21:24 七七7777 2025-09-30 12:55